1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
|
# Debugger-Mutator Communication Protocol
**ExecutionHandler Thread Synchronization in WebAssembly Debugging**
This document describes the thread communication protocol used in `WasmExecutionHandler.cpp` to coordinate between the debugger thread (LLDB commands) and mutator threads (WebAssembly execution), along with VMManager coordination for stop-the-world operations.
---
## Thread Actors
### 1. **Debugger Thread**
- Runs on the debug server's WorkQueue thread
- Processes LLDB commands: continue, step, interrupt, thread selection
- Initiates debugging operations
- Thread ID: `m_debugServerThreadId`
### 2. **Mutator Thread(s)**
- Runs WebAssembly bytecode execution
- One per VM (main thread + worker threads)
- Responds to debugging commands
- Thread ID: `VM::ownerThread()->uid()`
### 3. **VMManager** (Coordinator)
- Singleton managing all VMs in the process
- Implements stop-the-world coordination
- Calls `wasmDebuggerOnStop()` and `wasmDebuggerOnResume()` callbacks
- Ensures all VMs stopped before debugger accesses state
---
## Synchronization Primitives
### Key State Variables
```cpp
m_debuggeeContinue // Debugger signals debuggee to continue
m_debuggerContinue // Debuggee signals debugger to continue
m_debuggerState // Current debugger operation state
m_debuggee // VM being debugged
m_awaitingResumeNotification // Resume barrier flag
```
### Debugger States
```cpp
enum class DebuggerState {
Replied, // Idle, ready for next command
InterruptRequested, // Async interrupt in progress
ContinueRequested, // Resume all VMs
StepRequested, // Single-step with breakpoints
SwitchRequested // VM context switch
};
```
---
## Protocol Flow Diagrams
### 1. **Continue Operation** (Resume All VMs)
```
Debugger Thread Debuggee Thread Other Thread(s)
================== ==========================================================================================================================
ExecutionHandler::stopCode() VMManager::notifyVMStop()
┌──>├─ wait(debuggeeCV) ┌────────>├─ wait(worldCV)
resumeImpl() │ │ │ └─ Other VMs exiting notifyVMStop()
├─ Set state = ContinueRequested │ │ │
├─ notifyOne(debuggeeCV) ────────┘ │ │
├─ wait(debuggerCV) <──┐ │ │
│ │ │ │
│ │ ├─ Check state -> ContinueRequested │
│ │ ├─ Set m_awaitingResumeNotification = true │
│ │ └─ Return ResumeMode::All │
│ │ └─ VMManager::resumeAll() ─────────────────┘
│ │ └─ Debuggee VM exiting notifyVMStop()
│ │ Some VMs May exit notifyVMStop() early and hit breakpoints
│ │ ├─ stopTheWorld()
│ │ ┌───>├─ wait(debuggeeCV) on m_awaitingResumeNotification
│ │ Last VM exiting notifyVMStop() ─> wasmDebuggerOnResume() │ │
│ │ ├─ handlePostResume() │ │
│ │ ├─ Set m_awaitingResumeNotification = false
│ └───────────────────────────────────├─ notifyOne(debuggerCV) │ │
│ └─ notifyAll(debuggeeCV) ┘ │
└─ Debugger resumes └─ notifyVMStop() and send stop reply to debugger
```
**Key Points:**
- Debugger waits for **post-resume** confirmation to prevent interrupt() race
- `m_awaitingResumeNotification` acts as resume barrier
- VMManager ensures all VMs actually resumed before callback
---
### 2. **Interrupt Operation** (Async Stop Request)
```
Debugger Thread All Thread(s)
================== ===========================================================================================
[Running Wasm]
interrupt()
├─ Set state = InterruptRequested
├─ VMManager::requestStopAll() ──────────────────-----───> VMTraps fire
├─ wait(debuggerCV) <──┐ notifyVMStop() notifyVMStop()
│ │ ├─ One VM picked as debuggee └─ Other VMs wait(worldCV)
│ │ ├─ On stop callback
│ │ ├─ stopCode()
│ │ ├─ setStopped()
│ │ ├─ Check state -> InterruptRequested
│ └───────────────┼─ notifyOne(debuggerCV)
│ └─ wait(debuggeeCV)
│
└─ sendStopReply()
```
**Key Points:**
- **Asynchronous**: Debugger initiates trap via VMManager
- All VMs stopped via trap mechanism (no direct wait/notify initially)
- Once debuggee VM stops, it notifies debugger
- Multiple VMs coordinate through VMManager stop-the-world
---
### 3. **Single Step Operation**
```
Debugger Thread Mutator Thread (Debuggee) Other Thread(s)
================== ==========================================================================================================================
ExecutionHandler::stopCode() VMManager::notifyVMStop()
┌──>├─ wait(debuggeeCV) └─ wait(worldCV)
step() │ │
├─ stepAtBreakpoint() │ │
│ └─ Set one-time breakpoints │ │
├─ Set state = StepRequested │ │
├─ notifyOne(debuggeeCV) ────────┘ │
├─ wait(debuggerCV) <──┐ │
│ │ ├─ Check state -> StepRequested
│ │ └─ Return ResumeMode::One
│ │ └─ VMManager::resumeOne(debuggee)
│ │ └─ Debuggee VM exiting notifyVMStop() (others stay stopped)
│ │ ├─ Debuggee VM executes
│ │ ├─ Hits one-time breakpoint
│ │ └─ stopTheWorld()
│ │ └─ notifyVMStop()
│ │ └─ stopCode()
│ │ ├─ setStopped()
│ │ ├─ Check state -> StepRequested
│ └────────────────────────────────────┼─ notifyOne(debuggerCV)
│ └─ wait(debuggeeCV)
└─ sendStopReply()
```
**Key Points:**
- One-time breakpoints set before resume
- Only debuggee VM runs (RunOne mode)
- All other VMs remain stopped
- Breakpoint hit triggers another stop-the-world
---
### 4. **Step-Into (Call/Throw)** - Two-Phase Protocol
```
Debugger Thread Mutator Thread (Debuggee) Other Thread(s)
================== ==========================================================================================================================
ExecutionHandler::stopCode() VMManager::notifyVMStop()
┌──>├─ wait(debuggeeCV) └─ wait(worldCV)
step() │ │
└─ stepAtBreakpoint() │ │
├─ Detect call/throw │ │
├─ Set hasStepIntoEvent flag │ │
├─ notifyOne(debuggeeCV) ────┘ │
├─ wait(debuggerCV) <──┐ │
│ │ ├─ Wakes up, executes call/throw
│ │ └─ setStepIntoBreakpointForCall/Throw()
│ │ ├─ Set breakpoint at callee or exception handler
│ │ └─ stopTheWorld()
│ │ └─ notifyVMStop()
│ │ ├─ stopCode(StepIntoSiteReached)
│ │ ├─ Check state -> StepRequested
│ └─────────────────────┼─ notifyOne(debuggerCV)
│ ┌──────────────> ├─ wait(debuggeeCV)
│ │ │
│ │ │
├─ notifyOne(debuggeeCV) ───┘ │
├─ wait(debuggerCV) <┐ │
│ │ ├─ Wakes up, runs to step-into breakpoint
│ │ ├─ Breakpoint hit -> stopCode()
│ │ ├─ setStopped()
│ │ ├─ Check state -> StepRequested
│ └───────────────────────┼─ notifyOne(debuggerCV)
│ └─ wait(debuggeeCV)
└─ sendStopReply()
```
**Key Points:**
- **Two-phase handshake**:
1. Execute call/throw and register breakpoint
2. Resume and hit step-into breakpoint
- Breakpoint set dynamically at call/throw site
---
### 5. **VM Context Switch**
```
Debugger Thread Old Mutator Thread (Old Debuggee) New Mutator Thread (New Debuggee) Other Thread(s)
================== ======================================== ================================== ========================
ExecutionHandler::stopCode() VMManager::notifyVMStop() VMManager::notifyVMStop()
┌─────>├─ wait(debuggeeCV) ┌──>├─ wait(worldCV) └─ wait(worldCV)
switchTarget(threadId) │ │ │ │
├─ m_debuggee = newDebuggee │ │ │
├─ Set state = SwitchRequested │ │ │
├─ notifyOne(debuggeeCV)┘ │ │ │
├─ wait(debuggerCV) <────┐ │ │ │
│ │ ├─ Check state -> SwitchRequested │ │
│ │ └─ Return ResumeMode::Switch │ │
│ │ └─ VMManager switch context ───────┘ │
│ │ └─ wait(worldCV) │
│ │ └─ wasmDebuggerOnStop()
│ │ └─ handleStopTheWorld() -> stopCode()
│ │ ├─ setStopped()
│ │ ├─ Check state -> SwitchRequested
│ └────────────────────────────────────────────────────────┼─ notifyOne(debuggerCV)
│ └─ wait(debuggeeCV)
└── sendStopReply()
```
**Key Points:**
- Switch debuggee between VMs
- VMManager coordinates transition
|