- Rust 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Broadens the trace from the CPU die to the whole board: every hwmon temperature and fan, GPU columns, and disk throughput differenced from /proc/diskstats. Adds run metadata, workload wrapping and an end-of-run summary to the CLI around it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RBkv7V5GNmhMu8pv3JBT1D |
||
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
pulse
A real-time system monitoring dashboard for the terminal, and a trace recorder for the runs you want to keep.
Built for a Ryzen 9 9950X3D workstation running Omarchy, which is why it cares about per-CCD die temperatures, board sensors, and whether clocks held for the length of a burn-in.
The dashboard
pulse
| key | |
|---|---|
q |
quit |
t |
cycle theme |
s |
cycle the process sort |
r |
start / stop recording a trace |
↑↓ |
move through the process table |
K |
kill the selected process |
The theme follows the Omarchy desktop palette and re-reads it when the desktop
theme changes, until you cycle away with t.
Panels shrink and drop from the bottom of each column on a short terminal rather than drawing off the edge of the screen; the process table always survives.
Tracing
A trace is one tab-separated row per sample, flushed as it is written -- a run that ends in a hard lock still has every sample up to the moment it went down.
pulse -t burn.tsv # trace while you watch
pulse --headless -d 30m -t burn.tsv # no TUI, stop after half an hour
pulse -- stress-ng --cpu 32 # trace exactly as long as the workload runs
Options:
| flag | |
|---|---|
-t, --trace [FILE] |
start recording immediately; defaults to pulse-trace-<timestamp>.tsv |
--headless |
record without drawing the TUI |
-i, --interval DUR |
time between samples (default 1s) |
-d, --duration DUR |
stop after this long; headless only |
-n, --note TEXT |
free text recorded in the file's preamble |
-f, --force |
overwrite an existing trace file |
--summarize FILE |
report on a trace already on disk |
Durations take a unit -- 500ms, 90s, 30m, 2h. A bare number is seconds.
What gets recorded
Roughly fifty columns, fixed at the moment recording starts and matched by name on every row, so a sensor that drops out mid-run leaves its own column blank instead of shifting every reading to its right onto the wrong header:
- CPU: total and busiest-core percent, average and peak clock, 1/5/15 load
- Memory: used, percent, swap
- Network: rx and tx bytes per second
- Disk: read, write,
%utiland queue depth per physical device, from/proc/diskstats-- partitions and device-mapper are skipped so traffic is not counted twice - GPU: utilisation, VRAM, temperature, fan and power, per device
- Every plausible hwmon temperature: die and per-CCD, VRM, motherboard, both NVMe controllers, all four DIMMs, and fan RPM
The file opens with #-prefixed metadata -- pulse version, host, kernel, CPU
and GPU, sample interval, the workload, and your --note. Readers skip those
lines by default (pandas.read_csv(..., sep='\t', comment='#'), gnuplot,
awk '/^#/ {next}').
The summary
Every run ends with the arithmetic nobody wants to do by hand on a two-thousand-row TSV:
pulse: 1980 samples over 33m 0s to burn.tsv
cpu_pct mean 99.8 peak 100.0 start→end +0.0 (+0.0%)
avg_mhz mean 5142.0 peak 5163.0 start→end -1.2 (-0.0%)
Tctl mean 78.2 peak 82.4 start→end +0.1
CPU_Opt_rpm mean 1620.0 peak 1780.0 start→end +42.0 (+2.7%)
start→end compares the mean of the first thirty samples against the last
thirty -- half the run at each end if it is shorter than that -- so one
scheduler hiccup in the final second does not read as thermal decay. No
percentage is shown for temperatures: Celsius is an interval scale, and "3%
hotter" is not a statement about anything.
An open-ended soak usually ends in Ctrl-C, which kills the process before it
can report. pulse --summarize burn.tsv recomputes the same figures from the
file, including one recorded months ago.
Building
cargo build --release
cp target/release/pulse ~/.cargo/bin/
NVIDIA GPU panels need NVML (the proprietary driver); without it the GPU panel
says so and everything else works. Temperatures come from /sys/class/hwmon
and need no privileges.