CLI for inspecting the heap of a node application
npm install https://github.com/seth4618/nodeheap
The program undertest needs to run a tcpserver which you get from:
var heapserver = require('nodeheap/lib/heapserver'); // include basic code
heapserver(port); // start heapserver on port 'port'
For example, in my code I define a runtime argument profileMemOn and profilePort and do the following:
if (profileMemOn) {
HeapServer = (/** @type {function (number, boolean=):?} */require('nodeheap/lib/heapserver'));
HeapServer(profilePort);
}
Then to see what is going on run the command:
nodeheap --port <port>
on the command line will connect to the program undertest. The following commands are available:
-
login: gets things started
-
quit: will exit
-
help: print a set of commands
-
base: Will set the base profile to the last profile used. Or, if none has been used will create a profile.
-
comp: Will compare the current heap profile with whatever was set as the base profile.
-
info <type> <name>: show information on all objects of type 'type' with name 'name'. A '*' in either will include everything. I.e., info Object * will show all objects, while info Object Foo will only show those objects with the name Foo.
-
chain <id>: will show the retainer chain for object with id, <id>.
-
value <id> <depth> <hidden>: will show the value of object with id <id> to a depth of <depth>. <depth> is optional, if not supplied will do <depth> = 2. If 'hidden' is specified will also include hidden members.
-
inspect <id> <path>: will show the value of the object <id>.<path>
You can test things out by running nodeheap with the --test switch, specify a port and nodeheap will run on itself. For example, here is a sample session:
> nodeheap --test 8877
Welcome to the heap probe utility.
You are testing nodeheap on itself!
Will probe application running on localhost:8877
use "login" command when you are ready to connect to the application
#
# enter the 'login' command to connect to the application
#
notlogged in> login
#
# enter the 'base' command to get a snapshot you can refer to
#
> base
#
# ask for info on all things that are of type 'Object' with name 'HeapConnection'
#
> info Object HeapConnection
{ data:
[ { name: 'HeapConnection',
type: 'Object',
size: 24,
#
# the id listed here can be used to get the actual value of this object
#
id: 241,
retainers: [Object] },
{ name: 'HeapConnection',
type: 'Object',
size: 104,
id: 25891,
retainers: [Object] } ],
command: 'info' }
#
# show the value of the last of the objects shown above to depth 1, no hidden fields
#
> value 25891 1
showing value of 25891:HeapConnection:Object:104 ret:17
Value of Object:HeapConnection size:104 retainers:17
String: [object Object]
Value:
{ server:
{ name: 'test',
port: 8877,
verbose: false,
connClass: [Object],
_events: [Object],
server: [Object] },
socket:
{ _handle: [Object],
_pendingWriteReqs: 0,
_flags: 0,
_connectQueueSize: 0,
destroyed: false,
errorEmitted: false,
bytesRead: 80,
bytesWritten: 805,
allowHalfOpen: false,
writable: true,
readable: true,
server: [Object],
_events: [Object] },
buffer: '',
verbose: false,
_events: { data: [Function], malformed: [Function], connect: [Function] },
base:
{ type: 'Full',
root: [Object],
nodesCount: 15697,
uid: 1,
title: 'org.nodejs.profiles.heap.user-initiated.1',
getNodeById: [Function: getNodeById],
getNode: [Function: getNode],
delete: [Function: delete],
serialize: [Function: serialize] } }
-------------
#
# inspect the 'base' field of the above object
#
> inspect 25891 base
showing value of 25891:HeapConnection:Object:104 ret:17
Value of Object:HeapConnection size:104 retainers:17
String: [object Object]
Partial path upto: base
Value:
{ type: 'Full',
root:
{ size: 0,
name: '',
id: 1,
ptr: 1965359232,
dominatorNode:
{ size: 0,
name: '',
id: 1,
ptr: 1965359232,
dominatorNode: [Object],
type: 'Object',
retainersCount: 0,
childrenCount: 2,
getChild: [Function: getChild],
retainedSize: [Function: retainedSize],
getRetainer: [Function: getRetainer],
getHeapValue: [Function: getHeapValue],
getHeapValueSafe: [Function: getHeapValueSafe] },
type: 'Object',
retainersCount: 0,
childrenCount: 2,
getChild: [Function: getChild],
retainedSize: [Function: retainedSize],
getRetainer: [Function: getRetainer],
getHeapValue: [Function: getHeapValue],
getHeapValueSafe: [Function: getHeapValueSafe] },
nodesCount: 15697,
uid: 1,
title: 'org.nodejs.profiles.heap.user-initiated.1',
getNodeById: [Function: getNodeById],
getNode: [Function: getNode],
delete: [Function: delete],
serialize: [Function: serialize] }-------------
#
# inspect the 'object.base.title'
#
> inspect 25891 base.title
showing value of 25891:HeapConnection:Object:104 ret:17
Value of Object:HeapConnection size:104 retainers:17
String: [object Object]
Partial path upto: base.title
Value:
'org.nodejs.profiles.heap.user-initiated.1'-------------
#
# compare the heap now to the snapshot set when we executed the 'base' command
# each line shows the change in the number of type/name objects in the heap. That is,
# There are 68 more Array's with the name '(map descriptor content)' and
# 1 fewer object of type 'Code' with the name: 'symToFamily'.
#
> comp
{ data:
[ { type: 'Array', name: '(map descriptor content)', val: 68 },
{ type: 'Hidden', name: 'system / Map', val: 62 },
{ type: 'Array', name: '(map descriptors)', val: 53 },
{ type: 'Hidden', name: 'system / Foreign', val: 43 },
{ type: 'Array', name: '', val: 26 },
{ type: 'Object', name: 'Object', val: 19 },
{ type: 'Array', name: '(code deopt data)', val: 17 },
{ type: 'Hidden', name: 'system / AccessorInfo', val: 17 },
{ type: 'Hidden',
name: 'system / FunctionTemplateInfo',
val: 12 },
{ type: 'Array', name: '(object elements)', val: 9 },
{ type: 'Hidden', name: 'system / CallHandlerInfo', val: 9 },
{ type: 'Object', name: 'Array', val: 7 },
{ type: 'HeapNumber', name: 'number', val: 5 },
{ type: 'Hidden', name: 'system / ObjectTemplateInfo', val: 3 },
{ type: 'RegExp', name: '\'', val: 1 },
{ type: 'Object', name: 'getRetainer', val: 1 },
{ type: 'Closure', name: '', val: 1 },
{ type: 'Hidden', name: 'system / JSGlobalPropertyCell', val: 1 },
{ type: 'Object', name: 'getHeapValueSafe', val: 1 },
{ type: 'Code', name: 'retainedSize', val: 1 },
{ type: 'RegExp', name: '\\\\\\\'', val: 1 },
{ type: 'RegExp', name: '^"([a-zA-Z_][a-zA-Z_0-9]*)"$', val: 1 },
{ type: 'Closure', name: 'getChild', val: 1 },
{ type: 'RegExp', name: 'all', val: 1 },
{ type: 'Object', name: 'delete', val: 1 },
{ type: 'Closure', name: 'getNode', val: 1 },
{ type: 'Closure', name: 'getNodeById', val: 1 },
{ type: 'Object', name: 'getNodeById', val: 1 },
{ type: 'Object', name: 'getHeapValue', val: 1 },
{ type: 'Code', name: 'getChild', val: 1 },
{ type: 'Object', name: 'getChild', val: 1 },
{ type: 'Closure', name: 'retainedSize', val: 1 },
{ type: 'Object', name: 'getNode', val: 1 },
{ type: 'Closure', name: 'getHeapValue', val: 1 },
{ type: 'RegExp', name: '^\\d+$', val: 1 },
{ type: 'Code', name: 'getHeapValue', val: 1 },
{ type: 'Code', name: 'getNodeById', val: 1 },
{ type: 'Closure', name: 'delete', val: 1 },
{ type: 'Code', name: 'serialize', val: 1 },
{ type: 'RegExp', name: '\\\\n', val: 1 },
{ type: 'Closure', name: 'getRetainer', val: 1 },
{ type: 'Code', name: 'getRetainer', val: 1 },
{ type: 'RegExp', name: 'string', val: 1 },
{ type: 'RegExp', name: '^"|"$', val: 1 },
{ type: 'Code', name: 'getHeapValueSafe', val: 1 },
{ type: 'Code', name: 'delete', val: 1 },
{ type: 'RegExp', name: '[0-9]+', val: 1 },
{ type: 'Object', name: 'serialize', val: 1 },
{ type: 'Code', name: 'getNode', val: 1 },
{ type: 'Object', name: 'retainedSize', val: 1 },
{ type: 'Closure', name: 'serialize', val: 1 },
{ type: 'RegExp', name: '\\\\"', val: 1 },
{ type: 'Closure', name: 'getHeapValueSafe', val: 1 },
{ type: 'Code', name: 'familyToSym', val: -1 },
{ type: 'Code', name: 'realpathSync', val: -1 },
{ type: 'Code', name: 'trim', val: -1 },
{ type: 'Code', name: '$assert', val: -1 },
{ type: 'Object', name: 'Timer', val: -1 },
{ type: 'Code', name: 'symToFamily', val: -1 },
{ type: 'Object', name: 'Date', val: -1 },
{ type: 'Array', name: '(object properties)', val: -2 },
{ type: 'Code', name: 'f', val: -2 },
{ type: 'Array', name: '(code relocation info)', val: -26 },
{ type: 'Array', name: '(function scope info)', val: -29 },
{ type: '', name: 'String', val: -60 },
{ type: 'Code', name: '', val: -81 } ],
command: 'comp' }
#
# all done. use the 'help' command for a list of commands.
#
> quit