Platform Support
stax runs on macOS and Linux. The analysis half — the aggregator, flamegraphs, top-N, per-thread breakdowns, annotated disassembly, the CLI, the web UI — is one codebase and behaves identically on both. What differs is the capture backend underneath, and therefore some of what each platform can see.
At a glance
| capability | macOS | Linux |
|---|---|---|
| capture backend | kperf / kdebug | perf_event_open |
privileged helper (staxd) | LaunchDaemon — required | systemd service — optional |
record without sudo | ✅ | ✅ |
| on-CPU sampling | ✅ | ✅ |
| off-CPU intervals | ✅ | ✅ |
| per-thread blocked reason | ✅ | ✅ |
| wakeup attribution | ✅ | ✅ with staxd |
| user call stacks | ✅ | ✅ |
| kernel call stacks | ✅ | ✅ when the host permits |
| frame-pointer unwinding | ✅ (in-kernel) | ✅ (in-kernel) |
DWARF .eh_frame unwinding | — | ✅ x86-64 |
| PMU / hardware counters | ✅ | ✅ with staxd |
| debuginfod + separate debug files | — | ✅ |
| dyld shared-cache symbols | ✅ | — |
| JIT (jitdump) symbolication | ✅ | — |
| annotated disassembly | ✅ (x86_64 + aarch64) | ✅ (x86_64 + aarch64) |
✅ works today, — not applicable to that platform.
macOS
On macOS, stax uses Apple's private kperf and kdebug frameworks —
the low-level interfaces the OS exposes for periodic sampling and scheduler
tracing.
- The PET sampler.
kperf's periodic event timer fires on every thread at the configured frequency. The kernel walks the stack and writes records into thekdebugtrace buffer. - User and kernel stacks. Each tick yields both a user-space backtrace and a kernel-space one.
- Off-CPU intervals and wakeups. stax subscribes to
kdebugscheduler events (MACH_SCHED,MACH_MAKERUNNABLE), so it knows when a thread went off the CPU, for how long, why, and — for the wakeups it catches — which thread woke it. - PMU counters.
kperfexposes the CPU's performance counters; stax records per-thread cycles and instructions, plus L1-data-cache misses and branch mispredicts. - JIT and system libraries. stax tails a perf jitdump file to symbolicate JIT'd code, and parses the dyld shared cache so addresses inside system libraries resolve to names.
These frameworks need root, so they live behind
staxd — a LaunchDaemon you install once with
sudo stax setup. On macOS staxd is required: it is the only path to
kperf. Once installed, stax record itself is unprivileged. Both Apple
Silicon and Intel Macs are supported.
Code signing is required.
cargo xtask installcodesigns the binaries; the daemons will not run otherwise — see Getting Started. Hardened-runtime targets — apps with the hardened runtime and noget-task-allowentitlement — cannot be attached to.
Linux
On Linux, stax uses perf_event_open directly — the same kernel
interface perf itself uses. It opens a sampling event per CPU, maps the
kernel's ring buffers, and drains and parses PERF_RECORD_* records.
- On-CPU sampling via a frequency-driven software clock event with
PERF_SAMPLE_CALLCHAIN. - Off-CPU intervals from a side-band
PERF_RECORD_SWITCHcontext-switch ring. A voluntary switch-out opens an off-CPU span; the matching switch-in closes it with a true scheduler duration. - Per-thread blocked reasons from
/proc/<tid>/wchan— the kernel wait-site the thread is parked on leads its off-CPU stack, and stax classifies it into the same buckets as macOS. - Wakeup attribution from the
sched:sched_wakingtracepoint. The tracepoint lives in tracefs, which is root-only — so wakeup attribution needs thestaxdbroker (below). Without it, off-CPU intervals still flow, they are just not attributed to a waker. - Kernel call stacks when the host permits it (see
perf_event_paranoid); user stacks always. - PMU counters — cycles, instructions, L1-data read misses, branch
mispredicts — opened as a hardware-counter group. Like the tracepoint,
hardware counters on a locked-down host need the
staxdbroker. - Stripped binaries. Linux ships most system libraries stripped; stax recovers their symbols from local separate-debug files and debuginfod — see Symbolication.
- DWARF unwinding. When the target omits frame pointers, stax can replay
the unwind in userspace against
.eh_frame— see Stack Unwinding.
Two recording modes
Linux can record with or without the staxd daemon:
- In-process — when
perf_event_paranoidis permissive,stax-serveropensperf_event_openitself. No daemon needed. - Via the
staxdbroker —staxddoes the one privilegedperf_event_openper CPU and hands the file descriptors back to the unprivileged side over a Unix socket. From that point the daemon is out of the data path entirely.
The broker is what makes stax work on a host with a restrictive
perf_event_paranoid, and it is also what unlocks hardware counters and
wakeup attribution (the tracepoint), which a non-root process cannot open.
Installing it is the same one-time sudo stax setup as on macOS — see
Getting Started and
Architecture.
perf_event_paranoid
The kernel's kernel.perf_event_paranoid sysctl gates what
perf_event_open allows a non-root process to do:
cat /proc/sys/kernel/perf_event_paranoid
# lower it for the session (kernel stacks need <= 1):
sudo sysctl kernel.perf_event_paranoid=1A restrictive value is the usual reason an in-process Linux recording comes
back with shallow stacks, no kernel frames, or no hardware counters. The fix
is either to lower perf_event_paranoid, or — better — to install the
staxd broker, which holds the privilege so the host setting stops mattering.
What's shared
Everything above the capture backend is one codebase. Both platforms feed
the same OS-neutral event stream — on-CPU samples, off-CPU intervals,
wakeups, image loads, thread names — into the same aggregator. A flamegraph,
a stax top table, an annotated disassembly, the web UI: all look and behave
identically regardless of where the recording happened. The differences are
only ever about what the OS let stax capture in the first place.
See also
- Architecture — what
staxddoes on each platform. - Stack Unwinding — frame pointers and DWARF.
- Symbolication — debuginfod and friends.
- Sampling — on-CPU vs off-CPU, explained.