blob: d6d39cfd7c7da021f55e2aae598cac436623b092 [file] [view] [edit]
# Getting Started with PerfettoSQL Pipe Syntax
This page is a prototype of the introductory guide to querying traces with
PerfettoSQL pipe syntax.
> **Warning:** Pipe syntax is **experimental and unstable**. Do **not** rely on
> it in dashboards, scripts, or production queries. Anything can and will break
> without notice. To run the examples on this page in the Perfetto UI or
> `trace_processor_shell`, start your query with `PERFETTO PRAGMA pipelines = 1;`.
Related pages:
- [Migrating to PerfettoSQL Pipe Syntax](/docs/analysis/perfetto-sql-pipe-migration.md)
- [PerfettoSQL Pipe Syntax Reference](/docs/analysis/perfetto-sql-pipe-syntax.md)
- [PerfettoSQL Pipe Operator Implementation Notes](/docs/design-docs/perfetto-sql-pipelines.md)
---
## How Pipelines Work
Standard SQL queries read inside-out: you start with `SELECT` to list the
columns you want at the end, then write `FROM` to say where the data comes
from, and nest subqueries or CTEs whenever you need multi-step transformations.
With pipe syntax, queries read top-to-bottom in the order data is processed:
1. Start with a **source**: `FROM <table>`, `FROM (<subquery>)`, or
`INTERVAL INTERSECTION OF (...)`.
2. Chain one or more **pipe stages** with `|>` that transform, aggregate, or
reshape the rows step by step.
```sql
PERFETTO PRAGMA pipelines = 1;
FROM slice
|> RENAME dur AS duration_ns
|> SELECT id, name, ts, duration_ns;
```
Every `|>` stage takes the table produced by the previous step and outputs a new
table for the next step.
### Saving a pipeline as a table
You can run a pipeline directly as a query, or save its output into a
`PERFETTO TABLE` so you can query it further with SQL:
```sql
PERFETTO PRAGMA pipelines = 1;
CREATE PERFETTO TABLE named_slices AS
FROM (SELECT id, ts, dur, name FROM slice WHERE dur > 0)
|> RENAME dur AS duration_ns;
SELECT name, duration_ns
FROM named_slices
ORDER BY duration_ns DESC
LIMIT 10;
```
---
## Intersecting Time Intervals: `INTERVAL INTERSECTION OF`
Many trace analysis questions ask where two or more sets of time intervals
`[ts, ts + dur)` overlap:
- What CPU frequency was each core running at during every scheduling slice?
- Which slices were active while a thread was in a particular state?
- How much power was used by each task during active GPU regions?
`INTERVAL INTERSECTION OF` takes two or more interval sources and emits one row
for every overlapping time region they share:
```sql
PERFETTO PRAGMA pipelines = 1;
-- 1. Scheduling slices with positive duration
CREATE PERFETTO TABLE running_sched AS
SELECT ts, dur, cpu, utid
FROM sched
WHERE dur > 0;
-- 2. CPU frequency intervals per CPU
CREATE PERFETTO TABLE cpu_freq AS
SELECT
c.ts,
lead(c.ts) OVER (PARTITION BY t.cpu ORDER BY c.ts) - c.ts AS dur,
t.cpu,
c.value AS freq
FROM counter AS c
JOIN cpu_counter_track AS t ON c.track_id = t.id
WHERE t.name = 'cpufreq';
-- 3. Intersect scheduling slices with CPU frequency per CPU
CREATE PERFETTO TABLE sched_with_freq AS
INTERVAL INTERSECTION OF (running_sched AS s, cpu_freq AS f) PER cpu
|> SELECT ts, dur, cpu, s.utid, f.freq;
```
### How it works
```
running_sched (s): [------- utid 10 -------) [--- utid 20 ---)
cpu_freq (f): [--- 1.8 GHz ---)[-------- 2.4 GHz --------)
-----------------------------------------------
Result regions: [-- 10 @ 1.8G --)[- 10 -] [-- 20 @ 2.4G --)
```
- **Region `ts` and `dur`:** Bare `ts` and `dur` in the output are the start and
duration of the overlapping region, clipped to where all inputs were active at
the same time.
- **Partitioning with `PER`:** Adding `PER cpu` intersects intervals only when
they occur on the same `cpu`. The `cpu` column is available directly by its
bare name.
- **Accessing input columns:** Each input has an alias, here `AS s` and `AS f`.
All columns from `s` and `f` are carried along and accessed via `s.<col>` and
`f.<col>`. This includes each input's original unclipped `s.ts` and `s.dur`.
---
## Summing Over Trees: `TREE ACCUMULATE`
Traces often contain trees where each row has an `id` and a `parent_id`, with
`parent_id IS NULL` at the roots:
- Nested userspace slices in `slice`
- Sampled callstacks from CPU profiling, perf, and native heap profiles
- Java heap graph shortest-path trees
`TREE ACCUMULATE` computes cumulative sums over an `id` and `parent_id` tree in
a single stage, keeping all existing columns and appending the totals:
- **`TREE ACCUMULATE UP`:** Sums each node's value with all of its descendants.
- **`TREE ACCUMULATE DOWN`:** Sums each node's value with all of its ancestors
from the root down to the node.
```sql
PERFETTO PRAGMA pipelines = 1;
CREATE PERFETTO TABLE tree AS
SELECT 0 AS id, NULL AS parent_id, 'root' AS name, 10 AS self
UNION ALL SELECT 1, 0, 'a', 20
UNION ALL SELECT 2, 0, 'b', 30
UNION ALL SELECT 3, 1, 'c', 40;
CREATE PERFETTO TABLE tree_totals AS
FROM tree
|> TREE ACCUMULATE UP SUM(self) AS subtree_total
|> TREE ACCUMULATE DOWN SUM(self) AS path_total;
SELECT name, self, subtree_total, path_total
FROM tree_totals
ORDER BY id;
```
For the tree `root (10) -> a (20) -> c (40)` and `root (10) -> b (30)`:
| `name` | `self` | `subtree_total` | `path_total` |
| :--- | ---: | ---: | ---: |
| `root` | 10 | 100 | 10 |
| `a` | 20 | 60 | 30 |
| `b` | 30 | 30 | 40 |
| `c` | 40 | 40 | 70 |
---
## Reshaping Columns
Instead of wrapping queries in outer `SELECT`s just to add, drop, or rename a
column, you can modify the row in place with column-reshaping stages:
- **`|> SELECT`:** Pick, reorder, or rename columns. You can also use
`* EXCEPT (col1, col2)` to exclude columns or `* REPLACE (other_col AS col1)`
to substitute a column in place:
```sql
FROM slice
|> SELECT * EXCEPT (arg_set_id, parent_id)
```
- **`|> EXTEND`:** Append new columns to the end of the current row while
keeping all existing columns:
```sql
FROM slice AS s
|> EXTEND s.dur AS original_dur
```
- **`|> DROP`:** Remove one or more columns by name:
```sql
FROM slice
|> DROP category, track_id, arg_set_id
```
- **`|> RENAME`:** Rename columns in place without listing every other column:
```sql
FROM slice
|> RENAME ts AS start_ts, dur AS duration_ns
```
- **`|> SET`:** Replace an existing column's value in place:
```sql
FROM tree AS t
|> TREE ACCUMULATE UP SUM(self) AS total
|> SET self = total
|> DROP total
```
---
## Next Steps
- [PerfettoSQL Pipe Syntax Reference](/docs/analysis/perfetto-sql-pipe-syntax.md)
- [Migrating to PerfettoSQL Pipe Syntax](/docs/analysis/perfetto-sql-pipe-migration.md)
- [PerfettoSQL Pipe Operator Implementation Notes](/docs/design-docs/perfetto-sql-pipelines.md)