The Debug Adapter Protocol (DAP) is a protocol proposed by VS Code that separates debugging functionality from debugger implementation, enabling the editor to support multiple debuggers.
DAP Architecture
Protocol Layers
- Client: VS Code editor, responsible for debug UI and user interaction
- Adapter: Debug adapter, converts DAP requests to debugger-specific commands
- Debugger: Actual debugger implementation (such as GDB, LLDB, Chrome DevTools)
Communication Method
- Uses JSON-RPC protocol
- Communicates via standard input/output or WebSocket
- Supports asynchronous message passing
Core Concepts
Session
A debug session represents a complete debugging process, from launch to termination.
Thread
Execution thread in the debugger, can contain multiple stack frames.
Stack Frame
One frame in the function call stack, containing local variables and execution position.
Scope
Logical grouping of variables, such as local variables, global variables, etc.
Variable
Variable in the debugger, can be viewed and modified.
DAP Request Types
Initialize Request
json{ "seq": 1, "type": "request", "command": "initialize", "arguments": { "adapterID": "my-debugger", "pathFormat": "path", "linesStartAt1": true, "columnsStartAt1": true } }
Launch/Attach Request
json{ "seq": 2, "type": "request", "command": "launch", "arguments": { "program": "/path/to/program", "stopOnEntry": false } }
Set Breakpoints Request
json{ "seq": 3, "type": "request", "command": "setBreakpoints", "arguments": { "source": { "path": "/path/to/file.js" }, "breakpoints": [ { "line": 10 } ] } }
Continue Execution Request
json{ "seq": 4, "type": "request", "command": "continue", "arguments": { "threadId": 1 } }
DAP Event Types
Initialized Event
json{ "seq": 1, "type": "event", "event": "initialized" }
Stopped Event
json{ "seq": 2, "type": "event", "event": "stopped", "body": { "reason": "breakpoint", "threadId": 1, "allThreadsStopped": false } }
Output Event
json{ "seq": 3, "type": "event", "event": "output", "body": { "category": "console", "output": "Hello World\n" } }
Implementing Debug Adapter
Create Adapter Project
bashnpm init -y npm install @vscode/debugadapter
Basic Adapter Structure
typescriptimport { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent } from '@vscode/debugadapter'; class MyDebugSession extends DebugSession { constructor() { super(); } protected initializeRequest(response: DebugProtocol.InitializeResponse): void { response.body = { supportsConfigurationDoneRequest: true, supportsEvaluateForHovers: true, supportsStepBack: false }; this.sendResponse(response); } protected launchRequest(response: DebugProtocol.LaunchResponse, args: any): void { // Launch debugger this.sendResponse(response); } protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void { // Set breakpoints response.body = { breakpoints: args.breakpoints.map(bp => ({ verified: true, line: bp.line })) }; this.sendResponse(response); } }
Configuring Debug Adapter
package.json Configuration
json{ "contributes": { "debuggers": [ { "type": "my-debugger", "label": "My Debugger", "program": "./out/debugAdapter.js", "runtime": "node", "configurationAttributes": { "launch": { "required": ["program"], "properties": { "program": { "type": "string", "description": "Program to debug" } } } } } ] } }
launch.json Configuration
json{ "version": "0.2.0", "configurations": [ { "type": "my-debugger", "request": "launch", "name": "Debug with My Debugger", "program": "${workspaceFolder}/app.js" } ] }
Debug Adapter Testing
Debugging the Adapter
- Press F5 to start extension development host
- Open project with debug configuration
- Start debug session
- Test various debug features
Testing Checklist
- Breakpoint setting and triggering
- Step execution
- Variable viewing and modification
- Call stack viewing
- Expression evaluation
Advanced Features
Custom Evaluation
typescriptprotected evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): void { const result = this.evaluateExpression(args.expression); response.body = { result: String(result), variablesReference: 0 }; this.sendResponse(response); }
Custom Variables
typescriptprotected variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): void { const variables = this.getVariables(args.variablesReference); response.body = { variables: variables.map(v => ({ name: v.name, value: String(v.value), variablesReference: v.children ? 1 : 0 })) }; this.sendResponse(response); }
Important Notes
- Properly handle asynchronous operations
- Provide clear error messages
- Support cancellation operations
- Consider performance optimization
- Follow DAP specification