diff --git a/HANDOFF.md b/HANDOFF.md index 459d7a5..c2d98bc 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -760,6 +760,43 @@ Instrumentation (`FR`, `STG`, `DECODE`) is gated behind the `--features perf` Cargo feature so production builds have zero runtime overhead. Detailed bug notes and analysis are preserved in `docs/BUGS/scroll-jank-120hz.md`. +### 8.10 Zero-jank campaign: the reliable perf tool (2026-10-30) + +The zero-jank work needed a referee that cannot be argued with. Two tools: + +- **`tools/jank_eval.py`** — the strict referee. Slot grid built from the + hardware VSYNC edges in the log (not the app's clock); a window is PASS + only with: no dropped slot, no same-slot double, no present past latch + (+5 ms edge phase), no >20 ms present gap, phase p10-p90 ≤ 0.5 ms. + 7-case self-test (`--selftest`). Exit 0=PASS, 1=FAIL, 3=SUSPECT. +- **`tools/jank_capture.py`** — owns the whole capture pipeline so + provenance can never be wrong: + `python3 tools/jank_capture.py capture --app wow.gmp.sltest --drags 4,2,4 --tag --tag-file ` + records build identity → force-stops → **deletes the log** (the file can + then contain exactly ONE process) → cold-launches → verifies + ResumedActivity → scripted drags → pulls the complete log → reports + PROVENANCE + jank verdict + **MISS forensics** (each miss episode + classified: CHAIN-STALL / LOOP-STUCK / GUARD-REJECT / NO-WAKE-ITER, + with the ±33 ms all-thread context: UI loop, vsync thread, workers, + slow C calls, IO). Exit: 0=PASS, 1=FAIL/INVALID/SUSPECT. + `jank_capture.py report FILE` re-analyzes any captured log. + +Both validated on the emulator (x86_64, 60 Hz SF): capture pipeline +×3 drag profiles, pause/resume mid-drag, determinism, multi-process +→ INVALID, explicit windows, period override, no-VSYNC → SUSPECT path, +small-log and PASS paths. Stress-testing found and fixed a circular +auto-window threshold (a janky app's low present rate hid it as +"inactive"), a BUCK multiplier bug, and a useless fallback period fit. + +**Zero-jank status (phone, sltest):** phase locked (single wake source: +Choreographer; early waker disabled — it raced Choreographer and caused +±4 ms wander). Remaining issue: 20-40 ms chains of lost wakes (the +Choreographer callback chain breaks); every frame that RAN took 3.7 ms +(6 ms budget) — the CPU mystery is answered: no missing CPU work, the +jank is frames that never ran. Next phone run: `jank_capture.py capture +... --tag `; the MISS forensics (sw/sn/eage/lastage fields) +will classify each break. + ### 8.8 Jank-hunt round 2 + the keep-alive incident (2026-10-28) Follow-up on "still seeing jank" (post-GPU-texture). Instrumentation: