调试器扩展概述
调试器是 VS Code 生态中最复杂的扩展类型之一:它让编辑器可以启动外部程序、设置断点、单步执行、查看变量。语言支持补全悬停是"锦上添花",而调试器是把编辑器变成完整 IDE 的最后一环。
调试器扩展是什么
调试器扩展充当 VS Code 与"被调试程序"之间的翻译官。VS Code 本身不关心被调试程序是什么语言、什么运行时,它只认一套统一的协议——DAP。
| 角色 | 说明 |
|---|---|
| VS Code 编辑器 | 调试 UI:断点管理、调用栈、变量、监视、调试控制台 |
| 调试器扩展 | 注册调试类型、解析 launch.json 配置、启动调试适配器 |
| 调试适配器 | 与真实调试器/运行时通信,翻译 DAP 消息 |
VS Code 内置了对 Node.js、Python、Java 等语言的调试支持,这些语言都通过调试器扩展接入。自定义运行时(脚本语言、虚拟机、模拟器)则需要自己实现调试器扩展。
调试架构总览
VS Code 的调试采用三层架构:
+----------------+ DAP +------------------+
| VS Code UI | <--------------> | 调试器扩展 |
| (调试面板/断点) | JSON-RPC 消息 | Extension Host |
+----------------+ +--------+---------+
|
DAP 转发(stdio / socket)
|
+--------+---------+
| 调试适配器 |
| (Debug Adapter) |
+--------+---------+
|
调试协议(协议相关)
|
+--------+---------+
| 被调试程序 |
| (runtime) |
+------------------+关键点:
| 层 | 职责 |
|---|---|
| VS Code 核心 | 只懂 DAP,负责渲染调试 UI、管理断点状态 |
| 调试器扩展 | 在 Extension Host 中运行,负责把 DAP 消息桥接到调试适配器 |
| 调试适配器 | 独立进程,把 DAP 翻译成目标调试器(gdb、lldb、Node inspector 等)的指令 |
调试器扩展宿主
调试器扩展并不直接接触被调试程序。它运行在 Extension Host 中,职责是:
- 通过
contributes.debuggers声明支持哪种调试类型 - 实现
DebugAdapterDescriptorFactory,告诉 VS Code 如何启动调试适配器进程 - 通过
DebugConfigurationProvider提供/校验调试配置
注册调试器
在 package.json 中通过 contributes.debuggers 声明调试器:
{
"contributes": {
"debuggers": [
{
"type": "my-script",
"label": "MyScript Debugger",
"languages": ["myscript"],
"configurationAttributes": {
"launch": {
"required": ["program"],
"properties": {
"program": {
"type": "string",
"description": "要调试的脚本文件路径"
},
"args": {
"type": "array",
"items": { "type": "string" },
"description": "传给脚本的命令行参数"
}
}
}
},
"configurationSnippets": [
{
"label": "MyScript: Launch",
"description": "启动并调试脚本",
"body": {
"type": "my-script",
"request": "launch",
"name": "Launch Script",
"program": "${workspaceFolder}/${1:main.ms}"
}
}
],
"initialConfigurations": [
{
"type": "my-script",
"request": "launch",
"name": "Launch Script",
"program": "${workspaceFolder}/main.ms"
}
]
}
]
}
}debuggers 字段说明
| 字段 | 作用 |
|---|---|
type | 调试类型标识,对应 launch.json 中的 type,必须唯一 |
label | 调试器显示名称 |
languages | 关联语言,用于内联断点等语言特性 |
configurationAttributes | 定义 launch/attach 两种请求的配置结构(JSON Schema 子集) |
configurationSnippets | 创建 launch.json 时的配置片段提示 |
initialConfigurations | 首次创建 launch.json 时预置的配置模板 |
DAP 协议概述
DAP(Debug Adapter Protocol)是 VS Code 团队设计并开源的语言无关调试协议,与 LSP 同源(都基于 JSON-RPC)。DAP 定义了调试过程的全部交互消息:
请求类型
| 请求 | 方向 | 作用 |
|---|---|---|
initialize | 编辑器 → 适配器 | 协商能力与版本,启动会话前必发 |
launch | 编辑器 → 适配器 | 启动程序并开始调试 |
attach | 编辑器 → 适配器 | 附加到已运行的进程 |
setBreakpoints | 编辑器 → 适配器 | 设置/清除断点 |
threads | 编辑器 → 适配器 | 获取当前线程列表 |
stackTrace | 编辑器 → 适配器 | 获取指定线程的调用栈帧 |
scopes | 编辑器 → 适配器 | 获取栈帧的作用域列表 |
variables | 编辑器 → 适配器 | 展开作用域中的变量 |
continue / next / stepIn / stepOut | 编辑器 → 适配器 | 控制程序执行 |
evaluate | 编辑器 → 适配器 | 求值表达式(监视/悬停/控制台) |
事件类型
| 事件 | 方向 | 作用 |
|---|---|---|
initialized | 适配器 → 编辑器 | 初始化完成,可以设置断点了 |
stopped | 适配器 → 编辑器 | 程序停在断点/单步处,附 reason(breakpoint/step/pause) |
continued | 适配器 → 编辑器 | 程序继续执行 |
output | 适配器 → 编辑器 | 调试控制台输出文本 |
terminated | 适配器 → 编辑器 | 调试会话结束 |
DAP 消息格式与 LSP 完全一致:
{
"seq": 5,
"type": "request",
"command": "stackTrace",
"arguments": {
"threadId": 1,
"startFrame": 0,
"levels": 20
}
}调试四大视图
VS Code 调试 UI 有四个核心视图,全部由 DAP 数据驱动:
运行和调试
会话控制区:启动/停止调试、选择配置、暂停/继续。对应 DAP 的 launch/terminate/continue/pause 请求。
断点
断点视图展示所有已设置的断点,支持启用/禁用/删除,并可通过 setBreakpoints 同步到调试适配器。断点可以关联条件表达式或命中次数。
调用堆栈
程序停在断点处时,调试适配器通过 stopped 事件通知编辑器,编辑器依次请求 threads、stackTrace、scopes、variables 渲染出完整的调用栈与变量树。
监视
监视表达式通过 evaluate 请求求值,在 context 参数中标记为 watch。悬停查看变量则使用 hover 上下文。
调试会话生命周期
一次完整调试会话的消息时序:
VS Code 调试适配器
|---- initialize ----->| 协商能力
|<--- initialize ------|
|<--- initialized -----| 适配器就绪
|---- setBreakpoints ->| 设置断点
|<--- setBreakpoints --|
|---- configurationDone->| 配置完成,开始执行
|<--- configurationDone -|
|<--- stopped ---------| 命中断点
|---- threads --------->|
|<--- threads ---------|
|---- stackTrace ------>|
|<--- stackTrace ------|
|---- scopes ---------->|
|<--- scopes ----------|
|---- variables ------->|
|<--- variables -------|
|---- next ------------>| 单步
|<--- continued -------|
...
|<--- terminated ------| 会话结束调试器扩展的最小构成
一个调试器扩展至少包含三部分:
package.json contributes.debuggers 声明
src/extension.ts 注册 DebugAdapterDescriptorFactory + DebugConfigurationProvider
src/debugAdapter.ts 调试适配器实现(可独立进程)// extension.ts
import * as vscode from 'vscode';
import { DebugAdapterDescriptorFactory } from './debugFactory';
import { DebugConfigProvider } from './debugConfig';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.debug.registerDebugAdapterDescriptorFactory(
'my-script',
new DebugAdapterDescriptorFactory()
),
vscode.debug.registerDebugConfigurationProvider(
'my-script',
new DebugConfigProvider()
)
);
}调试器扩展 VS Code 端相对固定,真正的复杂度集中在调试适配器(DAP Server)的实现,接下来的文章将逐步搭建一个完整的调试器。
调试类型 vs 调试请求
| 概念 | 说明 |
|---|---|
| 调试类型 | launch.json 中的 type 字段,如 node、python、my-script |
| 请求类型 | launch(启动新进程)或 attach(附加已有进程) |
| 配置 | launch.json 中一个完整的配置对象,含 type/request/name 及自定义字段 |
同一个调试类型可以同时支持 launch 和 attach 两种请求,在 configurationAttributes 中分别定义各自的结构。