mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
97 lines
2.8 KiB
JavaScript
97 lines
2.8 KiB
JavaScript
// The live heap by V8 node type, read from a heap snapshot as it streams in:
|
|
// only the nodes array is parsed, number by number, so a snapshot of a large
|
|
// page is never held whole. The long soak asserts on the heap without the
|
|
// `code` nodes (compiled code grows with V8's tier-ups over a long run, and
|
|
// the browser drops it under memory pressure).
|
|
|
|
/**
|
|
* A parser fed with a heap snapshot's JSON text in chunks. `result()` gives
|
|
* the self size summed per node type name.
|
|
*/
|
|
export function createHeapSnapshotSizes() {
|
|
let head = '';
|
|
let fields = null;
|
|
let typeOffset = -1;
|
|
let sizeOffset = -1;
|
|
let typeNames = null;
|
|
let inNodes = false;
|
|
let done = false;
|
|
let number = '';
|
|
let field = 0;
|
|
let type = 0;
|
|
const sizes = new Map();
|
|
|
|
function take(value) {
|
|
if (field === typeOffset) type = value;
|
|
if (field === sizeOffset) {
|
|
const name = typeNames[type] ?? String(type);
|
|
sizes.set(name, (sizes.get(name) ?? 0) + value);
|
|
}
|
|
field = (field + 1) % fields.length;
|
|
}
|
|
|
|
function nodes(text, from) {
|
|
for (let i = from; i < text.length; i++) {
|
|
const c = text.charCodeAt(i);
|
|
if (c >= 48 && c <= 57) number += text[i];
|
|
else {
|
|
if (number) {
|
|
take(Number(number));
|
|
number = '';
|
|
}
|
|
if (c === 93) {
|
|
done = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
push(chunk) {
|
|
if (done) return;
|
|
if (inNodes) {
|
|
nodes(chunk, 0);
|
|
return;
|
|
}
|
|
head += chunk;
|
|
const start = head.indexOf('"nodes":[');
|
|
if (start < 0) return;
|
|
const meta = JSON.parse(
|
|
head.slice(head.indexOf('"meta":') + 7, head.indexOf(',"node_count"')),
|
|
);
|
|
fields = meta.node_fields;
|
|
typeOffset = fields.indexOf('type');
|
|
sizeOffset = fields.indexOf('self_size');
|
|
[typeNames] = meta.node_types;
|
|
inNodes = true;
|
|
const rest = head.slice(start + '"nodes":['.length);
|
|
head = '';
|
|
nodes(rest, 0);
|
|
},
|
|
result() {
|
|
return Object.fromEntries(sizes);
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Take a heap snapshot over a CDP session and size it by node type. */
|
|
export async function heapSizesByType(cdp) {
|
|
const sizes = createHeapSnapshotSizes();
|
|
const onChunk = ({ chunk }) => sizes.push(chunk);
|
|
cdp.on('HeapProfiler.addHeapSnapshotChunk', onChunk);
|
|
try {
|
|
await cdp.send('HeapProfiler.takeHeapSnapshot', {
|
|
reportProgress: false,
|
|
captureNumericValue: false,
|
|
});
|
|
} finally {
|
|
cdp.off('HeapProfiler.addHeapSnapshotChunk', onChunk);
|
|
// A snapshot leaves the profiler tracking object ids, which slows the
|
|
// page for the rest of the run (Navara → Cesium switches about 2.5 times
|
|
// slower in the long soak); it stops here.
|
|
await cdp.send('HeapProfiler.disable').catch(() => {});
|
|
}
|
|
return sizes.result();
|
|
}
|