Files
tracy/tests/cuda/repro/on_demand
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
..

Tracy CUDA On-Demand Profiling Repro

Demonstrates that unpatched Tracy crashes when a profiler connects to a CUDA application built with TRACY_ON_DEMAND.

Root cause

CUDACtx's constructor (TracyCUDA.hpp) writes a GpuNewContext queue item but does not call DeferItem(), unlike every other GPU backend (Vulkan, OpenGL, D3D11/12, Metal, WebGPU, Rocprof), which all defer it under #ifdef TRACY_ON_DEMAND.

Under on-demand mode, a new connection clears the client's pending serial queue (ClearQueues()) before replaying deferred items. Since GpuNewContext is never deferred, it never survives that clear — no matter how early the connection happens, even immediately after the context is created. The GpuContextName message that Name() sends right after (already correctly deferred) is then replayed to a server that has no record of the context, triggering:

Assertion `ctx' failed in Worker::ProcessGpuContextName

In release builds (assert compiled out) this is undefined behavior — a null-pointer dereference, typically a segfault.

This is the same bug already fixed for the Rocprof backend in #1336; see also the closed issue #1171, which describes this exact crash.

Build and run

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/repro &
tracy-capture -o repro.tracy -s 5

The reproducer is also registered as a ctest target:

ctest --test-dir build -R repro

Verifying the GPU zones

check_gpu_zones loads a .tracy file and prints, for each GPU context, whether it is named and how many GPU zones it recorded. It links the Tracy server library, so it is built only on request:

cmake -B build -DBUILD_CHECK_TOOL=ON
cmake --build build --target check_gpu_zones
./build/check_gpu_zones repro.tracy
# Expected (patched): "GPU context 0: line_scan_processing_cuda, 200 zones"

Exit codes: 0 = at least one named context with recorded zones, 2 = otherwise.

What to expect

Tracy version Result
Unpatched tracy-capture crashes: Assertion 'ctx' failed
Patched Capture succeeds, GPU context named and populated with zones