# How Magic-Trace Integrates with Perf for Intel PT Decoding

> Learn how Magic-Trace integrates with Linux perf to decode Intel PT streams, converting raw hardware data into structured function call events for advanced debugging.

- Repository: [Jane Street/magic-trace](https://github.com/janestreet/magic-trace)
- Tags: how-to-guide
- Published: 2026-05-24

---

**Magic-Trace leverages Linux perf to record Intel Processor Trace (PT) streams and decodes the raw hardware data through a custom C decoder that translates machine-level instructions into structured function call events.**

Magic-Trace is an open-source execution tracing tool developed by Jane Street that captures nanosecond-precision application behavior using Intel PT technology. The integration between magic-trace and the Linux `perf` subsystem follows a multi-stage pipeline: detecting hardware capabilities, constructing precise event configurations, recording binary trace data, and decoding the stream into symbolic execution events.

## Detecting Intel PT Capabilities

Before recording, magic-trace verifies kernel support for Intel PT features by probing system files and querying the perf binary version. In [`src/perf_capabilities.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_capabilities.ml), the `detect_exn` function orchestrates this discovery process.

The implementation reads capability flags from sysfs paths such as `/sys/bus/event_source/devices/intel_pt/caps/psb_cyc` through helper functions like `supports_configurable_psb_period`. This determines whether the kernel supports configurable PSB (Packet Stream Boundary) periods, kernel-level tracing, snapshot-on-exit, and other advanced features.

```ocaml
let detect_exn () =
  let%bind perf_version_proc = Process.create_exn … in
  …
  empty
  |> set_if (supports_configurable_psb_period ()) configurable_psb_period
  |> set_if (supports_tracing_kernel ()) kernel_tracing
  |> set_if (supports_kcore version) kcore
  |> set_if (supports_snapshot_on_exit version) snapshot_on_exit
  |> set_if (supports_last_branch_record ()) last_branch_record
  |> set_if (supports_dlfilter version) dlfilter
  |> set_if (supports_ctlfd version) ctlfd
;;

```

## Constructing the Perf Command Line

Using the detected capabilities, magic-trace builds the specific `intel_pt` event specification in [`src/perf_tool_backend.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_tool_backend.ml). The `perf_intel_pt_config_of_timer_resolution` function translates user-selected timer resolutions (`Low`, `Normal`, `High`, or `Custom`) into kernel configuration strings.

```ocaml
let perf_intel_pt_config_of_timer_resolution ~capabilities timer_resolution =
  match timer_resolution with
  | Low    -> Or_error.return ""
  | Normal -> Or_error.return "cyc=1,cyc_thresh=1,mtc_period=0"
  | High   -> Or_error.return "cyc=1,cyc_thresh=1,mtc_period=0,noretcomp=1"
  | Custom { … } -> …   (* builds “cyc=…,mtc=…,psb_period=…” list *)
;;

```

The `perf_args_of_collection_mode` function assembles the final `--event` argument, combining the Intel PT configuration with a scope selector (`u` for userspace, `k` for kernel, or `uk` for both).

```ocaml
let%map.Or_error primary_event =
  match collection_mode with
  | Intel_processor_trace _ ->
      let%map.Or_error intel_pt_config =
        perf_intel_pt_config_of_timer_resolution ~capabilities timer_resolution
      in
      [%string "intel_pt/%{intel_pt_config}/%{selector}"]
…
let arg_string = String.concat ~sep:"," (primary_event :: extra_events) in
[ [%string "--event=%{arg_string}"] ]

```

This generates commands resembling:

```bash
perf record --event=intel_pt/cyc=1,cyc_thresh=1,mtc_period=0/uk …

```

## Recording Raw Processor Trace Data

Magic-Trace invokes the `perf` binary through the `attach_and_record` function in [`src/perf_tool_backend.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_tool_backend.ml). This spawns a child process that executes `perf record` with the constructed arguments, writing the raw PT data to `perf.data` and auxiliary buffer files containing the actual hardware trace packets.

When the recording triggers (either by snapshot or continuous mode), the Intel PT hardware writes compressed branch trace packets to physical memory, which perf captures in the AUX buffer region alongside metadata about the traced process.

## Decoding the PT Stream with the Direct Backend

After recording completes, magic-trace decodes the raw binary stream using the **direct backend** located in [`direct_backend/manual_perf.ml`](https://github.com/janestreet/magic-trace/blob/main/direct_backend/manual_perf.ml) and [`direct_backend/decoding.ml`](https://github.com/janestreet/magic-trace/blob/main/direct_backend/decoding.ml). The process involves three distinct phases:

**Setup Phase:** The `Tracing_state.attach` function reads the current memory maps of the traced process via `read_current_maps`, then constructs a `Setup_info.t` record describing the process layout. This data serializes to a side-band file that the C decoder consumes.

```ocaml
let%map.Result () = Stub.attach state config trace_meta |> Errno.to_result in
Out_channel.write_all setup_file
  ~data:([%sexp ({ initial_maps = read_current_maps pid; … } : Setup_info.t)]
         |> Sexp.to_string);

```

**C Decoder Initialization:** The OCaml code calls into native stubs such as `magic_pt_init_decoder_stub` and `magic_pt_run_decoder_stub` defined in [`decoding.ml`](https://github.com/janestreet/magic-trace/blob/main/decoding.ml). These functions interface with the Intel PT decoding library to parse the raw AUX buffer into discrete trace events (`Call`, `Ret`, `Jump`, `Start_trace`).

**Event Conversion:** The `convert_trace_event` function maps raw PT events to the `Backend_intf.Event.t` type, constructing high-level execution events suitable for the magic-trace viewer.

## Resolving Symbols from Trace Data

Raw instruction pointers from the PT stream require translation to human-readable function names. In [`src/perf_decode.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_decode.ml), functions like `parse_symbol_and_offset` and `parse_location` extract address information from perf's output.

Magic-Trace attempts symbol resolution through multiple sources:

- **Perf-map files:** For JIT-compiled code or dynamically generated symbols, the decoder checks `Perf_map.Table.symbol` in [`src/perf_map.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_map.ml) for per-process symbol tables.
- **ELF side-band maps:** When perf-map files are unavailable, the system falls back to the memory maps captured during the setup phase to resolve addresses against the binary's symbol tables.

## Summary

- **Capability Detection:** Magic-Trace probes `/sys/bus/event_source/devices/intel_pt/caps/psb_cyc` and other sysfs files via [`src/perf_capabilities.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_capabilities.ml) to determine available Intel PT features before recording.

- **Command Construction:** The `perf_intel_pt_config_of_timer_resolution` function in [`src/perf_tool_backend.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_tool_backend.ml) translates timer settings into kernel event specifications like `intel_pt/cyc=1,cyc_thresh=1,mtc_period=0,noretcomp=1/uk`.

- **Hardware Recording:** Magic-Trace forks the `perf` binary with constructed arguments, capturing raw PT packets to `perf.data` and auxiliary buffer files.

- **Binary Decoding:** The direct backend in [`direct_backend/manual_perf.ml`](https://github.com/janestreet/magic-trace/blob/main/direct_backend/manual_perf.ml) initializes a C decoder via `magic_pt_init_decoder_stub`, converting raw packets to structured events through `convert_trace_event`.

- **Symbol Resolution:** Address-to-symbol translation occurs in [`src/perf_decode.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_decode.ml), utilizing perf-map files and side-band ELF data to attribute instructions to source functions.

## Frequently Asked Questions

### How does magic-trace detect if Intel PT is available on the system?

Magic-Trace checks for Intel PT support by reading kernel capability files in `/sys/bus/event_source/devices/intel_pt/caps/`, specifically probing `psb_cyc` for configurable PSB periods. The `detect_exn` function in [`src/perf_capabilities.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_capabilities.ml) aggregates these checks alongside version parsing of the `perf` binary to build a comprehensive capability profile before attempting recording.

### What perf event configuration does magic-trace use for high-resolution tracing?

For high-resolution traces, magic-trace configures the event string as `intel_pt/cyc=1,cyc_thresh=1,mtc_period=0,noretcomp=1/uk` through the `perf_intel_pt_config_of_timer_resolution` function. This enables cycle-accurate timestamps with micro-time counter (MTC) packets while disabling return compression to ensure complete call stack reconstruction.

### How does magic-trace convert raw Intel PT packets into function call events?

After perf records the raw data, magic-trace uses the direct backend to initialize a C decoder via `magic_pt_init_decoder_stub` in [`direct_backend/decoding.ml`](https://github.com/janestreet/magic-trace/blob/main/direct_backend/decoding.ml). The decoder processes the PT AUX buffer and emits low-level events that `convert_trace_event` translates into the `Backend_intf.Event.t` type, distinguishing between `Call`, `Ret`, and `Jump` operations with symbolic resolution.

### Where does magic-trace store the recorded Intel PT trace data?

Magic-Trace writes the raw trace data to standard perf output files (`perf.data`) alongside auxiliary buffer files containing the compressed PT packets. During decoding, it generates side-band files (such as `setup.sexp`) in [`direct_backend/manual_perf.ml`](https://github.com/janestreet/magic-trace/blob/main/direct_backend/manual_perf.ml) that describe the process memory maps, enabling accurate instruction pointer resolution during analysis.