企業中的 IBM 和 Node.js: 針對 IBM SDK for Node.js 的核心轉儲調試
關于本系列
作為 IBM 的開放技術承諾的一部分,IBM 與 Node.js 社區的合作非常緊密。IBM 是 Node.js Foundation 的鉑金級贊助商和董事會成員,而且加入了 Node 技術指導委員會。請跟隨本系列文章,了解 IBM 對 Node.js 技術和支持性工具的貢獻的最新信息。
在 Node.js 的開發中,核心轉儲分析可以幫助調試程序崩潰和內存泄漏。IBM SDK for Node.js 通過 IBM Monitoring and Diagnostic Tools — Interactive Diagnostic Data Explorer (IDDE),為 Node.js 應用程序提供了一種新的核心轉儲分析和調試方法。IDDE 是一個 Eclipse 插件,可通過免費許可獲得,它支持來自 IBM SDK for Node.js 發布到的所有平臺的核心轉儲文件,但 Mac OS X 除外。而且這些轉儲是可移植的;您可以獲取一個計算機的核心轉儲文件并在另一個計算機上的 IDDE 中打開它,甚至是在運行不同的(受支持)OS 的計算機上也可以打開它。
可以從 Eclipse 市場 或使用 更新站點 安裝 IDDE。請繼續閱讀,了解在開發 Node.js 應用程序的過程中,如何使用 IDDE 調試程序崩潰和內存泄漏。
生成核心轉儲文件
生成核心轉儲文件的方法在不同系統中有所不同。Joyent(Node.js 的企業管理人)推薦使用 --abort-on-uncaught-exception
標記運行所有生產級 Node.js 系統。在 UNIX 系統上,還需要設置 ulimit -c unlimited
來支持生成沒有大小限制的核心文件。
擴展 IDDE 支持
IDDE 支持探索和調試 IBM Java 核心轉儲已有多年。隨著 2013 年 IBM SDK for Node.js 的發布,支持在 IDDE 1.2 版中添加 Node.js。IDDE 1.2.2 還添加了對 IBM SDK for Node.js 1.2.x 版(等效于 Node 0.12.x)的支持。
如果沒有拋出異常,則需要使用系統工具(比如 LInux 上的 gcore
或 AIX 上的 gencore
)來生成核心文件,或者使用 kill -11
結束進程。在 Windows 7 和更高版本上,可以使用任務管理器來生成核心轉儲文件:按下 Ctrl+Alt+Delete 并選擇 Start Task Manager。從 Processes 選項卡中,右鍵單擊 Node.js 進程并選擇 Create Dump File。還可以使用用于 Windows 的免費的 ProcDump 實用程序。
為了生成程序崩潰,我們將使用 Test.js 腳本。這個簡單腳本循環 5 次,然后拋出一個錯誤。
清單 1. Test.js
function main() {
var inputObject = {
input: ["one", "two", "three", "fifteen", "one hundred"],
counter:0,
};
for(; inputObject.counter< inputObject.input.length; inputObject.counter++) {
if (inputObject.input[inputObject.counter].length > 8) {
throw "Input String Too Big";
}
}
}
main();</code></pre>
可在 Linux 上像這樣運行 test.js:
$ cd node-v0.12.4-linux-x64/bin/
$ ulimit -c unlimited
$ ./node --abort-on-uncaught-exception ../../test.js
Uncaught Input String Too Big
FROM
main (/home/sian/test.js:11:7)
Object.<anonymous> (/home/sian/test.js:16:1)
Module._compile (module.js:460:26)
Object.Module._extensions..js (module.js:478:10)
Module.load (module.js:355:32)
Function.Module._load (module.js:310:12)
Function.Module.runMain (module.js:501:10)
startup (node.js:129:16)
node.js:814:3
Illegal instruction (core dumped)</code></pre>
在 IDDE 中打開一個核心轉儲文件
如果 IDDE 已安裝,并在核心轉儲文件所在的相同計算機上運行,您可以直接從磁盤位置打開核心轉儲文件。右鍵單擊 PD Navigator View(PD 表示問題判定)并選擇 New PD Artifact。
圖 1. 選擇 New PD Artifact
.jpg)

瀏覽到您的核心轉儲文件的位置并單擊 Finish。
要在另一個計算機上打開該轉儲文件,可以使用常用工具復制該轉儲文件,然后采用我們剛才介紹的方法在第二個系統上的 IDDE 中打開它。要在將轉儲文件復制到不同的位置或不同的計算機時實現更好的原生堆棧軌跡,還可以將 Node 可執行程序復制到同一個目錄來啟用要解析的符號。
您現在需要使用 IDDE 編輯器。從您的新核心文件下選擇 Start Investigation 來打開該編輯器。
圖 2. 打開 IDDE 編輯器。
.jpg)

大型轉儲文件可能需要更多的時間來完成加載。
IDDE 命令
它有助于將 IDDE 編輯器視為編輯器與控制臺的一種混合體。您可以像使用控制臺一樣在 IDDE 編輯器中輸入并運行命令,但您的進度保存在編輯器中。
要運行命令,必須在命令的前面添加 !
并按下 Ctrl+Enter。例如,輸入 !help
并按下 Ctrl+Enter 會輸出幫助消息,其中列出了處理轉儲的其他可用命令。Node 命令集與 Java 集不同,甚至對于不同的 Node 版本(或 Java)可能也會有所不同,因為新的 IDDE 版本中不斷添加新的命令。
與其他 Eclipse 編輯器一樣,Ctrl+Space 會顯示完成建議,這在 IDDE 編輯器中就是命令列表。
圖 3. 使用 Ctrl+Space 打開的命令列表
.jpg)

一個首先使用的不錯命令是 nodeoverview
,它提供了您運行的 Node 版本、依賴關系等的基本摘要。
!nodeoverview {
Node Property Value
Node version 0.12.4
Path to executable /home/sian/node-v0.12.4-linux-x64/bin/node
Architecture x64
Platform linux
Command Line Arguments /home/sian/node-v0.12.4-linux-x64/bin/node /home/sian/test.js
Execution Arguments --abort-on-uncaught-exception
Process ID 5643
Dependency Version
http_parser 2.3
node 0.12.4
v8 3.28.71.19 2.2
[...]</code></pre>
從前面的控制臺輸出中可以看出,該程序將會崩潰并拋出了一個 uncaught 異常,所以在這里要做的第一件事就是查看堆棧軌跡。運行 threads
命令查找線程 ID。
!threads {
Thread ID: 0x74c9 (29897) IP: 0x0000000000eb112d
!stack 29897
}</code></pre>
只有一個線程在運行,所以它一定是 JavaScript 線程。
嘗試對該線程運行 stack
命令。threads
命令的輸出中包含該命令的快捷鍵,所以您可以將光標放在包含 !stack 29897
的行的末尾處并按下 Ctrl+Space。這是輸出(為了提高可讀性,我們刪除了參數列并截斷了一些行):
可以看到,崩潰發生在 main
方法中。要返回到代碼來查看可能已發生的情況,不需要離開 IDDE;如果運行前面的輸出中以粗體顯示的命令,IDDE 會顯示源代碼。
!jsobject 0x00001519ED09B451 {
Object has fast properties
Number of descriptors : 5
Name Value More Information
length 0x0000079578A0FE19 <EXECUTABLE_ACCESSOR_INFO_TYPE>
name 0x0000079578A0FE51 <EXECUTABLE_ACCESSOR_INFO_TYPE>
arguments 0x0000079578A0FE89 <EXECUTABLE_ACCESSOR_INFO_TYPE>
caller 0x0000079578A0FEC1 <EXECUTABLE_ACCESSOR_INFO_TYPE>
prototype 0x0000079578A0FEF9 <EXECUTABLE_ACCESSOR_INFO_TYPE>
Object is a function
Name: main
Source:
() {
var inputObject = {
input: ["one", "two", "three", "fifteen", "one hundred"],
counter:0,
};
for(; inputObject.counter< inputObject.input.length; inputObject.counter++) {
if (inputObject.input[inputObject.counter].length > 8) {
throw "Input String Too Big";
}
}
}
}</code></pre>
備注:此特性有助于確認您認為在運行的代碼確實是實際運行的代碼。
從代碼中可以明顯看到,mainObject.counter
肯定已經達到 4,它指向字符串 "one hundred"
,該字符串的長度大于 8。可以使用另外兩個 IDDE 命令確認事實就是這樣:jsfindbyproperty
和 jsobject
。jsfindbyproperty
搜索堆中所有具有所提供的名稱的屬性的對象 — 在本例中為 counter
。jsobject
顯示了該對象的屬性。
!jsfindbyproperty counter {
!jsobject 0x00001519ED09B539
!jsobject 0x00001519ED09B689
}
!jsobject 0x00001519ED09B689 {
Object has fast properties
Number of descriptors : 2
Name Value More Information
input 0x00001519ED09B6C1 <JS Array[5]> :- !jsobject 0x00001519ed09b6c1
counter 0x0000000400000000 SMI = 4
}</code></pre>
jsfindbyproperty
生成的十六進制數(例如 0x00003CF51F09B441
)顯示了該命令找到的對象的內存地址。可以對其中一個十六進制地址運行 jsobject
命令,如果某個 JavaScript 對象位于該地址中,該命令會打印出該對象的屬性信息。在本例中,我們可以看到 counter
在此對象上達到了 4。
現在使用來自前面的輸出的快捷鍵命令,查看 input
數組。
!jsobject 0x00001519ed09b6c1 {
Array at !hexdump 0x00001519ED09B4E1
Array len = 5
0 : 0x00000CEAAC1A3679, one
1 : 0x00000CEAAC1A3699, two
2 : 0x00000CEAAC1A36B9, three
3 : 0x00000CEAAC1A36D9, fifteen
4 : 0x00000CEAAC1A36F9, one hundred
}</code></pre>
可以看到 test.js 代碼中未通過測試并導致拋出了一個異常的數組元素。
查找內存泄漏
內存泄漏可能是所有程序中的常見問題。IDDE 擁有多個命令來幫助跟蹤哪些對象正在占用內存。在本例中,我們首先從我們認為存在內存泄漏的 Node.js 應用程序中獲取一個核心轉儲文件。
跟蹤泄漏的一種方式是獲取兩個或更多具有較大時間差的轉儲文件,比較在兩個轉儲文件之間執行一些命令的輸出。
jsmeminfo
命令有助于直觀顯示是否有非常大的對象正在使用大量空間,像本例中一樣。
!jsmeminfo {
Memory allocator, used: 1423 MB, available: 0 MB
Total Heap Objects: 29497
Largest 5 heap objects Type Size (bytes) More information
0x0000000088a6d319 JS_OBJECT_TYPE 1311125 !jsobject 0x0000000088a6d319
0x0000000088aac6d9 FIXED_ARRAY_TYPE 98360 !array 0x000003ff88aac6d9
0x000003ff8abe31b9 ASCII_STRING_TYPE 48176 !string 0x000003ff8abe31b9
0x000003ff8ab34f09 ASCII_STRING_TYPE 48104 !string 0x000003ff8ab34f09
0x000003ff8ab04101 ASCII_STRING_TYPE 40936 !string 0x000003ff8ab04101</code></pre>
與 調試崩潰 小節中一樣,對此對象運行 jsobject
命令可以將它與您程序中的一個對象相關聯并修復問題。
對于程序創建了許多對象但不處理它們的內存問題,jsgroupobjects
可將相同類型的對象分組到一起,并顯示程序中有多少對象。jsgroupobjects
還有助于識別 Node.js 緩沖區在何處被用來顯示對象的構造函數。(Node 中的緩沖區是分配超出堆范圍的內存的一種方式。)在本例中,Buffer
是最常出現的對象:
您可以通過識別緩沖區所分配的外部數組的位置(在以下輸出中用粗體顯示),在 IDDE 中打印緩沖區的內容。
print 0x000003ffec004101 {
Object at 0x000003FFEC004101 is JSObject
Class hierarchy :-
|-JSObject
| |- kElementsOffset 0x10 (EXTERNAL_UINT8_ARRAY_TYPE, !print 0x000003FFEC004159)
| |- kPropertiesOffset 0x8 (FIXED_ARRAY_TYPE, !print 0x000003FF92A04111)
| |-JSReceiver
| | |-HeapObject
| | | |- kMapOffset 0x0 (MAP_TYPE, !print 0x000003FF8BE1F6E9)
| | | |-Object
...</code></pre>
獲取 kElementsOffset
的地址并提供給 array
數組。
!array 0x000003FFEC004159 {
Array type : EXTERNAL_UINT8_ARRAY_TYPE
Len : 10
0 0x48 H
1 0x65 e
2 0x6c l
3 0x6c l
4 0x6f o
5 0x20
6 0x6e n
7 0x6f o
8 0x64 d
9 0x65 e
..</code></pre>
objtypes
命令對解決內存問題也很有用。它顯示了 V8 堆對象類型的計數和內存大小。
完整的命令參考列表
以下是 IDDE 的完整的命令參考列表。這里用斜體顯示的命令可用于任何核心轉儲文件;其他所有命令特定于 Node.js 轉儲文件:
array
顯示指定地址上一個固定數組的元素
find
、findall
、findnext
在內存中查找一個字符串
frame
顯示單個 JavaScript 堆棧幀的詳細信息
help
顯示命令列表
hexdump
以十六進制和 ASCII 格式輸出一個內存區段
jsfindbyproperty
找到具有指定屬性的 JavaScript 對象
jsgroupobjects
列出共享同一個 Map
的 JavaScript 對象組
jslistobjects
列出具有指定的 V8 對象類型的堆對象
jsmeminfo
顯示 JavaScript 內存使用信息,包括 5 個最大的對象
jsobject
顯示 JavaScript 對象細節
jsobjectsmatching
打印與所提供的對象共享同一個 Map
的 JavaScript 對象
jsstringsearch
在堆上搜索某個指定的字符串
locate
在內存中搜索某個指定的字符串
nodeoverview
顯示 Node 概述信息,包括版本
objtype
列出所有 V8 對象類型
objtypes
顯示 V8 實例類型的堆棧使用分類信息
print
顯示指定的堆棧對象的 C++ 分層結構
ranges
打印可用的內存范圍列表
stack
顯示某個給定線程的 JavaScript 堆棧軌跡
string
顯示指定地址上的字符串
threads
列出所有線程
在本教程中,您學習了如何生成 Node.js 核心轉儲文件,在 IDDE 中打開該轉儲文件,在 IDDE 編輯器中輸入并運行命令,獲取可用于您的轉儲文件的所有命令的列表。您還了解了哪些 IDDE 命令能夠以最佳方式幫助您跟蹤程序崩潰和內存泄漏的根源。
via:http://www.ibm.com/developerworks/cn/web/wa-ibm-node-enterprise-dump-debug-sdk-nodejs-trs/