A comprehensive debugging solution that enables LLDB debugging of WebAssembly code running in JavaScriptCore's IPInt (In-Place Interpreter) tier through the GDB Remote Serial Protocol.
Related Documentation:
- This document: JSC debug server implementation (both Standalone and RWI modes)
- RWI_ARCHITECTURE.md: WebKit integration architecture (RWI mode details)
- Debugger-Mutator-Protocol.md: Thread synchronization protocol and control flow diagrams
This project implements a WebAssembly debugger server that bridges the gap between LLDB (the LLVM debugger) and WebAssembly code execution in JavaScriptCore. It allows developers to:
The implementation follows the GDB Remote Serial Protocol standard with wasm extension.
┌─────────────────────────────────────────────────────────────────┐
│ LLDB Debugger │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Breakpoints │ │ Symbol Lookup │ │ Execution Ctrl │ │
│ │ Management │ │ & Modules │ │ & Stepping │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
│ GDB Remote Protocol (TCP:1234)
│
┌─────────────────────────────▼───────────────────────────────────┐
│ WasmDebugServer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │Execution Handler│ │ Memory Handler │ │ Query Handler │ │
│ │(Breakpoints) │ │ (WASM Memory) │ │(Capabilities) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Module Manager │ │Breakpoint Mgr │ │ │ │
│ │ (Virtual Addrs) │ │(Helper Class) │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
│ Module Tracking & Execution Hooks
│
┌─────────────────────────────▼───────────────────────────────────┐
│ JavaScriptCore WebAssembly Engine │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │JSWebAssemblyMod │ │ IPInt Execution │ │ Debug Info │ │
│ │(Module Tracking)│ │ (Interpreter) │ │ (PC Mapping) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
WasmDebugServer.h/cppThe debugger uses a sophisticated virtual address encoding system to present WebAssembly modules and memory to LLDB:
Address Format (64-bit):
- Bits 63-62: Address Type (2 bits)
- Bits 61-32: ID (30 bits) - ModuleID for code, InstanceID for memory
- Bits 31-0: Offset (32 bits)
Address Types:
- 0x00 (Memory): Instance linear memory
- 0x01 (Module): Module code/bytecode
- 0x02 (Invalid): Invalid/unmapped regions
- 0x03 (Invalid2): Invalid/unmapped regions
Virtual Memory Layout:
- 0x0000000000000000 - 0x3FFFFFFFFFFFFFFF: Memory regions
- 0x4000000000000000 - 0x7FFFFFFFFFFFFFFF: Module regions
- 0x8000000000000000 - 0xFFFFFFFFFFFFFFFF: Invalid regions
The debugger includes comprehensive unit tests that validate debug info generation for WebAssembly opcodes:
# Run unit tests via WebKit build system ./Tools/Scripts/run-javascriptcore-tests --testwasmdebugger
Opcode Coverage (Base OpType):
Extended Opcode Coverage:
The JSTests/wasm/debugger includes a comprehensive test framework with auto-discovery, parallel execution, and process isolation capabilities:
# Run comprehensive test framework with LLDB and wasm debugger python3 JSTests/wasm/debugger/test-wasm-debugger.py
For details, see JSTests/wasm/debugger/README.md.
Standalone Mode (JSC Shell):
Terminal 1 - Start JSC with debugger:
cd JSTests/wasm/debugger/resources/add VM=<Path-To-WebKitBuild>/Debug && DYLD_FRAMEWORK_PATH=$VM lldb $VM/jsc -- --verboseWasmDebugger=1 --wasm-debugger --useConcurrentJIT=0 main.js
Terminal 2 - Connect LLDB:
lldb -o 'log enable gdb-remote packets' -o 'process connect --plugin wasm connect://localhost:1234'
RWI Mode (WebKit/WebContent):
See RWI_ARCHITECTURE.md for complete setup instructions including:
__XPC_JSC_enableWasmDebugger=1 JSC_enableWasmDebugger=1 flaglldb platform process list, so users can attach to a specific website. Currently there is one WasmDebuggerDebuggable per WebContent process, so when Safari places multiple pages into the same process (and therefore the same VM), all their hostnames are aggregated into a single target entry (e.g. "earth.google.com, github.com").WasmDebugServer that owns all Wasm execution for every page it hosts. Splitting that into per-page debuggables would create multiple LLDB sessions backed by the same VM state, which is architecturally incorrect. Aggregation correctly reflects that one LLDB attach covers all pages in the process.WasmDebugServer), replace the aggregated URL with a per-page WasmDebuggerDebuggable so each URL appears as a distinct, independently attachable target.WasmDebugServer.cpp:348-349The following references correspond to the numbered citations used throughout the WebAssembly debugger implementation: