调试适配器协议(Debug Adapter Protocol,DAP)是 VS Code 提出的一种协议,用于将调试功能与调试器实现分离,使编辑器能够支持多种调试器。
DAP 架构
协议层次
- 客户端: VS Code 编辑器,负责调试 UI 和用户交互
- 适配器: 调试适配器,将 DAP 请求转换为调试器特定命令
- 调试器: 实际的调试器实现(如 GDB、LLDB、Chrome DevTools)
通信方式
- 使用 JSON-RPC 协议
- 通过标准输入/输出或 WebSocket 通信
- 支持异步消息传递
核心概念
会话(Session)
调试会话表示一次完整的调试过程,从启动到结束。
线程(Thread)
调试器中的执行线程,可以包含多个栈帧。
栈帧(Stack Frame)
函数调用栈中的一个帧,包含局部变量和执行位置。
作用域(Scope)
变量的逻辑分组,如局部变量、全局变量等。
变量(Variable)
调试器中的变量,可以查看和修改其值。
DAP 请求类型
初始化请求
json{ "seq": 1, "type": "request", "command": "initialize", "arguments": { "adapterID": "my-debugger", "pathFormat": "path", "linesStartAt1": true, "columnsStartAt1": true } }
启动/附加请求
json{ "seq": 2, "type": "request", "command": "launch", "arguments": { "program": "/path/to/program", "stopOnEntry": false } }
设置断点请求
json{ "seq": 3, "type": "request", "command": "setBreakpoints", "arguments": { "source": { "path": "/path/to/file.js" }, "breakpoints": [ { "line": 10 } ] } }
继续执行请求
json{ "seq": 4, "type": "request", "command": "continue", "arguments": { "threadId": 1 } }
DAP 事件类型
初始化完成事件
json{ "seq": 1, "type": "event", "event": "initialized" }
停止事件
json{ "seq": 2, "type": "event", "event": "stopped", "body": { "reason": "breakpoint", "threadId": 1, "allThreadsStopped": false } }
输出事件
json{ "seq": 3, "type": "event", "event": "output", "body": { "category": "console", "output": "Hello World\n" } }
实现调试适配器
创建适配器项目
bashnpm init -y npm install @vscode/debugadapter
基本适配器结构
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 { // 启动调试器 this.sendResponse(response); } protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void { // 设置断点 response.body = { breakpoints: args.breakpoints.map(bp => ({ verified: true, line: bp.line })) }; this.sendResponse(response); } }
配置调试适配器
package.json 配置
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 配置
json{ "version": "0.2.0", "configurations": [ { "type": "my-debugger", "request": "launch", "name": "Debug with My Debugger", "program": "${workspaceFolder}/app.js" } ] }
调试适配器测试
调试适配器
- 按 F5 启动扩展开发宿主
- 打开包含调试配置的项目
- 启动调试会话
- 测试各种调试功能
测试清单
- 断点设置和触发
- 单步执行
- 变量查看和修改
- 调用栈查看
- 表达式求值
高级功能
自定义求值
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); }
自定义变量
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); }
注意事项
- 正确处理异步操作
- 提供清晰的错误信息
- 支持取消操作
- 考虑性能优化
- 遵循 DAP 规范