Skip to content

Instantly share code, notes, and snippets.

@bmeck
Created June 24, 2021 14:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bmeck/7c57ed54b8ee17b7b8d20d0c13fae7ad to your computer and use it in GitHub Desktop.
Save bmeck/7c57ed54b8ee17b7b8d20d0c13fae7ad to your computer and use it in GitHub Desktop.
in-progress in-thread heapsnapshot reflective API
import * as ns from './index.js';
import {promisify} from 'util';
import {createReadStream, createWriteStream} from 'fs';
import {Session} from 'inspector';
import {PassThrough} from 'stream';
import {createHash} from 'crypto';
const session = new Session();
const stream = new PassThrough();
const post = promisify(session.post.bind(session));
session.connect();
session.on('HeapProfiler.addHeapSnapshotChunk', (m) => {
stream.write(m.params.chunk);
});
session.on('Debugger.scriptParsed', (m) => {
if (m.params.url === import.meta.url) {
// console.log(m.params)
session.post('Debugger.getScriptSource', {
scriptId: m.params.scriptId
}, (e, src) => {
if (e) return;
if (src) {
let results = {};
for (const algo of ['md5','sha1','sha256','sha384','sha512']) {
results[algo] = createHash(algo)
.update(src.scriptSource)
.digest('hex');
}
// console.log(results);
}
})
}
})
await post('Debugger.enable', {});
const filename = 'tmp.heapsnapshot';
const file = createWriteStream(filename);
stream.pipe(file);
class EASY_TO_TEST {};
async function main() {
let leak = new EASY_TO_TEST();
await post('HeapProfiler.takeHeapSnapshot')
let gcd = new EASY_TO_TEST;
let heapId = (await getHeapId(gcd)).heapSnapshotObjectId;
console.log(
'gcd-pre', await getObjectFromHeapId(heapId)
);
// console.log(gcd.toString());
gcd = null;
// leak = null;
console.log(
'gcd-nulled', await getObjectFromHeapId(heapId)
);
stream.end();
await new Promise((f, r) => {
file.on('close', f);
file.on('error', r);
});
// keep leak alive until here
setTimeout(() => console.log(leak.toString()), 10e3);
const snapshot = new ns.HeapSnapshot(
await ns.SplitSnapshotProvider.fromStream(
createReadStream(filename)
)
);
const iter = snapshot.walk({
onNodeOpen(node) {
const {fields} = node;
if (fields.name === EASY_TO_TEST.name) {
if (fields.type === 'object') {
console.log(node.fields);
}
}
},
});
// perform the walk
for (const _ of iter) {
}
snapshot.provider.writeToDirectory(
'tmp'
);
}
main();
async function getHeapId(value) {
await post('Debugger.enable');
await post('HeapProfiler.enable');
let result;
session.once('Debugger.paused', ({params: {callFrames}}) => {
result = post('Debugger.evaluateOnCallFrame', {
callFrameId: callFrames[0].callFrameId,
expression: 'value'
});
});
debugger;
const {result: {objectId}} = await result;
return post('HeapProfiler.getHeapObjectId', {
objectId
});
}
async function getObjectFromHeapId(id) {
await post('HeapProfiler.enable');
let result = await post('HeapProfiler.getObjectByHeapObjectId', {
objectId: id,
objectGroup: 'getObjectFromHeapId'
});
let value;
session.once('Debugger.paused', ({params: {callFrames}}) => {
const objectId = result.result.objectId;
result = post('Debugger.setVariableValue', {
callFrameId: callFrames[0].callFrameId,
variableName: 'value',
scopeNumber: 0,
newValue: {objectId}
});
});
debugger;
return value;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment