VSCode插件调试必须用attach模式而非launch,因extensionHost进程需主动加载插件并注入vscode API;launch模式未启用--inspect-brk导致断点不命中,而attach可确保在activate()前完成连接。

VSCode 的插件调试不依赖独立进程或黑盒代理,而是通过 DAP 协议让插件进程主动连接调试器,核心在于 extensionHost 进程的启动方式和 vscode.debug API 的注入时机。
为什么 attach 模式比 launch 更可靠
当你点击「调试扩展」时,VSCode 并非直接运行你的 extension.ts,而是先拉起一个干净的 Code - Extension Host 进程,并在该进程中动态加载你的插件代码。这个过程由 ExtensionHostProcess 控制,它会在初始化阶段检查环境变量 VSCODE_INSPECTOR_OPTIONS 是否存在 —— 若存在,则自动启用 Node.js 的 --inspect-brk 参数并监听指定端口。
常见错误现象:
- launch 模式下断点不命中:因为 VSCode 默认未传入
--inspect-brk,插件代码已执行完毕才挂载调试器 - attach 后无堆栈:没等插件完成注册就手动 attach,
activate()尚未触发
实操建议:
- 始终用
attach方式调试,配置port为9229(默认值),并在launch.json中设"timeout": 10000防止超时断连 - 在
activate()开头加debugger;,确保 attach 完成后再执行逻辑 - 避免在
package.json的activationEvents中写过于宽泛的模式(如*),否则插件会提前加载,错过断点
vscode.debug.startDebugging() 是如何触发子调试会话的
这个 API 表面是「启动另一个调试器」,实际是向 DAP 服务发送一个 launch 请求,由 DebugSessionManager 分发给对应适配器(Adapter)。关键点在于:它不 fork 新进程,而是复用当前 extensionHost 的 Node.js 运行时,仅新建一个 DebugSession 实例。
使用场景:
- 插件内需要调试用户脚本(如自定义语言解释器)
- 集成第三方调试器(如针对 GoSu 或嵌入式目标的适配器)
参数差异:
-
configuration.type必须与已注册的 Adapter ID 一致(如"pwa-node"、"chrome") -
configuration.request为"launch"时,program路径必须是绝对路径;为"attach"时,需确保目标进程已开启 inspector - 若漏掉
configuration.name,VSCode 会静默失败,且控制台无报错
调试器无法停在 deactivate() 的根本原因
deactivate() 不是被「调用」的,而是由 extensionHost 主动触发的清理钩子,其执行前提是插件已被标记为「可卸载」——这要求所有异步操作(如 setTimeout、未 resolve 的 Promise、活跃的 WebSocket)全部结束。一旦有任一微任务未清空,deactivate() 就永远不会进入调用栈。
容易踩的坑:
- 在
activate()中启动了setInterval却未在deactivate()中clearInterval - 使用了全局事件监听(如
process.on('unhandledRejection'))但未移除 - Webview 或 WebViewPanel 未调用
dispose()
验证方法:在 deactivate() 开头插入 console.log('deactivating'),然后关闭插件所在窗口,观察是否输出 —— 若无输出,说明插件仍被持有引用。
真正难调试的从来不是代码逻辑,而是生命周期与资源释放的隐式耦合。DAP 协议把调试行为标准化了,但 extensionHost 对插件的调度策略(比如延迟卸载、按需激活)才是决定断点能否命中的底层变量。别只盯着 debugger; 有没有生效,先确认你的插件是不是真的进入了那个上下文。


















