Files
tracy/tests/cuda/repro/on_demand/check_gpu_zones.cpp
Robin Oger 5df9ac73c7 Defer CUDA GpuNewContext for on-demand connections.
CUDACtx's constructor writes GpuNewContext directly through
QueueSerialFinish(), unlike every other GPU backend (Vulkan, OpenGL,
D3D11/12, Metal, WebGPU, Rocprof), which all defer it via
GetProfiler().DeferItem() so it survives on-demand's per-connection
queue clear.

A profiler connecting any time after the CUDA context is created (in
practice: any time after process start) never receives GpuNewContext.
The GpuContextName message that Name() sends right after (already
correctly deferred) then crashes the server's
Worker::ProcessGpuContextName with an unregistered context id
(assert(ctx) fails; undefined behavior in release builds).

Same fix already applied to the Rocprof backend in #1336. Fixes #1171.

Includes a repro test under tests/cuda/repro/on_demand/, mirroring the
structure #1336 added for Rocprof: a minimal CUDA program that creates
an on-demand context (repro.cu/CMakeLists.txt), and a check_gpu_zones
tool that loads the resulting .tracy file and verifies the GPU context
was named and populated with zones. Verified locally: unpatched
tracy-capture crashes on the first connection attempt; patched, three
consecutive connect/disconnect cycles all succeed and check_gpu_zones
reports a named context with recorded zones.
2026-08-03 10:48:20 +02:00

59 lines
1.8 KiB
C++

// Loads a .tracy file and prints, for each GPU context, whether it is
// named and how many GPU zones it recorded. Used to verify that a
// late-connecting client correctly received the deferred GpuNewContext
// (and, downstream, actual GPU zone data) rather than the connection
// having crashed the server before any capture was possible.
//
// Usage: ./check_gpu_zones trace.tracy
// Expected output: "GPU context 0: line_scan_processing_cuda, 100 zones"
#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 2;
}
bool all_good = true;
for( size_t i = 0; i < gpuData.size(); i++ )
{
const auto& ctx = gpuData[i];
const bool has_name = ctx->name.Active() && worker.GetString( ctx->name )[0] != '\0';
const char* name = has_name ? worker.GetString( ctx->name ) : "(unnamed)";
printf( "GPU context %zu: %s, %lu zones\n", i, name, (unsigned long)ctx->count );
if( !has_name || ctx->count == 0 ) all_good = false;
}
return all_good ? 0 : 2;
}
catch( const std::exception& e )
{
fprintf( stderr, "Error: %s\n", e.what() );
return 1;
}
}