MongoDB Shell 的 VSCode 调试扩展:基于 DAP 与 SpiderMonkey 的 JS 断点调试实战
【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo
导读
本文基于 MongoDB 官方仓库 src/mongo/shell/debugger/vscode/README.md 及对应源码,系统讲解如何在 VSCode 中通过图形化调试器对运行于 Mongo Shell(MozJS/SpiderMonkey)的 JS 测试代码进行断点调试。你将掌握扩展的安装与 launch.json 配置、与 resmoke--jsdbg标志的协作方式、断点/变量/调用栈/REPL 的完整使用流程,并深入理解"VSCode 扩展(session.js)↔ Shell(adapter.cpp)↔ SpiderMonkey(debugger.cpp)"三层架构及新行分隔 JSON 协议,最终具备在 jstests 开发与排障中直接落地的实战能力。
背景与适用场景
MongoDB 的 JS 测试(jstests)运行在 Mongo Shell 的 MozJS 引擎之上,传统调试手段是在代码中插入debugger;语句并在终端获得交互提示。这种方式的局限在于:无法直接在编辑器里设置/管理断点,变量查看、调用栈导航都依赖文本界面,排查复杂逻辑效率较低。
该仓库在src/mongo/shell/debugger/目录中实现了一套完整的 JS 调试基础设施:服务端(Shell 进程内)由 adapter.cpp、debugger.cpp、protocol.cpp 构成,客户端则是vscode/目录下的 VSCode 扩展(extension.js、adapter.js、session.js)。它遵循 Microsoft 的 Debug Adapter Protocol (DAP),让 VSCode 的原生调试 UI 直接对接 Mongo Shell。
典型使用场景:
- 在
jstests/测试文件中设置断点,逐步排查查询、聚合、复制集等测试逻辑; - 观察 Shell 全局对象(如
ObjectId、assert、tojson等)与局部变量的实时值; - 在
debugger;语句处暂停,用 Debug Console 交互求值; - 与 resmoke 测试框架配合,在真实的多进程测试环境(如
no_passthrough套件)中定位问题。
核心功能
扩展提供的调试能力完全通过 VSCode 调试 UI 暴露:
- 断点:直接在 JS 文件的编辑器行号旁点击添加/移除断点(红点);
- 暂停执行:命中断点自动暂停;未捕获异常自动停止;
- 变量检查:在 Variables 侧边栏查看各作用域的全部变量;可直接在侧边栏内修改变量值;在编辑器中悬停变量名可查看当前值的 tooltip;
- 导航:查看完整 JS 调用栈并跳转到对应文件/行;继续执行到下一个断点;
- REPL:在 Debug Console 中检查/修改变量、求值表达式;
debugger;语句会在终端暂停等待用户输入,以便检查/修改变量与求值表达式。
已知限制
文档明确列出了当前实现的边界(这些限制在 session.js 中有对应实现证据):
- 不支持单步(step / step in / step out):三者均被转发为 Continue。在 session.js 中,
nextRequest、stepInRequest、stepOutRequest只是打印 "not supported in this debugger. Use continue instead." 后调用continueRequest; - 不支持 Watch 变量;
- 深层嵌套变量被截断:Variables 面板仅展示 1 层展开。此时应改用 Debug Console 深入检查(对应 Jira SERVER-121664);
- 断点生效时机:Shell 已暂停时设置的断点立即生效;Shell 运行中设置的断点需等下次命中断点时才应用(不会打断执行中的代码);
- 端口冲突:调试器使用默认 Chrome 调试端口 9229,若 Chrome 标签页开着 Developer Tools 会与 VSCode 调试器冲突。
安装
一键安装脚本
仓库提供 install.sh,它会自动执行npm install、用vsce打包.vsix并通过code --install-extension安装:
./src/mongo/shell/debugger/vscode/install.sh完成时输出类似:
DONE Packaged: /home/ubuntu/mongo/src/mongo/shell/debugger/vscode/mongo-shell-debugger-1.0.0.vsix (7 files, 8.88 KB) Installing extensions on SSH: steve-mcclure-0ed.workstations.build.10gen.cc... Extension 'mongo-shell-debugger-1.0.0.vsix' was successfully installed.从脚本源码可以看到几个关键细节:
- npm 版本要求:脚本会检查
npm --version主版本号,低于 10 直接报错退出; - 打包命令:
npm run package执行 package.json 中的vsce package --skip-license --dependencies; - 版本一致性:安装成功后脚本删除
.vsix并清理node_modules(注释说明是为了避免干扰 bazel 构建工具对文件结构的预期)。
扩展的版本自检机制
extension.js 中实现了checkIfNewerVersionAvailable:激活扩展时,它会通过工作区文件夹内的标记文件src/mongo/shell/debugger/vscode/package.json定位 mongo 仓库,比对已安装版本与仓库内package.json的 version(当前为1.4.2);若不一致,会弹出提示并可直接复制install.sh路径进行更新。
一次性配置:launch.json
安装后需在.vscode/launch.json中添加mongo-shell类型的"attach"配置:
{ "version": "0.1.0", "configurations": [ { "type": "mongo-shell", "request": "attach", "name": "Attach to MongoDB Shell", "debugPort": 9229 } ] }参数说明(依据 package.json 的contributes.debuggers.configurationAttributes):
type:固定为mongo-shell,与扩展注册的调试类型一致;request:当前仅支持attach(扩展自身充当 DAP 服务器,等待 Shell 连入,而非 launch 子进程);name:显示在 "Run and Debug" 下拉框中的名称;debugPort:调试服务器监听端口,默认 9229;trace(可选):布尔值,开启后会输出 DAP 协议日志,便于排查协议问题。
同时,extension.js 的provideDebugConfigurations会向 VSCode 提供上述默认配置,用户也可通过 "MongoDB Shell: Attach" 配置片段快速生成。
使用流程(五步断点调试)
- 在 VSCode 中打开一个
.js测试文件; - 在行号旁点击添加断点(出现红点);
- 启动调试器,二选一:
- 在 JS 文件内按
F5启动(VSCode)调试服务器; - 或在 "Run and Debug" 侧边栏的下拉框中选择 "Attach to MongoDB Shell" 并点击播放按钮。
此时 VSCode 的 "Debug Console" 应出现如下输出:
Debug server listening on port 9229 Waiting for mongo shell to connect on port 9229... Use resmoke's --jsdbg flag when running a JS test file to stop on breakpoints. - 在 JS 文件内按
- 用 resmoke 的
--jsdbg标志运行该 JS 测试文件,Shell 启动后会主动连接 9229 端口并暂停在断点上; - 使用 VSCode 断点 UI 导航(继续执行、查看作用域变量等)。
对应的实际命令行(参考 src/mongo/shell/debugger/README.md):
buildscripts/resmoke.py run --suites=no_passthrough --jsdbg jstests/my_test.js提示:在 Shell 层面,对应参数是
--jsDebugMode;启用后debugger;语句会触发交互调试提示。VSCode 扩展在此基础上进一步把调试体验搬进了图形界面。
下图为扩展运行时的真实界面:代码暂停在断点处,左侧 Variables 面板展示 Local 作用域变量(a=2、b=3、c=5、x=42、y=Array(4)、z=Object等),CALL STACK 面板显示jstests/my_test.js并标注 "Paused on breakpoint",顶部调试工具栏显示当前配置 "Attach to MongoDB Shell"。
断点命中的交互细节
在 session.js 中,Shell 上报的stopped事件会被转换为StoppedEvent并附上reason(如breakpoint)与去除 ANSI 控制字符后的说明文本。之后 VSCode 发起stackTrace/scopes/variables请求,均由 session.js 原样转发到 Shell 侧执行并回传结果。
架构总览
组件拓扑
┌─────────────────┐ │ VSCode UI │ └────────┬────────┘ │ DAP │ ┌─────────────────┐ │ session.js │ (VSCode Extension) └────────┬────────┘ │ JSON/TCP │ :9229 │ │ ┌─────────────────┐ │ adapter.cpp │ (MongoDB Shell) └────────┬────────┘ │ DAP Messages │ ┌─────────────────┐ │ debugger.cpp │ └────────┬────────┘ │ SM Debugger API │ ┌─────────────────┐ │ SpiderMonkey │ (JS Execution) └─────────────────┘各层职责
客户端(VSCode 扩展,位于 vscode/)
extension.js:注册扩展及其配置。它注册了mongo-shell调试类型的配置提供器(registerDebugConfigurationProvider)与调试适配器工厂(registerDebugAdapterDescriptorFactory,通过node adapter.js启动子进程),并注册"暂停期间保存文件"的告警(详见下文);adapter.js:VSCode 调试适配器入口,仅 13 行,创建MongoShellDebugSession并交给DebugSession.run运行;session.js:DAP 服务器。负责在 TCP 端口上监听、在 VSCode 协议与 Shell 协议之间做翻译,管理断点对象(Map<path, {source, breakpoints}>)、请求序列号messageSeq与待处理请求表pendingRequests。
服务端(MongoDB Shell 进程内,位于 debugger/)
adapter.h/cpp:DAP 消息处理器 + TCP 客户端。它实现RequestHandler访问者接口(protocol.h 定义了ConfigurationDoneRequest、SetBreakpointsRequest、ContinueRequest、StackTraceRequest、ScopesRequest、VariablesRequest、EvaluateRequest、SetVariableRequest等请求的访问器),负责连接 session.js、收发消息并驱动握手;debugger.h/cpp:SpiderMonkey Debugger API 封装。其中DebuggerObject是 Debugger 对象的门面(见 debugger.h),负责创建 Debugger 实例、添加 debuggee、安装onDebuggerStatement/onNewScript/onExceptionUnwind回调、加载 helpers.js,以及注册isPaused、storeEvalResult、storeScopes、getBreakpoints、hasBPUpdateRequest、fromInteractiveREPL等一批 C++ 原生回调函数;helpers.js:共享 JS 工具(__spinwait、__processScopes、__storeCallStack、__applyPendingBPUpdates等),被三个处理器文件onDebuggerStatement.js、onExceptionUnwind.js、onNewScript.js共同使用。
协议:新行分隔 JSON over TCP
VSCode 与 Shell 之间通过 TCP 端口 9229 传输换行分隔的 JSON 消息,例如:
{"type":"request","seq":1,"command":"setBreakpoints","arguments":{...}} {"type":"response","seq":1,"success":true,"body":{...}} {"type":"event","event":"stopped","body":{"reason":"breakpoint"}}在 session.js 的sendCommand中可以看到协议细节:每条请求分配自增seq,写入JSON.stringify(...) + "\n",并注册 5 秒超时;session.js 则按\n切分接收缓冲逐条解析。服务端 protocol.cpp 与 protocol.h 定义了对应的 C++ 消息模型,并有 protocol_test.cpp 对协议解析做单元测试。
消息流详解
初始化(握手)
- VSCode 启动 →
session.js在 9229 端口创建 TCP 服务器(startDebugServer,绑定localhost); - Shell 以
--jsdbg启动 →adapter.cpp作为 TCP 客户端连接 9229; - Shell 等待来自 session.js 的"握手"(
configurationDone)——对应 adapter.h 中的waitForHandshake(); session.js把此前已设置的全部断点发给 Shell,随后发送configurationDone(见 session.js 的sendDeferredConfigurationDoneRequest与 session.js 的sendQueuedBreakpoints);- Shell 开始执行,命中断点即暂停。
Shell 连接前设置的断点
session.js用带本地分配 ID 的Breakpoint对象存储断点,并立即以unverified状态响应 VSCode(避免 UI 卡死,见 session.js)。Shell 连入后,步骤 4 将全部断点送达;Shell 回传verified状态后,session.js使用相同 ID 发出BreakpointEvent("changed"),VSCode 据此刷新行号处的断点圆点。
Shell 连接后设置的断点
setBreakpoints被立即转发给 Shell(session.js)。Shell 端会追溯应用到已加载的脚本(通过debugger.findScripts()),并对未来加载的脚本自动生效(通过onNewScript)。Shell 的响应直接回传给 VSCode。
断点命中
- JS 执行命中断点 → SpiderMonkey 调用
hit()处理器(对应 debugger.h 的DebuggerScript::breakpointHandler); debugger.cpp记录位置,通过 adapter 发送stopped事件;- adapter 阻塞执行(C++ 线程被暂停标志 + 条件变量挂起,见下文"执行控制");
- VSCode 显示暂停状态,请求
stackTrace; - 用户点击继续 → adapter 解除阻塞,执行恢复。
SpiderMonkey 集成原理
两个 Compartment
- 主 Compartment:运行用户 JS,被 Debugger 实例观察;
- Debugger Compartment:持有 Debugger 实例,与被调试方隔离。
MozJS 禁止 compartment "re-entry":可以在 JS 里自旋等待并调用 C++,但 C++ 不能再回调进入 JS 执行——它只能 get/set 属性,不能发起任何执行。这正是整个调试器采用"JS 自旋等待(spinwait)"+ C++ 阻塞/唤醒设计的原因。
断点机制
_breakpoints(源码 URL → 行号集合)是服务端唯一的权威状态,无论断点何时设置都保持最新:
- 新脚本:
onNewScript触发,读取_breakpoints,调用script.setBreakpoint(offset, {hit: handler}); - 已加载脚本:运行中收到
setBreakpoints时,URL 被排入_pendingBPUpdateUrls。共享的__spinwait(定义于 helpers.js)在任意暂停场景(断点命中、异常、debugger;语句)检测到该标志后,调用debugger.findScripts({url})、清掉旧断点并重新应用当前断点集。对应实现见 helpers.js 的__applyPendingBPUpdates(遍历__getBPUpdatedUrls,对每个脚本clearAllBreakpoints()后按getLineOffsets(line)重新setBreakpoint); - 命中处理器:记录位置、调用 C++ 暂停逻辑、再进入共享的
__spinwait。
共享暂停逻辑(__spinwait、__processScopes、__storeCallStack、__applyPendingBPUpdates)全部位于 helpers.js,被三个处理器文件共同复用。helpers.js 的__spinwait循环轮询__isPaused(),期间处理断点更新请求(__hasBPUpdateRequest→__applyPendingBPUpdates)与求值请求(__hasEvalRequest→frame.eval()包裹的表达式并回存结果);helpers.js 的__storeCallStack则沿frame.older链遍历最多 50 帧,收集 URL 与行号后通过__storeStackFrames交给 C++ 侧。
执行控制
- 暂停:
_paused原子标志 + 条件变量阻塞 C++ 线程(Shell 主线程被真正挂起,JS 侧由__spinwait自旋等待); - 继续:清除标志、通知条件变量,执行恢复;
- REPL:stdin 线程接收命令,在暂停的上下文中通过
frame.eval()求值——这正是"暂停在debugger;处可用终端输入检查/修改表达式"的实现基础。
异常处理
onExceptionUnwind回调(由 debugger.h 的setOnExceptionUnwindCallback安装)负责"未捕获异常自动停止":异常展开时拦截并暂停,向 VSCode 发送带原因文本的stopped事件。
体验增强与注意事项
暂停期间保存文件会告警
extension.js 的registerFileSaveWarning通过registerDebugAdapterTrackerFactory跟踪 DAP 消息:会话处于stopped状态时若保存了带断点的源文件,会弹出警告"Source file edited while paused. The highlighted line may not match the new source. Restart the debug session for accurate behavior."。原因是 Shell 报告的断点行号基于加载时的文件内容,运行中编辑会导致高亮位置与断点错位。
调试过程中的实用建议
- 断点务必先设置、后启动调试器,可走"连接前断点"的可靠路径;运行中追加断点依赖
_pendingBPUpdateUrls机制,需等待下次命中才生效; - 需要深入检查嵌套对象时,放弃 Variables 面板的 1 层展开,改在 Debug Console 用表达式求值;
- 若 VSCode 显示 "Command timeout"(见 session.js 的 5 秒超时),通常是 Shell 尚未连接或协议未握手完成;
- 若端口 9229 被占用(Chrome DevTools 等),可通过 launch.json 的
debugPort改用其他端口,并确保 Shell 侧--jsdbg使用相同端口; - 单步功能未实现,习惯"断点 + Continue"的工作流。
相关源码索引
- 关联文档:src/mongo/shell/debugger/vscode/README.md
- 扩展主入口与配置注册:src/mongo/shell/debugger/vscode/extension.js
- DAP 会话实现(TCP 服务器 + 协议翻译):src/mongo/shell/debugger/vscode/session.js
- 调试适配器入口:src/mongo/shell/debugger/vscode/adapter.js
- 打包与安装脚本:src/mongo/shell/debugger/vscode/install.sh
- 扩展清单与版本:src/mongo/shell/debugger/vscode/package.json
- Shell 侧 DAP 消息处理器:src/mongo/shell/debugger/adapter.h、src/mongo/shell/debugger/adapter.cpp
- SpiderMonkey Debugger 封装:src/mongo/shell/debugger/debugger.h、src/mongo/shell/debugger/debugger.cpp
- 共享 JS 暂停逻辑:src/mongo/shell/debugger/helpers.js
- 暂停处理器:src/mongo/shell/debugger/onDebuggerStatement.js、src/mongo/shell/debugger/onExceptionUnwind.js、src/mongo/shell/debugger/onNewScript.js
- DAP 协议 C++ 模型与测试:src/mongo/shell/debugger/protocol.h、src/mongo/shell/debugger/protocol.cpp、src/mongo/shell/debugger/protocol_test.cpp
- 调试模式总览:src/mongo/shell/debugger/README.md
【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考