通过 vscode 调试 nodejs 源代码

2023年11月08日

我是 nodejs 开发,所以一直想要深入了解 nodejs 底层的一些东西,想要了解 nodejs 是如何启动的,底层的 v8、libuv 等是如何组合在一起的。

通过 debug 去打一些断点,然后运行命令,看程序是如何去运行的,可以帮助自己去了解它的一些运行流程。

通过我的一些学习与实践,本文就记录一下通过 vscode 去调试 nodejs 源代码的过程。

编译步骤:

首先需要将 nodejs 克隆至本地,然后我将分支切换至最近的一个 node 稳定版本 v20.9.0。然后参考 building 官方文档,去编译出 debug 版本的 node 二进制文件。

1git clone git@github.com:nodejs/node.git
2cd node
3git checkout v20.9.0
4
5# 编译
6./configure --debug
7make -j4

编译过程很慢,我是 mac 电脑,感觉中间编译了有半个小时以上了。编译好了之后,out/Debug/node即为编译出来的可用于 debug 的 node 二进制程序。

此时可以尝试运行一下 helloworld.js ,看一下是不是符合预期。

1./out/Debug/node test/fixtures/test-repl-tab-completion/helloworld.js

vscode debug 配置

编译好了之后,就是 vscode 的 debug 配置了。

因为 nodejs 源代码也是 c/c++写的,所以 vscode 需要首先安装扩展 CodeLLDB。

debug c/c++

然后设置 .vscode/launch.json 的内容如下,program 项设置为 debug 版本的 node 二进制文件,args 跟随我们要运行的 js 脚本即可:

 1{
 2  "version": "0.2.0",
 3  "configurations": [
 4    {
 5      "type": "lldb",
 6      "request": "launch",
 7      "name": "lldb:node",
 8      "program": "${workspaceFolder}/out/Debug/node",
 9      "args": ["test/fixtures/test-repl-tab-completion/helloworld.js"],
10      "cwd": "${workspaceFolder}"
11    }
12  ]
13}

先在 src/node_main.ccint main 入口 debug 打点,之后运行 vscode 侧边栏的 debug,就可以看到程序可以在断点处停下了,之后就可以调试 c/c++ 文件了。

chrome 中 debug js

除了 c/c++文件,我还想 debug js 文件,这个时候我们就需要再做一些配置了。修改 .vscode/launch.json 内容如下,在 node 启动时,新增 --inspect-brk 启动参数:

 1{
 2  "version": "0.2.0",
 3  "configurations": [
 4    {
 5      "type": "lldb",
 6      "request": "launch",
 7      "name": "lldb:node",
 8      "program": "${workspaceFolder}/out/Debug/node",
 9      "args": ["--inspect-brk", "test/fixtures/test-repl-tab-completion/helloworld.js"],
10      "cwd": "${workspaceFolder}"
11    }
12  ]
13}

新增的 --inspect-brk 参数表示开启 js 的调试,设置好了之后,在 vscode 中开启 debug 后。通过 chrome 浏览器进入地址 chrome://inspect,就会自动监听显示可以 debug 的 js 代码。

vscode 中 debug js

chrome 中可以调试 js,但是 vscode 本来就支持调试 js,所以我们再进一步,修改 ./vscode/launch.json 文件如下,添加 node:attach 一项配置:

 1{
 2  "version": "0.2.0",
 3  "configurations": [
 4    {
 5      "name": "node:attach",
 6      "port": 9229,
 7      "request": "attach",
 8      "skipFiles": ["<node_internals>/**"],
 9      "type": "node"
10    },
11    {
12      "type": "lldb",
13      "request": "launch",
14      "name": "lldb:node",
15      "program": "${workspaceFolder}/out/Debug/node",
16      "args": ["--inspect-brk", "test/fixtures/test-repl-tab-completion/helloworld.js"],
17      "cwd": "${workspaceFolder}"
18    }
19  ]
20}

debug 时先启动 lldb:node,然后当终端打印出可以接入 js debug 时,再在 vscode 侧边 debug 栏开启 node:attach debug 选项。之后就可以愉快地在 c/c++和 js 文件 dubug 了。

compounds

上一步 debug 还是需要先后点两次,此时更近一步,我就点一次,就可以了。(但是偶尔会碰到它并没有 attach 上的情况,这时就要重来了。)

 1{
 2  "version": "0.2.0",
 3  "configurations": [
 4    {
 5      "name": "node:attach",
 6      "port": 9229,
 7      "request": "attach",
 8      "skipFiles": ["<node_internals>/**"],
 9      "type": "node"
10    },
11    {
12      "type": "lldb",
13      "request": "launch",
14      "name": "lldb:node",
15      "program": "${workspaceFolder}/out/Debug/node",
16      "args": ["--inspect-brk", "test/fixtures/test-repl-tab-completion/helloworld.js"],
17      "cwd": "${workspaceFolder}"
18    }
19  ],
20  "compounds": [
21    {
22      "name": "node debug",
23      "configurations": ["lldb:node", "node:attach"]
24    }
25  ]
26}

可以看到修改的配置中多了 compounds 内容,之后 debug 时,在 debug 栏开启 node debug 这个即可。

debug 过程可以通过视频来进一步详细了解

视频介绍了 debug 的过程,且了解了一下 nodejs 的启动过程,然后简单 debug 一下 nodejs 的事件循环(event loop)与 microTask。

一些可参考链接