WebAssembly (Wasm) is a low-level binary instruction format. It was originally created to run in the browser, but has gained traction in many non-browser environments as well.

Debugging WebAssembly applications presents unique challenges compared to traditional native code debugging. Unlike native executables that run directly on the processor, WebAssembly code executes within a virtual machine that abstracts away the underlying hardware. This abstraction, while providing portability and security benefits, complicates the debugging process because debuggers cannot rely on standard debugging techniques.

Debug Info

WebAssembly supports the DWARF debug information format. While much of standard DWARF applies, Wasm imposes unique constraints due to its design. For example, WebAssembly instructions are not stored in its linear memory address space. Wasm also does not have registers and instead uses three distinct kinds of virtual registers: globals, locals, and the operand stack. This document describes how DWARF is used with Wasm.

Several major toolchains have implemented DWARF support for WebAssembly. LLVM-based toolchains such as Clang, Swift, Emscripten, and Rust generate DWARF information when building with debug info.

Runtimes

When it comes to using DWARF to debug native code, different runtimes take different approaches based on their architecture and target use cases. Below is a non-exhaustive overview of the current debugging support.

Wasmtime

Wasmtime is a WebAssembly runtime developed by the Bytecode Alliance. It’s a standalone runtime that can execute WebAssembly modules outside of web browsers. Wasmtime uses Cranelift (a code generator) to compile Wasm to native machine code through just-in-time (JIT) compilation.

Debugging Wasm in Wasmtime works by attaching a debugger to the runtime process itself. Since Wasmtime JIT-compiles WebAssembly to native code and translates the Wasm-specific DWARF to match the generated native instructions, standard debuggers like LLDB or GDB work without requiring any modifications.

Wasmtime has approved an RFC outlining the methodology and priorities for future debugging support. The plan involves implementing the Debug Adapter Protocol (DAP) to provide a unified debugging experience for both native and interpreted languages.

Chrome DevTools Extension for C/C++

Chrome has a DevTools Extension for debugging C/C++ WebAssembly using DWARF. Chrome DevTools communicates directly with Chrome’s V8 JavaScript engine and WebAssembly runtime through the Chrome DevTools Protocol (CDP), which is the same protocol used for debugging JavaScript.

While the extension doesn’t use LLDB to debug the runtime or the Wasm process, it leverages LLDB’s DWARF parsing capabilities. The system creates a dummy process, loads the Wasm module into it, and relies on LLDB to parse the DWARF information, create type information, and pretty-print variable values using data retrieved directly from the V8 runtime.

WebAssembly Micro Runtime (WAMR)

The WebAssembly Micro Runtime (WAMR) is a lightweight standalone WebAssembly runtime. Like Wasmtime, it’s also developed by the Bytecode Alliance.

WAMR takes another approach to debugging by implementing remote debugging capabilities. When built with debugging support, WAMR includes a GDB remote stub. A debugging stub is a small piece of code that acts as a bridge between the debugger (like LLDB) and, in this case, the Wasm runtime. It uses the GDB Remote Serial Protocol (RSP), a simple, extensible protocol that is widely recognized as the standard for remote debugging scenarios.

Similar to DWARF, debugging Wasm requires some extensions to the standard GDB remote protocol to handle WebAssembly’s unique execution model. Currently, this approach requires changes to LLDB, but upstream support is actively being worked on, which will make this debugging approach more widely available and standardized.

Runtime Comparison

Runtime Approach Trade-offs
Wasmtime JIT + Native Debugging Mature tooling, but limited debugging experience
Chrome DevTools Browser-Integrated Debugging Seamless web development, but browser-specific
WAMR Remote Debugging Protocol Native LLDB experience, but requires protocol extensions