Tracy 与 ROCm 下的按需(On-Demand)GPU 性能剖析复现指南:定位 rocprof 后端的三个致命缺陷 Tracy 与 ROCm 下的按需On-DemandGPU 性能剖析复现指南定位 rocprof 后端的三个致命缺陷【免费下载链接】tracyFrame profiler项目地址: https://gitcode.com/GitHub_Trending/tr/tracyTracy 是一款帧级frame性能剖析器其客户端支持通过宏标记 CPU/GPU 区域并通过TRACY_ON_DEMAND开启按需剖析模式——只有剖析器tracy-capture或 GUI真正连接到被测程序时客户端才开始采集数据。当这一模式与 ROCm 的 rocprofiler-sdk 后端TRACY_ROCPROF组合使用时未修补的 Tracy 会在剖析器迟到连接时崩溃。本文以仓库中的 on-demand 复现工程 为核心系统梳理三个根因缺陷、完整的复现与验证步骤并结合TracyRocprof.cpp、TracyProfiler.hpp等源码解释修复原理帮助读者在 AMD GPU ROCm 环境下正确构建、复现、验证并理解该问题的全貌。一、问题背景为什么按需模式会暴露 GPU 剖析缺陷1.1 三种宏的作用复现工程的核心是同时启用三个编译期宏见 CMakeLists.txtset(REPRO_DEFINES TRACY_ENABLE TRACY_ON_DEMAND TRACY_ROCPROF __HIP_PLATFORM_AMD__)TRACY_ENABLE开启 Tracy 客户端采集功能不加则所有宏为空操作TRACY_ON_DEMAND开启按需模式。客户端默认不连接、不采集剖析器连接后才启动数据发送TRACY_ROCPROF启用基于 rocprofiler-sdk 的 AMD GPU 后端负责捕获 kernel dispatch、GPU zone 与计数器__HIP_PLATFORM_AMD__HIP 平台宏供 ROCm 头文件使用。1.2 按需模式的时序矛盾在按需模式下TracyIsStarted在剖析器连接之后才被置位。rocprof 后端有一个专门的校准线程等待该标志void calibration_thread( void* ptr ) { while( !TracyIsStarted ) ; ToolData* data static_castToolData*( ptr ); >auto* item tracy::Profiler::QueueSerial(); tracy::MemWrite( item-hdr.type, tracy::QueueType::GpuNewContext ); ... tracy::MemWrite( item-gpuNewContext.type, tracy::GpuContextType::Rocprof ); #ifdef TRACY_ON_DEMAND GetProfiler().DeferItem( *item ); #endif tracy::Profiler::QueueSerialFinish();见 TracyRocprof.cpp。后果按需模式下剖析器晚连接客户端此前发出的GpuNewContext已经随着普通串行队列发送完毕无人接收又没有被放入延迟队列因此永远不会被重放给迟到连接的服务端。服务端收到针对未知上下文的GpuZoneBegin事件时会触发断言失败Assertion ctx failed in ProcessGpuZoneBeginImplCommon服务端处理 GPU 上下文的逻辑位于 TracyWorker.cppProcessGpuNewContext与ProcessGpuZoneBeginImplCommon上下文不存在时直接断言。缺陷 2GpuContextName未被延迟同一个函数还会写入 GPU 上下文名称rocprofv3常量定义于 TracyRocprof.cpp发送逻辑见 TracyRocprof.cppauto* item tracy::Profiler::QueueSerial(); tracy::MemWrite( item-hdr.type, tracy::QueueType::GpuContextName ); tracy::MemWrite( item-gpuContextNameFat.context, context_id ); tracy::MemWrite( item-gpuContextNameFat.ptr, uint64_t( cloned_name ) ); tracy::MemWrite( item-gpuContextNameFat.size, uint16_t( name_length ) ); #ifdef TRACY_ON_DEMAND GetProfiler().DeferItem( *item ); #endif tracy::Profiler::QueueSerialFinish();后果即使修复了缺陷 1晚连接的剖析器虽然能看到 GPU 上下文却拿不到名字上下文在剖析器中显示为 unnamed。注意该处的名称字符串必须用tracy_malloc克隆——Tracy 内部会无条件释放该内存且其分配器与 libc 不同必须同源分配。缺陷 3data-init守卫在初始化前丢掉了 kernel 符号tool_callback_tracing_callback()顶层曾以data-init作为所有回调的总开关。但data-init只有在校准线程分配 GPU 上下文之后才被置true见上文calibration_thread而kernel 符号注册事件CODE_OBJECT_DEVICE_KERNEL_SYMBOL_REGISTER发生在 HIP 初始化阶段、即data-init为 false 的窗口内因此被静默丢弃。即使绕过了崩溃剖析器中也会缺失 kernel 名称。修复后的代码将符号注册与init守卫解耦并区分 LOAD/UNLOAD 相位维护client_kernels表if( record.kind ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT record.operation ROCPROFILER_CODE_OBJECT_DEVICE_KERNEL_SYMBOL_REGISTER ) { ... if( record.phase ROCPROFILER_CALLBACK_PHASE_LOAD ) >cmake -B build -DCMAKE_BUILD_TYPERelease cmake --build build该 CMake 工程CMakeLists.txt会做三件事以TRACY_ENABLE TRACY_ON_DEMAND TRACY_ROCPROF __HIP_PLATFORM_AMD__编译 TracyClient.cpp即开启 rocprof 后端的 Tracy 客户端把 repro.cpp 作为 HIP 源码编译为repro可执行文件链接TracyClient与librocprofiler-sdk可选见下文在BUILD_CHECK_TOOLON时编译验证工具check_gpu_ctx_name。4.2 复现程序做了什么repro.cpp 是一个最小化 HIP 程序定义vectorAdd内核__global__执行c a b分配并初始化 1024 个 float 的输入数组拷贝到设备端循环 100 次每次用ZoneScopedN( iteration )标记 CPU zone、启动一次 kernel、hipDeviceSynchronize()同步、usleep( 100000 )休眠 100ms、FrameMark标记帧——总运行时长约 10 秒为tracy-capture的连接留出充足窗口回拷结果并校验打印Result: PASS或Result: FAIL以此作为程序退出码。4.3 触发崩溃在第一个终端启动被测程序第二个终端启动捕获器./build/repro tracy-capture -o repro.tracy -s 5./build/repro 后台运行复现程序程序会打印 waiting for profiler to connect...tracy-capture -o repro.tracy -s 5连接并按 5 秒采集输出到repro.tracy。在未修补的 Tracy 上捕获器会因Assertion ctx failed崩溃完全修补后捕获成功生成的repro.tracy包含约 50 个 GPU zone上下文名为rocprofv3。4.4 通过 ctest 回归复现器同时注册为 ctest 测试目标CMakeLists.txt可一键执行ctest --test-dir build -R repro这为 CI 或本地回归验证提供了标准入口。五、验证工具check_gpu_ctx_name为验证上下文名是否被正确延迟给晚连接客户端工程提供了一个独立验证工具check_gpu_ctx_name。它加载.tracy捕获文件并打印每个 GPU 上下文的名称返回码约定0所有上下文都有名字2发现未命名上下文1参数错误或文件打开/解析失败。5.1 构建需显式开启该工具链接 Tracy 服务端库TracyServer因此默认不构建仅在请求时编译cmake -B build -DBUILD_CHECK_TOOLON cmake --build build --target check_gpu_ctx_name从 CMakeLists.txt 可见它通过引入 cmake/vendor.cmake 与 cmake/server.cmake 以与tracy-capture相同的方式组装服务端及其供应商依赖并要求 C20。5.2 运行与预期输出./build/check_gpu_ctx_name repro.tracy修补后预期GPU context 0: rocprofv3未修补仅修复缺陷 1预期GPU context 0: (unnamed)其实现check_gpu_ctx_name.cpp通过tracy::FileRead::Open打开捕获文件构造tracy::WorkerEventType::None不做事件分析遍历worker.GetGpuData()用worker.GetString( ctx-name )解析上下文名字符串并区分名字未激活/名字为空串两种情况输出(unnamed)。六、修复原理延迟队列Deferred Queue机制缺陷 1 与缺陷 2 的修复方式完全一致在TRACY_ON_DEMAND下把关键上下文消息送入延迟队列。DeferItem的定义位于 TracyProfiler.hpp#ifdef TRACY_ON_DEMAND tracy_force_inline uint64_t ConnectionId() const { ... } tracy_force_inline void DeferItem( const QueueItem item ) { m_deferredLock.lock(); auto dst m_deferredQueue.push_next(); memcpy( dst, item, sizeof( item ) ); m_deferredLock.unlock(); } #endif其语义是把队列项复制进客户端的延迟队列当剖析器连接时客户端会把延迟队列中的消息按顺序重放给服务端。凡是晚连接客户端也需要知道的状态建立类消息都必须走DeferItem——这正是其他 GPU 后端如TracyVulkan.hpp、TracyOpenGL.hpp、TracyD3D11.hpp中DeferItem被普遍调用的原因可在 public/tracy 与 public/client 中检索到大量同类调用点。需要特别注意的是普通 zone 数据GpuZoneBegin/GpuZoneEnd/GpuTime不需要延迟——它们是时变的事件流晚连接的剖析器只关心连接之后的事件而上下文建立消息GpuNewContext、GpuContextName、GpuAnnotationName等是后续所有事件的前置条件必须保证重放。缺陷 1 的致命性就在于此GpuNewContext缺失导致服务端把后续 zone 事件映射到不存在的上下文从而触发断言。七、修复前后行为对照Tracy 版本结果未修补tracy-capture崩溃Assertion ctx failed仅修补缺陷 1GpuNewContext 延迟捕获成功但 GPU 上下文无名字缺陷 2 未修完全修补三处缺陷均修复捕获成功约 50 个 GPU zone上下文名为rocprofv3对照表明确说明只有三处缺陷全部修复按需模式下的 rocprof 剖析才同时具备不崩溃与信息完整两个属性。缺陷 1 解决崩溃缺陷 2 解决上下文命名缺陷 3 解决 kernel 名称缺失——三者缺一不可。八、源码指引与延伸阅读若希望深入阅读相关实现建议按以下路径浏览当前仓库复现工程本身README.md、repro.cpp、check_gpu_ctx_name.cpp、CMakeLists.txtrocprof 后端核心TracyRocprof.cppgpu_context_allocate、calibration_thread、tool_callback_tracing_callback、tool_init/tool_fini延迟队列机制TracyProfiler.hppDeferItem与m_deferredQueue服务端 GPU 上下文处理TracyWorker.cppProcessGpuNewContext、ProcessGpuZoneBeginImplCommon、ProcessGpuContextName服务端构建方式cmake/server.cmake、cmake/vendor.cmakecheck_gpu_ctx_name与tracy-capture共用。需要注意的是本文描述的复现与验证均基于当前仓库TRACY_ROCPROF后端已修复的实现若要在旧版本或本地修改版上复现崩溃需回到未调用DeferItem、以data-init门控全部回调的原始状态并确保被测程序在剖析器连接前完成 HIP 初始化才能稳定命中三个缺陷的时序窗口。【免费下载链接】tracyFrame profiler项目地址: https://gitcode.com/GitHub_Trending/tr/tracy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考