summaryrefslogtreecommitdiff
path: root/docs/tutorial/tutorial.md
blob: 01201a2880f5fbf262f4324d8f0d8eaa6c3119c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# I/O Riot NG: a guided tour

This tutorial walks through every major surface of `ior` (the dashboard tabs, the live stream, recording, headless modes, and the in-TUI flamegraph) using short animated GIFs so you can *see* what the keys actually do.

Every GIF in this document is regenerated from a [VHS](https://github.com/charmbracelet/vhs) tape under [`tapes/`](./tapes). To rebuild them all, run `sudo -v && mage demo` (see [Regenerating the demo](#regenerating-the-demo)).

## Contents

1. [Installing ior](#installing-ior)
2. [First launch: the PID picker](#first-launch-the-pid-picker)
3. [Touring the dashboard tabs](#touring-the-dashboard-tabs)
   - [1 · Flamegraph (default landing tab)](#1--flamegraph-default-landing-tab)
   - [2 · Overview](#2--overview)
   - [3 · Syscalls](#3--syscalls)
   - [4 · Files](#4--files)
   - [5 · Processes](#5--processes)
   - [6 · Latency + Gaps](#6--latency--gaps)
   - [7 · Stream](#7--stream)
4. [Mastering the Stream tab](#mastering-the-stream-tab)
   - [Pause + stacked filters](#pause--stacked-filters)
   - [Regex search](#regex-search)
   - [CSV export](#csv-export)
5. [Choosing what to trace](#choosing-what-to-trace)
6. [Recording for offline analysis](#recording-for-offline-analysis)
   - [TUI Parquet recording](#tui-parquet-recording)
   - [Headless modes](#headless-modes)
7. [Regenerating the demo](#regenerating-the-demo)

## Installing ior

See the [main README](../../README.md) for full install steps. The quickest path from any Docker-capable Linux host:

```shell
git clone https://codeberg.org/snonux/ior ~/git/ior
cd ~/git/ior
mage buildDocker          # builds inside a Rocky 9 container, ~15 min on first run
```

For a native build (libbpfgo must be cloned alongside the repo first; see the README):

```shell
mage all
```

ior needs `CAP_BPF`, so every invocation below uses `sudo`.

The build dance only has to happen once. The resulting `ior` binary is fully statically linked and uses CO-RE, so the same binary runs on any BTF-enabled Linux kernel without recompilation. See the [Compile once, run everywhere](../../README.md#compile-once-run-everywhere) section for details.

## First launch: the PID picker

`sudo ./ior` starts with the **PID picker**. The cursor is on **All PIDs**, so pressing `Enter` traces the whole system. Type into the filter box to narrow the list by PID, comm, or cmdline; arrow keys move the selection.

![Cold start: PID picker, then the dashboard appears](./assets/01-launch.gif)

The same picker can be re-opened later from the dashboard with `p`.

![PID picker default state](./assets/screenshot-pidpicker.png)

## Touring the dashboard tabs

The dashboard has seven tabs, addressable by number key. The default landing tab is **Flamegraph**. `tab` / `shift+tab` step forward / back.

| Key | Tab               | What it shows                                                            |
|-----|-------------------|--------------------------------------------------------------------------|
| `1` | Flamegraph (`Flm`)| Live FlameGraph of the configured stack (`comm`/`path`/`tracepoint`)     |
| `2` | Overview (`Ovr`)  | Sparkline + top syscalls + top paths summary                             |
| `3` | Syscalls (`Sys`)  | Sortable per-syscall counters, latency, byte volume                      |
| `4` | Files (`Fil`)     | Per-path counters; `d` toggles directory grouping                        |
| `5` | Processes (`Pro`) | Per-process / per-comm counters                                          |
| `6` | Latency (`Lat`)   | Latency + inter-syscall gap histograms                                   |
| `7` | Stream (`Str`)    | Live tail of individual traced events                                    |

### 1 · Flamegraph (default landing tab)

The first thing you see after dismissing the PID picker is the **live flamegraph**. Bars grow as new events come in. `o` cycles the stack ordering (e.g. `comm/path/tracepoint` ↔ `comm/tracepoint/path`); `b` toggles the size metric (event count vs. bytes).

![Live flamegraph rebuilding from real workload](./assets/13-tui-flamegraph.gif)

### 2 · Overview

Press `2`. The Overview tab is the at-a-glance view: a sparkline of recent event volume, the top syscalls, and the top paths.

![Overview tab populating](./assets/02-overview-tab.gif)

### 3 · Syscalls

Press `3`. A sortable table of every traced syscall (count, average latency, total bytes). `j` / `k` (or arrow keys) scroll the rows; `←` / `→` move the selected column; `s` sorts by the selected column using its default direction; `S` reverses.

![Syscalls table with sort + reverse-sort](./assets/03-syscalls-tab.gif)

### 4 · Files

Press `4`. Per-path counters. The most useful key here is `d`, which toggles **directory grouping**: paths roll up into their parent directory, which is essential when one process touches thousands of files in `/usr/share/...`.

![Files tab toggling directory grouping](./assets/04-files-tab.gif)

### 5 · Processes

Press `5`. Per-process / per-comm view. `S` reverse-sorts; combine with `←` / `→` to pick a column.

![Processes tab](./assets/05-processes-tab.gif)

### 6 · Latency + Gaps

Press `6`. Two histograms: syscall **latency** (how long the syscall ran) and the inter-syscall **gap** (idle time on the same thread between syscalls). The big-write workload running in the background spreads the latency distribution noticeably.

![Latency + gap histograms](./assets/06-latency-gaps-tab.gif)

### 7 · Stream

Press `7`. A live tail of every traced event row: comm, PID, TID, syscall, file, FD, return value, bytes, latency, gap. This is the workhorse view; the next section explores it in depth.

![Stream tab live-tailing rows](./assets/07-stream-live.gif)

## Mastering the Stream tab

Stream has two modes: **Live** (rows scroll past) and **Pause** (`space` toggles). Almost everything interesting happens in pause mode.

### Pause + stacked filters

In pause mode, navigate with `j`/`k` (rows) and `←` / `→` (columns). Pressing `Enter` on the selected cell **pushes a new filter onto a stack** and immediately re-filters the ring buffer. Filters are stackable, so you can drill down: first by `Comm`, then by `Syscall`, then by `File`. `Esc` pops the most recent filter (LIFO); keep hitting `Esc` to undo all the way back.

![Pause, push two filters, undo with Esc](./assets/08-stream-pause-filter.gif)

The filter is reflected in the bottom status line, and matches the same syntax you'd type by hand: `comm~bash`, `syscall~openat`, `latency>=100000`, etc.

### Regex search

`/` opens a forward regex prompt; `?` opens a backward one. `n` jumps to the next match in the same direction; `N` reverses. The search runs against every column on every row in the ring buffer and wraps at the end.

![Regex search with /, n, n, then ?](./assets/09-stream-regex-search.gif)

### CSV export

Four keys, four flavours:

- `e`: quick export of the **current TUI-filter snapshot** to `ior-stream-<timestamp>.csv` in the current working directory. Works from any tab, not just Stream.
- `x`: quick export of the **paused stream view** specifically (preserves your filter stack).
- `X`: same as `x`, but prompts for a filename first.
- `E`: open the most recent stream-exported CSV in your `$EDITOR` (`hx` / `vi` fallback).

![Press 'e', then ls the resulting CSV](./assets/10-stream-csv-export.gif)

If you don't want CSV export at all, start ior with `-tuiExport=false`; the help footer hides the export keys and `e` becomes a no-op.

## Choosing what to trace

Three modal pickers reshape what the rest of the TUI sees:

- `p`: **PID picker** (re-opens the launch picker).
- `t`: **TID picker** for thread-level focus.
- `o`: **Probes** dialog: enable / disable individual syscall tracepoints.

![PID, TID, and probe pickers](./assets/11-pid-tid-probe.gif)

Restricting to a single PID is also exposed as a CLI flag (`-pid <n>`), as is comm/path filtering (`-comm`, `-path`). Tracepoint subsetting on the command line uses `-tps <regex>` / `-tpsExclude <regex>`.

## Recording for offline analysis

ior has three persistence flows; each solves a different problem.

| Flow                   | How                                          | What you get                                            |
|------------------------|----------------------------------------------|---------------------------------------------------------|
| TUI Parquet recording  | `R` from the dashboard                       | streaming Parquet of every row that passes your filter  |
| Headless `.ior.zst`    | `sudo ./ior -flamegraph -name <name>`        | one aggregated native trace artifact (bandwidth-cheap)  |
| Headless Parquet       | `sudo ./ior -parquet trace.parquet`          | streaming Parquet, full firehose, no TUI                |
| Plain CSV              | `sudo ./ior -plain`                          | one CSV row per event on stdout                         |

### TUI Parquet recording

Press `R` in the dashboard, accept the default filename (`ior-recording-<timestamp>.parquet`) with `Enter`, and rows start streaming to disk. The footer shows the active recording path (or the last error). Press `R` again to stop.

![Start, run, and stop a parquet recording](./assets/12-parquet-recording.gif)

The recorder follows your *current* TUI global filter. Narrow with `p`/`t`/`o` first if you want a focused capture.

### Headless modes

For unattended captures or scripting, skip the TUI entirely. The demo runs all three back-to-back, capped with `-duration` so each terminates on its own.

![Three headless flows in one tape](./assets/14-headless-modes.gif)

`-flamegraph` writes one aggregated `.ior.zst` artifact at shutdown, ideal for `ior`'s native flamegraph and integration workflows. `-parquet` streams every row, so the file grows continuously. `-plain` is the lightest weight: CSV to stdout you can pipe into anything.

## Regenerating the demo

The whole asset pipeline is reproducible:

```shell
mage installDemoTools          # one-time: VHS via go install + ttyd via dnf
sudo -v                        # warm the sudo timestamp once
mage demo                      # regen all 14 GIFs + screenshots (~10 min)
```

Or rebuild a single tape after editing it:

```shell
TAPE=07-stream-live mage demoOne
```

Tapes live in [`tapes/`](./tapes), the background workload that drives them is [`scripts/workload.sh`](./scripts/workload.sh), and the resulting assets land in [`assets/`](./assets). VHS records headlessly under `ttyd` + Chromium, so no real terminal window opens; `mage demo` is safe to run in the background while you keep working.

## Hotkey Quick Reference

### Global keys

| Key | Action |
|-----|--------|
| `tab` / `shift+tab` | next / previous tab |
| `1`–`7` | jump to tab by number (1=Flame, 2=Overview, 3=Syscalls, 4=Files, 5=Processes, 6=Latency, 7=Stream) |
| `H` | toggle bottom help panel |
| `e` | export filtered stream snapshot to CSV |
| `R` | start / stop Parquet recording |
| `p` | re-open PID picker |
| `t` | open TID picker |
| `o` | open probe selection dialog |
| `r` | refresh dashboard snapshot |
| `q` / `ctrl+c` | quit |

### Tab-specific keys (`3:Syscalls`, `4:Files`, `5:Processes`)

| Key | Action |
|-----|--------|
| `s` | sort by selected column (default direction) |
| `S` | reverse-sort by selected column |
| `j`/`k` or `↑`/`↓` | scroll list |
| `d` (Files only) | toggle directory grouping |

### Stream tab (`7:Stream`)

| Key | Action |
|-----|--------|
| `space` | toggle live / pause mode |
| `g` / `G` | jump to top / tail |
| `j`/`k` or `↑`/`↓` | move row (pause) / scroll (live) |
| `←`/`→` or `h`/`l` | move selected column (pause only) |
| `enter` | push cell value as filter (pause) |
| `esc` | pop most recent filter (LIFO) |
| `c` | clear all stream filters |
| `f` | open advanced filter modal |
| `/` / `?` | regex search forward / backward |
| `n` / `N` | next / previous search match |
| `x` | quick CSV export of paused view |
| `X` | CSV export with filename prompt |
| `E` | open last CSV export in `$EDITOR` |