Skip to content

Emit line number information for native AOT/WASM - #135740

Merged
MichalStrehovsky merged 2 commits into
dotnet:mainfrom
MichalStrehovsky:linenumber
Oct 14, 2026
Merged

MichalStrehovsky merged 2 commits into
dotnet:mainfrom
MichalStrehovsky:linenumber

Conversation

@MichalStrehovsky

Copy link
Copy Markdown
Member

wasmtime with WASMTIME_BACKTRACE_DETAILS=1 can now print the backtrace including line numbers. Locals/parameters are not emitted because RyuJIT seems to be lacking that info.

d:\git\runtime2>wasmtime artifacts\bin\repro\x64\Debug\reproZero.wasm
Hi
Hi
Error: failed to run main module `artifacts\bin\repro\x64\Debug\reproZero.wasm`

Caused by:
    0: failed to invoke command default
    1: error while executing at wasm backtrace:
    0:    0x86d - abort
                    at wasisdk://v34.0/build/sysroot/wasi-libc-wasm32-wasip1-build-prefix/src/wasi-libc-wasm32-wasip1-build-build/wasisdk://v34.0/src/wasi-libc/libc-bottom-half/sources/abort.c:5:3
    1:    0x43b - reproZero_Program__DoAbort
                    at D:\git\runtime2\src\coreclr\tools\aot\ILCompiler\reproZero\zerosharp.cs:164:0
    2:    0x3d5 - reproZero_Program__Main
                    at D:\git\runtime2\src\coreclr\tools\aot\ILCompiler\reproZero\zerosharp.cs:156:0
    3:    0x349 - reproZero_Program__NativeMain
                    at D:\git\runtime2\src\coreclr\tools\aot\ILCompiler\reproZero\zerosharp.cs:141:0
    4:    0x773 - reproZero.wasm!__main_void
    5:    0x2d7 - _start
                    at wasisdk://v34.0/build/sysroot/wasi-libc-wasm32-wasip1-lto-build-prefix/src/wasi-libc-wasm32-wasip1-lto-build-build/wasisdk://v34.0/src/wasi-libc/libc-bottom-half/crt/crt1-command.c:55:11
    2: wasm trap: wasm `unreachable` instruction executed

wasmtime with `WASMTIME_BACKTRACE_DETAILS=1` can now print the backtrace including line numbers:

```
d:\git\runtime2>wasmtime artifacts\bin\repro\x64\Debug\reproZero.wasm
Hi
Hi
Error: failed to run main module `artifacts\bin\repro\x64\Debug\reproZero.wasm`

Caused by:
    0: failed to invoke command default
    1: error while executing at wasm backtrace:
    0:    0x86d - abort
                    at wasisdk://v34.0/build/sysroot/wasi-libc-wasm32-wasip1-build-prefix/src/wasi-libc-wasm32-wasip1-build-build/wasisdk://v34.0/src/wasi-libc/libc-bottom-half/sources/abort.c:5:3
    1:    0x43b - reproZero_Program__DoAbort
                    at D:\git\runtime2\src\coreclr\tools\aot\ILCompiler\reproZero\zerosharp.cs:164:0
    2:    0x3d5 - reproZero_Program__Main
                    at D:\git\runtime2\src\coreclr\tools\aot\ILCompiler\reproZero\zerosharp.cs:156:0
    3:    0x349 - reproZero_Program__NativeMain
                    at D:\git\runtime2\src\coreclr\tools\aot\ILCompiler\reproZero\zerosharp.cs:141:0
    4:    0x773 - reproZero.wasm!__main_void
    5:    0x2d7 - _start
                    at wasisdk://v34.0/build/sysroot/wasi-libc-wasm32-wasip1-lto-build-prefix/src/wasi-libc-wasm32-wasip1-lto-build-build/wasisdk://v34.0/src/wasi-libc/libc-bottom-half/crt/crt1-command.c:55:11
    2: wasm trap: wasm `unreachable` instruction executed
```
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The Wasm writer silently ignores the requested --gdwarf-5 option.

1 open finding
What changed in this PR

Adds DWARF line information to NativeAOT WebAssembly output for source-level Wasmtime backtraces.

Changes:

  • Emits DWARF custom sections and Wasm relocations.
  • Maps methods and funclets to source locations.
  • Adds Wasm-specific DWARF handling.
File Description
WasmRelocatableObjectWriter.cs Emits and relocates DWARF sections.
WasmObjectWriter.cs Creates debug custom sections.
WasmSection.cs Exposes section prefix sizes.
MethodCodeNode.cs Restarts mappings for funclets.
DwarfLineSequenceWriter.cs Completes line sequences correctly.
DwarfInfo.cs Omits unsupported Wasm frame expressions.
DwarfBuilder.cs Adds Wasm architecture support.

🧠 Review effort: Balanced

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@adamperlin

Copy link
Copy Markdown
Contributor

@MichalStrehovsky are the line numbers here correct? We had been concerned that IL -> Native offset mappings in the JIT were incorrect on Wasm.

@MichalStrehovsky

Copy link
Copy Markdown
Member Author

@MichalStrehovsky are the line numbers here correct? We had been concerned that IL -> Native offset mappings in the JIT were incorrect on Wasm.

They looked good for the stack I pasted in top-post.

@MichalStrehovsky
MichalStrehovsky merged commit 81ce95c into dotnet:main Oct 14, 2026
100 of 102 checks passed
@MichalStrehovsky
MichalStrehovsky deleted the linenumber branch October 14, 2026 21:21
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 12.0-preview1 milestone Oct 19, 2026
lewing added a commit that referenced this pull request Nov 2, 2026
Re-enables RyuJIT variable debug info for ReadyToRun WebAssembly code,
which was disabled before wasm locals could be encoded, and defines the
encoding that readers consume.

- **Register fields** hold `localIndex + WASM_LOCAL_REGNUM_BASE` (3);
values 0–2 keep their meanings (`REGNUM_PC`, `REGNUM_COUNT`,
`REGNUM_AMBIENT_SP`).
- **Stack records** use base 2 (`REG_FPBASE` = `REG_SPBASE` =
`REGNUM_AMBIENT_SP` on wasm), so they are slots relative to the logical
frame, which readers reconstruct from the stack walk.
- **Scopes** open in the first emitted block: wasm is AOT-only, so
`getVars` always reports method-wide scopes, and wasm block order
doesn't follow IL order.
- The hidden portable-entrypoint argument is excluded from source
variables, and R2RDump decodes wasm registers.

Both constants are fixed for DebugInfo contract `c1` and documented in
`DebugInfo.md`. Changing either needs a new contract version and,
because R2R images persist the encoding, a ReadyToRun format version
change. Browser and WASI publishes strip debug info by default
(#134690), so these records reach an image only with
`PublishReadyToRunStripDebugInfo=false`; generated code is unchanged.
The cDAC consumer is #133890.

<details><summary>Test coverage and follow-ups</summary>

`WasmWebcilModule` checks invariants rather than exact codegen: register
records name declared locals with the expected wasm types, stack records
use base 2 without overlapping ranges, recorded frame slots are actually
accessed (including from a finally funclet), and GC locals' ranges cover
the safepoint. Removing the `+3` bias makes it fail.

Follow-ups: NativeAOT DWARF (#135740) can describe locals with
`DW_OP_WASM_location` (local index = register − 3); frame-slot variables
additionally need the engine local holding the frame pointer, which this
format doesn't record. #134808 plans R2R DWARF on top of these records.
</details>

> [!NOTE]
> This pull request description was updated with the assistance of
GitHub Copilot.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Copilot-Session: 51b56d7e-45e5-464d-8c2b-78c744680ff8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-NativeAOT-coreclr

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants