diff --git a/lib/internal/test/heap.js b/lib/internal/test/heap.js deleted file mode 100644 index 6565e8fbd8ef13..00000000000000 --- a/lib/internal/test/heap.js +++ /dev/null @@ -1,89 +0,0 @@ -'use strict'; - -process.emitWarning( - 'These APIs are for internal testing only. Do not use them.', - 'internal/test/heap'); - -const { - createHeapSnapshot, - buildEmbedderGraph -} = internalBinding('heap_utils'); -const assert = require('internal/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 createJSHeapSnapshot() { - const dump = createHeapSnapshot(); - 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, - to: toNode, - from: 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(node.edge_count === node.outgoingEdges.length, - `${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 = { - createJSHeapSnapshot, - buildEmbedderGraph -}; diff --git a/node.gyp b/node.gyp index 1c7484378fbf5c..12038dcba4b7bd 100644 --- a/node.gyp +++ b/node.gyp @@ -178,7 +178,6 @@ 'lib/internal/repl/utils.js', 'lib/internal/socket_list.js', 'lib/internal/test/binding.js', - 'lib/internal/test/heap.js', 'lib/internal/timers.js', 'lib/internal/tls.js', 'lib/internal/trace_events_async_hooks.js', diff --git a/src/heap_utils.cc b/src/heap_utils.cc index c54f0c41d7b09c..6690abc78d6c67 100644 --- a/src/heap_utils.cc +++ b/src/heap_utils.cc @@ -200,40 +200,6 @@ void BuildEmbedderGraph(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(ret); } - -class BufferOutputStream : public v8::OutputStream { - public: - BufferOutputStream() : buffer_(new JSString()) {} - - void EndOfStream() override {} - int GetChunkSize() override { return 1024 * 1024; } - WriteResult WriteAsciiChunk(char* data, int size) override { - buffer_->Append(data, size); - return kContinue; - } - - Local ToString(Isolate* isolate) { - return String::NewExternalOneByte(isolate, - buffer_.release()).ToLocalChecked(); - } - - private: - class JSString : public String::ExternalOneByteStringResource { - public: - void Append(char* data, size_t count) { - store_.append(data, count); - } - - const char* data() const override { return store_.data(); } - size_t length() const override { return store_.size(); } - - private: - std::string store_; - }; - - std::unique_ptr buffer_; -}; - namespace { class FileOutputStream : public v8::OutputStream { public: @@ -370,17 +336,6 @@ inline bool WriteSnapshot(Isolate* isolate, const char* filename) { } // namespace -void CreateHeapSnapshot(const FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - BufferOutputStream out; - TakeSnapshot(isolate, &out); - Local ret; - if (JSON::Parse(isolate->GetCurrentContext(), - out.ToString(isolate)).ToLocal(&ret)) { - args.GetReturnValue().Set(ret); - } -} - void CreateHeapSnapshotStream(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); HandleScope scope(env->isolate()); @@ -430,9 +385,6 @@ void Initialize(Local target, env->SetMethodNoSideEffect(target, "buildEmbedderGraph", BuildEmbedderGraph); - env->SetMethodNoSideEffect(target, - "createHeapSnapshot", - CreateHeapSnapshot); env->SetMethodNoSideEffect(target, "triggerHeapSnapshot", TriggerHeapSnapshot); diff --git a/test/common/heap.js b/test/common/heap.js index 8bda1284bd7e57..97686e05a7fccf 100644 --- a/test/common/heap.js +++ b/test/common/heap.js @@ -3,14 +3,88 @@ const assert = require('assert'); const util = require('util'); -let internalTestHeap; +let internalBinding; try { - internalTestHeap = require('internal/test/heap'); + internalBinding = require('internal/test/binding').internalBinding; } catch (e) { console.log('using `test/common/heap.js` requires `--expose-internals`'); throw e; } -const { createJSHeapSnapshot, buildEmbedderGraph } = internalTestHeap; + +const { buildEmbedderGraph } = internalBinding('heap_utils'); +const { getHeapSnapshot } = require('v8'); + +function createJSHeapSnapshot() { + const stream = getHeapSnapshot(); + stream.pause(); + const dump = JSON.parse(stream.read()); + 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, + to: toNode, + from: 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, + `${node.edge_count} !== ${node.outgoingEdges.length}`); + } + return nodes; +} + +function readHeapInfo(raw, fields, types, strings) { + const items = []; + + for (let i = 0; i < raw.length; i += fields.length) { + const item = {}; + for (let 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; +} function inspectNode(snapshot) { return util.inspect(snapshot, { depth: 4 });