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

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:

(* 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, the Control.create function examines the capability mask to construct the appropriate control value:

(* 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 module implements the bidirectional pipe protocol that enables reliable command acknowledgment. The create function establishes two pipes:

(* 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:

(* 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:

(* 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) 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

(* 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

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

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
  • 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 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.

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →