16-进阶调试-debugger

164 阅读6分钟

进阶调试-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

调试界面如下,简直不能更亲切。

segmentfault.com/img/bVCNFt

方式二:更加灵活的方式

步骤 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。成功

segmentfault.com/img/bVCNFu

实现原理

从上面的例子不难猜想到。(不负责任猜想)

  • 通过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 调试器来实现代码的断点。

segmentfault.com/img/bVCNFC

可以看到,用到了v8-debug,这个就待深挖了。

segmentfault.com/img/bVCNFD

通过 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 地址愉快的访问调试界面。是不是很简单捏。

segmentfault.com/img/bVCNFF

常见问题:安全限制

远程调试常见的问题就是请求被拒绝。这是服务器安全策略的限制。遇到这种情况,开放端口就完事了。

segmentfault.com/img/bVCNHD

在我们的云主机上,默认安装了firewall-cmd,可以通过--add-port选项来开放8080端口的开放。如果本机没有安装firewall-cmd,也可以通过iptables来实现同样的功能。

[root@iZ94wb7tioqZ ex]# firewall-cmd --add-port=8080/tcp
success

然后,就可以愉快的远程调试了。

segmentfault.com/img/bVCNHE