// examples/smarm_ctx_switch.js // // Demonstrates the v0.7 event-listener system by inspecting smarm's naked-asm // context switch. smarm (https://git.kalsbeek.dev/Markk116/smarm) is a green- // thread actor runtime whose core is two `#[naked]` switch shims that swap rsp // between a scheduler stack and an actor's mmap'd stack. // // Build the probe binary (frame pointers on, single scheduler thread): // cd smarm // RUSTFLAGS="-C force-frame-pointers=yes" cargo build --example ctx_switch_probe // // Then pipe this script into llmdbg, substituting: // - SPAWN_PATH: absolute path to the built ctx_switch_probe- binary // - ACTOR_ASM: mangled name of switch_to_actor_asm (from `nm BIN | grep`) // - SCHED: mangled name of switch_to_scheduler // // What it shows: // 1. A 'breakpoint' listener that logs each switch with rsp + 16-alignment. // 2. A 'step' listener that single-steps the shim's tail (mov rsp,rax; the // six pops; ret) and proves the actor entry lands with rsp % 16 == 8 — // the x86-64 ABI state expected at a function entry reached via `call`. // // This was the session that caught the off-by-one-slot error in // init_actor_stack's layout comment (entry is at aligned_top-16, not -8). spawn("SPAWN_PATH") // --- (1) per-switch alignment trace via a breakpoint listener -------------- ctx.sched = addr("SCHED") ctx.actor_asm = addr("ACTOR_ASM") addEventListener('breakpoint', e => { const sp = e.state.sp; const where = (e.state.pc === ctx.sched) ? "to_sched" : "to_actor"; ctx.hits = (ctx.hits ?? 0) + 1; print(`#${ctx.hits} ${where} rsp=${hex(sp)} rsp%16=${sp % 16n}`); }); bp.set(ctx.sched, "to_sched") bp.set(ctx.actor_asm, "to_actor") ---END--- // Drive through the first several switches. Each cont() fires the listener. for (let i = 0; i < 6; i++) { let r = cont(); if (!r.startsWith("breakpoint")) { print("stop: " + r); break; } } ---END--- // --- (2) prove the ret lands at rsp%16==8 in the actor entry --------------- // Remove the breakpoint listener; add a step listener for the tail trace. clearListeners('breakpoint') // Reach `mov rsp,rax` (offset 0x17 in the shim) on the next switch_to_actor. ctx.movrsp = ctx.actor_asm + 0x17n bp.remove("to_sched") bp.remove("to_actor") bp.set(ctx.movrsp, "movrsp") cont() ---END--- clearListeners() addEventListener('step', e => { const ins = e.state.disasm(1)[0]; print(`${hex(e.state.pc)} rsp=${hex(e.state.sp)} %16=${e.state.sp % 16n} ${ins.text}`); }); // mov rsp,rax ; pop r15..rbx (6) ; ret ; first insn of entry step(8) ---END---