调试核心转储文件
在Node.js开发中,核心转储分析可以帮助调试程序崩溃和内存泄漏。 IBM SDK for Node.js通过IBM监视和诊断工具-交互式诊断数据资源管理器 (IDDE)为Node.js应用程序提供了一种新的核心转储分析和调试方法。 IDDE是Eclipse附加组件,可以免费获得,它可以从分布有Node.js的IBM SDK的所有平台(除Mac OS X之外)支持核心转储。 您可以从一台计算机上进行核心转储,然后在另一台计算机上的IDDE中打开它-甚至是运行不同(受支持)操作系统的计算机。
您可以从Eclipse Marketplace安装IDDE或使用更新站点 。 继续阅读以了解如何在开发Node.js应用程序时将IDDE用于程序崩溃和内存泄漏。
生成核心转储
生成核心转储的方法因系统而异。 Joyent(Node.js的企业管理员)建议使用--abort-on-uncaught-exception
标志运行所有生产Node.js系统。 在UNIX系统上,还需要设置ulimit -c unlimited
以使核心文件的生成不受大小限制。
如果尚未引发异常,则需要使用系统工具(例如Linux上的gcore
或AIX上的gencore
来生成核心文件,或者-如果您乐意杀死该进程,则使用kill -11
。 在Windows 7和更高版本上,可以使用任务管理器生成核心转储:按Ctrl + Alt + Delete并选择启动任务管理器 。 在“进程”选项卡中,右键单击Node.js进程,然后选择“ 创建转储文件” 。 也可以使用Windows的免费ProcDump实用程序。
调试崩溃
为了产生程序崩溃,我们将使用Test.js脚本。 这个简单的脚本循环执行五次,然后引发错误。
清单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();
您可以像这样在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)
在IDDE中打开核心转储
如果在核心转储所在的同一台计算机上安装并运行了IDDE,则可以直接从其磁盘位置打开核心转储。 右键单击PD Navigator视图(PD代表问题确定),然后选择New PD Artifact 。
图1.选择新的PD工件
浏览到核心转储的位置,然后单击“ 完成” 。
要在另一台计算机上打开转储,请使用常用工具复制转储文件,然后按照我们刚才描述的方式在第二个系统上的IDDE中打开它。 为了在将转储复制到其他位置或另一台计算机时获得更好的本机堆栈跟踪,还请将Node可执行文件复制到同一目录中以使符号得以解析。
现在,您需要在IDDE编辑器中工作。 从新的核心文件下面选择“ 开始调查”以打开编辑器。
图2.打开IDDE编辑器
大的转储文件可能需要花费更多的时间来加载。
IDDE命令
可以将IDDE编辑器视为编辑器和控制台之间的混合体。 您可以像在控制台中一样在IDDE编辑器中输入和运行命令,但是您的进度将保存为编辑器中的命令。
要运行命令,您必须在其前面加上!
然后按Ctrl + Enter 。 例如, !help
,然后按Ctrl + Enter ,输出帮助消息,其中列出了用于转储的其他可用命令。 Node命令集与Java集不同,对于不同版本的Node(或Java)甚至可以有所不同,因为在更高版本的IDDE中添加了新命令。
与其他Eclipse编辑器一样, Ctrl + Space会显示完成建议,对于IDDE编辑器,这是命令列表。
图3.使用Ctrl + Space打开的命令列表
最好的命令开头是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
[...]
调查问题
您可以从前面的控制台输出中看到程序由于未捕获的异常而崩溃,因此这里要做的第一件事是查看堆栈跟踪。 运行threads
命令以查找线程ID。
!threads {
Thread ID: 0x74c9 (29897) IP: 0x0000000000eb112d
!stack 29897
}
只有一个线程在运行,因此它必须是JavaScript线程。
尝试对线程执行stack
命令。 该命令的快捷方式包含在threads
命令的输出中,因此您可以将光标放在包含!stack 29897
的行的末尾,然后按Ctrl + Space 。 这是输出(为了提高可读性;我们删除了arguments列并截断了一些行):
!stack 29897 {
Instruction Pointer Frame Address Location / Frame Type
------------------- ------------------ ------------------------------------------------------------------------------------
0x0000000000eb112d 0x00007FFF0A81F960 node::_ZN2v84base2OS11GetUserTimeEPjS2_+0x9d
0x0000000000a890c1 0x00007FFF0A81FAC0 node::_ZN2v88internal7Isolate29CaptureAndSetSimpleStackTraceENS0_6HandleINS0_8JSO...
0x0000000000b63c9d 0x00007FFF0A81FAF0 node::_ZN2v88internal25Runtime_ThrowNotDateErrorEiPPNS0_6ObjectEPNS0_7IsolateE+0xdd
0x00000E7CAA2A8E32 0x00007FFF0A81FB40 main [/home/sian/test.js]
!jsobject 0x00001519ED09B451
0x00000E7CAA2A8B34 0x00007FFF0A81FB78 <anonymous> [/home/sian/test.js]
!jsobject 0x00001519ED09B341
0x00000E7CAA224AC6 0x00007FFF0A81FBE0 INTERNAL FRAME
0x00000E7CAA2A7D26 0x00007FFF0A81FC58 Module._compile [module.js]
!jsobject 0x00000DA39E6185F9
0x00000E7CAA2A220C 0x00007FFF0A81FCA0 Module._extensions..js [module.js]
!jsobject 0x00000DA39E618691
0x00000E7CAA29E940 0x00007FFF0A81FCE8 Module.load [module.js]
!jsobject 0x00000DA39E618569
0x00000E7CAA295565 0x00007FFF0A81FD70 Module._load [module.js]
!jsobject 0x00000DA39E6184D9
0x00000E7CAA294F24 0x00007FFF0A81FDB8 Module.runMain [module.js]
!jsobject 0x00000DA39E618721
0x00000E7CAA26B31F 0x00007FFF0A81FE28 startup [node.js]
!jsobject 0x0000079578AC8A61
0x00000E7CAA269D10 0x00007FFF0A81FE58 <anonymous> [node.js]
!jsobject 0x0000079578A6EF49
0x00000E7CAA21EF40 0x00007FFF0A81FE98 INTERNAL FRAME
0x00000E7CAA21DE90 0x00007FFF0A81FF20 ENTRY FRAME
0x00000E7CAA21DE90 0x00007FFF0A81FF20 ENTRY FRAME
0x0000000000914E28 0x00007FFF0A81FFF0 NONE FRAME
0x0000000000813bda 0x00007FFF0A820060 node::_ZN2v88Function4CallENS_6HandleINS_5ValueEEEiPS3_+0xba
0x0000000000c9d40f 0x00007FFF0A820190 node::_ZN4node15LoadEnvironmentEPNS_11EnvironmentE+0x1df
0x0000000000c9d67f 0x00007FFF0A8202E0 node::_ZN4node15LoadEnvironmentEPNS_11EnvironmentE+0x44f
0x0000003d0fa1ed5d 0x0000000000000000 node::_fini+0x3d0eb6c8c5
}
您可以看到崩溃是在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";
}
}
}
}
注意:此功能对于确认您认为正在运行的代码是否确实在运行也很有用。
从代码看来, mainObject.counter
必须达到4,该长度指向长度超过8的字符串"one hundred"
。您可以通过使用其他两个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
}
jsfindbyproperty
生成的十六进制数字(例如0x00003CF51F09B441
)向您显示该命令找到的对象的内存地址。 您可以针对这些十六进制地址之一运行jsobject
命令,并且如果JavaScript对象位于该地址,则该命令将输出有关该对象属性的信息。 在这种情况下,我们可以看到该对象的counter
已达到4。
现在,使用前面输出中的shortcut命令查看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
}
您可以在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
与在调试崩溃部分中一样,对该对象运行jsobject
命令将使您能够将其与程序中的对象相关联并解决问题。
对于另一种类型的内存问题,即程序正在创建许多对象而不处理它们, jsgroupobjects
将相同类型的对象分组, jsgroupobjects
您显示程序中有多少对象。 jsgroupobjects
还可以通过显示对象的构造函数来帮助确定Node.js缓冲区的使用位置。 (Node中的缓冲区是一种在堆外部分配内存的方法。)在此示例中, Buffer
是最经常出现的对象:
!jsgroupobjects {
Representative Object Address Object Type Num Objects Constructor Num Properties Properties
----------------------------- ------------- ---------- ----------- -------------- ---------------
!jsobject 0x000003ffec004101 JS_OBJECT_TYPE 2572 Buffer 2 length, parent
...
您可以通过标识缓冲区分配的外部数组的位置来打印出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
...
获取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
..
objtypes
命令对于内存问题也很有用。 它显示了V8堆对象类型在内存中的计数和大小。
完整命令参考
这是IDDE的完整命令参考。 此处以斜体显示的命令可用于任何核心转储。 所有其他特定于Node.js转储:
- 在指定地址显示固定数组的元素
- 在内存中查找字符串
- 显示有关单个JavaScript堆栈框架的详细信息
- 显示命令列表
- 以十六进制和ASCII格式输出一部分内存
- 查找具有指定属性JavaScript对象
-
列出共享相同
Map
JavaScript对象组 - 列出指定V8对象类型的堆对象
- 显示有关JavaScript内存使用情况的信息,包括五个最大的对象
- 显示JavaScript对象的详细信息
-
打印与提供的对象共享相同
Map
JavaScript对象 - 在堆上搜索给定的字符串
- 在内存中搜索给定的字符串
- 显示节点信息的概述,包括版本
- 列出所有V8对象类型
- 显示按V8实例类型分类的堆使用情况
- 显示指定堆对象的C ++层次结构
- 打印可用内存范围列表
- 显示给定线程JavaScript堆栈跟踪
- 在指定地址显示字符串
- 列出所有线程
array
find
, findall
, findnext
frame
help
hexdump
jsfindbyproperty
jsgroupobjects
jslistobjects
jsmeminfo
jsobject
jsobjectsmatching
jsstringsearch
locate
nodeoverview
objtype
objtypes
print
ranges
stack
string
threads
结论
在本教程中,您学习了如何生成Node.js核心转储,如何在IDDE中打开转储,在IDDE编辑器中输入并运行命令,以及获取可用于转储的所有命令的列表。 而且,您已经了解了哪些IDDE命令可以最好地帮助您找到程序崩溃和内存泄漏的根本原因。
如果您对使用IDDE有任何疑问,或者想报告该工具的错误或问题,请发送电子邮件至javatool@uk.ibm.com 。
调试核心转储文件