# How Magic-Trace Controls perf Using ctlfd vs Signals: Implementation Deep Dive

> Explore how Magic-Trace expertly manages perf control using ctlfd or signals. Discover the implementation details and benefits of its dynamic kernel version detection for reliable performance tracing.

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

---

**Magic-Trace dynamically selects between the modern control-fd (`ctlfd`) pipe interface and traditional POSIX signals based on kernel version detection, preferring `ctlfd` on Linux ≥5.10 for reliable synchronous control of the perf recorder.**

Magic-Trace, Jane Street's high-precision tracing tool for OCaml and C applications, orchestrates the Linux `perf` recorder through two distinct control mechanisms. The implementation in the `janestreet/magic-trace` repository automatically negotiates between the robust `ctlfd` (control file descriptor) interface available in newer kernels and legacy POSIX signals for broader compatibility. This architectural decision ensures maximum reliability when triggering snapshots while maintaining backward compatibility with older systems.

## Detecting ctlfd Support at Runtime

When a recording begins, Magic-Trace queries the system capabilities to determine whether the kernel supports the `ctlfd` interface. The detection logic resides in **[`src/perf_capabilities.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_capabilities.ml)**:

```ocaml
(* src/perf_capabilities.ml *)
let supports_ctlfd = kernel_version_at_least ~major:5 ~minor:10

let detect_exn () =
  …
  |> set_if (supports_ctlfd version) ctlfd

```

The `detect_exn` function executes `perf --version` and checks if the running kernel is at least version 5.10. If so, it sets the `ctlfd` bit in a capability bitmask of type `Perf_capabilities.t`. This bitmask drives all subsequent control mechanism decisions.

## Choosing Between ctlfd and Signals

Inside **[`src/perf_tool_backend.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_tool_backend.ml)**, the `Control.create` function examines the capability mask to construct the appropriate control value:

```ocaml
(* src/perf_tool_backend.ml, lines 176-190 *)
let control =
  if Perf_capabilities.(do_intersect capabilities ctlfd) then (
    (* ctlfd path – requires kernel support *)
    let shutdown = Perf_ctlfd.Command.stop in
    let snapshot = select ~at_exit:Perf_ctlfd.Command.stop
                        ~function_call:Perf_ctlfd.Command.snapshot in
    Ctlfd { ctlfd = Perf_ctlfd.create (); shutdown; snapshot })
  else if perf_snapshot_on_exit then (
    (* signal path – fallback when ctlfd unavailable *)
    let shutdown = Signal.term in
    let snapshot = select ~at_exit:Signal.int ~function_call:Signal.usr2 in
    Signals { shutdown; snapshot })
  else (
    let shutdown = Signal.term in
    let snapshot = Signal.usr2 in
    Signals { shutdown; snapshot })

```

When `ctlfd` is available, Magic-Trace creates a **`Perf_ctlfd.t`** record containing pipe file descriptors. Otherwise, it falls back to a `Signals` record specifying `SIGUSR2` for snapshots and `SIGTERM` or `SIGINT` for shutdown.

## The ctlfd Pipe Protocol Implementation

The **[`src/perf_ctlfd.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_ctlfd.ml)** module implements the bidirectional pipe protocol that enables reliable command acknowledgment. The `create` function establishes two pipes:

```ocaml
(* src/perf_ctlfd.ml, lines 26-35 *)
let create () =
  let ctl_rx, ctl_tx = Core_unix.pipe ~close_on_exec:false () in
  let ack_rx, ack_tx = Core_unix.pipe ~close_on_exec:false () in
  …
  { ctl_rx = Some ctl_rx; ctl_tx; ack_rx; ack_tx = Some ack_tx; … }

```

- **`ctl_tx`** (control transmit): Used by Magic-Trace to send commands to perf
- **`ack_rx`** (acknowledge receive): Used by Magic-Trace to receive confirmations from perf

When constructing the perf command line, `Perf_ctlfd.control_opt` generates the required flag:

```ocaml
(* src/perf_ctlfd.ml, lines 46-50 *)
let control_opt ({ ctl_rx; ack_tx; _ } as t) =
  let p fd = Core_unix.File_descr.to_int (Option.value_exn fd) in
  ([%string "--control=fd:%{p ctl_rx#Int},%{p ack_tx#Int}"], fun () -> close_perf_side_fds t)

```

To trigger a snapshot, Magic-Trace writes `"snapshot\n"` to `ctl_tx` and blocks until it reads `"ack\n"` from `ack_rx` via the `dispatch_and_block_for_ack` function. This synchronous handshake guarantees that the snapshot operation completes before the tool proceeds, eliminating race conditions inherent in signal-based control.

## Signal-Based Control Fallback

When `ctlfd` is unavailable (kernels older than 5.10), Magic-Trace uses asynchronous POSIX signals:

```ocaml
(* src/perf_tool_backend.ml, lines 1008-1016 *)
let take_snapshot t pid =
  match t with
  | Signals { snapshot; _ } -> Signal_unix.send_i snapshot (`Pid pid)
  | Ctlfd { ctlfd; snapshot; _ } ->
      Perf_ctlfd.dispatch_and_block_for_ack ctlfd snapshot |> ignore_perf_exit

let shutdown t pid =
  match t with
  | Signals { shutdown; _ } -> Signal_unix.send_i shutdown (`Pid pid)
  | Ctlfd { ctlfd; shutdown; _ } ->
      Perf_ctlfd.dispatch_and_block_for_ack ctlfd shutdown |> ignore_perf_exit

```

- **`Signal.usr2`** (`SIGUSR2`): Triggers function-breakpoint snapshots during execution
- **`Signal.term`** (`SIGTERM`) or **`Signal.int`** (`SIGINT`): Initiates clean shutdown

The tool places the perf child process in its own process group (lines 124-128 in [`src/perf_tool_backend.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_tool_backend.ml)) to ensure that `Ctrl-C` sent to Magic-Trace does not automatically deliver `SIGINT` to perf, allowing the tool to send `SIGUSR2` first for proper snapshot capture before termination.

## Complete Recording Workflow

The integration of these mechanisms follows a precise sequence:

1. **Capability Detection**: `Perf_capabilities.detect_exn` queries the system and sets the `ctlfd` flag if the kernel is ≥5.10
2. **Control Initialization**: `Control.create` instantiates either a `Ctlfd` record with pipe pairs or a `Signals` record with signal specifications
3. **Process Launch**: `perf record` starts with the `--control=fd:...` argument (when using `ctlfd`) or standard arguments (when using signals)
4. **Runtime Control**: `Control.take_snapshot` and `Control.shutdown` dispatch commands via the appropriate channel—writing to pipes with acknowledgment waits or sending POSIX signals

## Practical Code Examples

### Creating a Control Value

```ocaml
(* Example extracted from src/perf_tool_backend.ml *)
let capabilities = Perf_capabilities.detect_exn () in
let snapshot_when = Snapshot_when.At_exit in
let control = Control.create ~capabilities ~snapshot_when

```

### Sending a Snapshot Command

```ocaml
match control with
| Ctlfd { ctlfd; snapshot; _ } ->
    (* Synchronous: blocks until perf acknowledges *)
    Perf_ctlfd.dispatch_and_block_for_ack ctlfd snapshot
| Signals { snapshot; _ } ->
    (* Asynchronous: fire and forget *)
    Signal_unix.send_i snapshot (`Pid perf_pid)

```

### Building the perf Command Line

```ocaml
let control_opt, invoke_after_fork = Control.control_opt control in
let perf_argv = ["perf"; "record"; "-g"] @ control_opt in
(* Execute perf with constructed arguments *)

```

## Summary

- **Magic-Trace automatically detects** `ctlfd` support by checking for Linux kernel version 5.10 or higher in [`src/perf_capabilities.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_capabilities.ml)
- **The `ctlfd` interface** uses bidirectional pipes for synchronous command acknowledgment, providing superior reliability over signals
- **Signal fallback** employs `SIGUSR2` for snapshots and `SIGTERM`/`SIGINT` for shutdown when `ctlfd` is unavailable
- **Synchronous handshake** via `dispatch_and_block_for_ack` guarantees that snapshot and stop operations complete before Magic-Trace continues execution
- **Process group isolation** prevents signal propagation issues between Magic-Trace and the perf child process

## Frequently Asked Questions

### What is the minimum Linux kernel version required for ctlfd support in Magic-Trace?

Magic-Trace requires Linux kernel version **5.10 or higher** to use the `ctlfd` interface. The `supports_ctlfd` function in [`src/perf_capabilities.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_capabilities.ml) explicitly checks `kernel_version_at_least ~major:5 ~minor:10`. On older kernels, the tool automatically falls back to POSIX signals.

### Why does Magic-Trace prefer ctlfd over POSIX signals for controlling perf?

The **`ctlfd` interface provides synchronous acknowledgment** through a bidirectional pipe protocol. When Magic-Trace writes `"snapshot\n"` to the control pipe, it blocks until perf writes `"ack\n"` to the acknowledgment pipe. This eliminates race conditions and ensures the snapshot completes before the tool proceeds, whereas signals are asynchronous and offer no confirmation of successful handling.

### What signals does Magic-Trace use when ctlfd is unavailable?

When `ctlfd` is not supported, Magic-Trace uses **`SIGUSR2` (User Signal 2)** to trigger snapshots during function calls, and **`SIGTERM`** or **`SIGINT`** to initiate shutdown. These are configured in the `Signals` record created by `Control.create` in [`src/perf_tool_backend.ml`](https://github.com/janestreet/magic-trace/blob/main/src/perf_tool_backend.ml).

### How does the ctlfd handshake work between Magic-Trace and perf?

The handshake occurs through two unidirectional pipes created in `Perf_ctlfd.create`. Magic-Trace writes command strings (`"snapshot\n"` or `"stop\n"`) to the `ctl_tx` pipe and blocks on the `ack_rx` pipe until perf responds with `"ack\n"`. This is implemented in `dispatch_and_block_for_ack`, providing guaranteed delivery semantics that signals cannot replicate.