Node 調試指南—— Inspector 協議
Node v6.3+的版本提供了兩個用於調試的協議: v8 Debugger Protocol和v8 Inspector Protocol可以使用第三方的Client/IDE等監測和介入Node(v8)運行過程,進行調試。
v8 Debugger Protocol是Node v6.3之前的版本就支持的調試協議,使用一個TCP端口(通常是5858)與Client/IDE交互,之前比較流行的第三方調試工具node-inspector就是基於這個協議。 node-inspector雖然名為inspector,實際使用的是早期的v8 Debugger Protocol ,其工作原理如下:
1. 使用node --debug=5858 yourScript.js 啟動你的js 腳本,則node 在運行腳本時會將5858 作為調試端口
2. 啟動node-inspector,它會開啟一個後台進程,通過8080 端口提供http 服務。
3.在瀏覽器中打開http:// 127.0.0.1:8080/? port=5858則會連接到node-inspector後台進程,同時告訴後台連接使用5858作為調試端口的node進程。後台會提供一個類似於chrome devtools 的UI 調試界面。
v8 Inspector Protocol是node v6.3新加入的調試協議,通過websocket (通常使用9229端口)與Client/IDE交互,同時基於Chrome/Chromium瀏覽器的devtools提供了圖形化的調試界面。
開啟調試
單進程
使用node --inspect=9229 yourScript.js 啟動你的腳本,9229 是指定的端口號
多進程
- 使用child_process.fork() 開啟子進程時可以傳入execArgv,使用execArgv.push('--inspect=9229') 添加執行參數,開啟子進程的調試端口。
- 在已經開啟調試端口(以9229 為例)的進程使用cluster.fork() 克隆進程,新進程會自動開啟調試,使用的端口在原進程的端口上遞增(9230、9231、9232)。
調試工具接入
chrome/chromium瀏覽器
chrome (v55+) 系的瀏覽器提供了三種接入調試方法
1) 使用控制台node --inspect=9229 yourScript.js 啟動腳本時控制台會輸出用於打開調試界面的URL ,複製到chrome 中打開就可以了。

2) 在地址欄輸入chrome://inspect/#devices

勾選Discover network targets ,然後點擊Configure 設置地址和端口,添加使用的9229 端口。
確認Done 後,刷新一下chrome://inspect/#devices 界面,等一會兒就可以看到Remote target 中出現node --inspect=9229 yourScript.js 啟動的腳本,點擊inspect 進入調試界面。

3)安裝一個chrome擴展程序NIM ,配置好調試端口後擴展程序會自動查找並加載方法1中的URL。

VS Code
Vs Code內置了Node debugger ,支持v8 Debugger Protocol和v8 Inspector Protocol兩種協議。對於v8 Inspector Protocol ,只需要在配置裡添加一條Attach類型配置
{"name":"Attach to Process","type":"node","request":"attach","port":9229}
調試工具
Console Panel
chrome 接入要調試的node 進程後,可以在Console 中代理Node 進程中所有的控制台輸出,提供了靈活的Filter 過濾功能,還可以在Node 進程代碼的上下文中直接執行代碼。


Sources Panel
Sources 中可以查看所有加載的腳本,還包括第三方庫和Node 核心庫,選中文件可以進行編輯,Ctrl + C 保存可以直接修改運行中的腳本。


Profile Panel
Profile 用於對運行中的腳本進行性能監測,包括CPU和內存的使用,共有四種Profiling type。

CPU profile
CPU 相關的Profile 只有第一種,可以記錄時間線上Javascript 函數執行時佔用的CPU 時間。
時間段的選取
有兩種方式
1) 手動開始/停止:單擊start 開始記錄,單擊stop 停止記錄



2) 在代碼中插入開始/停止的API 調用console.profile('tag') console.profileEnd('tag') ,可以在Sources 面板中直接編輯保存代碼,然後F5 刷新一下。

開始和結束記錄profile都會在Console 中打印,然後生成一個profile 記錄。

profile記錄分析
有三種視圖
- chart:俗稱火焰圖,以時間為橫軸顯示函數調用棧。下面簡單舉例分析

火焰圖的函數調用棧是倒置的,最上面為棧底,最下面為棧頂。一個棧是一個tick ,一個tick 一定是由Node 底層開始調用的,在Node 中使用process.nextTick(fn) 和setTimeout(fn, deloy) 的系統回調會產生新的tick ,對應產生新的調用棧。上圖中有兩個tick ,第一個是由setInterval(fn, deloy) 回調產生的,第二個是由process.nextTick(fn) 回調產生的。
函數的調用順序是從棧底到棧頂。上圖中第一個棧listOnTimeout 由底層調用,listOnTimeout 中調用了tryOnTimeout , tryOnTimeout 中調用了ontimeout ...依次類推。
調用棧的寬度是函數執行的時間。一個函數的執行時間包含了其內部調用其他函數的執行時間,所以相對靠近棧底的函數的調用時間一定比靠近棧頂的函數的調用時間長。除去內部調用其他函數的執行時間,就是當前函數的執行時間。
點擊函數會跳轉到Sources面板中函數定義的位置。
將鼠標懸停在函數上可顯示其名稱和數據:
下面解釋摘自chrome-devtools文檔-加速執行JavaScript
- Name 。函數的名稱。
- Self time 。完成函數當前的調用所需的時間,僅包含函數本身的聲明,不包含函數調用的任何函數。
- Total time 。完成此函數和其調用的任何函數當前的調用所需的時間。
- URL 。形式為file.js:100 的函數定義的位置,其中file.js 是定義函數的文件名稱,100 是定義的行號。
- Aggregated self time 。記錄中函數所有調用的總時間,不包含此函數調用的函數。
- Aggregated total time 。函數所有調用的總時間,不包含此函數調用的函數。
- Not optimized 。如果分析器已檢測出函數存在潛在的優化,會在此處列出。
- heavy(Bottom Up):統計數據,自底向上,底指的是火焰圖的底。參考chrome-devtools文檔-加速執行JavaScript
- tree(Top Down):統計數據,自頂向下,頂指的是火焰圖的頂。參考chrome-devtools文檔-加速執行JavaScript
Memory profile
待補充