src: replace heap_utils.createHeapSnapshot with v8.getHeapSnapshot
Remove the internal testing utility and use the public API instead. PR-URL: https://github.com/nodejs/node/pull/26671 Reviewed-By: Richard Lau <riclau@uk.ibm.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Minwoo Jung <minwoo@nodesource.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
parent
6d090124fd
commit
ebcd50298e
@ -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
|
|
||||||
};
|
|
1
node.gyp
1
node.gyp
@ -178,7 +178,6 @@
|
|||||||
'lib/internal/repl/utils.js',
|
'lib/internal/repl/utils.js',
|
||||||
'lib/internal/socket_list.js',
|
'lib/internal/socket_list.js',
|
||||||
'lib/internal/test/binding.js',
|
'lib/internal/test/binding.js',
|
||||||
'lib/internal/test/heap.js',
|
|
||||||
'lib/internal/timers.js',
|
'lib/internal/timers.js',
|
||||||
'lib/internal/tls.js',
|
'lib/internal/tls.js',
|
||||||
'lib/internal/trace_events_async_hooks.js',
|
'lib/internal/trace_events_async_hooks.js',
|
||||||
|
@ -200,40 +200,6 @@ void BuildEmbedderGraph(const FunctionCallbackInfo<Value>& args) {
|
|||||||
args.GetReturnValue().Set(ret);
|
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<String> 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<JSString> buffer_;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class FileOutputStream : public v8::OutputStream {
|
class FileOutputStream : public v8::OutputStream {
|
||||||
public:
|
public:
|
||||||
@ -370,17 +336,6 @@ inline bool WriteSnapshot(Isolate* isolate, const char* filename) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void CreateHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Isolate* isolate = args.GetIsolate();
|
|
||||||
BufferOutputStream out;
|
|
||||||
TakeSnapshot(isolate, &out);
|
|
||||||
Local<Value> ret;
|
|
||||||
if (JSON::Parse(isolate->GetCurrentContext(),
|
|
||||||
out.ToString(isolate)).ToLocal(&ret)) {
|
|
||||||
args.GetReturnValue().Set(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateHeapSnapshotStream(const FunctionCallbackInfo<Value>& args) {
|
void CreateHeapSnapshotStream(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
HandleScope scope(env->isolate());
|
HandleScope scope(env->isolate());
|
||||||
@ -430,9 +385,6 @@ void Initialize(Local<Object> target,
|
|||||||
env->SetMethodNoSideEffect(target,
|
env->SetMethodNoSideEffect(target,
|
||||||
"buildEmbedderGraph",
|
"buildEmbedderGraph",
|
||||||
BuildEmbedderGraph);
|
BuildEmbedderGraph);
|
||||||
env->SetMethodNoSideEffect(target,
|
|
||||||
"createHeapSnapshot",
|
|
||||||
CreateHeapSnapshot);
|
|
||||||
env->SetMethodNoSideEffect(target,
|
env->SetMethodNoSideEffect(target,
|
||||||
"triggerHeapSnapshot",
|
"triggerHeapSnapshot",
|
||||||
TriggerHeapSnapshot);
|
TriggerHeapSnapshot);
|
||||||
|
@ -3,14 +3,88 @@
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
|
|
||||||
let internalTestHeap;
|
let internalBinding;
|
||||||
try {
|
try {
|
||||||
internalTestHeap = require('internal/test/heap');
|
internalBinding = require('internal/test/binding').internalBinding;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('using `test/common/heap.js` requires `--expose-internals`');
|
console.log('using `test/common/heap.js` requires `--expose-internals`');
|
||||||
throw e;
|
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) {
|
function inspectNode(snapshot) {
|
||||||
return util.inspect(snapshot, { depth: 4 });
|
return util.inspect(snapshot, { depth: 4 });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user