diff options
author | Anna Henningsen <anna@addaleax.net> | 2018-07-01 02:27:09 +0300 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2018-07-15 21:35:41 +0300 |
commit | 45ad8df318390b54ea6cb54b4b4b320875f9c88f (patch) | |
tree | df48dc0d6595cf726fe604a1133284ad79aad931 /lib/internal/test | |
parent | 266a7e62585b975f2ea2d25473b21395f5ca5a3f (diff) |
src: make heap snapshot & embedder graph accessible for tests
Add methods that allow inspection of heap snapshots and a JS
version of our own embedder graph.
These can be used in tests and might also prove useful for
ad-hoc debugging. Usage requires `--expose-internals` and
prints a warning similar to our other modules whose primary
purpose is test support.
PR-URL: https://github.com/nodejs/node/pull/21741
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Diffstat (limited to 'lib/internal/test')
-rw-r--r-- | lib/internal/test/heap.js | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/lib/internal/test/heap.js b/lib/internal/test/heap.js new file mode 100644 index 00000000000..a9260f651b9 --- /dev/null +++ b/lib/internal/test/heap.js @@ -0,0 +1,87 @@ +'use strict'; + +process.emitWarning( + 'These APIs are exposed only for testing and are not ' + + 'tracked by any versioning system or deprecation process.', + 'internal/test/heap'); + +const { internalBinding } = require('internal/bootstrap/loaders'); +const { createHeapDump, buildEmbedderGraph } = internalBinding('heap_utils'); +const assert = require('assert'); + +// This is not suitable for production code. It creates a full V8 heap dump, +// parses it as JSON, and then creates complex objects from it, leading +// to significantly increased memory usage. +function createJSHeapDump() { + const dump = createHeapDump(); + const meta = dump.snapshot.meta; + + const nodes = + readHeapInfo(dump.nodes, meta.node_fields, meta.node_types, dump.strings); + const edges = + readHeapInfo(dump.edges, meta.edge_fields, meta.edge_types, dump.strings); + + for (const node of nodes) { + node.incomingEdges = []; + node.outgoingEdges = []; + } + + let fromNodeIndex = 0; + let edgeIndex = 0; + for (const { type, name_or_index, to_node } of edges) { + while (edgeIndex === nodes[fromNodeIndex].edge_count) { + edgeIndex = 0; + fromNodeIndex++; + } + const toNode = nodes[to_node / meta.node_fields.length]; + const fromNode = nodes[fromNodeIndex]; + const edge = { + type, + toNode, + fromNode, + name: typeof name_or_index === 'string' ? name_or_index : null + }; + toNode.incomingEdges.push(edge); + fromNode.outgoingEdges.push(edge); + edgeIndex++; + } + + for (const node of nodes) + assert.strictEqual(node.edge_count, node.outgoingEdges.length); + + return nodes; +} + +function readHeapInfo(raw, fields, types, strings) { + const items = []; + + for (var i = 0; i < raw.length; i += fields.length) { + const item = {}; + for (var j = 0; j < fields.length; j++) { + const name = fields[j]; + let type = types[j]; + if (Array.isArray(type)) { + item[name] = type[raw[i + j]]; + } else if (name === 'name_or_index') { // type === 'string_or_number' + if (item.type === 'element' || item.type === 'hidden') + type = 'number'; + else + type = 'string'; + } + + if (type === 'string') { + item[name] = strings[raw[i + j]]; + } else if (type === 'number' || type === 'node') { + item[name] = raw[i + j]; + } + } + items.push(item); + } + + return items; +} + +module.exports = { + createJSHeapDump, + buildEmbedderGraph +}; |