如何用 TRACY_PLATFORM_HEADER 和自定义平台头把 Tracy 移植到不受支持的平台?
【免费下载链接】tracyFrame profiler项目地址: https://gitcode.com/GitHub_Trending/tr/tracy
你的程序要跑在一个 Tracy 没有原生支持的目标上(官方手册中确认可用的平台只有 Windows、Linux、Android、FreeBSD、WSL、OSX、iOS、QNX,主机/游戏机平台不在其列),直接编译 Tracy 客户端时,部分平台相关的代码路径要么编译不过,要么行为不对。按手册的说法,此时不应去改 Tracy 源码里的#if平台分支,而是把 Tracy 指向一个你自己提供的平台头文件,用TRACY_PLATFORM_HEADER机制替换掉它依赖的几个底层原语。完成移植后,你的程序可以像其他平台一样把 trace 数据发给 Tracy Profiler。
这个机制能替换什么、不能替换什么
TRACY_PLATFORM_HEADER指向的头文件只用于TRACY_HAS_CUSTOM_*这一组钩子。手册明确列出当前暴露出来的可插拔类别只有四类:
| 钩子宏 | 对应函数 | 说明 |
|---|---|---|
TRACY_HAS_CUSTOM_THREAD_ID | tracy::PlatformGetThreadId() | 手册标注为Required(必需) |
TRACY_HAS_CUSTOM_USER_INFO | tracy::PlatformGetHostname()、tracy::PlatformGetUserLogin()、tracy::PlatformGetUserFullName() | 标识 trace 头里的机器与用户 |
TRACY_HAS_CUSTOM_SAFE_COPY | tracy::PlatformSafeMemcpy() | 采样时快照可能未映射的内存 |
TRACY_HAS_CUSTOM_ALLOCATOR | tracy::PlatformMalloc()/PlatformFree()/PlatformRealloc()/PlatformAllocatorInit()/PlatformAllocatorThreadInit()/PlatformAllocatorFinalize()/PlatformAllocatorThreadFinalize() | 替换 Tracy 内部分配器 |
其余平台相关子系统——调用栈采集、上下文切换捕获、崩溃处理、系统追踪等——不能通过平台头插桩。如果你的平台不支持它们,要在构建系统层面用对应的TRACY_NO_*宏关闭,例如TRACY_NO_CALLSTACK、TRACY_NO_SAMPLING、TRACY_NO_CONTEXT_SWITCH、TRACY_NO_SYSTEM_TRACING、TRACY_NO_CRASH_HANDLER。
第一步:复制模板文件到工程里
仓库提供了成对的模板文件:
- examples/CustomPlatform/CustomPlatform.h — 平台头模板
- examples/CustomPlatform/CustomPlatform.cpp — 各钩子函数的模板实现
把这两个文件复制进你的工程,重命名为你自己的平台头(手册示例里叫my_platform.h),然后只填你启用的那些钩子的函数体。模板里每个函数都带契约注释,几个关键约束:
PlatformGetThreadId():返回的是内核线程 ID。注释特别警告pthread_self()不可用——它返回的是库句柄而不是内核 ID。- 用户信息三个函数:如果平台没有对应概念,返回占位字符串(如
"(?)")即可。 PlatformSafeMemcpy():必须对不可读的输入不崩溃,而是返回false;直接memcpy()不是合法实现。- 分配器:
Malloc/Free/Realloc必须线程安全;ThreadInit只是可选的预热(prime),不是前置条件;Finalize还必须拆掉调用线程自己的 per-thread 状态——Tracy 不会在调用Finalize前为那个正在退出的线程调用ThreadFinalize。
模板 CustomPlatform.cpp 给出了一份可以直接链接的桩实现:线程 ID 返回 0、主机名/用户名返回"(?)"、PlatformSafeMemcpy直接返回false(让 Tracy 跳过快照,真实实现可用 Win32 的 SEH、POSIX 的pipe(2)或等价的探测复制原语)、分配器转发到 C 运行时的malloc/free/realloc。你的工作就是把需要的桩换成目标平台上的真实实现。
第二步:在头里启用钩子,并把实现链接进最终二进制
平台头里,把你需要的钩子对应的TRACY_HAS_CUSTOM_*宏定义打开(模板里默认都注释掉了),并声明对应的tracy::Platform*函数。函数实现必须放在一个单独的文件里,该文件链接进你的最终二进制。
头文件模板里的注释还强调了一条纪律:只放TRACY_HAS_CUSTOM_*钩子和匹配的函数声明,不要在这个头里设置TRACY_ENABLE、TRACY_ON_DEMAND这类无关的TRACY_*选项——其中一些选项在Tracy.hpp包含平台头之前就会被检查,放这里会导致不同编译单元看到不一致的结果。这些开关一律在构建系统层面设置。
第三步:构建时指定 TRACY_PLATFORM_HEADER
把 Tracy 指向你的平台头,构建参数是:
-DTRACY_PLATFORM_HEADER="\"my_platform.h\""注意值里内嵌的引号是宏展开后再#include所需要的,原样保留。用 CMake 集成时,TRACY_PLATFORM_HEADER是 Build options 表 中列出的选项之一,与布尔开关不同,它取的是值(头文件路径)而不是 ON/OFF;启用 profiling 仍需显式设置TRACY_ENABLE(CMake 下默认 OFF)。如果你不用 CMake,则像手册 Initial client setup 一节描述的那样,自己把这些宏定义加进全局编译标志。
实现文件记得加进最终二进制的链接对象里,否则运行时会缺符号。
验证:宏一致性与功能检测结果
手册给出两条可以实际操作的核对手段:
链接期的宏失配检测。Tracy 会把关键构建选项编码进内部函数名,各编译单元宏定义不一致时会直接产生链接错误,例如(MSVC 示例):
error LNK2019: unresolved external symbol "int __cdecl GetProfiler_CFG_E1_OD1_DI0_ML0_F0_DHT0_TF0(void) ...对照 public/client/TracyMangle.hpp 中的缩写表和当前构建设置,就能定位是哪个宏在哪个编译单元设错了。
TRACING_VERBOSE功能检测输出。定义TRACING_VERBOSE(对应 CMake 选项TRACY_VERBOSE)后,客户端会把检测到的功能详情打到标准错误输出;把这些 debug 打印和源码对照,可以判断目标平台上哪些功能缺失、为什么缺失。客户端自身的诊断信息默认也会作为 Message log 发到服务端。
需要留意的一个手册点名的盲区:如果某处漏了TRACY_ENABLE而库是带它构建的,链接会成功但 Tracy 仍会启动并尝试连接 Profiler——这类失配链接期检测不到,属于多模块工程里需要靠构建纪律保证的事项。
限制
TRACY_HAS_CUSTOM_*四组钩子是当前机制暴露的全部可插拔面;手册原文说明其他子系统"cannot be plugged this way",只能用TRACY_NO_*关闭,不能试图用平台头去桩掉它们。- 平台头不是通用配置头:往里面塞
TRACY_ENABLE、TRACY_ON_DEMAND之类的选项会产生依赖编译单元的未定义行为,这类开关必须走构建系统。 - 移植完成后,目标平台能否完整出数据仍受 Tracy 通用限制约束(仅小端 CPU、48 位虚拟地址空间等,见手册 Limitations 一节);这些限制与平台头机制本身无关,但目标架构不满足时移植没有意义。
【免费下载链接】tracyFrame profiler项目地址: https://gitcode.com/GitHub_Trending/tr/tracy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考