Skip to content

Commit ab53508

Browse files
Add DWARF source mapping, disassembly, and statement stepping
Debug Soroban contracts at Rust-source granularity, not just wasm. Source mapping & disassembly (M3–M7 of the DWARF plan): - Pure-TS DWARF v4/v5 line parser (src/dwarf/*) mapping komet `pos` (code-section-payload-relative) to Rust file:line, with per-record validation against a static disassembly (src/wasm/*, via wasmparser). - DwarfSourceMapper / NullSourceMapper behind the SourceMapper seam; DWARF absence or parse failure degrades gracefully, never fatal. - DAP disassembleRequest + instruction breakpoints; retire the soroban-wat trace-listing virtual document and showWat command. - Statement- vs instruction-granularity stepping engine (stops.ts): visible/mapped/depth/line-run model; depth reconstructed from function-body membership to survive komet's missing implicit returns. Build prerequisite — statement stepping starved by optimization: - opt-level "z"/"s"/"3" collapse a whole function onto one source line, which is what made incrementer stepping appear completely broken. - ContractBuilder now injects CARGO_PROFILE_RELEASE_OPT_LEVEL=0 (with DEBUG=true / STRIP=none) for debug builds; the crate Cargo.toml is never edited. See docs/stepping.md. Systematic stepping suite: - examples/control: one entry point per Rust construct (sequence, if/else, `for`, `while`+call, `match`) plus examples/stepper. - Five golden opt-0 komet traces sharing one control-debug.wasm; test/controlStepping + test/dapControlStepping pin exact per-construct stop sequences (model and DAP level) against docs/stepping.md. - scripts/make-fixtures.sh regenerates every wasm+trace pair together. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 25ff1fb commit ab53508

65 files changed

Lines changed: 13590 additions & 420 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.mocharc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"spec": "out/test/**/*.test.js",
3-
"timeout": 10000
3+
"timeout": 30000
44
}

README.md

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ Verification's local Stellar testnet built on K formal semantics.
77
komet-node executes a whole transaction and returns the *entire* execution trace
88
as JSON Lines (one record per WebAssembly instruction) via its `traceTransaction`
99
RPC. This extension loads that trace and lets you **step forward and backward**
10-
through the execution inside VSCode's debugger UI.
10+
through the execution inside VSCode's debugger UI — at the **Rust source level**
11+
when DWARF debug info is available, and at the instruction level in VSCode's
12+
built-in **Disassembly View** always.
1113

1214
## Status
1315

@@ -32,20 +34,58 @@ through the execution inside VSCode's debugger UI.
3234
JSON-RPC; `TurnkeyPipeline` orchestrates build → spawn → seed → deploy →
3335
invoke-with-trace → replay. `attach` mode connects to a running node.
3436

35-
See [the roadmap](#roadmap) for what's next.
37+
**Milestones M4+M5 (done): DWARF → Rust source mapping + DAP disassembly.**
38+
39+
- The build injects `CARGO_PROFILE_RELEASE_DEBUG=true` /
40+
`CARGO_PROFILE_RELEASE_STRIP=none` into `stellar contract build`, so the wasm
41+
carries DWARF — no `Cargo.toml` changes needed (set `"debugInfo": false` in
42+
the launch config to opt out). The **pristine linker output**
43+
(`target/…/release/deps/*.wasm`) is used and uploaded, because the Stellar
44+
CLI's metadata-injection step rewrites the wasm and strips the DWARF line
45+
programs.
46+
- An in-repo DWARF v4/v5 line-table parser (`src/dwarf/`) maps wasm code
47+
offsets to Rust `file:line`. Stack frames open the real `.rs` file;
48+
**breakpoints set in Rust source** verify against the executed trace (sliding
49+
forward to the nearest executed line); stepping is **statement-granular** in
50+
source and **instruction-granular** in the Disassembly View
51+
(`supportsSteppingGranularity`).
52+
- The raw WebAssembly view is VSCode's built-in **Disassembly View**
53+
(right-click a stack frame → *Open Disassembly View*): a static
54+
`wasmparser`-backed disassembly with Rust line annotations, plus
55+
**instruction breakpoints** (`supportsDisassembleRequest`,
56+
`supportsInstructionBreakpoints`, `supportsBreakpointLocationsRequest`).
57+
- **No-DWARF fallback:** a prebuilt wasm without debug info, or a `rawTrace`
58+
replay without `wasmPath`, degrades to disassembly-only debugging — frames
59+
carry an instruction pointer but no source.
60+
61+
Trace positions are **validated** before use: komet-node's `pos` is relative to
62+
the section being executed (code section for function code, e.g. the globals
63+
section for global initializers), so every record is cross-checked against the
64+
static disassembly and only trusted when the mnemonics agree. Note that
65+
komet-node's tracer stops at instructions it cannot decode (it prints them as
66+
`unknown`, e.g. `if`), so a trace can be a prefix of the full execution.
3667

3768
## Verification
3869

39-
The replay logic and pipeline are exercised by 38 tests (`npm test`):
70+
The replay logic and pipeline are exercised by 180+ tests (`npm test`):
4071

41-
- **DAP protocol tests** drive the real adapter (capability, entry stop, forward
42-
and reverse stepping, locals/stack, run-to-breakpoint + reverse-continue).
72+
- **DAP protocol tests** drive the real adapter (capabilities, entry stop,
73+
forward and reverse stepping at both granularities, locals/stack, Rust source
74+
breakpoints with forward slide, run-to-breakpoint + reverse-continue,
75+
disassembly windows, instruction breakpoints, breakpoint locations).
76+
- **DWARF tests** decode the committed debug build of `examples/adder`
77+
(`test/fixtures/adder-debug.wasm`) and pin the `a + b` line mapping against
78+
its **matched real trace** (`adder-debug.trace.jsonl`); regenerate the pair
79+
together with `scripts/make-fixtures.sh`.
4380
- **tx-builder tests** decode every built envelope back with the SDK; envelopes
4481
are additionally cross-checked against the Stellar CLI's own XDR decoder.
4582
- **pipeline tests** run `TurnkeyPipeline` against an in-process mock komet-node
4683
and assert the full deploy+invoke sequence and the parsed trace.
4784
- **build integration test** (auto-skips without the toolchain) runs a real
4885
`stellar contract build` through `ContractBuilder`.
86+
- `scripts/verify-addresses.mjs` re-derives the address-space ground truth
87+
(komet `pos` convention, DWARF address space) against a live komet-node — the
88+
regression tool for the M0 findings above.
4989

5090
## Devcontainer toolchain
5191

@@ -63,16 +103,25 @@ in-process in the extension host (`DebugAdapterInlineImplementation`).
63103
```
64104
extension.ts VSCode glue: config provider + inline adapter factory
65105
debugAdapter/
66-
SorobanDebugSession DAP handlers (cursor moves + StoppedEvents)
67-
TraceModel records, cursor, call-depth, breakpoint navigation
106+
SorobanDebugSession DAP handlers (cursor moves + StoppedEvents, disassembly)
107+
TraceModel records, cursor, call-depth, line + instruction stepping
108+
artifacts.ts wasm bytes -> { mapper, disassembly, validated positions }
68109
backends/
69-
RawTraceBackend M1: replay a JSONL trace file
70-
LiveBackend M2: turnkey build + spawn + deploy + trace
110+
RawTraceBackend replay a JSONL trace file (+ optional wasmPath for symbols)
111+
LiveBackend turnkey build + spawn + deploy + trace
71112
komet/
72-
trace.ts JSONL -> TraceRecord[]
113+
trace.ts JSONL -> TraceRecord[] (K-style mnemonics, section-relative pos)
114+
mnemonics.ts K-style instr arrays -> wasm mnemonics ('i64.const 255')
73115
KometClient.ts JSON-RPC client (getHealth/sendTransaction/traceTransaction/...)
74116
soroban/scval.ts launch args -> ScVals (@stellar/stellar-sdk)
75-
sourcemap/SourceMapper pos <-> displayed line; v1 renders the trace listing
117+
wasm/
118+
sections.ts wasm section walker (offsets, custom-section lookup)
119+
Disassembly.ts static disassembly (wasmparser), code-offset addressed
120+
dwarf/ DWARF v4/v5 .debug_line/.debug_info parser -> LineTable
121+
sourcemap/
122+
SourceMapper the mapping seam the adapter talks to
123+
DwarfSourceMapper trace index / code offset -> Rust file:line (+ breakpoints)
124+
NullSourceMapper no-DWARF fallback (disassembly-only)
76125
```
77126

78127
All replay logic is free of the `vscode` API so it can be unit-tested in plain
@@ -83,28 +132,27 @@ Node. The `vscode`-only glue lives in `extension.ts`.
83132
```bash
84133
npm install
85134
npm run build # bundle to dist/extension.js (esbuild)
86-
npm test # tsc + mocha (27 tests, incl. DAP protocol tests)
135+
npm test # tsc + mocha
87136
npm run check-types # tsc --noEmit
88137
npm run lint
89138
```
90139

91140
Press **F5** (Run Extension) to open an Extension Development Host with the
92141
extension loaded. It opens the [`examples/`](examples/) workspace — a
93-
self-contained Soroban project with two contracts (`adder`, `increment`) and a
94-
captured trace — so there is something real to debug immediately.
142+
self-contained Soroban project with three contracts and captured traces — so
143+
there is something real to debug immediately.
95144

96145
Pick a config from the Run and Debug view. Start with **Soroban: Replay
97-
add(4, 3) trace**, which replays the bundled trace so you can step
98-
forward/backward, set breakpoints on instructions, and inspect the stack/locals
99-
— end to end, with no komet-node required. The **Debug add(1, 2)** and **Debug
100-
increment(5)** configs exercise the full build → deploy → trace pipeline.
146+
add(4, 3) with symbols**, which replays the bundled trace *with* the matching
147+
debug wasm: frames land in `adder/src/lib.rs`, breakpoints work on Rust lines,
148+
and the Disassembly View shows annotated wasm — no komet-node required. The
149+
**Debug add(1, 2)** and **Debug increment(5)** configs exercise the full
150+
build → deploy → trace pipeline.
101151

102152
See [`examples/README.md`](examples/README.md) for details.
103153

104154
## Roadmap
105155

106-
- **M3 — Richer time travel:** call-stack reconstruction from call/return depth.
107-
- **M4 — WAT source view:** disassemble the wasm code section and map `pos` to
108-
WAT lines for breakpoints on real instructions.
109-
- **M5 — DWARF -> Rust source:** map `pos` to Rust `file:line` using embedded
110-
DWARF, for source-level stepping.
156+
- **M3 — Richer time travel:** call-stack reconstruction from call/return depth
157+
(multi-frame stack traces, frame-scoped locals).
158+
- Column-level breakpoints and inline `values` from DWARF variable info.

0 commit comments

Comments
 (0)