Two staleness gaps a cold agent would hit: get_sections()'s
documented {start, end, text} shape didn't match the actual
return value once category was added, and is_background_done()
-- introduced to close the stats-read race exercised while
testing the lock fix -- wasn't mentioned anywhere, so a cold
agent had no way to discover it short of dir(ctx).
5.2 KiB
Tracy MCP eval guide
This document covers the bindings-layer detail that the analysis
guidance (tracy://prompt) does not.
ctx
ctx is a TracyServerBindings.Worker — the same object Tracy Assist's
C++ tools query through Worker::Get*. The pybind methods are the canonical
data surface. Common entry points:
- Zones:
get_all_zone_stats()(every callsite, large),get_root_zone_stats()(top-level zones only, useful for "where is the program spending time"),get_zone_stats(srcloc_id),get_child_zone_stats(srcloc_id)(subtract for self-time),get_zone_durations(name),get_zone_count(),get_all_zone_source_locations() - GPU zones:
get_all_gpu_zone_stats(),get_gpu_zone_durations(...),get_gpu_contexts() - Frames:
get_frame_count(),get_frame_times(),get_frame_times_named(name),get_frame_boundaries(),get_zones_in_frame(...) - Threads:
get_threads(),get_thread_name(tid),get_thread_context_switches(tid) - Messages / plots / locks / memory / callstacks:
get_messages(),get_plots(),get_locks(),get_memory_events(),get_callstack_frames(...) - Sections:
get_sections()— timed code sections fromTracySectionEnter/TracySectionLeaveinstrumentation. Returns a list of{category, start, end, text}dicts (start/end in ns);categoryis the section's named group (multiple unrelated section sets can coexist in one capture). - Capture metadata:
get_capture_name(),get_capture_program(),get_first_time(),get_last_time(),get_resolution(),get_host_info() - Readiness:
is_background_done()— see "Instance lifecycle" below before querying zone/symbol stats right afterload_capture.
Run print([m for m in dir(ctx) if not m.startswith('_')]) for the full list.
Units and conventions
- All time values returned by Worker methods are nanoseconds (int).
get_first_time()/get_last_time()bound the capture timeline. ZoneStatsfields:count,total,min,max,avg,sum_sq.totalis the inclusive aggregate; useget_child_zone_stats(srcloc_id)to subtract child time when you need self-time.get_all_zone_stats()returnsdict[str, ZoneStats]keyed by an opaque label of the form'name (addr)[arch] <srcloc_id>'. The trailing<id>is the source-location ID — the int accepted byget_zone_stats(int),get_zone_durations_by_id, and friends. Parse it with a regex if you need to join across calls.- Source-location IDs from
get_all_zone_source_locations()are the join key between zone-name lookups and per-callsite queries.
Common query patterns
Small Python snippets for the queries you'll reach for most often:
# top 10 hottest zones by total time
top = sorted(ctx.get_all_zone_stats().items(),
key=lambda kv: kv[1].total, reverse=True)[:10]
for k, v in top:
print(f"{v.total/1e6:.2f}ms count={v.count} {k}")
# primary frame set timing
times = ctx.get_frame_times() # ns per frame
print(f"frames={len(times)} avg={sum(times)/len(times)/1e6:.2f}ms "
f"p99={sorted(times)[int(len(times)*0.99)]/1e6:.2f}ms")
# stats for a named zone — find the srcloc id, then drill in
import re
matches = [k for k in ctx.get_all_zone_stats() if k.startswith("MyFunc ")]
sid = int(re.search(r"<(\d+)>$", matches[0]).group(1))
stats = ctx.get_zone_stats(sid)
Async mode
For long-running queries pass async_mode=True to eval; it returns
{task_id, status: "running"}. Poll with the task tool
(action="poll", task_id=...).
Instance lifecycle
tracy_mcp.py runs as a long-lived singleton process shared across every
MCP client (all VS Code windows), and every live_connect/load_capture
instance holds its entire trace — zones, messages, callstacks, memory
events — resident in memory for as long as that process runs. Nothing
frees it just because your conversation ends.
load_capturereturns before zone/symbol statistics finish building — a background thread populates them after the file itself is read. Checkis_background_done()(orbackground_doneinlist_instances) before relying onget_all_zone_stats()and similar stats-based queries; they can return empty or partial results until it's true. For small captures this finishes before you'd notice; for multi-GB ones it can take seconds.- Call
unload_capture(instance_id)as soon as you're done analyzing a capture. Don't rely on automatic eviction as your primary cleanup path — treat it as a backstop for forgotten sessions, not a substitute. list_instancesreportsidle_seconds,connected, andbackground_doneper instance; use it to spot stale ones before starting a new session, especially if you're about to load several captures for comparison.- The server does provide two backstops, both configurable via environment
variables at startup: it evicts the least-recently-used evictable
instance (disconnected live sessions, or any loaded file capture — never
a still-connected live one) once the instance count reaches
TRACY_MCP_MAX_INSTANCES(default 4), and it drops a disconnected live instance that's sat idle forTRACY_MCP_DISCONNECTED_TTL_S(default 1800s / 30 min) so a session is still there for analysis right after the target disconnects, but doesn't linger forever if forgotten.