-
feat(gig-log): a session recorder, so a run-through can be REVIEWED and not just felt · 4c57362d
After a set, "did it glitch?" and "did it get hot?" and "which controls did I actually use?" are answered from memory. Memory is a bad instrument, and the answers matter: #8 (thermal impact under load), #56 (Pulsar starving audio), #46/#11 (which surface controls are really in the hands). PLN records the run-through in Ardour; this gives that take a machine-readable twin. WHAT IT DOES `gig-log.py record` 1 Hz JSONL: package/core temp, fan, freq, throttle counters, per-core cpu%, and per-gear %cpu + RSS for scsynth / sclang / ArdourGUI / the whole pulsar process group. Plus PipeWire xruns and every LCXL control move. `gig-log.py mark ...` annotate the live session ("gimme acid drop") `gig-log.py report` render it back: sparklines, xrun timeline, gear table, a per-CC surface table with first/last touch `gig-log.py status` is it running, what is it writing `gig-log.py install` systemd --user unit, enabled, starts with the session `gig-log.py selftest` prove the parsers AND the cost Wall-clock stamped in the header, so the timeline lines up with the Ardour take. THE OBSERVER MUST NOT PERTURB — measured, not asserted This rig has twice produced the fault it was measuring: probe-chain's capture streams caused the xruns it was hunting, and the LED daemon's per-event fork caused the lag it was reporting. So: no audio capture at all, no per-sample subprocess spawn (every number comes from sysfs/procfs), and exactly two long-lived children (`pw-top -b`, `aseqdump`) drained by threads so a filling pipe can never block them. Measured cost of the whole thing: 0.40% of one core with 270 pw-top lines parsed in 10 s; 0.79% as the live systemd unit. The first selftest reported 0.66% and was FLATTERING ITSELF — it never started the reader threads, so it measured a sampler with nothing to parse. Same mistake as the first --bench run in the LED work; fixed, then re-measured lower and honest. AND THE LOGGER MUST NOT BE A FIREHOSE A fader sweep is 100+ events/s. A logger that writes them all costs more than what it measures, so CC/pitchbend are coalesced to one line per control per second carrying count + first/last/min/max — the #71/#72 fix applied to ourselves. 128 events on one knob is one line that still shows the whole travel. NOTES ARE NEVER COALESCED: a CC is a state and may be superseded, a note is an event and dropping one loses a thing that happened. COUNTERS ARE DELTAS, AND THE BASELINE IS WHERE THE BUGS LIVED Two absolute counters here are large and meaningless alone: package_throttle was 17024 after 2 idle days, and Ardour's PipeWire ERR was 67072. Both count history, including power excursions and device changes that never touched audio. So the header records baselines and samples record deltas. Getting Ardour's baseline right took three rules, and the first report caught each one: 1. baseline on FIRST sight -> pw-top prints a zero-filled snapshot before the profiler has data, so base=0 and a 25-second IDLE session reported 72096 xruns. 2. baseline on SECOND sight -> usually right. pw-top emits a non-deterministic NUMBER of zero tables, so it silently reported 67123 on the next real run. A rule that is right most of the time is the worst kind for a gig log, because the one bad reading looks exactly like a disaster. 3. baseline = MAX over each node's first 5 sightings. ERR is monotonic within a node's lifetime, so the max over a warm-up IS the true starting count — no timing assumption at all. A value below the baseline means the node was destroyed and recreated, so re-baseline instead of reporting negative. Validated by three independent 15 s runs, all reporting 0 (measure twice in time before trusting one reading). The report DECLARES the warm-up blind spot rather than hiding it. Also learned on the way: Ardour accumulates ~6 xruns/min even with nothing playing (67072 -> 67123 -> 67134 across captures minutes apart), which is exactly why only the session delta may ever be quoted. FIXED IN THE SHARED READER perf._read(None) raised TypeError instead of returning the default, so on any machine without a coretemp/dell_smm hwmon the thermal read CRASHED rather than reading "unknown" — the Bridge shares this code path. Guarded. Refactored _proc_cpu_rss into a pure parse_proc_stat() to make it testable, and it needed to be: pulsar's renderer comms look like `(pulsar) --type=renderer`, so splitting /proc/pid/stat on whitespace from the left shifts every field and silently reports some other column as CPU. TESTS: 71 new (tools/tests/test_gig_log.py), suite 211 -> 282, all green. Covers real pw-top/aseqdump lines, every baseline regression above, coalescing invariants, notes-survive-a-CC-flood, a torn final line costing one sample not the log, absent gear degrading to partial data instead of a crash, and sparklines bucketing by MAX so a one-second burst inside a 40-minute set cannot be averaged away.PLN (Algolia) authored4c57362d
| Name |
Last commit
|
Last update |
|---|---|---|
| .. | ||
| test_gig_log.py | Loading commit data... |