# Multi-machine recording

This document describes how to record a single Perfetto trace that captures
events from two Linux machines simultaneously. It uses `traced_relay` on
the second machine to forward producer IPC to a `traced` running on the
first machine.

For background on what multi-machine tracing is and how it works under the
hood, see
[Multi-machine architecture](/docs/deployment/multi-machine-architecture.md).

## Use case

You have a workload split across two Linux machines — e.g. a client on
machine A driving a server on machine B, or a host running a Linux VM —
and you want a single trace covering both, so cross-machine causality is
visible in one timeline and queryable in one trace file.

In the rest of this guide, `host` is the machine that will run `traced`
and own the trace buffers, and `guest` is the second machine whose
producers feed into the same trace via `traced_relay`. Substitute
`<host-ip>` with the IP address (or hostname) of `host` as reachable from
`guest`.

## {#approaches} Choosing an approach

Live relaying, which this guide covers, is one of three ways to get a
single trace spanning several machines. Which one fits depends on whether
the machines can reach each other at recording time and how much control
you have over the producers:

1. **Record it as one trace (this guide).** `traced_relay` on each guest
   forwards producers to a single `traced` on the host, which owns the
   buffers and clock-syncs the guests while recording. Best fidelity
   (cross-machine clock sync is measured, not assumed), but needs a live
   network path between the machines during the recording.

2. **Record independently with pre-assigned machine ids.** Each machine
   records its own trace, but SDK producers are initialized with a unique
   `machine_id` (`TracingInitArgs::machine_id` in the C++ SDK,
   `PerfettoProducerBackendInitArgsSetMachineId()` in the C SDK). Merging
   the files later needs no configuration: every packet already says which
   machine it came from, and the traces align through their wall clocks.
   See [Merging traces from the command line](/docs/analysis/merging-traces.md#no-config).

3. **Record independently, merge afterwards.** Each machine records a
   normal trace; nothing is coordinated at recording time. Machine
   attribution and any clock relations the traces do not carry are supplied
   when merging, either interactively in
   [the Perfetto UI](/docs/visualization/merging-traces.md) or with a
   [perfetto_manifest](/docs/reference/perfetto-manifest.md) file. The most
   flexible option, and the only one available for traces that already
   exist.

All three produce the same result in the trace model: one trace, one
timeline, one [`machine` table][machine-table] row per machine. They can
also be combined, for example by merging two relay-recorded traces from
different sites.

## Prerequisites

* `tracebox` available on both machines. See
  [Start Using Perfetto](/docs/getting-started/start-using-perfetto.md) for
  how to obtain a binary.
* A network path from `guest` to `host` on a chosen TCP port (e.g. port
  `20001`). If there's a firewall between them, open the port.
* No `traced` already running on either machine. On the `guest`, `traced`
  and `traced_relay` would contest the same local producer socket; on the
  `host` you want the `traced` you start below, not a system one.
* `host` and `guest` are separate OS images — two machines, a host plus a
  VM, etc. Pointing both producers at the same kernel does not work.

NOTE: This guide records ftrace events for the example, which on Linux
typically requires running the producer commands as root (or with
`CAP_SYS_ADMIN`). The IPC commands themselves do not require root.

## Usage

### Step 1: Start `traced` on the host, listening on TCP

On `host`:

```bash
PERFETTO_PRODUCER_SOCK_NAME=0.0.0.0:20001 \
  tracebox traced --enable-relay-endpoint
```

`PERFETTO_PRODUCER_SOCK_NAME` rebinds the producer socket from the default
UNIX path to a TCP listener that remote machines can reach.
`--enable-relay-endpoint` makes that socket accept `traced_relay`
connections in addition to ordinary local producers.

For deployments that need to keep a local AF_UNIX producer socket
listening alongside the TCP relay socket — so host-side unprivileged
producers and remote `traced_relay` clients use separate sockets —
list both in `PERFETTO_PRODUCER_SOCK_NAME` and use the narrower
`--enable-relay-endpoint-on` form, which names the socket that should
carry `RelayPort`:

```bash
PERFETTO_PRODUCER_SOCK_NAME=/tmp/perfetto-producer,0.0.0.0:20001 \
  tracebox traced --enable-relay-endpoint-on=0.0.0.0:20001
```

In this form the local UNIX socket continues to serve unprivileged
producers and never accepts relay calls, which keeps the `RelayPort`
service unreachable from local apps. The flag selects from the producer
sockets already listed in `PERFETTO_PRODUCER_SOCK_NAME` — it does not
introduce a new endpoint, so the named socket must appear in the env
var.

Leave this process running.

### Step 2: Start `traced_probes` on the host

In a second shell on `host`:

```bash
PERFETTO_PRODUCER_SOCK_NAME=127.0.0.1:20001 \
  sudo -E tracebox traced_probes
```

The same env var that rebound `traced`'s listener also tells local
producers where to connect — without it, `traced_probes` would still try
the default UNIX socket and fail. `sudo -E` preserves the env var across
the privilege escalation needed for ftrace. (If you used the
`--enable-relay-endpoint-on` form in Step 1 instead, omit the env var:
the default UNIX producer socket is still up, and `traced_probes` will
connect to it without any extra configuration.)

### Step 3: Start `traced_relay` on the guest

On `guest`:

```bash
PERFETTO_RELAY_SOCK_NAME=<host-ip>:20001 \
  tracebox traced_relay
```

`traced_relay` opens the standard local producer socket on `guest` and
forwards every producer IPC frame to the host's relay endpoint. You should
see a startup line of the form:

```
Started traced_relay, listening on /tmp/perfetto-producer, forwarding to <host-ip>:20001
```

(The listening path may instead be `/run/perfetto/traced-producer.sock` if
that directory exists — both are valid Linux defaults.)

Leave this process running.

### Step 4: Start `traced_probes` on the guest

In a second shell on `guest`:

```bash
sudo tracebox traced_probes
```

No env var is needed: with `PERFETTO_PRODUCER_SOCK_NAME` unset,
`traced_probes` connects to the default Linux producer socket — which is
exactly the path `traced_relay` is listening on — so the two find each
other automatically.

### Step 5: Record a trace from the host

Multi-machine tracing requires an explicit `TraceConfig` — the
`tracebox perfetto -t 10s ... sched/sched_switch` shorthand records on
the host machine only (see
[Multi-machine architecture](/docs/deployment/multi-machine-architecture.md#data-source-dispatch)).

On `host`, write a config file:

```bash
cat > config.pbtx <<'EOF'
buffers {
  size_kb: 32768
  fill_policy: RING_BUFFER
}
trace_all_machines: true
data_sources {
  config {
    name: "linux.ftrace"
    ftrace_config {
      ftrace_events: "sched/sched_switch"
    }
  }
}
duration_ms: 10000
EOF
```

Then record:

```bash
tracebox perfetto --txt -c config.pbtx -o trace.pftrace
```

### Step 6: Verify both machines are in the trace

Open `trace.pftrace` at <https://ui.perfetto.dev>. In the SQL query view,
run:

```sql
SELECT id, raw_id, sysname, release, arch, num_cpus FROM machine;
```

Expect two rows. `id = 0` is always the host; remote machines have a
non-zero `raw_id`. See the [`machine` table reference][machine-table] for
the full set of columns.

To confirm that events from both machines made it into the trace, group
ftrace events by machine. `ftrace_event` does not carry `machine_id`
directly — each row references a `cpu` (via `ucpu`), and `cpu` carries
the `machine_id`:

```sql
SELECT cpu.machine_id, COUNT(*) AS num_events
FROM ftrace_event
JOIN cpu USING (ucpu)
GROUP BY cpu.machine_id;
```

You should see one row per machine, each with a non-zero count. The same
join pattern works against the `thread` or `process` tables to slice by
machine through different dimensions.

## Troubleshooting

* **Only one row in `machine`.** Connectivity problem. Check that
  `<host-ip>:20001` is reachable from `guest` (e.g. with `nc -zv`), that
  the firewall is open, and that `traced` on the host bound `0.0.0.0`
  (not `127.0.0.1`).
* **`traced_relay` exits immediately and prints usage.**
  `PERFETTO_RELAY_SOCK_NAME` is unset or empty — `traced_relay` has no
  host to forward to.
* **`traced_probes` on the guest fails with a connect error.** Make sure
  `traced_relay` is running on the guest (Step 3) and that no stale
  `traced` is also running there contesting the producer socket.
* **Producers on the host fail to connect.** Confirm `traced` started
  with `PERFETTO_PRODUCER_SOCK_NAME=0.0.0.0:20001` (Step 1) and that the
  producers are pointed at the same address (Step 2).

## Next steps

* [Multi-machine architecture](/docs/deployment/multi-machine-architecture.md) —
  the why: how `traced_relay`, machine identity, and cross-kernel clock
  sync fit together.
* [Merging traces from the command line](/docs/analysis/merging-traces.md) —
  the post-hoc alternative: combine independently recorded traces onto one
  timeline when a live network path is not an option.
* [PerfettoSQL: Getting Started](/docs/analysis/perfetto-sql-getting-started.md) —
  for slicing the resulting trace by `machine_id` across `cpu`, `thread`,
  and `process`.
* [Trace Processor](/docs/analysis/trace-processor.md) — embed analysis
  in scripts or pipelines once the recording is repeatable.

[machine-table]: /docs/analysis/sql-tables.autogen#machine
