进阶调试-debugger
// 谈到node断点调试,目前只要有三种方式,通过 node内置调试工具; 通过IDE(如vscode); 通过node-inspector, 三者本质上差不多,本文重点在于介绍 "如何在本地通过node-inspector 调用远程服务器上的node代码"
// 首先对三种调试方式进行入门讲解
内置 debug 功能
进入调试模式(在第一行断点)
// connecting to 127.0.0.1:9229 ... ok
// < Debugger attached.
// <
// Break on start in index.js:16
// 14
// 15 // 1.1 内置debug功能
// >16 // 1.1.1 进入调试模式(在第一行断点)
// 17
进入调试模式 (在第 n 行断点)
// 比如在第三行断点
// 方法一: debugger
console.log(1);
debugger;
console.log(2);
// connecting to 127.0.0.1:9229 ... ok
// < Debugger attached.
// <
// Break on start in index.js:46
// 44 console.log(2);
// 45 }
// >46 aa1()
// 方式二: 通过sb(line)
console.log(3);
// sb()
console.log(4);
执行下一步 next
console.log(1);
debugger;
console.log(2);
// connecting to 127.0.0.1:9229 ... ok
// < Debugger attached.
// <
// Break on start in index.js:73
// 71 console.log(2);
// 72 }
// >73 aa2()
// debug> next 输入后执行到 -> 70行
// < 1
// <
// break in index.js:70
// 68 function aa2(params) {
// 69 console.log(1);
// >70 debugger
// 71 console.log(2);
// 72 }
// debug> next 输入后执行到 -> 71行
// break in index.js:71
// 69 console.log(1);
// 70 debugger
// >71 console.log(2);
// 72 }
// 73 aa2()
// debug>
跳到下一个断点
console.log(1);
debugger;
console.log(2);
debugger;
console.log(3);
// connecting to 127.0.0.1:9229 ... ok
// < Debugger attached.
// <
// Break on start in index.js:106
// 104
// 105 }
// >106 aa4()
// 107
// debug> cont 输入后执行到 -> 第一个断点
// < 1
// <
// break in index.js:99
// 97 function aa4(params) {
// 98 console.log(1);
// > 99 debugger;
// 100 console.log(2);
// 101 debugger;
// debug> cont 输入后执行到 -> 第二个断点
// < 2
// <
// break in index.js:101
// 99 debugger;
// 100 console.log(2);
// >101 debugger;
// 102 console.log(3);
// 103
查看某个变量的值
// 输入 repl 命令后,再次输入变量名,就可以看到变量对应的值, 如果需要继续执行代码, 可以按 ctrl+c 退出
let aa = 1;
let bb = 1;
debugger;
// connecting to 127.0.0.1:9229 ... ok
// < Debugger attached.
// <
// Break on start in index.js:119
// 117 debugger;
// 118 }
// >119 aa5();
// 120
// debug> next
// break in index.js:117
// 115 let aa = 1;
// 116 let bb = 1;
// >117 debugger;
// 118 }
// 119 aa5();
// debug> repl 输入 repl
// Press Ctrl+C to leave debug repl
// > aa 输入 aa
// 1 输出 1
添加/删除 watch
// 通过 watch(expr) 来添加监听对象
// 通过 watchers 查看当前所有的监视对象
// 通过 unwatch(expr) 来删除监视对象
// 添加watch
debugger;
let aa = 1;
let bb = 1;
console.log(aa);
console.log(bb);
aa = 2;
bb = 2;
// connecting to 127.0.0.1:9229 ... ok
// < Debugger attached.
// <
// Break on start in index.js:127
// 125 bb = 2;
// 126 }
// >127 aa5()
// debug> next
// break in index.js:119
// 117
// 118 // 添加watch
// >119 debugger
// 120 let aa = 1;
// 121 let bb = 1;
// debug> watch('aa') 在这里watch aa
// debug> next
// break in index.js:120
// Watchers:
// 0: aa = undefined 输入 next 后 第一次打印 aa
// 118 // 添加watch
// 119 debugger
// >120 let aa = 1;
// 121 let bb = 1;
// 122 console.log(aa);
// debug> next
// break in index.js:121
// Watchers:
// 0: aa = 1 输入 next 后 第二次打印 aa
// 119 debugger
// 120 let aa = 1;
// >121 let bb = 1;
// 122 console.log(aa);
// 123 console.log(bb);
// 查看watchers
// debug> watchers
// 0: aa = 1
// 移除watch
// connecting to 127.0.0.1:9229 ... ok
// < Debugger attached.
// <
// Break on start in index.js:171
// 169
// 170 }
// >171 aa5()
// debug> watch('aa') 添加 watch aa
// debug> next
// break in index.js:119
// Watchers:
// 0: aa = undefined 执行 next 打印一次 aa
// 117
// 118 // 添加watch
// >119 debugger
// 120 let aa = 1;
// 121 let bb = 1;
// debug> unwatch('aa') 移除 监听 aa
// debug> next 执行 next 就不打印 aa 了
// break in index.js:120
// 118 // 添加watch
// 119 debugger
// >120 let aa = 1;
// 121 let bb = 1;
// 122 console.log(aa);
// debug>
进入/跳出函数(step in, step out)
// 进入函数 通过 step 或者 s
// 跳出函数 通过 out 或者 o
// 实例代码,假设代码运行到 logger(str) 这一行,首先跳入函数内部,再跳出
const nick = "liang";
const country = "China";
const str = nick + "live in" + country;
const logger = function (msg) {
console.log("msg"); //这里
console.log("这行会跳过"); //跳过这行
};
debugger;
logger(str);
console.log(str);
// connecting to 127.0.0.1:9229 ... ok
// < Debugger attached.
// <
// Break on start in index.js:306
// 304 // debug>
// 305 }
// >306 aa7()
// debug> next
// break in index.js:240
// 238 console.log("这行会跳过"); //跳过这行
// 239 };
// >240 debugger
// 241 logger(str);
// 242 console.log(str);
// debug> next 往下继续走
// break in index.js:241
// 239 };
// 240 debugger
// >241 logger(str); 到 这个方法这里了
// 242 console.log(str);
// 243
// debug> s 进去方法
// break in index.js:237
// 235
// 236 const logger = function (msg) {
// >237 console.log("msg"); //这里
// 238 console.log("这行会跳过"); //跳过这行
// 239 };
// debug> o 离开方法
// < msg
// <
// < 这行会跳过
// <
// break in index.js:242
// 240 debugger
// 241 logger(str);
// >242 console.log(str);
// 243
// 244 // connecting to 127.0.0.1:9229 ... ok
// debug>
重新运行
debugger;
let aa = 1;
let bb = 1;
// debug> next
// break in index.js:291
// 289 function aa8(params) {
// 290 debugger;
// >291 let aa = 1;
// 292 let bb = 1;
// 293 }
// debug> restart
远程调试
// 比如远程机器ip是 192.168.1.126 在远程机器上进入调试模式
// [root@localhost ex]# node --debug-brk app.js
// Debugger listening on port 5858
// 然后,在本地机器通过`node debug 192.168.1.126:5858`连接远程机器进行调试。
// node debug 192.168.1.126:5858
// ➜ /tmp node debug 192.168.1.126:5858
// connecting to 192.168.1.126:5858 ... ok
// break in /tmp/ex/app.js:1
// > 1 var Logger = require('./logger');
// 2
// 3 Logger.info('hello');
// debug> n
// break in /tmp/ex/app.js:3
// 1 var Logger = require('./logger');
// 2
// > 3 Logger.info('hello');
// 4
// 5 });
通过 IDE(vscode)
// 首先在vscode里代开项目
// 1. 打开vsocde,打开目录
// 2. 点击设置,修改launch.json "program": "${workspaceRoot}/practice/debug/index.js"
// 3. 运行即可
// 或者直接 F5即可打开调试
通过 node-inspector (缺失)
#### 方式一:通过`node-debug`启动调试
启动调试,它会自动帮你在浏览器里打开调试界面。
```powershell
➜ debugger git:(master) ✗ node-debug app.js
Node Inspector v0.12.8
Visit http://127.0.0.1:8080/?port=5858 to start debugging.
Debugging `app.js`
Debugger listening on port 5858
调试界面如下,简直不能更亲切。
方式二:更加灵活的方式
步骤 1:通过node-inspector启动 Node Inspector Server
➜ debugger git:(master) ✗ node-inspector
Node Inspector v0.12.8
Visit http://127.0.0.1:8080/?port=5858 to start debugging.
步骤 2:通过传统方式启动调试。加入--debug-brk,好让代码在第一行断住。
➜ debugger git:(master) ✗ node --debug-brk app.js
Debugger listening on port 5858
步骤 3:在浏览器里打开调试 UI 界面。就是步骤 1 里打印出来的地址 http://127.0.0.1:8080/?port=5858。成功
实现原理
从上面的例子不难猜想到。(不负责任猜想)
- 通过
node --debug-brk启动调试,监听5858端口。 node-inspector启动服务,监听 8080 端口。- 在浏览器里访问
http://127.0.0.1:8080/?port=5858。可以看到port=5858这个参数。结合之前讲到的 node 内置远程调试的功能,可以猜想,在返回 UI 调试界面的同时,服务内部通过5858端口开始了断点调试。
另外,从下面截图可以看出,UI 调试工具(其实是个网页)跟 inspector服务 之间通过websocket进行通信。
用户在界面上操作时,比如设置断点,就向 inspector服务 发送一条消息,inspector服务 在内部通过 v8 调试器来实现代码的断点。
可以看到,用到了v8-debug,这个就待深挖了。
通过 node-inspector 调试远程代码
细心的同学可能会发现,node 远程调试其实在上面node-inspector章节的讲解里已经覆盖到了。这里还是来个实际的例子。
假设我们的 node 代码app.js运行在阿里云的服务器上,服务器 ip 是xxx.xxx.xxx.xxx。
首先,服务器上启动 node-inspector 服务
[root@iZ94wb7tioqZ ~]# node-inspector
Node Inspector v0.12.8
Visit http://127.0.0.1:8080/?port=5858 to start debugging.
其次,通过--debug-brk参数,进入调试模式
[root@iZ94wb7tioqZ ex]# node --debug-brk app.js
Debugger listening on port 5858
最后,在本地通过 ip 地址愉快的访问调试界面。是不是很简单捏。
常见问题:安全限制
远程调试常见的问题就是请求被拒绝。这是服务器安全策略的限制。遇到这种情况,开放端口就完事了。
在我们的云主机上,默认安装了firewall-cmd,可以通过--add-port选项来开放8080端口的开放。如果本机没有安装firewall-cmd,也可以通过iptables来实现同样的功能。
[root@iZ94wb7tioqZ ex]# firewall-cmd --add-port=8080/tcp
success
然后,就可以愉快的远程调试了。