10 Commits
Author SHA1 Message Date
llmdbg-dev f7a2cd0600 docs: field notes from smarm context-switch session (v0.8)
Friction-and-wishlist from one real profiling/verification session, not a
defect report. Highlights: demangling and trace({over,capture}) worked well;
pc-range checks are treacherous when helpers sit adjacent to the function
under test (symbol extent, not address, is the missing primitive); no
float/vector register access forced the XMM verification out to objdump.

Wishlist, JS-expressible first: symStart/symSize (or inFn), countTo, hexdump
— candidates for a v0.9 prelude with no Rust changes. xmm reads and
prologue-aware breakpoints need native support.
2026-05-28 15:19:43 +00:00
llmdbg-dev f0698573cd v0.8: step-over (next/trace), symbol demangling, disasm len/is_call, ergonomics
Driven by the friction the v0.7 smarm session hit: single-stepping the
context-switch shim dove into every helper call, and every step needed
`nm | grep` for a mangled name whose hash changes each rebuild. This round
makes tracing the shim pleasant.

Rust (kept minimal — only what JS genuinely can't do):
- symbols.rs: demangle Rust symbols. addr("name") now resolves by exact
  mangled/case-insensitive (unchanged), then by full demangled path
  (smarm::context::switch_to_scheduler), then by *unique* final path
  component (switch_to_scheduler). Ambiguous short names resolve to None and
  bp.set reports "matches N symbols" rather than guessing. sym()/name_for_addr
  return demangled names so traces are readable. Adds rustc-demangle dep.
- disasm.rs: Decoded gains `len` (instruction byte length) and `is_call`
  (iced FlowControl::Call, incl. indirect). js.rs exposes both on disasm
  records so step-over finds the return address without re-parsing text.

Prelude (everything else, like until/finish):
- next(n): step OVER calls — one-shot bp at pc+len, cont, cleanup; else
  single-step. A stepped-over call fires the `step` listener once. Detects
  calls that don't return to pc+len (stack-switching shims, longjmp): on a
  non-matching stop it dispatches the real event and stops instead of hanging.
- trace(from, to, {over, range, capture, print, max}): run-to-A then
  step(-over)-to-B with per-step capture; `range:[lo,hi]` stops when pc leaves
  a range. Drives the native steppers directly (no listener coupling).
- watch(specs): a `step` listener printing named expressions each step.
- fmt(): BigInt-safe stringify. state.insn: disasm record at pc.
  stackContains/stackDistance/region: stack-region legibility for cross-stack
  handoffs. step(n, {summary}): first/last/count for large N.

Docs: README status row + prelude reference + sections on the next() caveat,
the conditional-breakpoint listener idiom, and BigInt ergonomics.
examples/smarm_ctx_switch_next.js traces a full switch_to_scheduler in 16
readable lines using trace({over:true}).

No-listener fast paths and the v0.7 example are unaffected (verified).
2026-05-28 06:20:43 +00:00
llmdbg-dev ce96a98829 v0.7: event-listener system (addEventListener/step/breakpoint/signal/exit)
- Native step/cont moved to __step_native/__cont_native; public step()/cont()
  defined in prelude.js wrap them and dispatch JS listeners on each stop.
- addEventListener/removeEventListener/clearListeners/listeners() registry on
  globalThis, persistent for the daemon lifetime (independent of ctx.reset()).
- Event object carries {state, prev, stop_reason} plus breakpoint_id/signal/
  exit_code as appropriate. Implements the SPEC 'Event Listeners' + 'Core Loop'.
- No-listener fast path keeps existing scripts zero-cost.
- examples/smarm_ctx_switch.js: worked session inspecting smarm's naked-asm
  context switch; README updated (status table, listener API, deferred list).
2026-05-28 05:50:09 +00:00
claude 98e3347b4c docs: add SPEC.md (original vision) and README.md 2026-05-27 21:31:36 +00:00
claude 2f64981729 v0.6: state.stack, until(loc), finish(), prelude show.* helpers
- StackRegion from /proc/<pid>/maps, exposed as state.stack
  ({top, usable_base, guard_size} BigInts per spec)
- until(loc) one-shot bp wrapper that cleans up via try/finally,
  works even if a different stop fires first
- finish() reads return address from [rbp+8] and uses until()
- updated js.rs module doc; allow(dead_code) on the kept-for-future
  BreakpointSet::find_by_name
2026-05-27 21:30:18 +00:00
claude 2ed17f0286 v0.5: x86-64 disassembly via iced-x86
- state.disasm(n=8), disasm(addr, n=8) -> [{addr, bytes, text, target?, target_sym?}]
- read_mem_pristine() overlays orig bytes through active INT3 breakpoints
- branch/call targets get symbolized when they land in known symbols
- show.disasm / show.bp / show.regs prelude helpers for ergonomic printing
2026-05-27 21:24:01 +00:00
claude 8ae6103ce7 v0.4: INT3 software breakpoints with by-name/by-id ops
- StopReason::Breakpoint(id), rewinds RIP on hit
- bp.set(addr_or_symbol, name?), bp.remove(id_or_name), bp.list()
- step/cont disarm-step-rearm when PC sits on an enabled bp
- fix: PIE load_base = mapping_base - mapping_file_offset (was using the
  first executable mapping with its offset, which is wrong on toolchains
  that split read-only header pages)
2026-05-27 21:21:29 +00:00
claude d587dbcd4c v0.3: ELF symbol parsing, sym()/addr()/symbols() globals, addrstr/here prelude helpers 2026-05-27 21:13:10 +00:00
claude 19c6154831 v0.2: prev snapshot of regs, refactored state/prev to share build_register_view 2026-05-27 21:10:23 +00:00
claude 912f5003fe v0.1: spawn/step/cont, state.{pc,sp,registers,memory}, prelude.js 2026-05-27 21:08:28 +00:00