diff --git a/FIELD_NOTES.md b/FIELD_NOTES.md new file mode 100644 index 0000000..414505b --- /dev/null +++ b/FIELD_NOTES.md @@ -0,0 +1,71 @@ +# Field notes — smarm context-switch session (v0.8) + +Notes from one real session using llmdbg v0.8 to profile and verify smarm's +naked-asm context switch (the TLS-accessor perf question and the XMM-not-saved +correctness question). Recorded as friction-and-wishlist, not a defect report — +nothing here blocked the work; every rough edge had a workaround. + +## What worked + +- **Demangling.** `addr("switch_to_scheduler")` resolving directly meant zero + `nm | grep` for mangled names whose hash changes every rebuild. Biggest + single quality-of-life win. +- **`trace({over, range, capture})`.** The capture closure returning a + structured record per step (`{pc, insn, len, is_call}`) and letting the + caller format it is the right shape — produced a clean 16-line shim trace in + one pass. +- **`next()` vs `step()`.** The distinction the docs emphasise is exactly the + axis this task needed: stepping *into* the accessors (164 insns of debug-build + `LocalKey` ceremony) vs *over* them is the difference between readable and + unreadable, and `step()` *through* the stack-swapping `ret` is what let me + walk the switch itself. +- **The worked example** (`examples/smarm_ctx_switch_next.js`) was close enough + to the real task to adapt directly. Good investment. + +## What bit me + +- **Range checks on `state.pc` are treacherous when helpers sit adjacent to the + function under test.** `set_actor_sp` lived at `sched+0x50`, *inside* my + `+0x60` window, so a "stop when pc returns to the shim" loop terminated the + instant it entered the accessor. Cost me two attempts before I abandoned + range-based exit detection and dumped a flat `step()` stream to count by eye. + The missing primitive is symbol *extent* (start ≤ pc < start+size), not + symbol *address*. +- **The `push rbx` consumed-by-breakpoint quirk** is correctly documented, but + I had to re-apply "the first observed instruction is the second one" mentally + at every session. A documented footgun is still a footgun. +- **No float/vector register access.** The XMM-not-saved verification had to + leave llmdbg entirely and fall back to `objdump` to confirm the compiler + spills live XMM around the switch call. For a tool whose reason for existing + is x86-64 context-switch correctness, watching `xmm0..15` across a switch + feels squarely in scope — it would have turned a static inference into a + dynamic proof (XMM dead at switch entry, live again after reload). + +## Wishlist (rough priority order) + +1. **Symbol-extent queries** — `symStart(pc)` / `symSize(pc)`, or an + `inFn(pc, "name")` membership test. Fixes the range-check footgun directly. + This is the highest-value add for asm work, where "am I still inside this + function" is a constant question. Pure-JS-expressible if the native `sym` + layer exposes extents. +2. **`countTo(stop, {over})`** — step until an address/predicate and return + just the instruction count, instead of hand-rolling `let n=0; while(...){ + step(); n++ }` and getting the boundary wrong. Counting per-call instruction + cost was the core measurement of this task and had no first-class support. + Pure-JS prelude helper. +3. **XMM/SSE register reads** — `state.xmm[i]` or `reg("xmm0")`. Needs + Rust-side support (ptrace `GETFPREGS`/`xsave`). In scope for this tool's + purpose. +4. **Prologue-aware breakpoints** — a mode (or `bp.setAfterPrologue()`) that + removes the "first instruction eaten" caveat rather than documenting around + it. +5. **`hexdump(addr, n)`** — peek at raw memory, e.g. a freshly-built initial + actor stack from `init_actor_stack`. Pure-JS over a `readMem` primitive. + Nice-to-have; didn't strictly need it this round. + +## Suggested next step + +Items 1, 2, and 5 are pure-JS and fit the SPEC ethos ("everything expressible +in JS lives in JS") — they belong in `src/prelude.js` and would be a clean v0.9 +with no Rust changes (assuming `sym` extents and a `readMem` primitive are +reachable from JS). Items 3 and 4 need native support and are larger.