feat: a real call stack, shared by the editor and the CLI - #15
Open
RaoulSchaffranek wants to merge 1 commit into
Open
feat: a real call stack, shared by the editor and the CLI#15RaoulSchaffranek wants to merge 1 commit into
RaoulSchaffranek wants to merge 1 commit into
Conversation
The Callstack view showed one frame, named after a wasm instruction and carrying the replay position in its label. It now shows every frame that led to the current line, and `soroban-trace` reports the same stack per stop as `frames` — both from one derivation, `debugAdapter/callStack.ts`, so a script and a debug session can never disagree about who called whom. The stack is assembled from three sources, ordered by how much each can be trusted, and every frame states which one placed it: The trace's own wasm activations are the STRUCTURE. `computeDepths` becomes `computeFrames`, which returns the linked activation stack (`fn`, `depth`, `callSite`, `caller`) the depths were always a projection of; depth is now that projection, so the frame count and step-over/ step-out are derived from the same walk and cannot drift apart. An outer frame stands at the call it is suspended in, which is also the record its own locals were last observed at. DWARF adds the Rust frames inlining erased. `ScopeIndex.inlineScopesAt` walks the DW_TAG_inlined_subroutine instances covering a pc, resolving names through abstract_origin/specification/linkage_name and call sites through call_file/call_line; LineTable keeps each unit's file table so a call-site file INDEX resolves to a path. An instance whose range this parser cannot read (a v5 .debug_rnglists list) is skipped, never guessed at: a missing frame degrades the view, an invented one misreports the program. The trace's contract-call boundaries close the stack below as labels. Names come off a precision ladder: the DWARF subprogram name qualified by its enclosing namespaces and types, else the module's `name`-section symbol demangled (new wasm/names.ts — rustc leaves some method DIEs anonymous, so this rung matters even in a DWARF build), else the function index, else the raw offset. A frame is never nameless, and a release build with no debug info still gets `control::Control::while_call+0x1a`. Frames are inspectable per frame, not just at the top: a variables reference now encodes the frame together with the scope kind, so Locals, Value Stack and Variables answer for the SELECTED frame, read from that frame's own record. Linear memory stays at the cursor — a callee may have written through a reference the caller still holds. `stackTrace` honors the client's paging window, gives each frame its own id and instruction pointer reference (so Disassembly follows the selection), and reports a non-workspace or sourceless frame as `subtle` rather than dropping it: an optimized build can put eight SDK conversion frames between the user's code and the pc, and a stack that quietly hid them would be a lie about how the program got here. The replay position moves out of the frame name into the thread label (`soroban-vm [29/40]`). A frame name says what the program is doing; where the cursor sits is a property of the recorded thread. The rules are specified as C1-C8 in the new docs/callstack.md, pinned by test/callStack.test.ts and test/dapFrames.test.ts across the fixture spread that matters: opt-0, optimized, stripped of DWARF, without a name section, and with no wasm at all.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Callstack view showed one frame, named after a wasm instruction and carrying the replay position in its label. It now shows every frame that led to the current line, and
soroban-tracereports the same stack per stop asframes— both from one derivation,debugAdapter/callStack.ts, so a script and a debug session can never disagree about who called whom.The stack is assembled from three sources, ordered by how much each can be trusted, and every frame states which one placed it:
The trace's own wasm activations are the STRUCTURE.
computeDepthsbecomescomputeFrames, which returns the linked activation stack (fn,depth,callSite,caller) the depths were always a projection of; depth is now that projection, so the frame count and step-over/ step-out are derived from the same walk and cannot drift apart. An outer frame stands at the call it is suspended in, which is also the record its own locals were last observed at.DWARF adds the Rust frames inlining erased.
ScopeIndex.inlineScopesAtwalks the DW_TAG_inlined_subroutine instances covering a pc, resolving names through abstract_origin/specification/linkage_name and call sites through call_file/call_line; LineTable keeps each unit's file table so a call-site file INDEX resolves to a path. An instance whose range this parser cannot read (a v5 .debug_rnglists list) is skipped, never guessed at: a missing frame degrades the view, an invented one misreports the program. The trace's contract-call boundaries close the stack below as labels.Names come off a precision ladder: the DWARF subprogram name qualified by its enclosing namespaces and types, else the module's
name-section symbol demangled (new wasm/names.ts — rustc leaves some method DIEs anonymous, so this rung matters even in a DWARF build), else the function index, else the raw offset. A frame is never nameless, and a release build with no debug info still getscontrol::Control::while_call+0x1a.Frames are inspectable per frame, not just at the top: a variables reference now encodes the frame together with the scope kind, so Locals, Value Stack and Variables answer for the SELECTED frame, read from that frame's own record. Linear memory stays at the cursor — a callee may have written through a reference the caller still holds.
stackTracehonors the client's paging window, gives each frame its own id and instruction pointer reference (so Disassembly follows the selection), and reports a non-workspace or sourceless frame assubtlerather than dropping it: an optimized build can put eight SDK conversion frames between the user's code and the pc, and a stack that quietly hid them would be a lie about how the program got here.The replay position moves out of the frame name into the thread label (
soroban-vm [29/40]). A frame name says what the program is doing; where the cursor sits is a property of the recorded thread.The rules are specified as C1-C8 in the new docs/callstack.md, pinned by test/callStack.test.ts and test/dapFrames.test.ts across the fixture spread that matters: opt-0, optimized, stripped of DWARF, without a name section, and with no wasm at all.