寫在前面
Q:Node 要怎麼調試?
A:啥叫調試?
一般我們所說的「調試」應該是指步過、步入、步出,盯著源碼一句一句執行吧。這樣的話,調試服務端的程序和一般客戶端程序沒什麼區別,都是想要窺視執行流、調用棧、變量當前值等等,也就是說調試 Node 服務和調試頁面中 JS 腳本的方式一樣
當然,服務端有服務端的特殊性,很多代碼與客戶端環境密切相關,比如 Cookie、Request Header、localStorage 等等,想要一行一行調試似乎不太現實,首先,服務的輸入(客戶端請求)從哪裡來?可以模擬請求,也可以直接模擬數據。。。再扯下去其實是測試的內容了,不是「調試」
一。Debugger
Node 內建了斷點調試,也就是 debugger 語句,如下:
// console.js
debugger;
var value = 1;
var fn1 = function() {
debugger;
value = '2';
console.log('fn1');
};
var fn2 = function() {
debugger;
value = 3;
fn1();
console.log('fn2');
};
console.log(1);
process.nextTick(function () {
debugger;
value = 4;
fn2();
console.log(2);
});
console.log(3);
其中 debugger; 表示設置斷點,異步 執行流也會被斷點中斷。當然,debugger 只在調試環境有效,通過命令行進入 debug 模式:
node debug ./console.js
// 對於已經在運行的服務,向其進程發送 SIGUSR1 信號可以進入調試模式
// kill -s USR1 [pid]
然後可以看到提示信息和 debug 交互提示:
< Debugger listening on port 5858
debug> . ok
break in E:\node\learn\debug\console.js:1
> 1 debugger;
2
3 var value = 1;
debug>
執行流停在了第一行的 debugger; 處,光標跳動等待輸入調試命令,Node 沒有對 V8 調試命令提供完整支持,可用命令如下:
###1. 控制執行流
-
c/cont
繼續執行到下一個斷點
-
n/next
執行到下一句
-
s/step
步進到函數內部
-
o/out
從函數內部跳出
-
pause
暫停執行
比如輸入 c 會輸出 1 和 3,然後停在 value = 4; 的上一行。再輸入 n 會停在 value = 4;,然後先輸入 n 再輸入 s 就進入 fn2 的函數體,停在函數體第一行。。。
注意:調試命令不太友好,如果程序全部執行完畢了,再輸入 c,就需要kill 進程 結束調試了
###2. 設置/清除斷點
-
sb()/setBreakpoint()
在當前行設置斷點
-
sb(line)/...
在第 line 行設置斷點
-
sb('fn()')/...
在函數體的開頭設置斷點
-
sb('script.js', 1)/...
在 script.js 文件的第一行設置斷點
-
cb()/clearBeakpoint()
清除斷點
注意:這些命令很不好用,建議在源碼中用 debugger; 語句設置斷點
###3. 查看狀態信息
-
bt/backtrace
輸出當前堆棧信息
-
list(3)
列出當前前後各 3 行源碼
-
watch('expr')
添加 expr 到觀察列表
-
unwatch('expr')
從觀察列表刪除 expr
-
watchers
列出觀察列表中所有表達式和值
-
repl
打開調試的上下文,直接輸入調試代碼
這些命令挺好用,watch 頻繁變化的變量/表達式,或者直接 repl 進入調試環境中的調試環境,想輸出什麼都行
二。調試工具
手動輸入命令比較麻煩,所以有了調試工具:node-inspector,npm 全域安裝,然後直接運行起來:
E:\node\learn\debug>node-inspector
Node Inspector v0.12.7
Visit http://127.0.0.1:8080/?port=5858 to start debugging.
Cannot send response - there is no front-end connection.
瀏覽器(瀏覽器必須帶有Blink 開發者工具,比如 Chrome 和 Opera,而 FF45 不行)訪問 http://127.0.0.1:8080/?port=5858,啥都沒有啊,等等,最後一行說沒找到要調的東西,那好,我們運行程序並進入 debug 模式:
node debug ./console.js
// 當然,向進程發送 SIGUSR1 信號也可以
// kill -s USR1 [pid]
然後刷新瀏覽器,頁面出現源碼了,並且顯示了熟悉的 Dev Tools:
(function (exports, require, module, __filename, __dirname) { debugger;
var value = 1;
var fn1 = function() {
debugger;
value = '2';
console.log('fn1');
};
var fn2 = function() {
debugger;
value = 3;
fn1();
console.log('fn2');
};
console.log(1);
process.nextTick(function () {
debugger;
value = 4;
fn2();
console.log(2);
});
console.log(3);
});
發現代碼被模組包裝了,所以調試的一個優勢 是可以窺探 node 源碼,想要追根溯源,無限步入就可以扯出一整條執行流。同理,也可以方便的查看第三方模組內部的具體執行流,分析源碼時比較有用
P.S. 這裡先運行 node-inspector 是為了說明先後順序無所謂,不必每次都嚴格地先 debug 執行待調試的程序,原理是 node 提供了 TCP 調試協議,通過協議可以從進程外部進行調試,node-inspector 通過該協議與待調試的進程通信,執行 debugger 命令,並通過 Blink 開發者工具界面顯示出來
三。總結
其實無論是使用命令還是用工具,感覺都比較麻煩,而且,為什麼需要調試?
理由,筆者只能想到 2 種情況:
- 代碼糊了(發生了奇怪的錯誤,單純靜態分析找不到錯誤原因)
搞不清執行流了,需要捋一捋
- 想要分析源碼
弱類型的 js 沒有辦法轉到定義,通過調試分析源碼是不錯的選擇
調試會引起執行中斷,必須離線進行。開發中需要一行一行盯著走的情況極少,應該通過單元測試 保證代碼健壯性,而不是一出問題就調��
對於服務端程序,除單元測試(mocha, should, muk, rewire,內容比較多,我們以後再說)外,日誌(pm2 自帶簡單的錯誤日誌)也是非常必要的。通過分析日誌鎖定錯誤原因,進而找到相應的單元測試用例,進行修復
參考資料
- 《深入淺出 NodeJS》
暫無評論,快來發表你的看法吧