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 perfack_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 executionSignal.term(SIGTERM) orSignal.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:
- Capability Detection:
Perf_capabilities.detect_exnqueries the system and sets thectlfdflag if the kernel is ≥5.10 - Control Initialization:
Control.createinstantiates either aCtlfdrecord with pipe pairs or aSignalsrecord with signal specifications - Process Launch:
perf recordstarts with the--control=fd:...argument (when usingctlfd) or standard arguments (when using signals) - Runtime Control:
Control.take_snapshotandControl.shutdowndispatch 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
ctlfdsupport by checking for Linux kernel version 5.10 or higher insrc/perf_capabilities.ml - The
ctlfdinterface uses bidirectional pipes for synchronous command acknowledgment, providing superior reliability over signals - Signal fallback employs
SIGUSR2for snapshots andSIGTERM/SIGINTfor shutdown whenctlfdis unavailable - Synchronous handshake via
dispatch_and_block_for_ackguarantees 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →