Pandoc 3.11 appends the LaTeX caption label to Markdown table
captions as a {#id} identifier; strip it in tablecaption() since the
manual renderer has no identifiers.
The monitor embeds the capture side of the protocol (TracyWorker) to
save traces to disk; like tracy-capture, build it without statistics
and at C++20.
Only one daemon can write to a given output directory at a time. The
lock holds the daemon PID and is removed on a clean exit; a lock whose
owner is verifiably gone is taken over on the next start, and a lock
whose owner cannot be read or that reads as alive must be removed by
hand.
The data is written to <fn>.tmp in the same directory as the target
and moved to <fn> on Commit(); a failed or interrupted write leaves
the previous file untouched, with an orphaned .tmp.
The final compressed block was left in the stdio buffer until the
destructor closed the file, so a caller that inspected the file after
Finish() saw a truncated trace.
The old pre-flight truncated the output at startup with -f, so an
interrupted capture lost the trace, and the probe fopen followed a
symlink. The existing file is now moved to FILE~ for the duration of
the capture and restored if the trace cannot be written.
HasData() flips when the connection is established, but the rate
list is only filled by the worker periodic stats update, whose first
entry arrives ~200ms later, so the first render could read an empty
list.
As it is, the CI-built macOS binaries refuse to work on
macOS Sequoia because it defaults to the runner machine's level.
This sets a lower-than-default default for the target.
Adopting a pre-bound socket via SetReservedListenSocket left Worker()'s
port bookkeeping untouched, so GetPort() and the broadcast announcement
reported the computed default instead of the port the socket listens on.
The monitor kept this correct by mirroring the reserved port into
TRACY_PORT; direct users of the public API had no such coupling.
The bound socket is now authoritative: Worker() reads the local port
back via getsockname after binding. The search-loop bookkeeping stays
only as a fallback for a getsockname failure.
External sampling opens one event per CPU for each existing thread
(attach) or per CPU (launch) per event type, so a multithreaded target
needs many fds.
TRACY_NO_CALLSTACK, TRACY_NO_SYSTEM_TRACING, TRACY_NO_SAMPLING and
TRACY_SAMPLING_PROFILER_MANUAL_START either fail to build (the external
target API lives in the callstack code) or silently produce an empty
capture (no sampling event fails, so IsSystemTracingFailed stays false).
The first three are #error'd at compile time; the TRACY_NO_SYS_TRACE /
TRACY_NO_SAMPLING environment variables are refused at startup.
A caught signal (the handlers are inherited by the child) delivered
between PTRACE_TRACEME and exec first produces a signal-delivery stop;
without handling it, the setup would run on the still pre-exec child and
capture the monitor's own image as the target's. Re-inject the caught
signal and wait again until the exec stop is reached. Job-control stops
are the exception - re-injecting SIGSTOP/SIGTSTP re-stops the child, and
PTRACE_CONT rejects a stop carrying the 0x80 job-control bit - so resume
those without a signal instead.
The client's system tracing cannot start without a tracefs (or debugfs)
mount - the tracepoint ids live under it - so starting anyway would
capture no samples.
IsSystemTracingFailed reports a SysTraceStart that the preflight could
not predict (the target exiting between the checks, the kernel rejecting
the event setup); starting anyway would capture no samples.
Polling IsDataPortListening is the only reliable check: probing the port
itself cannot distinguish the client's listener from another process
holding it.
When TRACY_PORT is not pinned the client probes 8086..8105 at startup.
Reserve the first free port up front and hand the already-bound socket
to the client via SetReservedListenSocket, so the reservation is atomic
and the reported port matches what the client listens on. A pinned
TRACY_PORT is validated like --port, since the client pins to any
nonzero value with no fallback.
The name is matched against /proc/<pid>/comm, which the kernel truncates
to 15 characters. Several matches are listed so the user can pick one
with -p; a match is only accepted if /proc/<pid>/exe is readable, which
excludes zombies (they keep a comm but have no executable to map) and
inaccessible processes.
The old preflight opened one generic event, which said little about
whether the client's actual setup would work. PreflightSamplingEvent now
opens the client's exact external event shape (the per-CPU pid-filtered
CPU_CLOCK callstack event of SysTraceStart), verifies the ring mmap
works, and probes the hardware PMU counters informationally. On
EACCES/EPERM retry user-space only - both event and callchain, since the
callchain part still requires perf_allow_kernel - and report kernel
frames as unavailable; any other error is fatal, as the client's
identical shape would fail the same way.
kill(pid, 0) succeeds for zombies, so a SIGKILLed attach target whose
parent had not reaped it yet kept the monitor polling indefinitely - in
attach mode the monitor cannot reap it. Liveness now also reads the
/proc state and treats Z as dead.
A thread exiting between fopen and read leaves the comm and status
buffers under-filled or uninitialized: zero-initialize them, treat a
short or empty read as unknown, and never scan the status when the read
returned nothing.
A task directory yielding no numeric entries (the target exiting
mid-enumeration) returned 0 with the freshly allocated array still
handed back through *out, and the caller's failure path returned without
freeing it.
SysTraceStart only failed when no event of any kind opened, but in
external mode the global sched/vsync tracepoints (pid -1) succeed
independently of the target: a target exiting after thread enumeration
left every per-target open failing while the startup still reported
success. s_ctxBufferIdx is the per-target ring count right after the
per-target setup, so in external mode require it to be non-zero as well.