如何對(duì)Node應(yīng)用"死后驗(yàn)尸"

為了讓前端工作更有效率唆樊,必須徹底掌握一些必要的調(diào)試技巧宛琅。平常在開發(fā)Node應(yīng)用的過程中,最常使用的是本地調(diào)試逗旁,但是一旦你的代碼到了生產(chǎn)環(huán)境嘿辟,就必須采取其他策略進(jìn)行追蹤和解決問題。

在編程領(lǐng)域片效,有一個(gè)專門的術(shù)語“Post-mortem debugging"红伦,它的意思是在程序奔潰后再進(jìn)行的調(diào)試工作。對(duì)于Node程序來說淀衣,如果你遇到了難以重現(xiàn)昙读、線上環(huán)境無法調(diào)試等問題都可以采用這種方案進(jìn)行操作。而且線上問題一般都是比較緊急的膨桥,所以我們一般都希望能最快定位到問題發(fā)生的代碼蛮浑。

那么我們具體要怎么做呢唠叛?下面舉個(gè)簡(jiǎn)單的例子。

收集奔潰信息

假設(shè)現(xiàn)在你有這樣一段代碼:

const demo = (data) => {
        const {id, profile} = person;
        console.log(id);
        console.log(profile.name);
}

demo({id: 1, profile: {age: 12}});

運(yùn)行后它會(huì)報(bào)錯(cuò)并退出沮稚。這時(shí)候你需要做的是收集奔潰信息并對(duì)其做分析玻墅。Core Dump就是這樣用來記錄程序運(yùn)行信息的一種工具,它包含了程序運(yùn)行過程中的內(nèi)存狀態(tài)壮虫,調(diào)用棧等,能最真實(shí)地還原當(dāng)時(shí)的“案發(fā)現(xiàn)場(chǎng)“环础。

那么囚似,在Node.js中我們?cè)趺传@得Core Dump的文件呢?

首先线得,我們先設(shè)置一下系統(tǒng)中的內(nèi)核限制:

ulimit -c unlimited

然后你需要在啟動(dòng)應(yīng)用的時(shí)候饶唤,使用--abort-on-uncaught-exception這個(gè)flag來手動(dòng)觸發(fā)程序奔潰后寫core文件的操作:

node --abort-on-uncaught-exception app.js
draggingScreenshot.png

這樣當(dāng)程序突然奔潰的時(shí)候,就會(huì)在linux或mac系統(tǒng)的/cores目錄下生成類似core.81371這樣的一個(gè)文件贯钩。這個(gè)文件就是我們用來調(diào)試調(diào)查程序奔潰的核心募狂。

如果你的程序正在執(zhí)行過程中,我們也可以手動(dòng)捕獲core dump文件角雷,類似于實(shí)時(shí)檢查祸穷,主要用于程序假死等狀態(tài)。

手動(dòng)捕獲的話需要使用Linux系統(tǒng)自帶的 gcore 命令勺三,具體用法是找出當(dāng)前進(jìn)程的pid(這里假設(shè)是123),然后執(zhí)行命令:

gcore 123

生成對(duì)應(yīng)的core dump文件雷滚。

另外一種方式是采用lldb調(diào)試工具,mac系統(tǒng)下使用該命令進(jìn)行安裝:

brew install --with-lldb --with-toolchain llvm

然后執(zhí)行:

lldb --attach-pid <pid> -b -o 'process save-core' "core.<pid>"'

這樣就能在不重啟程序的情況下導(dǎo)出特定進(jìn)程的core dump文件。

調(diào)試步驟

得到具體的core dump文件后吗坚,我們就要進(jìn)入調(diào)試分析階段了祈远。

首先,需要使用選擇順手的分析工具商源。你可以選擇mdb_v8或者llnode车份。這兩個(gè)工具用起來都差不多。

這里以llnode為例牡彻,先介紹幾個(gè)常用命令:

命令 意義
v8 help 查看幫助信息
v8 bt get stack trace at crash 查看堆棧信息
v8 souce list 顯示stack frame的源碼
v8 inspect <addr> 查看對(duì)應(yīng)地址的對(duì)象內(nèi)容
frame select <num> 選擇對(duì)應(yīng)的stack frame

在分析前扫沼,先需要用llnode加載core文件:

llnode -c /cores/core.81371

然后獲取對(duì)應(yīng)的堆棧信息:

// 查看堆棧信息
(llnode) v8 bt
// 根據(jù)堆棧信息找到可疑的地址,并查看對(duì)應(yīng)的對(duì)象內(nèi)容
(llnode) v8 inspect <address>
// 指定對(duì)應(yīng)的stack frame
(llnode) frame select 6
// 查看源碼
(llnode) v8 source list

最后通過結(jié)合堆棧信息和源碼就能找到錯(cuò)誤發(fā)生的原因了讨便。

內(nèi)存泄漏

除了程序奔潰充甚,有時(shí)候你還會(huì)發(fā)現(xiàn)應(yīng)用隨著運(yùn)行時(shí)間增長(zhǎng),速度開始變慢霸褒。這可能就是內(nèi)存泄漏搗的鬼伴找。

比如下面這段代碼:

const requests = new Map();

app.get("/", (req, res) => {
  requests.set(req.id, req);
  res.status(200).send("hello")
})

通常來說,內(nèi)存泄漏容易發(fā)生在閉包等場(chǎng)景下废菱。針對(duì)內(nèi)存泄漏的調(diào)試技矮,可以使用如下命令:

node --trace_gc --trace_gc_verbose app.js

啟動(dòng)應(yīng)用后抖誉,通過壓測(cè)工具運(yùn)行如下命令:

ab -k -c200 -n10000000 http://localhost:3000
draggingScreenshot.png

可以看到隨著程序的運(yùn)行,內(nèi)存使用越來越大衰倦。

另外袒炉,我們還可以使用heap snapshot來獲取快照信息:

process.on('SIGUSR2', () => {
    const { writeHeapSnapshot } = require("v8");
    
    console.log("Heap snapshot has written:", writeHeapSnapshot())
})

在命令行中執(zhí)行:

kill -SIGUSR2 <pid>

就能夠獲得對(duì)應(yīng)的快照文件,然后我們可以使用Chrome Devtools的Memory菜單加載對(duì)應(yīng)的快照文件進(jìn)行比對(duì)分析了樊零。

如果是開發(fā)階段我磁,你也可以直接使用調(diào)試模式啟動(dòng)應(yīng)用:

node --inspect app.js

然后使用菜單Devtools > Memory > take heap snapshot獲得快照文件。


draggingScreenshot.png

通過比較兩個(gè)不同的內(nèi)存快照驻襟,我們可以很快找到內(nèi)存增長(zhǎng)最快的那個(gè)對(duì)象夺艰,然后進(jìn)而分析對(duì)應(yīng)的源碼就能知道問題出在了哪里。

除了采用Chrome瀏覽器沉衣,在linux主機(jī)上郁副,我們還能使用萬能的llnode調(diào)試器進(jìn)行內(nèi)存泄漏的分析。原理大致相同豌习,也是通過分析core 文件存谎,然后安裝對(duì)象大小排序,針對(duì)可疑的對(duì)象進(jìn)行源碼查看肥隆。

(llnode) v8 findjsobjects
(llnode) v8 findjsinstances -d <Object>
(llnode) v8 inspect -m <address》
(llnode) v8 findrefs <address>

其它策略

另外一種收集報(bào)告的策略是使用參數(shù)既荚,適用于13.0以上版本:

node \
    --experimental-report \
    --diagnostic-report-uncaught-exception \
    --diagnostic-report-on-fatalerror \
    app.js

這樣在程序奔潰的時(shí)候,就能夠獲取到對(duì)應(yīng)的報(bào)告巷屿。你還可以通過代碼顯式控制報(bào)告的輸出文件名等:

process.report.writeReport('./foo.json');

更多說明可以參考官方文檔: https://nodejs.org/api/report.html

——--轉(zhuǎn)載請(qǐng)注明出處--———

微信掃描二維碼固以,關(guān)注我的公眾號(hào).jpg

最后,歡迎大家關(guān)注我的公眾號(hào)嘱巾,一起學(xué)習(xí)交流憨琳。

參考資料

https://medium.com/netflix-techblog/debugging-node-js-in-production-75901bb10f2d
https://en.wikipedia.org/wiki/Debugging
https://www.bookstack.cn/read/node-in-debugging/2.1gcorellnode.md
https://github.com/bnoordhuis/node-heapdump

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市旬昭,隨后出現(xiàn)的幾起案子篙螟,更是在濱河造成了極大的恐慌,老刑警劉巖问拘,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遍略,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡骤坐,警方通過查閱死者的電腦和手機(jī)绪杏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纽绍,“玉大人蕾久,你說我怎么就攤上這事“柘模” “怎么了僧著?”我有些...
    開封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵履因,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我盹愚,道長(zhǎng)栅迄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任皆怕,我火速辦了婚禮毅舆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘愈腾。我一直安慰自己朗兵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開白布顶滩。 她就那樣靜靜地躺著,像睡著了一般寸爆。 火紅的嫁衣襯著肌膚如雪礁鲁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天赁豆,我揣著相機(jī)與錄音仅醇,去河邊找鬼。 笑死魔种,一個(gè)胖子當(dāng)著我的面吹牛析二,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播节预,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼叶摄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了安拟?” 一聲冷哼從身側(cè)響起蛤吓,我...
    開封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎糠赦,沒想到半個(gè)月后会傲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拙泽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年淌山,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顾瞻。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泼疑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出朋其,到底是詐尸還是另有隱情王浴,我是刑警寧澤脆炎,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站氓辣,受9級(jí)特大地震影響秒裕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钞啸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一几蜻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧体斩,春花似錦梭稚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蹬敲,卻和暖如春暇昂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伴嗡。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來泰國打工急波, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瘪校。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓澄暮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親阱扬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子泣懊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 很多Node.js初學(xué)者都會(huì)有這樣的疑惑,Node.js到底是單線程的還是多線程的麻惶?通過本章的學(xué)習(xí)嗅定,能夠讓讀者較為...
    越努力越幸運(yùn)_952c閱讀 3,653評(píng)論 4 36
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,111評(píng)論 1 32
  • 之前初步了解過Windows 下強(qiáng)大的調(diào)試工具WinDbg,也簡(jiǎn)單的整理了一個(gè)初級(jí)的文章《使用WinDbg用踩、Map...
    SunnyZhang的IT世界閱讀 3,725評(píng)論 0 1
  • 昨天的溫度不算太高也不低渠退,溫差大中午太陽撩人,收費(fèi)亭恨不能吸收所有光和熱脐彩,僅有一平米的空間還要和電腦親密接...
    芙蕖沉香閱讀 175評(píng)論 0 1
  • 今日體驗(yàn)碎乃,今天晚上公司組織培訓(xùn)輪胎,每一次培訓(xùn)都能學(xué)到一些東西惠奸,這也是精保的項(xiàng)目梅誓,每天都在干的,了解每一個(gè)項(xiàng)目產(chǎn)品...
    王全峰閱讀 80評(píng)論 0 0