debugger: a cheaper attached run #43
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
An attached run (
torb debug, no breakpoint set) costs two to three and a half times the baseline (DEBUGGER.md section 19):reachStatement(compiler/src/vm/debug.trb) writes a freshLoopRecordintoDebugger.loopsat every statement, counts down to the channel look, and testsDebugger.breakpoints(aSet<Int>of code offsets) at every statement. Two changes from DEBUGGER.md section 20 make that cheaper:The debugged conformance gate that was part of this issue is split out as #290 (independent: that run has no debugger attached).
Source
Where the changes go
compiler/src/vm/debug.trb:reachStatement,LoopRecord,beginLoop/finishLoop,Debugger.breakpoints,framesOf,beginStep.compiler/src/vm/interpret.trb: the.Statementarm /atStatement,execute(wherebeginLoopruns), and every site that starts a nestedexecute(native calls, witness calls, destructors, task resume): the outer loop's position (chunk, pc, base, depth, statement) must be published there.compiler/src/debugger/debuggee.trb(applyBreakpoints), andcompiler/src/backend/bytecode/format.trbonly if the bit table lives onChunk.Seed impact
None expected: ordinary TorbScript in
compiler/src/vmandcompiler/src/debugger; no syntax change, no new native, no driver flag, no std name looked up by string. If the implementation turns out to need one (for example a newMachinenative for bit operations), the two-commit seed rule ofCLAUDE.mdapplies and the item is re-planned before it is pushed; plainIntarithmetic or aBool/byte table needs none.Plan (one PR, two Conventional Commits)
test(debugger): measure the attached run in CI-tools/debug-cost.shplus programs and one session underbenchmarks/debug/(recursivefibonacci(32), a loops program,print "hello"; same three as section 19): per program, best and median of 7 interleaved runs of four kinds -torb run --vm,TORB_VM_DEBUGGED=1 torb run --vm, andtorb debugfed a session that launches the program, sets no breakpoint and lets it run to its end. An informational step at the end of tier B (after the lanes, when the machine is quiet), it never fails. Push this commit alone first and read the "before" numbers from its forge CI log; they are the baseline for this PR (the Windows numbers of section 19 are not).perf(vm): write a loop's record only where a call back begins, and test breakpoints by a bit per statement- both changes, the new tests, and the docs; read the "after" numbers from the CI log of this commit.Decision rule (decided by the loop, to be applied with the numbers of step 1 and 2)
The change is kept when the attached fibonacci run is at least 10% faster (best of 7, same CI runner, same job) after than before. If the combined gain is below that, the code change is reverted, the measurement and its numbers stay, and section 19/20 of the record say that the cheaper run was measured and is not worth its complexity (the open point is then answered, and this issue closes with that). The size is no argument against either option.
Acceptance criteria
LoopRecord; the record of anexecuteis current at every point a nestedexecutecan begin and at every stop (breakpoint, step, pause, entry, panic).Set<Int>of code offsets;setBreakpointsreplacing a file's breakpoints clears the old ones; a breakpoint moved to the next line with a statement still reportsverifiedandlineas before.sh tools/debug.sh(all existing sessions undertests/debug/) andcompiler/tests/debugger.test.trbpass with their.expectedfiles byte for byte.torb runandtorb test(not debugged) are not slower: no change to the loop's shape beyondatStatement; thehello/fibonacci"not debugged" numbers of the new script show no change beyond noise (reported, not a gate).Proving tests
tests/debug/callback.dap(+.expected,tests/debug/callback/main.trb): a breakpoint inside a closure thatlist.mapruns (a call back),stackTraceshows the closure frame, thenmap's frame and the caller's with the right lines,stepOutreturns to the caller's next line,stepIninto a closure called from a native, and a panic inside a call back stops with the same frames (panickedhook).compiler/tests/debugger.test.trb: breakpoints at the first and last statement of a chunk and in several chunks at once; replaced breakpoint sets leave no stale bit; a breakpoint on a line shared by a closure and its caller stops in both;stopOnEntryand a pause while anexecuteof a call back is running show the frames of every loop below.breakpoint,panic,testsunchanged.Model
opus / high (
compiler/src/vmhot loop, frame records, runtime interplay).Dependencies
None known. The open PR #289 (
feat(debugger): name the frames of test and group bodies) touches the same files (vm/debug.trb,debugger/debuggee.trb); the implementer rebases onto it when it lands first.[loop:torbscript] intake: in scope (author orion, label loop), source DEBUGGER.md section 20, not a duplicate, no collision with a design record. Two parts: an attached-run overhead measured in forge CI first (decided with the numbers, no owner question), then the debugged conformance run as a tier B gate. next: refine, waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z
[loop:torbscript] claim refine by session:loop-torbscript-20261010 until 2026-10-10T00:38Z
debugger: a cheaper attached run and the debugged conformance gateto debugger: a cheaper attached run[loop:torbscript] decided by the loop: (1) Split. The two halves of this issue are independent: the cheaper attached run touches the VM's
reachStatementand the breakpoint table (opus/high), while the debugged conformance run is lowered for the debugger with no debugger attached (TORB_VM_DEBUGGED=1), so it does not profit from the attached-run work; it is tooling and docs (sonnet/medium). The gate is now #290; this issue keeps the attached run. (2) Measurement: the attached run is not measured by any existing CI output (CI never runstorb debugon a program that runs long, and the only numbers are the owner machine's of DEBUGGER.md section 19), so it is the first implementation step, in forge CI, with a keep-or-revert rule at 10% (see body). (3) For #290 the existing CI output is enough for a plan: the last main run (https://git.torb.dev/torbscript/language/actions/runs/710, tier B) showsconformance suite: 324s,conformance suite in the VM: 75s,runtime tests: 73s,tests/language and examples/config-dsl: 74s,tier B: green (507s); lane b1 (both conformance legs) takes 399 s, lane b2 147 s. A debugged leg costs the plain VM leg plus 12% to 35% of the programs' run time (not of their compile time), so about 85 to 100 s; placed in lane b2 it does not lengthen tier B. The exact figure is still measured on the first commit of #290. (4) Seed: no impact in either issue.[loop:torbscript] refine: ready, #43 is now "a cheaper attached run" (the gate is split out as #290, ready too). Two changes in one PR of two commits: a loop's record written only where a call back begins and at a stop, breakpoints as a table indexed by chunk and statement. Commit 1
test(debugger)addstools/debug-cost.shand prints the attached-run cost in forge CI (before numbers); commit 2perf(vm)makes the change, adds tests and docs (after numbers); kept only if the attached fibonacci run gains at least 10%, else reverted and the numbers recorded. Acceptance: overhead measured before and after in CI and in DEBUGGER.md section 19; no per-statementLoopRecord; breakpoint table with replace semantics;tools/debug.shandcompiler/tests/debugger.test.trbunchanged; new sessiontests/debug/callbackand new unit tests for chunk edges, call back frames and shared lines. Seed impact none. Model opus/high. next: implement[loop:torbscript] implement: deferred until PR #289 lands (shared debugger code, debuggee.trb). next: waiting (PR #289 merge)
[loop:torbscript] claim implement by session:loop-torbscript-20261010 until 2026-10-10T07:00Z (PR #289, the dependency named in comment 3002, landed)
[loop:torbscript] claim implement by session:loop-torbscript-20261010 until 2026-10-10T14:02Z (re-claim: the previous worker died in the gateway gap between 02:10Z and 08:00Z without a write-back)
[loop:torbscript] implement: PR #298 (head
6ffd6cc5, mergeable, two commits, numbers in the PR body); the resumed worker was killed by a gateway restart after the push, so this write-back comes from the loop session. CI is running on the head, argus requested. next: waiting (review by argus; if CI fails, the next tick re-claims for a fix)