hal/tracy.lisp
Loading the client.
The flake builds a client with TRACY_ON_DEMAND and TRACY_MANUAL_LIFETIME
and names it in luv_TRACY_CLIENT. A client from somewhere else still
works; it may simply start collecting the moment it is loaded, and
start-tracy notices that it has no lifetime entry points to call.
Return the client luv_TRACY_CLIENT names, when it names one that exists.
Load the Tracy client library, preferring the one luv_TRACY_CLIENT names.
Answer whether a Tracy client can be loaded, without complaining if not.
The C API.
The entry points are spelled with the three leading underscores TracyC.h gives them; the Mach-O underscore on top of that is dlopen's business, not ours.
TracyCZoneCtx is deliberately opaque. In Tracy 0.14 it grew from two 32-bit fields to those fields plus a 64-bit on-demand connection id. The native shim retains exact contexts in a thread-local nesting stack, so the Lisp hot path neither translates a structure through libffi nor allocates a Lisp representation that can go stale at the next Tracy upgrade.
Interned names and source locations.
Return a stable foreign copy of string.
Tracy keeps names by pointer and resolves them long after the call that introduced them, so these copies are deliberately never freed.
Return a stable foreign source location describing zone name.
Locations are interned by their fields rather than allocated per expansion. Recompiling a file re-runs its LOAD-TIME-VALUE forms, and Tracy tells zones apart by the address of their location: without the table, recompiling a function in the middle of a capture would split its zone in two.
Render a zone name designator the way print-cpu-trace renders it.
Return designator's zone name when a macro can already read it, else NIL.
Lifecycle.
Answer whether the loaded client was built with TRACY_MANUAL_LIFETIME.
Describe this program to any viewer that later connects.
Name the calling thread in Tracy's timeline, if the profiler is running.
Worker threads are worth naming as they start: an unnamed thread still gets a
lane, but the lane is a thread id rather than a job. A thread that starts
before start-tracy keeps the anonymous lane, so start Tracy before the session
whose threads you want to read.
The test is on the profiler rather than on the loaded library, and that is not a detail to relax. A client built for delayed initialization asserts its way out of the whole process -- not into a Lisp condition -- when any entry point is called before startup or after shutdown.
Load the Tracy client, start the profiler, and answer whether it is running.
The client collects on demand, so starting it does not begin a capture: it makes this image discoverable, and zones start recording when a viewer connects. Leaving it started is the intended state for a durable image.
Answer whether a Tracy viewer is attached and therefore recording.
Zones.
Measure body as a Tracy zone named name.
When VALUE is supplied, evaluate it as the zone exits and attach the resulting unsigned integer to the zone. This is useful for semantic work counts such as sites visited: Tracy can then distinguish a slower realization from one which simply performed more work.
A literal name -- a string or a keyword, which is every zone luv writes by
hand -- gets one lazily initialized source-location cell per macro expansion.
The cell itself may be dumped into a standalone Lisp core, but its foreign
pointer is not allocated until the restored process first enters the zone.
This distinction matters: foreign memory allocated by LOAD-TIME-VALUE does not
survive SAVE-LISP-AND-DIE even though the Lisp pointer object does. A computed
name still works through luv's source-location table, which is slower but
reuses the same stable foreign record instead of growing Tracy's allocation
table on every entry.
Keep body in the expansion exactly once. Duplicating it into active
and inactive branches made nested frame instrumentation multiply a
large caller's compiler input even though only one branch ran.
Frames, plots, and messages.
End the current Tracy frame, or the secondary frame set called name.
Marking frames is what gives the viewer its frame-time graph, and what lets it say which frame a zone belongs to.
Record value on Tracy's plot name, drawn against the same timeline.
Describe how the viewer should draw plot name.
step suits a quantity that holds its value between changes, such as a count of
resident chunks, rather than one that is sampled continuously.
Post text to the viewer's message log at this instant on the timeline.
(&key (application-name "luv"))Load the Tracy client, start the profiler, and answer whether it is running. The client collects on demand, so starting it does not begin a capture: it makes this image discoverable, and zones start recording when a viewer connects. Leaving it started is the intended state for a durable image.
The loaded Tracy client library, or NIL before anything asked for one.
Native context-stack entry points required by luv's Tracy zone ABI.
Logical conjunction of tests and raw truth values.
Logical disjunction of tests and raw truth values.
(name &key (zone-function name) (file "") (line 0) (color 0))Return a stable foreign source location describing zone NAME. Locations are interned by their fields rather than allocated per expansion. Recompiling a file re-runs its LOAD-TIME-VALUE forms, and Tracy tells zones apart by the address of their location: without the table, recompiling a function in the middle of a…
Compare compatible quantities and produce dimensionless values.
TracyPlotFormatEnum, by the keyword luv names each format with.
(string)Return a stable foreign copy of STRING. Tracy keeps names by pointer and resolves them long after the call that introduced them, so these copies are deliberately never freed.
(designator)(trace &optional (stream *standard-output*))(designator)True while this image is offering zones to a Tracy viewer.
(name)Name the calling thread in Tracy's timeline, if the profiler is running. Worker threads are worth naming as they start: an unnamed thread still gets a lane, but the lane is a thread id rather than a job. A thread that starts before START-TRACY keeps the anonymous lane, so start Tracy before the session whose threads…
((name &key (color 0) (value nil value-supplied-p)) &body body)Measure BODY as a Tracy zone named NAME. When VALUE is supplied, evaluate it as the zone exits and attach the resulting unsigned integer to the zone. This is useful for semantic work counts such as sites visited: Tracy can then distinguish a slower realization from one which simply performed more work. A literal…
(&optional name)End the current Tracy frame, or the secondary frame set called NAME. Marking frames is what gives the viewer its frame-time graph, and what lets it say which frame a zone belongs to.
(name value)(name &key (format :number) (step t) (fill nil) (color 0))Describe how the viewer should draw plot NAME. STEP suits a quantity that holds its value between changes, such as a count of resident chunks, rather than one that is sampled continuously.
(text &key color)
A Tracy profiler client for luv's CPU zones.
Tracy takes every name by pointer and asks about it later, when a viewer wants to know what a zone it has already seen was called. Nothing handed to the profiler may therefore die with the call that handed it over, so names and source locations live in never-freed foreign memory, interned one copy per distinct zone rather than one per macro expansion.
The client is on-demand: it collects nothing until a viewer connects, so leaving the profiler started in a durable image costs a predictable nothing rather than an ever-growing buffer.
It is also built for delayed initialization, which buys the image control over when the profiler starts at the price of a sharp edge: every entry point here asserts that the profiler is running, and a failed assertion ends the process rather than signalling something a handler could catch. Everything below is therefore gated on
*TRACY*, and that gate is the one invariant this file cannot be sloppy about.