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

> Learn to attach magic-trace to running processes using PID or fzf fuzzy selection. Explore efficient debugging methods for your applications.

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

---

**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](https://github.com/janestreet/magic-trace) enables performance tracing on already-running processes without requiring application restart. This functionality is implemented in [`src/trace.ml`](https://github.com/janestreet/magic-trace/blob/main/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`](https://github.com/janestreet/magic-trace/blob/main/src/trace.ml) (lines 6069–6079), the flag definition parses user input:

```ocaml
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`](https://github.com/janestreet/magic-trace/blob/main/src/trace.ml)). This function enumerates running processes using the system `ps` command with specific arguments:

```ocaml
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`](https://github.com/janestreet/magic-trace/blob/main/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.

```ocaml
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`:

```ocaml
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`](https://github.com/janestreet/magic-trace/blob/main/src/perf_tool_backend.ml), which handles the actual perf-event attachment.

## Usage Examples

Attach to a specific process by PID:

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

```

Attach to multiple processes simultaneously:

```bash
magic-trace attach -pid 1234,5678,9012

```

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

```bash
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`](https://github.com/janestreet/magic-trace/blob/main/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`](https://github.com/janestreet/magic-trace/blob/main/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.