Replace the binary connection.history file and the ConnectionHistory class
with History, which keeps the history in a JSON file and writes it back when
the profiler exits. Existing history is not migrated.
Function-local statics are destroyed in reverse order of initialization,
on the same atexit stack that carries the shutdown handler installed at
profiler startup. Statics initialized lazily while profiling are
therefore destroyed before that handler runs, while worker and network
threads may still be using them; with TRACY_NO_EXIT the handler blocks
in the exit sequence until the server disconnects, keeping the threads
live across the entire destruction. On Android this let a symbol query
execute `s_mappings = ParseMappings()` on a destroyed vector, causing a
use-after-free and a double free. The legacy-GCC thread data key carried
the same hazard: pthread_key_delete at static destruction while threads
could still call pthread_getspecific on the key.
Construct the objects on the heap and never free them. The allocations
are one-time and stay referenced, so leak sanitizers classify them as
still reachable.
Co-authored-by: Tim Jammer <tim.jammer@web.de>
When Tracy is built into a DLL, the static `Profiler` is destroyed inside `DllMain(DLL_PROCESS_DETACH)` while the loader lock is held. `Thread::~Thread()` waited for the thread to exit, but a thread can only exit by taking the loader lock for `DLL_THREAD_DETACH`, so `FreeLibrary` hung forever (typically in `s_symbolThread->~Thread()`).
The thread function now signals an event when it is done and parks in an alertable sleep instead of returning. The destructor waits for that event rather than for the thread itself. Inside a loader callout (detected with `RtlIsThreadWithinLoaderCallout`) the parked thread is terminated, which is safe as it holds no locks at that point. Everywhere else it is woken with an APC and exits normally, so `thread_local` destructors still run. If the ntdll export is unavailable, the previous behaviour is kept.
Same approach as `asio::detail::win_thread`.
Reproduced with a test DLL embedding Tracy (`TRACY_ON_DEMAND`) loaded and freed in a loop: `FreeLibrary` hung on the first unload before the change, and 100 cycles pass after it when the DLL stays loaded for at least a few ms. Unloading immediately after loading still hangs if the Tracy threads are blocked on the loader lock during their startup (e.g. in `gethostname` or `SymInitialize`), as they never reach the parking point. Startup and shutdown timings are unchanged.
Move the shared ring comment onto `OpenSampleEvent`, where the sharing happens. Name the number of sampling event kinds with `NumSampleEventKinds` instead of using `EventVsync` directly, and assert in `OpenSampleEvent` that the event id is below it.
get_all_zone_stats mixes every thread, so the main-thread cost of a
zone could not be separated from worker threads. get_thread_zone_stats
returns the same per-name stats for the zones recorded on one thread,
and its per-thread sums match get_all_zone_stats exactly.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3DnWkXakGRgz9C8BaLM47
get_plots only summarises a whole trace, so a plot could not be
compared across windows. get_plot_samples returns the (time, value)
pairs in a range, thinned to a maximum count, and
get_plot_window_stats returns count, min, max and average over just
that range.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3DnWkXakGRgz9C8BaLM47
When the client disconnects while a disjoint query result is pending,
the not-connected branch of Collect resets the checkpoints without
re-issuing Begin. The next connected Collect then calls End twice, so
no GpuTime items are produced and every later connection shows GPU
zones with no timings.
Drain the pending result and Begin the query before resetting.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3DnWkXakGRgz9C8BaLM47
With CONCURRENT_KERNEL, MEMCPY or MEMSET activity enabled, CUPTI 13.0
faults the device with an illegal memory access once the nodes inside CUDA
graph conditional (IF/WHILE) bodies have executed about 699,050 times in a
context. TRACY_CUDA_SERIALIZED_KERNEL_ACTIVITY enables KERNEL activity
instead, which avoids the fault at the cost of serializing kernels. In that
mode CUPTI reports conditional-body kernels once per graph launch without
timestamps, so those records are skipped.
Sampling events of one kind on one CPU now share a ring: the first
thread's event owns it, and the others are redirected into it with
PERF_EVENT_IOC_SET_OUTPUT. Redirected fds are kept open and enabled by
the worker.
Pass the 50 ms budget as the timeout of wl_display_dispatch_timeout
instead of sleeping after a nonblocking dispatch. The dispatch flushes
before polling and wakes on any event, e.g. re-granted focus.