Compact xctrace (Instruments) summaries optimized for LLM consumption.
33,000 lines of XML → 10 actionable lines.
xctrace export generates exhaustive XML — every sample, every backtrace frame, every memory address. This is great for Instruments' interactive UI, but when an LLM needs to find hotspots, the signal-to-noise ratio is brutal.
$ ztrace summary ./MyApp.trace
Process: ghostty Duration: 3.8s Template: Time Profiler
Samples: 295 Total CPU: 295ms
SELF TIME
53.2% 157ms ghostty main
3.7% 11ms ghostty renderer.metal.RenderPass.begin
3.1% 9ms ghostty renderer.generic.Renderer(renderer.Metal).rebuildCells
2.7% 8ms ghostty renderer.generic.Renderer(renderer.Metal).drawFrame
1.7% 5ms ghostty font.shaper.coretext.Shaper.shape
CALL STACKS
53.2% 157ms main
1.7% 5ms main > @objc TerminalWindow.title.setter
- Runs
xctrace export --tocfor trace metadata (process, duration, template) - Runs
xctrace export --xpathto extract thetime-profiletable - Parses the XML, resolving xctrace's
id/refdeduplication system - Filters non-actionable frames (system libraries, Swift runtime internals, dyld stubs, unsymbolicated addresses)
- Aggregates by function with self time, total time, and call stacks
# With uv (recommended)
uv tool install git+https://github.com/frr149/ztrace
# From source
git clone https://github.com/frr149/ztrace
cd ztrace
uv sync# Summarize a .trace bundle
ztrace summary ./MyApp.trace
# Lower threshold to show more functions (default: 1%)
ztrace summary ./MyApp.trace --threshold 0.5
# Deeper call stacks (default: 5)
ztrace summary ./MyApp.trace --depth 10| Category | Examples | Why |
|---|---|---|
| System libraries | libdispatch, dyld, libsystem_m | Can't optimize what you don't own |
| Swift/ObjC runtime | __swift_instantiate*, _swift_* |
Runtime internals, not your code |
| Dyld stubs | DYLD-STUB$$sin |
Dynamic linker thunks |
| Unsymbolicated frames | 0x104885404 |
Stripped binaries — noted in output |
When a binary is stripped (e.g. Spotify), ztrace reports the percentage of unsymbolicated samples so you know you need dSYMs for a full picture.
ztrace was built specifically for LLM-assisted profiling workflows. Add this to your CLAUDE.md (global or per-project) so Claude Code uses it automatically:
### Profiling (xctrace)
- Use `ztrace summary <file.trace>` to read traces. NEVER read raw xctrace XML.
- Workflow: `xctrace record` → `ztrace summary`
- Flags: `--threshold 0.5` (more functions), `--depth 10` (deeper stacks)Typical workflow inside Claude Code:
# 1. Record a trace
xctrace record --template 'Time Profiler' --time-limit 5s --launch -- .build/debug/MyApp
# 2. Summarize (ztrace output fits in context window)
ztrace summary MyApp.trace
# 3. Claude Code reads the 10-line summary and suggests optimizationsWithout ztrace, step 2 would produce 30,000+ lines of XML that either overflows the context window or drowns the signal in noise.
- macOS (xctrace is Apple-only)
- Python ≥ 3.12
- Xcode Command Line Tools (
xcode-select --install)
- 33,000 lines of XML to tell you heavyWork() is slow — How and why ztrace was built.
- From /simplify to the Jedi Council — Using ztrace as Mike Acton's instrumentation tool inside an AI code review pipeline.
MIT