Node.js调试器仅处理JavaScript层,无法访问C++符号、调用栈、内存布局或寄存器状态,因此必须使用LLDB等底层调试器配合保留调试信息的Native Addon进行混合调试。

为什么不能只用 node 调试器调试 Native Addon
因为 node 调试器(type: "node")只处理 JS 层,它看不到 C++ 符号、调用栈、内存布局或寄存器状态。当你在 addon.cpp 里设了断点却毫无反应,或者 console.log 显示结果异常但 C++ 逻辑明显有问题——那基本就是 JS 和 Native 层脱节了,必须换底层调试器。
launch.json 中 type: "lldb" 和 type: "cppdbg" 的区别
二者都能调试 Native Addon,但行为差异直接影响调试体验:
-
type: "lldb"(推荐用vadimcn.vscode-lldb插件):直接调用系统 LLDB,支持条件断点、日志点、STL 容器可视化,对.node文件和node进程 attach 更稳定; -
type: "cppdbg"(C/C++ 扩展自带):依赖miDebuggerPath指向 GDB/LLDB,但在 macOS 或新版 Linux 上常因符号路径解析失败导致断点不命中; - 关键区别在于:用
type: "lldb"时,program字段必须填"node",而不是你的.node文件;而args必须包含 JS 入口路径,例如["${workspaceFolder}/index.js"]; - 若你用的是 AOSP 或 Android native service(如
audioserver),则必须用type: "lldb"+request: "attach",配合adb shell pidof获取进程 ID。
编译 Native Addon 时必须保留调试信息
没调试符号 = 断点无效 + 变量名全变成 $rdi $rbp。常见踩坑点:
-
node-gyp build默认生成 Release 版,去掉所有调试信息;必须加--debug参数:node-gyp build --debug; - 检查生成的
build/Debug/hello.node是否含调试段:file build/Debug/hello.node应显示with debug_info; - binding.gyp 中需显式启用调试符号:
"cflags": ["-g"], "xcode_settings": {"OTHER_CFLAGS": ["-g"]}; - 如果用 CMake 构建,确保
CMAKE_BUILD_TYPE是Debug,且未设置-DNDEBUG或-O2等优化标志。
混合调试 JS + Native 的实际操作链
这不是“配一个 launch.json 就完事”,而是两套调试器协同工作的过程:
- 第一步:用
type: "node"启动 JS,停在require('./build/Debug/hello.node')之前,确认 addon 加载路径正确; - 第二步:另起一个
type: "lldb"配置,request: "attach",用${command:pickMyProcess}选中刚启动的node进程(PID 一致); - 第三步:在
addon.cpp设置断点,触发 JS 调用后,LLDB 才会真正捕获到 C++ 执行流; - 注意:JS 断点和 C++ 断点不会自动同步;你在 JS 里
step into不会跳进 C++,必须手动切到 LLDB 视图再单步; - 如果看到
Thread 1: signal SIGSTOP却无法继续,大概率是 Node.js 进程被 ptrace 限制(Linux 上需echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope)。
真正的难点不在配置,而在符号加载时机和进程生命周期管理——JS 启动快,Native 加载慢,attach 晚了就错过初始化逻辑。建议始终先 node --inspect-brk 冻住 JS,再 attach LLDB,再 resume JS。


















