How to Attach magic-trace to a Running Process: PID vs. fzf Selection Methods

magic-trace supports two mutually exclusive attachment methods: explicitly supplying comma-separated PIDs via the -pid flag, or using an interactive fzf fuzzy finder that parses running processes from ps output.

The attach subcommand in janestreet/magic-trace enables performance tracing on already-running processes without requiring application restart. This functionality is implemented in src/trace.ml and provides flexibility for both scripted automation and interactive workflows.

Two Methods for Process Attachment

The magic-trace attach command resolves target processes through one of two code paths in the attach_command function. These methods are mutually exclusive—supplying a PID list bypasses the interactive selector entirely.

Explicit PID Specification (-pid Flag)

The PID list method accepts one or more process identifiers as comma-separated integers using the -pid (or -p) flag. The argument parser defines this as optional (Arg_type.comma_separated int), which the code later converts to Pid.t values via Pid.of_int.

In src/trace.ml (lines 6069–6079), the flag definition parses user input:

let pids =
  let open Param in
  flag "-pid" (optional (Arg_type.comma_separated int))
    ~doc:"PID Comma-separated process id(s) to attach to"
in

When the flag is present, attach_command maps the integers directly to internal PID representations and proceeds immediately to attach_and_record, skipping all interactive selection logic.

Interactive fzf Fuzzy Selection

When no -pid flag is provided, the system invokes select_pid () (implemented around lines 6048–6084 in src/trace.ml). This function enumerates running processes using the system ps command with specific arguments:

let select_pid () =
  let%bind lines =
    Shell.run_lines ~prog:"ps" ~args:["x"; "-w"; "--no-headers"; "-o"; "pid,args"] ()
  in
  (* ... processing logic ... *)

The captured lines are piped to Fzf.pick_one from the bundled fzf library (vendor/fzf/src/fzf.ml). The user selects a line containing both PID and command arguments; the code extracts the first space-delimited token as the target PID.

match choice with
| None -> Deferred.Or_error.error_string "No process selected"
| Some line ->
  let pid_str = String.strip line |> String.split ~on:' ' |> List.hd_exn in
  return (Pid.of_int (Int.of_string pid_str))

How PID Resolution Works Under the Hood

The attachment orchestration follows a strict flow in attach_command:

let%bind (pids : Pid.t list) =
  match pids with
  | None ->                      (* Interactive path *)
      select_pid () |> Deferred.Or_error.map ~f:(fun pid -> [ pid ])
  | Some pids ->                 (* Explicit path *)
      return (List.map ~f:Pid.of_int pids)
in
attach_and_record opts ~elf ~debug_print_perf_commands ~collection_mode pids

Key implementation details:

  • Type Safety: Uses Jane Street's Core library Pid.t type rather than raw integers, enforced through Pid.of_int conversion.
  • Duplicate Detection: The code explicitly rejects duplicate PIDs using List.contains_dup before attachment.
  • Backend Delegation: Resolved PIDs are passed to Backend.Recording.attach_and_record in src/perf_tool_backend.ml, which handles the actual perf-event attachment.

Usage Examples

Attach to a specific process by PID:

magic-trace attach -pid 1234
magic-trace attach -p 9012

Attach to multiple processes simultaneously:

magic-trace attach -pid 1234,5678,9012

Use interactive fzf selection (requires fzf in $PATH):

magic-trace attach

This presents an interface similar to:


? Choose a process to trace
  1234 /usr/bin/python3 my_script.py
  5678 /usr/bin/node server.js
  9012 /usr/local/bin/my_service --verbose

Error Handling and Edge Cases

The implementation includes specific safeguards:

  • Missing fzf: If Fzf.pick_one cannot locate the fzf executable, the system returns an explicit error instructing the user to utilize the -pid flag instead.
  • Parse Failures: Invalid PID strings trigger immediate Or_error failures before any perf attachment attempts.
  • Permission Checks: Standard Linux permissions apply; attaching to processes owned by other users requires appropriate privileges (handled at the perf backend level).

Summary

  • Explicit attachment uses -pid or -p with comma-separated integers, parsed via Arg_type.comma_separated int and converted to Pid.t via Pid.of_int in src/trace.ml.
  • Interactive attachment invokes select_pid (), which executes ps x -w --no-headers -o pid,args and pipes output to Fzf.pick_one when no PID flag is provided.
  • Architecture separates CLI parsing (Command.async_or_error), process enumeration (external ps utility), and backend attachment (src/perf_tool_backend.ml).
  • Fzf dependency is optional; missing binaries trigger a clear error message directing users to explicit PID specification.

Frequently Asked Questions

What happens if I specify an invalid PID to magic-trace attach?

The command validates PID conversion early using Pid.of_int and Int.of_string. If the string cannot be parsed as an integer or if the PID refers to a non-existent process, the Or_error monad returns a specific error before attempting any perf attachment, preventing confusing low-level failures.

Can I attach to multiple processes simultaneously with magic-trace?

Yes. The -pid flag accepts comma-separated values (e.g., -pid 1234,5678). The parser splits these into a list of integers, maps each to Pid.t, and passes the entire list to attach_and_record. Note that select_pid () (the fzf method) returns only a single PID.

Is fzf required to use magic-trace attach?

No. While the interactive fuzzy selector provides a convenient user experience, it is strictly optional. If fzf is not installed or not in $PATH, the code explicitly requires the -pid flag. This design ensures magic-trace remains functional in headless or containerized environments.

Where does magic-trace get the process list for fzf selection?

The select_pid function executes the system ps command with arguments ["x"; "-w"; "--no-headers"; "-o"; "pid,args"] using Shell.run_lines. This captures all processes visible to the user, formats them as "PID command" strings, and presents them through the Fzf library for interactive selection.

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 →