Repository navigation
Proposal: cross-platform source-level debugging for native, embedded, WASI, and browser WebAssembly #2164
Description
Summary
Provide source debugging across native, embedded, browser and WASI targets using shared DWARF/artifact identity and a versioned runtime schema, with target-specific execution control.
Updated 2026-10-06 against main f167e3c6d9ae1bff971669180753a223fe437461. The first-stage integration is merged. This umbrella remains open for the software capabilities below; physical-board/probe qualification is deferred because no device is currently available. The focused native LLDB scope is tracked in #2154.
Architecture and compatibility contracts
Keep three layers distinct:
- Compiler and artifacts: valid DWARF locations/scopes/types, executable identity, optional external DWARF, and independent runtime pclntab data.
- Execution control: native LLDB/GDB, embedded remote probes/emulators, browser DevTools, or a runtime's supported Wasm guest-debug protocol.
- Runtime presentation: versioned adapters for LLGo strings, slices, interfaces, functions, closures, maps, channels and supported goroutine/thread views.
Native compile units retain DW_LANG_C for stock-debugger interoperability and DW_AT_producer = LLGo. This does not introduce a Go-language LLDB fork or a general Go expression evaluator. The canonical debugger schema and native/Wasm records identify target size, byte order, ABI and runtime-layout versions. Unsupported records disable LLGo presentation with a clear explanation while preserving ordinary raw debugging.
Port the schema to the current single C ABI and runtime layouts. Removed none/cfunc/allfunc modes and the old module path must not become new compatibility obligations. Share schema and fixtures across LLDB/GDB/browser consumers; do not force them to share implementation language or private APIs.
Debug artifacts and defaults
Native macOS, Linux and Windows builds follow the platform toolchain's default DWARF packaging. Retention policy (-w) is separate from physical placement: this proposal does not require native DWARF embedding, extraction, standalone dSYM/debug-file generation, or an additional packaging acceptance gate. Explicit embedded/external packaging applies to Wasm; embedded devices retain the host debug ELF and separate deployment image. These rules follow the target, independently of the build-host OS.
| Mode | Contract |
|---|---|
embedded |
Retain DWARF in the Wasm module; native use only requests preservation with platform-default packaging |
external |
Publish a debugger-owned Wasm sidecar paired with the deployed module |
host |
Retain the linked debug ELF on the host and derive separate flash/deployment bytes |
none |
Omit DWARF according to the typed build policy |
The intended flag is -debug-artifact=embedded|external|host|none. Unsupported target/mode combinations must fail explicitly. Keep this separate from pclntab's runtime loading and packaging: external DWARF is read by a debugger, not by the running LLGo program.
Go -w controls DWARF, and -s implies -w unless explicitly overridden by -w=false. The native default-DWARF correction belongs to #2142. This consolidation does not turn ordinary Wasm/embedded production builds into debug builds. llgo debug explicitly requests retained debug information and defaults to O0 unless the user selects another supported optimization level.
Embedded debug sections remain host-side non-loadable data. Producing a debug ELF must not alter the corresponding flashed .bin/.hex/.uf2 bytes. Artifact reporting distinguishes executable, debug, deployment and runtime-symbolization roles and reports actual file sizes.
For Wasm, publish external DWARF with standard external_debug_info, verified module identity, URL-safe paths and atomic publication/cleanup. Validate the final artifact after all post-link transformations. Use the supported patched Binaryen build baseline from #2632/#2652; do not reintroduce a temporary, separate Binaryen pin or treat the already-fixed Asyncify DWARF issue as a new prerequisite.
Implemented in main
| Capability | Delivery | Status / boundary |
|---|---|---|
| Native DWARF correctness, defaults and source locations | #2142 | Merged; platform-default native DWARF packaging |
| Common debugger schema and LLDB/GDB runtime views | #2712 | Merged; shared fixtures and explicit unsupported-platform behavior |
llgo debug, Wasm embedded/external DWARF, embedded remote/load/reset |
#2269 | Merged; real QEMU transport and image download/reset acceptance |
| Browser source breakpoints, paused locals, artifact/source identity | #2713 | Merged; real Chrome Memory32/64 acceptance |
Native acceptance remains platform-specific: LLDB is the native choice on macOS arm64; GDB supports embedded remote sessions there. Stock GDB's blocked-worker unwind is limited on Intel macOS and Windows ARM64, while the narrower values/registry/main-stack acceptance is covered. Linux and Windows x64/x86 retain strict complete-worker acceptance. Broader MCU architectures and physical probes require their own qualification; QEMU results do not establish hardware coverage.
Remaining software work
| Task | Implementation / acceptance required | Progress |
|---|---|---|
| Browser logical G/source stacks | Live ID/parent/state/last-P registry, parked fiber function/file/line stacks, GC and exit cleanup; opt-in overhead and cache separation | #2742 submitted and rebased onto current main; local Memory32/64, 1/2/4-worker and embedded/external DWARF acceptance passes. Compiler/debug command/ABI units and all 24 extension tests pass on the latest base. Previous-head browser/Wasmtime/Wasmer checks pass and review comments are addressed; current Wasm/browser/runtime and patch-coverage checks pass at ede69416e4e7648a36189f578d4f1c69ed68ce05; host checks remain running/queued |
| Browser coordinated worker control | Pause/resume every relevant worker, manage worker lifecycle and stable stop IDs, then inspect shared memory consistently | The #2742 acceptance pauses all worker targets through Chrome's real debugger transport. Product users still pause other threads in DevTools; automatic coordination is not implemented |
| Parked-frame variables and complete runtime views | Recover selected logical-frame locals where the runtime/compiler can expose them; finish deep runtime-object inspection with GC/memory-growth/optimized-out cases | Still pending; logical function/file/line stacks alone do not satisfy this gate |
| W32 WASI interactive debugging | Real guest source breakpoints/stepping/locals, thread control and shared-memory/runtime views; preserve shared env.memory, wasi.thread-spawn, real thread exit and EH |
Still pending. Execution currently uses Wasmer; llgo debug -target=wasi correctly rejects the incompatible Wasmtime path |
| Maintained qualification record | Record exact compiler/runtime/debugger/browser versions and tested configurations, source identity and deployment/debug sizes | Each implementation carries its evidence; expand as the remaining adapters become available |
WASI engine selection must follow actual debugger capabilities. Wasmer's experimental artifact profiler can produce native source maps, but its current generated debugger DWARF covers functions/lines, not the complete guest-variable/shared-memory contract. WAMR's interpreter debugger documentation explicitly excludes pthread/thread-spawn debugging. Neither substitutes for current W32 pthread/EH acceptance. Do not introduce single-thread artifacts, synthetic imports or disabled EH to make a debugger test pass.
Acceptance and hardware
- Integrate the four first-stage contributions into main.
- Preserve native platform defaults and typed Wasm/embedded artifact contracts.
- Execute native LLDB/GDB and embedded QEMU transports against real binaries.
- Execute browser source/local-variable sessions with actual Emscripten hosts.
- Merge and qualify logical Wasm G/source stacks (debug: inspect parked Wasm goroutine source stacks #2742).
- Deliver automatic browser worker control, parked-frame locals and complete runtime views.
- Deliver a compatible W32 WASI guest-debugger transport and its real thread/EH/shared-memory acceptance.
- Physical-board/probe qualification: deferred until hardware is available; retain download/reset, source/value and device/tool/version evidence.
Standalone acceptance is under test/debug/{native,runtime,embedded,hardware,wasm}; package-private tests remain beside their implementation. #2742 adds a separately tested browser decoder and real parked-stack/GC/exit checks, using Chrome for Testing 152 and patched Binaryen llgo-v132.3. A local integration of #2738/#2743/#2742 also passes all ten Chrome Memory32/64 sessions, including 1/2/4 workers and embedded/external DWARF. Sequence guards detect changes during inspection; they do not provide a globally simultaneous snapshot while other workers run. Logical-stack inspection does not imply parked-frame local-variable evaluation.
Compact nil-store panic metadata (#2406) and size optimization (#2679) remain separate workstreams. The proposal is not complete merely because its first-stage PRs merged.
Activity