Skip to content

Commit fe0852f

Browse files
committed
doc, test: doc API design, add tests
1 parent 3e0aa22 commit fe0852f

File tree

2 files changed

+209
-0
lines changed

2 files changed

+209
-0
lines changed

JSAPI.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
```js
2+
class LLNode {
3+
/**
4+
* @param {string} dump path to the coredump
5+
* @param {string} executable path to the node executable
6+
* @returns {LLNode} an LLNode instance
7+
*/
8+
static fromCoreDump(dump, executable) {}
9+
10+
/**
11+
* @returns {string} SBProcess information
12+
*/
13+
getProcessInfo() {}
14+
15+
/**
16+
* @typedef {object} Frame
17+
* @property {string} function
18+
*
19+
* @typedef {object} Thread
20+
* @property {number} threadId
21+
* @property {number} frameCount
22+
* @property {Frame[]} frames
23+
*
24+
* @typedef {object} Process
25+
* @property {number} pid
26+
* @property {string} state
27+
* @property {number} threadCount
28+
* @property {Thread[]} threads
29+
*
30+
* @returns {Process} Process data
31+
*/
32+
getProcessobject() {}
33+
34+
/**
35+
* @typedef {object} HeapInstance
36+
* @property {string} address
37+
* @property {string} value
38+
*
39+
* @typedef {object} HeapType
40+
* @property {string} typeName
41+
* @property {string} instanceCount
42+
* @property {string} totalSize
43+
* @property {Iterator<HeapInstance>} instances
44+
*
45+
* @returns {HeapType[]}
46+
*/
47+
getHeapTypes() {}
48+
49+
/**
50+
* TODO: rematerialize object
51+
* @returns {HeapInstance}
52+
*/
53+
getobjectAtAddress(address) {}
54+
}
55+
```

test/jsapi-test.js

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
'use strict';
2+
3+
const fromCoredump = require('../').fromCoredump;
4+
5+
const debug = process.env.TEST_LLNODE_DEBUG ?
6+
console.log.bind(console) : () => { };
7+
8+
const common = require('./common');
9+
const tape = require('tape');
10+
11+
tape('llnode API', (t) => {
12+
t.timeoutAfter(common.saveCoreTimeout);
13+
14+
// Use prepared core and executable to test
15+
if (process.env.LLNODE_CORE && process.env.LLNODE_NODE_EXE) {
16+
test(process.env.LLNODE_NODE_EXE, process.env.LLNODE_CORE, t);
17+
t.end();
18+
} else if (process.platform === 'linux') {
19+
t.skip('No `process save-core` on linux');
20+
t.end();
21+
} else {
22+
common.saveCore({
23+
scenario: 'inspect-scenario.js'
24+
}, (err) => {
25+
t.error(err);
26+
t.ok(true, 'Saved core');
27+
28+
test(process.execPath, common.core, t);
29+
});
30+
}
31+
});
32+
33+
function test(executable, core, t) {
34+
debug('============= Loading ==============');
35+
// Equivalent to lldb executable -c core
36+
debug(`Loading core dump: ${core}, executable: ${executable}`);
37+
const llnode = fromCoredump(core, executable);
38+
39+
verifySBProcess(llnode, t);
40+
const typeMap = verifyBasicTypes(llnode, t);
41+
const processType = verifyProcessType(typeMap, llnode, t);
42+
verifyProcessInstances(processType, llnode, t);
43+
}
44+
45+
function verifySBProcess(llnode, t) {
46+
const processInfo = llnode.getProcessInfo();
47+
debug('Process info', processInfo);
48+
const procRe = new RegExp(
49+
'SBProcess: pid = (\\d+), state = (\\w+), ' +
50+
'threads = (\\d+), executable = .+');
51+
const procMatch = processInfo.match(procRe);
52+
t.ok(procMatch, 'SBProcess info should be formatted correctly');
53+
54+
const procObj = llnode.getProcessObject();
55+
debug('Process object', procObj);
56+
t.equal(procObj.pid, parseInt(procMatch[1]), 'SBProcess pid');
57+
t.equal(procObj.state, procMatch[2], 'SBProcess state');
58+
t.equal(procObj.threadCount, parseInt(procMatch[3]),
59+
'SBProcess thread count');
60+
t.ok(procObj.threadCount > 0,
61+
'SBProcess should have more than one thread');
62+
t.ok(Array.isArray(procObj.threads),
63+
'processObject.threads should be an array');
64+
t.equal(procObj.threads.length,
65+
procObj.threadCount,
66+
'processObject.threads should contain all the threads');
67+
68+
let i = 0;
69+
for (const thread of procObj.threads) {
70+
debug(`Thread ${i}:`, thread);
71+
t.equal(thread.threadId, i++, 'thread.threadId');
72+
t.ok(Array.isArray(thread.frames),
73+
'thread.frames should be an array');
74+
t.equal(thread.frameCount, thread.frames.length,
75+
'thread.frames should contain all the frames');
76+
77+
for (const frame of thread.frames) {
78+
debug(` #`, frame);
79+
t.ok(typeof frame.function === 'string',
80+
'frame.function should be a string');
81+
}
82+
}
83+
}
84+
85+
function verifyBasicTypes(llnode, t) {
86+
debug('============= Heap Types ==============');
87+
const heapTypes = llnode.getHeapTypes();
88+
// debug('Heap types', heapTypes);
89+
const basicTypes = [
90+
// basic JS types
91+
'(Array)', '(String)', 'Object', '(Object)', '(ArrayBufferView)',
92+
// Node types
93+
'process', 'NativeModule', 'console', 'TickObject'
94+
].sort();
95+
96+
const typeMap = new Map();
97+
for (const item of heapTypes) {
98+
if (basicTypes.indexOf(item.typeName) !== -1) {
99+
typeMap.set(item.typeName, item);
100+
}
101+
}
102+
103+
const foundTypes = Array.from(typeMap.keys()).sort();
104+
t.deepEqual(foundTypes, basicTypes,
105+
'The heap should contain all the basic types');
106+
107+
return typeMap;
108+
}
109+
110+
function verifyProcessType(typeMap, llnode, t) {
111+
const processType = typeMap.get('process');
112+
113+
t.equal(processType.typeName, 'process',
114+
'The typeName of process type should be "process"')
115+
t.ok(processType.instanceCount > 0,
116+
'There should be more than one process instances');
117+
t.ok(processType.totalSize > 0,
118+
'The process objects should have a non-zero size');
119+
return processType;
120+
}
121+
122+
function verifyProcessInstances(processType, llnode, t) {
123+
// TODO: should be implemented as an iterator
124+
let foundProcess = false;
125+
126+
const propRe = [
127+
/.pid=<Smi: \d+>/,
128+
/.platform=0x[0-9a-z]+:<String: ".+">/,
129+
/.arch=0x[0-9a-z]+:<String: ".+">/,
130+
/.version=0x[0-9a-z]+:<String: ".+">/,
131+
/.versions=0x[0-9a-z]+:<Object: Object>/,
132+
/.release=0x[0-9a-z]+:<Object: Object>/,
133+
/.execPath=0x[0-9a-z]+:<String: ".+">/,
134+
/.execArgv=0x[0-9a-z]+:<Array: length=\d+>/,
135+
/.argv=0x[0-9a-z]+:<Array: length=\d+>/
136+
];
137+
138+
const visited = new Map();
139+
140+
for (const instance of processType.instances) {
141+
t.ok(!visited.get(instance.address),
142+
'should not duplicate instances');
143+
visited.set(instance.address, instance.value);
144+
t.deepEqual(
145+
instance,
146+
llnode.getObjectAtAddress(instance.address),
147+
'instance should be the same as obtained from address')
148+
149+
if (propRe.every((re) => re.test(instance.value))) {
150+
foundProcess = true;
151+
}
152+
}
153+
t.ok(foundProcess, 'should find the process object');
154+
}

0 commit comments

Comments
 (0)