|
1 | 1 | # Perfetto Ingest Plugin (`lj-perfetto-ingest`) |
2 | 2 |
|
3 | 3 | Imports Perfetto trace files (`.pftrace` / `.perfetto-trace`) into the logjet |
4 | | -ecosystem as OTel traces, metrics, logs, and events. |
| 4 | +ecosystem as OTel logs, traces, and metrics. |
5 | 5 |
|
6 | 6 | ## Architecture |
7 | 7 |
|
8 | 8 | ``` |
9 | 9 | .pftrace ──→ trace_processor (spawned as subprocess) |
10 | | - ├── export sqlite ──→ sqlite_reader ──→ trace_mapper ──→ OTel spans |
| 10 | + ├── export sqlite ──→ sqlite_reader ──→ trace_mapper ──→ OTel spans |
11 | 11 | └── --run-metrics ──→ metrics_reader ──→ metric_mapper ──→ OTel metrics |
12 | | - log_mapper ──→ OTel logs |
| 12 | + log_mapper ──→ OTel logs |
| 13 | + │ |
| 14 | + ▼ |
| 15 | + buffer & sort by ts |
13 | 16 | │ |
14 | 17 | ▼ |
15 | 18 | ljd spool (.logjet) |
16 | 19 | ``` |
17 | 20 |
|
18 | 21 | The plugin is an **active source** (`mode: 1`). ljd calls `lj_ingest_fetch()` once, |
19 | | -which runs the full pipeline and streams OTel payloads through the generic record |
20 | | -callback. |
| 22 | +which runs the full pipeline. All records from traces, logs, and metrics are |
| 23 | +collected, sorted by timestamp, then streamed through the generic record callback |
| 24 | +to guarantee monotonic timestamps in the logjet block format. |
21 | 25 |
|
22 | 26 | ## Requirements |
23 | 27 |
|
24 | 28 | - Perfetto trace processor binary (`trace_processor` or `trace_processor_shell`). |
25 | 29 | Build it from the bundled Perfetto source: |
26 | 30 | ```bash |
27 | | - ./scripts/build-perfetto.sh |
| 31 | + ./demo/perfetto/build-perfetto.sh |
28 | 32 | ``` |
29 | 33 | - A `.pftrace` trace file to import. |
30 | 34 |
|
31 | 35 | ## Usage |
32 | 36 |
|
33 | 37 | ```bash |
34 | 38 | # Build the plugin and ljd: |
35 | | -make build |
| 39 | +make dev |
| 40 | + |
| 41 | +# Create a config file (ljd uses YAML config, not CLI flags): |
| 42 | +cat > /tmp/perfetto.conf <<EOF |
| 43 | +output: file |
| 44 | +file.path: ./spool |
| 45 | +file.size: 10mb |
| 46 | +file.name: perfetto.logjet |
| 47 | +ingest.protocol: plugin |
| 48 | +ingest.plugin-path: ./target/debug/liblj_perfetto_ingest.so |
| 49 | +EOF |
36 | 50 |
|
37 | 51 | # Run the import: |
38 | 52 | LJD_PERFETTO_TRACE_FILE=/path/to/trace.pftrace \ |
39 | 53 | LJD_PERFETTO_TRACE_PROCESSOR=/path/to/trace_processor_shell \ |
40 | | - ljd serve \ |
41 | | - --ingest-protocol plugin \ |
42 | | - --ingest-plugin perfetto \ |
43 | | - --storage ./otel-spool |
| 54 | + ljd serve --config /tmp/perfetto.conf |
44 | 55 | ``` |
45 | 56 |
|
| 57 | +See `demo/perfetto/perfetto-to-logjet/run-demo.sh` for a complete end-to-end |
| 58 | +example that records, imports, and opens the result in `ljx view`. |
| 59 | + |
46 | 60 | ## Environment Variables |
47 | 61 |
|
48 | 62 | | Variable | Required | Default | Description | |
49 | 63 | |----------|----------|---------|-------------| |
50 | 64 | | `LJD_PERFETTO_TRACE_FILE` | **Yes** | — | Path to the `.pftrace` input file. | |
51 | 65 | | `LJD_PERFETTO_TRACE_PROCESSOR` | No | PATH search | Path to `trace_processor_shell` binary. | |
52 | 66 | | `LJD_PERFETTO_TIMESTAMP_POLICY` | No | `best-effort` | `best-effort` or `require-realtime`. | |
53 | | -| `LJD_PERFETTO_METRICS` | No | (none) | Comma-separated metric names to run, e.g. `trace_stats,android_startup`. | |
| 67 | +| `LJD_PERFETTO_METRICS` | No | (none) | Comma-separated metric names to run, e.g. `trace_stats`. | |
54 | 68 |
|
55 | | -## Output Signals |
| 69 | +## Covered Perfetto Types |
56 | 70 |
|
57 | | -| Perfetto Source | OTel Signal | Record Type | |
58 | | -|-----------------|-------------|-------------| |
59 | | -| `slice` table | Traces (Spans) | `Traces` | |
60 | | -| Metrics JSON | Metrics (Gauges) | `Metrics` | |
61 | | -| Analysis summary | Logs | `Logs` | |
62 | | -| (reserved) | Events | `Events` | |
| 71 | +Every table in the exported SQLite DB has a typed model and DB reader. Types |
| 72 | +with data are mapped to OTel log records with structured attributes: |
63 | 73 |
|
64 | | -Spans are batched in groups of 200 per OTLP export request. |
| 74 | +| Perfetto Table | OTel Signal | Attributes | |
| 75 | +|---------------|-------------|------------| |
| 76 | +| `sched_slice` | Logs | cpu, end_state, dur_ns | |
| 77 | +| `thread_state` | Logs | state, dur_ns, cpu, io_wait, blocked_function | |
| 78 | +| `ftrace_event` | Logs | name, cpu, utid | |
| 79 | +| `spurious_sched_wakeup` | Logs | utid, waker_utid | |
| 80 | +| `instant` | Logs | name, track_id | |
| 81 | +| `slice` | Traces + Logs | name, dur_ns, depth, parent_id | |
| 82 | +| `counter` | Metrics (planned) | value | |
| 83 | +| `process`, `thread`, `cpu`, `machine`, `metadata`, `args`, `clock_snapshot` | Metadata | used internally | |
| 84 | +| `flow`, `heap_*`, `stack_*`, `memory_*`, `protolog`, `android_logs`, `filedescriptor` | Models ready | not yet mapped | |
| 85 | + |
| 86 | +Each log record carries integer attributes (`perfetto.sched.dur_ns`, etc.) for |
| 87 | +structured downstream consumption alongside a human-readable body. |
65 | 88 |
|
66 | 89 | ## Timestamp Policy |
67 | 90 |
|
68 | 91 | Perfetto timestamps are trace-clock values (typically `CLOCK_MONOTONIC`). The plugin |
69 | 92 | converts them to Unix epoch nanoseconds using `clock_snapshot` REALTIME entries. |
70 | 93 |
|
71 | | -- **best-effort** (default): Spans without realtime data are skipped. Spans before |
72 | | - the first snapshot are extrapolated backwards. |
| 94 | +- **best-effort** (default): Spans without realtime data use extrapolation. |
| 95 | + Spans before the first snapshot are extrapolated backwards. |
73 | 96 | - **require-realtime**: The pipeline fails if any span cannot be converted. |
74 | 97 |
|
| 98 | +Records from all mappers are collected into a buffer, sorted by timestamp, then |
| 99 | +emitted sequentially. This guarantees monotonicity within logjet blocks even when |
| 100 | +different mapper types produce interleaved time ranges. |
| 101 | + |
75 | 102 | ## Limitations |
76 | 103 |
|
77 | | -- **No flow-to-link mapping**: `flow` table entries are read but not yet mapped |
78 | | - to OTel span links. |
79 | | -- **No args-to-attributes mapping**: Per-slice key-value arguments are read but |
80 | | - not attached to spans. |
81 | | -- **Thread/process context**: Thread and process names are loaded but not fully |
82 | | - joined to spans via track relationships. |
83 | | -- **Replay/bridge**: Traces and metrics stored in `.logjet` can be exported via |
84 | | - `ljx export` but are not yet forwarded by `ljd bridge/replay` (which currently |
85 | | - only forwards logs). |
86 | | -- **Metrics**: Only scalar metric values are supported (no histograms). |
87 | | -- **Event signal**: The `Events` record type is reserved but not yet generated |
88 | | - by this plugin. |
| 104 | +- **Replay/bridge**: Only logs are forwarded by `ljd bridge/replay`. Traces and |
| 105 | + metrics stored in `.logjet` can be exported via `ljx export` (parquet). |
| 106 | +- **ljx view**: Only decodes `ExportLogsServiceRequest` — trace/metric records |
| 107 | + appear as binary data in the detail pane. Trace mapping is disabled by default. |
| 108 | +- **Trace span emission**: Currently disabled (ljx can't render it). Enable by |
| 109 | + uncommenting `trace_mapper::map_traces` in `lib.rs`. |
| 110 | +- **Histograms**: Metrics only support scalar gauge values. |
| 111 | +- **Event signal**: The `Events` record type is reserved but not yet generated. |
0 commit comments