Also defer GpuContextName for on-demand profiling

Without this, a late-connecting client receives the deferred
GpuNewContext but not the GpuContextName, so the GPU context appears
unnamed in the profiler.

Add check_gpu_ctx_name tool to verify context names in captured traces.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Basil Milanich
2026-04-16 09:05:34 -05:00
parent bc8d8f5302
commit ebd3d9c3e6
4 changed files with 100 additions and 8 deletions

View File

@@ -4,12 +4,13 @@ CXX := g++
ROCM_LIB := /opt/rocm/lib
TRACY_SRCS := $(TRACY_PUBLIC)/TracyClient.cpp
INCLUDES := -I$(TRACY_PUBLIC)
ROCM_INC := /opt/rocm/include
INCLUDES := -I$(TRACY_PUBLIC) -I$(ROCM_INC)
LIBS := -L$(ROCM_LIB) -lrocprofiler-sdk -lpthread -ldl
# On-demand mode (the default for this repro) — profiling starts when
# a client connects, not at program launch.
DEFINES := -DTRACY_ENABLE -DTRACY_ON_DEMAND -DTRACY_ROCPROF
DEFINES := -DTRACY_ENABLE -DTRACY_ON_DEMAND -DTRACY_ROCPROF -D__HIP_PLATFORM_AMD__
CXXFLAGS := -O2 $(DEFINES)
HIPCCFLAGS := -O2 $(DEFINES)
@@ -17,11 +18,14 @@ HIPCCFLAGS := -O2 $(DEFINES)
all: repro
repro: repro.cpp tracy_client.o
$(HIPCC) $(HIPCCFLAGS) $(INCLUDES) -o $@ $< tracy_client.o $(LIBS)
repro: repro.o tracy_client.o
$(HIPCC) -o $@ $^ $(LIBS)
repro.o: repro.cpp
$(HIPCC) $(HIPCCFLAGS) $(INCLUDES) -c -o $@ $<
tracy_client.o: $(TRACY_SRCS)
$(CXX) $(CXXFLAGS) $(INCLUDES) -c -o $@ $<
clean:
rm -f repro tracy_client.o
rm -f repro repro.o tracy_client.o

View File

@@ -5,7 +5,7 @@ HIP application built with `TRACY_ON_DEMAND` and `TRACY_ROCPROF`.
## Root cause
Two bugs in `TracyRocprof.cpp` break on-demand profiling:
Three bugs in `TracyRocprof.cpp` break on-demand profiling:
1. **GpuNewContext not deferred.** `gpu_context_allocate()` writes a
`GpuNewContext` queue item but does not call `DeferItem()`. When a
@@ -15,7 +15,12 @@ Two bugs in `TracyRocprof.cpp` break on-demand profiling:
Assertion `ctx' failed in ProcessGpuZoneBeginImplCommon
2. **Kernel symbols dropped before init.** The `data->init` guard at the
2. **GpuContextName not deferred.** Same function writes the context
name ("rocprofv3") without calling `DeferItem()`. Even after fixing
bug 1, a late-connecting client sees the GPU context but it appears
unnamed in the profiler. Use `check_gpu_ctx_name` to verify.
3. **Kernel symbols dropped before init.** The `data->init` guard at the
top of `tool_callback_tracing_callback()` blocks all callbacks before
the GPU context is allocated. Kernel symbol registrations
(`CODE_OBJECT_DEVICE_KERNEL_SYMBOL_REGISTER`) happen at HIP init
@@ -36,9 +41,24 @@ make
tracy-capture -o repro.tracy -s 5
```
## Verifying the context name
`check_gpu_ctx_name` loads a `.tracy` file and prints the GPU context
names. Build it against the Tracy server library (e.g. from a capture
build directory) and run:
```bash
./check_gpu_ctx_name repro.tracy
# Expected (patched): "GPU context 0: rocprofv3"
# Expected (unpatched): "GPU context 0: (unnamed)"
```
Exit codes: 0 = all contexts named, 2 = unnamed context found.
## What to expect
| Tracy version | Result |
|---|---|
| Unpatched | `tracy-capture` crashes: `Assertion 'ctx' failed` |
| Patched | Capture succeeds with ~50 GPU zones (`vectorAdd`) with kernel names |
| Patched (GpuNewContext only) | Capture succeeds but GPU context is unnamed |
| Fully patched | Capture succeeds with ~50 GPU zones and context named "rocprofv3" |

View File

@@ -0,0 +1,65 @@
// Loads a .tracy file and prints the GPU context names.
// Used to verify that on-demand profiling correctly defers the
// GpuContextName message so late-connecting clients see the name.
//
// Usage: ./check_gpu_ctx_name trace.tracy
// Expected output: "GPU context 0: rocprofv3"
// If name is missing: "GPU context 0: (unnamed)"
#include <cstdio>
#include <cstdlib>
#include "server/TracyFileRead.hpp"
#include "server/TracyWorker.hpp"
int main( int argc, char** argv )
{
if( argc != 2 )
{
fprintf( stderr, "Usage: %s <trace.tracy>\n", argv[0] );
return 1;
}
try
{
auto f = std::unique_ptr<tracy::FileRead>( tracy::FileRead::Open( argv[1] ) );
if( !f )
{
fprintf( stderr, "Cannot open %s\n", argv[1] );
return 1;
}
tracy::Worker worker( *f, tracy::EventType::None, false );
const auto& gpuData = worker.GetGpuData();
if( gpuData.empty() )
{
printf( "No GPU contexts found.\n" );
return 1;
}
bool all_named = true;
for( size_t i = 0; i < gpuData.size(); i++ )
{
const auto& ctx = gpuData[i];
if( ctx->name.Active() )
{
const char* name = worker.GetString( ctx->name );
bool has_name = name && name[0] != '\0';
printf( "GPU context %zu: %s\n", i, has_name ? name : "(unnamed)" );
if( !has_name ) all_named = false;
}
else
{
printf( "GPU context %zu: (unnamed)\n", i );
all_named = false;
}
}
return all_named ? 0 : 2;
}
catch( const std::exception& e )
{
fprintf( stderr, "Error: %s\n", e.what() );
return 1;
}
}

View File

@@ -124,6 +124,9 @@ uint8_t gpu_context_allocate( ToolData* data )
tracy::MemWrite( &item->gpuContextNameFat.context, context_id );
tracy::MemWrite( &item->gpuContextNameFat.ptr, (uint64_t)cloned_name );
tracy::MemWrite( &item->gpuContextNameFat.size, name_length );
#ifdef TRACY_ON_DEMAND
GetProfiler().DeferItem( *item );
#endif
tracy::Profiler::QueueSerialFinish();
}