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:
- Set breakpoints in WebAssembly functions
- Step through WebAssembly bytecode instruction by instruction
- Inspect WebAssembly locals, globals, and memory
- View call stacks across WebAssembly function calls
- Disassemble WebAssembly bytecode in real-time
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) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘- Location:
WasmDebugServer.h/cpp - Purpose: Central coordinator implementing GDB Remote Protocol
- Two Modes:
- Standalone Mode: TCP socket server (default port 1234) for JSC shell debugging
- RWI Mode: IPC-based communication for WebKit/WebContent debugging (see RWI_ARCHITECTURE.md)
- Key Features:
- Protocol packet parsing and response generation
- Contains all protocol handlers and helper classes
- ExecutionHandler: Breakpoints, continue, step, interrupt
- MemoryHandler: Memory read/write operations
- QueryHandler: Capability negotiation and queries
- ModuleManager: Virtual address space management and module tracking
- BreakpointManager: Breakpoint storage and management
- VirtualAddress: 64-bit virtual address encoding for LLDB compatibility
The 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- [DONE]
gdb-remote localhost:1234: Attach to debugger - [DONE]
process interrupt,ctrl+C: Stop execution at function entry - [DONE]
continue: Resume WebAssembly execution - [DONE]
breakpoint set: Set breakpoints at virtual addresses - [DONE]
step over: Step over function calls - [DONE]
step in: Step into function calls and exception handlers - [DONE]
step out: Step out of current function - [DONE]
step instruction: Single step through bytecode
- [DONE]
target modules list: List loaded WebAssembly modules - [DONE]
disassemble: Display WebAssembly bytecode - [DONE]
bt(backtrace): Show WebAssembly call stack - [DONE]
frame variable: List local variables - [DONE]
memory region --all: List memory regions - [DONE]
memory read: Read WebAssembly memory and module source - [TODO]
memory write: Write to memory? source? or both?
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 --testwasmdebuggerOpcode Coverage (Base OpType):
- [DONE] Special Ops (FOR_EACH_WASM_SPECIAL_OP)
- [DONE] Control Flow Ops (FOR_EACH_WASM_CONTROL_FLOW_OP)
- [DONE] Unary Ops (FOR_EACH_WASM_UNARY_OP)
- [DONE] Binary Ops (FOR_EACH_WASM_BINARY_OP)
- [DONE] Memory Load Ops (FOR_EACH_WASM_MEMORY_LOAD_OP)
- [DONE] Memory Store Ops (FOR_EACH_WASM_MEMORY_STORE_OP)
Extended Opcode Coverage:
- [TODO] Ext1OpType (FOR_EACH_WASM_EXT1_OP)
- [PARTIAL] ExtGCOpType (FOR_EACH_WASM_GC_OP) - 2 control flow ops fully tested (BrOnCast, BrOnCastFail), 29 non-control-flow ops have stub tests
- [TODO] ExtAtomicOpType (FOR_EACH_WASM_EXT_ATOMIC_OP)
- [TODO] ExtSIMDOpType (FOR_EACH_WASM_EXT_SIMD_OP)
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.pyFor 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.jsTerminal 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:
- Starting Safari/MiniBrowser with
--wasm-debuggerflag - Using WasmDebuggerRWIClient to relay LLDB commands
- Debugging WebContent processes via Remote Web Inspector
- Issue: Current implementation only supports WASM local variable inspection, missing WASM stack value types
- Current Support: Local variables with types (parameters and locals in function scope)
- Missing Support: Stack values with types
- Solution: Extend debugging protocol to expose WASM operand stack contents with proper type information
- Benefits: Complete variable inspection during debugging, better understanding of WASM execution state
- Issue: Current unit tests only cover base OpType opcodes; ExtGCOpType has partial coverage with stub implementations
- Complete Coverage:
- ExtGCOpType control flow: BrOnCast, BrOnCastFail (fully tested)
- Partial Coverage:
- ExtGCOpType non-control-flow: 29 opcodes have stub tests that need proper implementation
- Missing Coverage:
- Ext1OpType (table operations, saturated truncation)
- ExtAtomicOpType (atomic operations)
- ExtSIMDOpType (SIMD operations)
- Issue: Client disconnect, kill, and quit commands only stop the client session for debugging purposes
- Location:
WasmDebugServer.cpp:348-349 - Solution: Introduce various stop states and proper termination handling
- Issue: LLDB is not notified when new modules are loaded or unloaded
- Location:
WasmDebugServer.cpp:472, 484 - Solution: Implement proper LLDB notifications for dynamic module loading/unloading
- Issue: Thread select and stop reply protocol handlers need improvement to correctly display multi-VM data in LLDB
- Current Status: Multi-VM stop-the-world is implemented, but thread information may not display correctly in LLDB UI
- Issue: The WebAssembly debugger is currently restricted to ARM64 platforms only
- Known Problems on Other Platforms:
- x86_64: VMTraps race condition causes register corruption during interrupt handling
- ARM32, iOS: Untested
The following references correspond to the numbered citations used throughout the WebAssembly debugger implementation:
- [1] Interrupts
- [2] Packet Acknowledgment
- [3] Packets
- [4] General Query Packets
- [5] Standard Replies
- [6] Packet Acknowledgment
- [7] qSupported
- [8] qProcessInfo
- [9] qHostInfo
- [10] qRegisterInfo
- [11] qListThreadsInStopReply
- [12] qEnableErrorStrings
- [13] qThreadStopInfo
- [14] qXfer:library-list:read
- [15] qWasmCallStack
- [16] qWasmLocal
- [17] qMemoryRegionInfo