mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
lib: add options to the heap snapshot APIs
Support configuration of the HeapSnapshotMode and NumericsMode fields inf HeapSnapshotOptions in the JS APIs for heap snapshots. PR-URL: https://github.com/nodejs/node/pull/44989 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
parent
59938e3180
commit
b872d30d19
@ -61,13 +61,23 @@ following properties:
|
||||
}
|
||||
```
|
||||
|
||||
## `v8.getHeapSnapshot()`
|
||||
## `v8.getHeapSnapshot([options])`
|
||||
|
||||
<!-- YAML
|
||||
added: v11.13.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44989
|
||||
description: Support options to configure the heap snapshot.
|
||||
-->
|
||||
|
||||
* Returns: {stream.Readable} A Readable Stream containing the V8 heap snapshot
|
||||
* `options` {Object}
|
||||
* `exposeInternals` {boolean} If true, expose internals in the heap snapshot.
|
||||
**Default:** `false`.
|
||||
* `exposeNumericValues` {boolean} If true, expose numeric values in
|
||||
artificial fields. **Default:** `false`.
|
||||
|
||||
* Returns: {stream.Readable} A Readable containing the V8 heap snapshot.
|
||||
|
||||
Generates a snapshot of the current V8 heap and returns a Readable
|
||||
Stream that may be used to read the JSON serialized representation.
|
||||
@ -289,11 +299,14 @@ by [`NODE_V8_COVERAGE`][].
|
||||
When the process is about to exit, one last coverage will still be written to
|
||||
disk unless [`v8.stopCoverage()`][] is invoked before the process exits.
|
||||
|
||||
## `v8.writeHeapSnapshot([filename])`
|
||||
## `v8.writeHeapSnapshot([filename[,options]])`
|
||||
|
||||
<!-- YAML
|
||||
added: v11.13.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44989
|
||||
description: Support options to configure the heap snapshot.
|
||||
- version: v18.0.0
|
||||
pr-url: https://github.com/nodejs/node/pull/41373
|
||||
description: An exception will now be thrown if the file could not be written.
|
||||
@ -308,6 +321,11 @@ changes:
|
||||
generated, where `{pid}` will be the PID of the Node.js process,
|
||||
`{thread_id}` will be `0` when `writeHeapSnapshot()` is called from
|
||||
the main Node.js thread or the id of a worker thread.
|
||||
* `options` {Object}
|
||||
* `exposeInternals` {boolean} If true, expose internals in the heap snapshot.
|
||||
**Default:** `false`.
|
||||
* `exposeNumericValues` {boolean} If true, expose numeric values in
|
||||
artificial fields. **Default:** `false`.
|
||||
* Returns: {string} The filename where the snapshot was saved.
|
||||
|
||||
Generates a snapshot of the current V8 heap and writes it to a JSON
|
||||
|
@ -1067,14 +1067,23 @@ added: v10.5.0
|
||||
The `'online'` event is emitted when the worker thread has started executing
|
||||
JavaScript code.
|
||||
|
||||
### `worker.getHeapSnapshot()`
|
||||
### `worker.getHeapSnapshot([options])`
|
||||
|
||||
<!-- YAML
|
||||
added:
|
||||
- v13.9.0
|
||||
- v12.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44989
|
||||
description: Support options to configure the heap snapshot.
|
||||
-->
|
||||
|
||||
* `options` {Object}
|
||||
* `exposeInternals` {boolean} If true, expose internals in the heap snapshot.
|
||||
**Default:** `false`.
|
||||
* `exposeNumericValues` {boolean} If true, expose numeric values in
|
||||
artificial fields. **Default:** `false`.
|
||||
* Returns: {Promise} A promise for a Readable Stream containing
|
||||
a V8 heap snapshot
|
||||
|
||||
@ -1379,7 +1388,7 @@ thread spawned will spawn another until the application crashes.
|
||||
[`require('node:worker_threads').threadId`]: #workerthreadid
|
||||
[`require('node:worker_threads').workerData`]: #workerworkerdata
|
||||
[`trace_events`]: tracing.md
|
||||
[`v8.getHeapSnapshot()`]: v8.md#v8getheapsnapshot
|
||||
[`v8.getHeapSnapshot()`]: v8.md#v8getheapsnapshotoptions
|
||||
[`vm`]: vm.md
|
||||
[`worker.SHARE_ENV`]: #workershare_env
|
||||
[`worker.on('message')`]: #event-message_1
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
const {
|
||||
Symbol
|
||||
Symbol,
|
||||
Uint8Array,
|
||||
} = primordials;
|
||||
const {
|
||||
kUpdateTimer,
|
||||
@ -8,9 +9,22 @@ const {
|
||||
} = require('internal/stream_base_commons');
|
||||
const { owner_symbol } = require('internal/async_hooks').symbols;
|
||||
const { Readable } = require('stream');
|
||||
const { validateObject, validateBoolean } = require('internal/validators');
|
||||
const { kEmptyObject } = require('internal/util');
|
||||
|
||||
const kHandle = Symbol('kHandle');
|
||||
|
||||
function getHeapSnapshotOptions(options = kEmptyObject) {
|
||||
validateObject(options, 'options');
|
||||
const {
|
||||
exposeInternals = false,
|
||||
exposeNumericValues = false,
|
||||
} = options;
|
||||
validateBoolean(exposeInternals, 'options.exposeInternals');
|
||||
validateBoolean(exposeNumericValues, 'options.exposeNumericValues');
|
||||
return new Uint8Array([+exposeInternals, +exposeNumericValues]);
|
||||
}
|
||||
|
||||
class HeapSnapshotStream extends Readable {
|
||||
constructor(handle) {
|
||||
super({ autoDestroy: true });
|
||||
@ -37,5 +51,6 @@ class HeapSnapshotStream extends Readable {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
HeapSnapshotStream
|
||||
getHeapSnapshotOptions,
|
||||
HeapSnapshotStream,
|
||||
};
|
||||
|
@ -416,12 +416,16 @@ class Worker extends EventEmitter {
|
||||
return makeResourceLimits(this[kHandle].getResourceLimits());
|
||||
}
|
||||
|
||||
getHeapSnapshot() {
|
||||
const heapSnapshotTaker = this[kHandle] && this[kHandle].takeHeapSnapshot();
|
||||
getHeapSnapshot(options) {
|
||||
const {
|
||||
HeapSnapshotStream,
|
||||
getHeapSnapshotOptions
|
||||
} = require('internal/heap_utils');
|
||||
const optionsArray = getHeapSnapshotOptions(options);
|
||||
const heapSnapshotTaker = this[kHandle]?.takeHeapSnapshot(optionsArray);
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!heapSnapshotTaker) return reject(new ERR_WORKER_NOT_RUNNING());
|
||||
heapSnapshotTaker.ondone = (handle) => {
|
||||
const { HeapSnapshotStream } = require('internal/heap_utils');
|
||||
resolve(new HeapSnapshotStream(handle));
|
||||
};
|
||||
});
|
||||
|
23
lib/v8.js
23
lib/v8.js
@ -57,7 +57,10 @@ const {
|
||||
createHeapSnapshotStream,
|
||||
triggerHeapSnapshot
|
||||
} = internalBinding('heap_utils');
|
||||
const { HeapSnapshotStream } = require('internal/heap_utils');
|
||||
const {
|
||||
HeapSnapshotStream,
|
||||
getHeapSnapshotOptions
|
||||
} = require('internal/heap_utils');
|
||||
const promiseHooks = require('internal/promise_hooks');
|
||||
const { getOptionValue } = require('internal/options');
|
||||
|
||||
@ -65,23 +68,33 @@ const { getOptionValue } = require('internal/options');
|
||||
* Generates a snapshot of the current V8 heap
|
||||
* and writes it to a JSON file.
|
||||
* @param {string} [filename]
|
||||
* @param {{
|
||||
* exposeInternals?: boolean,
|
||||
* exposeNumericValues?: boolean
|
||||
* }} [options]
|
||||
* @returns {string}
|
||||
*/
|
||||
function writeHeapSnapshot(filename) {
|
||||
function writeHeapSnapshot(filename, options) {
|
||||
if (filename !== undefined) {
|
||||
filename = getValidatedPath(filename);
|
||||
filename = toNamespacedPath(filename);
|
||||
}
|
||||
return triggerHeapSnapshot(filename);
|
||||
const optionArray = getHeapSnapshotOptions(options);
|
||||
return triggerHeapSnapshot(filename, optionArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a snapshot of the current V8 heap
|
||||
* and returns a Readable Stream.
|
||||
* @param {{
|
||||
* exposeInternals?: boolean,
|
||||
* exposeNumericValues?: boolean
|
||||
* }} [options]
|
||||
* @returns {import('./stream.js').Readable}
|
||||
*/
|
||||
function getHeapSnapshot() {
|
||||
const handle = createHeapSnapshotStream();
|
||||
function getHeapSnapshot(options) {
|
||||
const optionArray = getHeapSnapshotOptions(options);
|
||||
const handle = createHeapSnapshotStream(optionArray);
|
||||
assert(handle);
|
||||
return new HeapSnapshotStream(handle);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ using v8::EscapableHandleScope;
|
||||
using v8::Function;
|
||||
using v8::FunctionTemplate;
|
||||
using v8::HandleScope;
|
||||
using v8::HeapProfiler;
|
||||
using v8::HeapSpaceStatistics;
|
||||
using v8::Integer;
|
||||
using v8::Isolate;
|
||||
@ -1790,7 +1791,10 @@ size_t Environment::NearHeapLimitCallback(void* data,
|
||||
|
||||
Debug(env, DebugCategory::DIAGNOSTICS, "Start generating %s...\n", *name);
|
||||
|
||||
heap::WriteSnapshot(env, filename.c_str());
|
||||
HeapProfiler::HeapSnapshotOptions options;
|
||||
options.numerics_mode = HeapProfiler::NumericsMode::kExposeNumericValues;
|
||||
options.snapshot_mode = HeapProfiler::HeapSnapshotMode::kExposeInternals;
|
||||
heap::WriteSnapshot(env, filename.c_str(), options);
|
||||
env->heap_limit_snapshot_taken_ += 1;
|
||||
|
||||
Debug(env,
|
||||
|
@ -25,6 +25,7 @@ using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
using v8::Global;
|
||||
using v8::HandleScope;
|
||||
using v8::HeapProfiler;
|
||||
using v8::HeapSnapshot;
|
||||
using v8::Isolate;
|
||||
using v8::JustVoid;
|
||||
@ -36,6 +37,7 @@ using v8::Number;
|
||||
using v8::Object;
|
||||
using v8::ObjectTemplate;
|
||||
using v8::String;
|
||||
using v8::Uint8Array;
|
||||
using v8::Value;
|
||||
|
||||
namespace node {
|
||||
@ -340,15 +342,19 @@ class HeapSnapshotStream : public AsyncWrap,
|
||||
HeapSnapshotPointer snapshot_;
|
||||
};
|
||||
|
||||
inline void TakeSnapshot(Environment* env, v8::OutputStream* out) {
|
||||
HeapSnapshotPointer snapshot {
|
||||
env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
|
||||
inline void TakeSnapshot(Environment* env,
|
||||
v8::OutputStream* out,
|
||||
HeapProfiler::HeapSnapshotOptions options) {
|
||||
HeapSnapshotPointer snapshot{
|
||||
env->isolate()->GetHeapProfiler()->TakeHeapSnapshot(options)};
|
||||
snapshot->Serialize(out, HeapSnapshot::kJSON);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Maybe<void> WriteSnapshot(Environment* env, const char* filename) {
|
||||
Maybe<void> WriteSnapshot(Environment* env,
|
||||
const char* filename,
|
||||
HeapProfiler::HeapSnapshotOptions options) {
|
||||
uv_fs_t req;
|
||||
int err;
|
||||
|
||||
@ -365,7 +371,7 @@ Maybe<void> WriteSnapshot(Environment* env, const char* filename) {
|
||||
}
|
||||
|
||||
FileOutputStream stream(fd, &req);
|
||||
TakeSnapshot(env, &stream);
|
||||
TakeSnapshot(env, &stream, options);
|
||||
if ((err = stream.status()) < 0) {
|
||||
env->ThrowUVException(err, "write", nullptr, filename);
|
||||
return Nothing<void>();
|
||||
@ -410,10 +416,28 @@ BaseObjectPtr<AsyncWrap> CreateHeapSnapshotStream(
|
||||
return MakeBaseObject<HeapSnapshotStream>(env, std::move(snapshot), obj);
|
||||
}
|
||||
|
||||
HeapProfiler::HeapSnapshotOptions GetHeapSnapshotOptions(
|
||||
Local<Value> options_value) {
|
||||
CHECK(options_value->IsUint8Array());
|
||||
Local<Uint8Array> arr = options_value.As<Uint8Array>();
|
||||
uint8_t* options =
|
||||
static_cast<uint8_t*>(arr->Buffer()->Data()) + arr->ByteOffset();
|
||||
HeapProfiler::HeapSnapshotOptions result;
|
||||
result.snapshot_mode = options[0]
|
||||
? HeapProfiler::HeapSnapshotMode::kExposeInternals
|
||||
: HeapProfiler::HeapSnapshotMode::kRegular;
|
||||
result.numerics_mode = options[1]
|
||||
? HeapProfiler::NumericsMode::kExposeNumericValues
|
||||
: HeapProfiler::NumericsMode::kHideNumericValues;
|
||||
return result;
|
||||
}
|
||||
|
||||
void CreateHeapSnapshotStream(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
HeapSnapshotPointer snapshot {
|
||||
env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
auto options = GetHeapSnapshotOptions(args[0]);
|
||||
HeapSnapshotPointer snapshot{
|
||||
env->isolate()->GetHeapProfiler()->TakeHeapSnapshot(options)};
|
||||
CHECK(snapshot);
|
||||
BaseObjectPtr<AsyncWrap> stream =
|
||||
CreateHeapSnapshotStream(env, std::move(snapshot));
|
||||
@ -424,13 +448,13 @@ void CreateHeapSnapshotStream(const FunctionCallbackInfo<Value>& args) {
|
||||
void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
|
||||
CHECK_EQ(args.Length(), 2);
|
||||
Local<Value> filename_v = args[0];
|
||||
auto options = GetHeapSnapshotOptions(args[1]);
|
||||
|
||||
if (filename_v->IsUndefined()) {
|
||||
DiagnosticFilename name(env, "Heap", "heapsnapshot");
|
||||
if (WriteSnapshot(env, *name).IsNothing())
|
||||
return;
|
||||
if (WriteSnapshot(env, *name, options).IsNothing()) return;
|
||||
if (String::NewFromUtf8(isolate, *name).ToLocal(&filename_v)) {
|
||||
args.GetReturnValue().Set(filename_v);
|
||||
}
|
||||
@ -439,8 +463,7 @@ void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
BufferValue path(isolate, filename_v);
|
||||
CHECK_NOT_NULL(*path);
|
||||
if (WriteSnapshot(env, *path).IsNothing())
|
||||
return;
|
||||
if (WriteSnapshot(env, *path, options).IsNothing()) return;
|
||||
return args.GetReturnValue().Set(filename_v);
|
||||
}
|
||||
|
||||
|
@ -382,7 +382,9 @@ class DiagnosticFilename {
|
||||
};
|
||||
|
||||
namespace heap {
|
||||
v8::Maybe<void> WriteSnapshot(Environment* env, const char* filename);
|
||||
v8::Maybe<void> WriteSnapshot(Environment* env,
|
||||
const char* filename,
|
||||
v8::HeapProfiler::HeapSnapshotOptions options);
|
||||
}
|
||||
|
||||
namespace heap {
|
||||
@ -423,6 +425,12 @@ std::ostream& operator<<(std::ostream& output,
|
||||
}
|
||||
|
||||
bool linux_at_secure();
|
||||
|
||||
namespace heap {
|
||||
v8::HeapProfiler::HeapSnapshotOptions GetHeapSnapshotOptions(
|
||||
v8::Local<v8::Value> options);
|
||||
} // namespace heap
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
@ -778,6 +778,8 @@ class WorkerHeapSnapshotTaker : public AsyncWrap {
|
||||
void Worker::TakeHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
|
||||
Worker* w;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
auto options = heap::GetHeapSnapshotOptions(args[0]);
|
||||
|
||||
Debug(w, "Worker %llu taking heap snapshot", w->thread_id_.id);
|
||||
|
||||
@ -797,10 +799,10 @@ void Worker::TakeHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
// Interrupt the worker thread and take a snapshot, then schedule a call
|
||||
// on the parent thread that turns that snapshot into a readable stream.
|
||||
bool scheduled = w->RequestInterrupt([taker = std::move(taker),
|
||||
env](Environment* worker_env) mutable {
|
||||
bool scheduled = w->RequestInterrupt([taker = std::move(taker), env, options](
|
||||
Environment* worker_env) mutable {
|
||||
heap::HeapSnapshotPointer snapshot{
|
||||
worker_env->isolate()->GetHeapProfiler()->TakeHeapSnapshot()};
|
||||
worker_env->isolate()->GetHeapProfiler()->TakeHeapSnapshot(options)};
|
||||
CHECK(snapshot);
|
||||
|
||||
// Here, the worker thread temporarily owns the WorkerHeapSnapshotTaker
|
||||
|
@ -211,7 +211,39 @@ function validateSnapshotNodes(...args) {
|
||||
return recordState().validateSnapshotNodes(...args);
|
||||
}
|
||||
|
||||
function getHeapSnapshotOptionTests() {
|
||||
const fixtures = require('../common/fixtures');
|
||||
const cases = [
|
||||
{
|
||||
options: { exposeInternals: true },
|
||||
expected: [{
|
||||
children: [
|
||||
// We don't have anything special to test here yet
|
||||
// because we don't use cppgc or embedder heap tracer.
|
||||
{ edge_name: 'nonNumeric', node_name: 'test' },
|
||||
]
|
||||
}]
|
||||
},
|
||||
{
|
||||
options: { exposeNumericValues: true },
|
||||
expected: [{
|
||||
children: [
|
||||
{ edge_name: 'numeric', node_name: 'smi number' },
|
||||
]
|
||||
}]
|
||||
},
|
||||
];
|
||||
return {
|
||||
fixtures: fixtures.path('klass-with-fields.js'),
|
||||
check(snapshot, expected) {
|
||||
snapshot.validateSnapshot('Klass', expected, { loose: true });
|
||||
},
|
||||
cases,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
recordState,
|
||||
validateSnapshotNodes
|
||||
validateSnapshotNodes,
|
||||
getHeapSnapshotOptionTests
|
||||
};
|
||||
|
18
test/fixtures/klass-with-fields.js
vendored
Normal file
18
test/fixtures/klass-with-fields.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
parentPort,
|
||||
isMainThread
|
||||
} = require('node:worker_threads');
|
||||
|
||||
class Klass {
|
||||
numeric = 1234;
|
||||
nonNumeric = 'test';
|
||||
}
|
||||
|
||||
globalThis.obj = new Klass();
|
||||
|
||||
if (!isMainThread) {
|
||||
parentPort.postMessage('ready');
|
||||
setInterval(() => {}, 100);
|
||||
}
|
@ -13,3 +13,18 @@ const { once } = require('events');
|
||||
code: 'ERR_WORKER_NOT_RUNNING'
|
||||
});
|
||||
})().then(common.mustCall());
|
||||
|
||||
(async function() {
|
||||
const worker = new Worker('setInterval(() => {}, 1000);', { eval: true });
|
||||
await once(worker, 'online');
|
||||
|
||||
[1, true, [], null, Infinity, NaN].forEach((i) => {
|
||||
assert.throws(() => worker.getHeapSnapshot(i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options" argument must be of type object.' +
|
||||
common.invalidArgTypeHelper(i)
|
||||
});
|
||||
});
|
||||
await worker.terminate();
|
||||
})().then(common.mustCall());
|
||||
|
39
test/sequential/test-get-heapsnapshot-options.js
Normal file
39
test/sequential/test-get-heapsnapshot-options.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
// Flags: --expose-internals
|
||||
|
||||
require('../common');
|
||||
|
||||
const { getHeapSnapshotOptionTests, recordState } = require('../common/heap');
|
||||
|
||||
const tests = getHeapSnapshotOptionTests();
|
||||
if (process.argv[2] === 'child') {
|
||||
const { getHeapSnapshot } = require('v8');
|
||||
require(tests.fixtures);
|
||||
const { options, expected } = tests.cases[parseInt(process.argv[3])];
|
||||
const snapshot = recordState(getHeapSnapshot(options));
|
||||
console.log('Snapshot nodes', snapshot.snapshot.length);
|
||||
console.log('Searching for', expected[0].children);
|
||||
tests.check(snapshot, expected);
|
||||
delete globalThis.obj; // To pass the leaked global tests.
|
||||
return;
|
||||
}
|
||||
|
||||
const { spawnSync } = require('child_process');
|
||||
const assert = require('assert');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
for (let i = 0; i < tests.cases.length; ++i) {
|
||||
const child = spawnSync(
|
||||
process.execPath,
|
||||
['--expose-internals', __filename, 'child', i + ''],
|
||||
{
|
||||
cwd: tmpdir.path
|
||||
});
|
||||
const stderr = child.stderr.toString();
|
||||
const stdout = child.stdout.toString();
|
||||
console.log('[STDERR]', stderr);
|
||||
console.log('[STDOUT]', stdout);
|
||||
assert.strictEqual(child.status, 0);
|
||||
}
|
@ -47,6 +47,24 @@ process.chdir(tmpdir.path);
|
||||
});
|
||||
});
|
||||
|
||||
[1, true, [], null, Infinity, NaN].forEach((i) => {
|
||||
assert.throws(() => writeHeapSnapshot('test.heapsnapshot', i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options" argument must be of type object.' +
|
||||
common.invalidArgTypeHelper(i)
|
||||
});
|
||||
});
|
||||
|
||||
[1, true, [], null, Infinity, NaN].forEach((i) => {
|
||||
assert.throws(() => getHeapSnapshot(i), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options" argument must be of type object.' +
|
||||
common.invalidArgTypeHelper(i)
|
||||
});
|
||||
});
|
||||
|
||||
{
|
||||
let data = '';
|
||||
const snapshot = getHeapSnapshot();
|
||||
|
21
test/sequential/test-worker-heapsnapshot-options.js
Normal file
21
test/sequential/test-worker-heapsnapshot-options.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const { recordState, getHeapSnapshotOptionTests } = require('../common/heap');
|
||||
const { Worker } = require('worker_threads');
|
||||
const { once } = require('events');
|
||||
|
||||
(async function() {
|
||||
const tests = getHeapSnapshotOptionTests();
|
||||
const w = new Worker(tests.fixtures);
|
||||
|
||||
await once(w, 'message');
|
||||
|
||||
for (const { options, expected } of tests.cases) {
|
||||
const stream = await w.getHeapSnapshot(options);
|
||||
const snapshot = recordState(stream);
|
||||
tests.check(snapshot, expected);
|
||||
}
|
||||
|
||||
await w.terminate();
|
||||
})().then(common.mustCall());
|
50
test/sequential/test-write-heapsnapshot-options.js
Normal file
50
test/sequential/test-write-heapsnapshot-options.js
Normal file
@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
// Flags: --expose-internals
|
||||
|
||||
require('../common');
|
||||
|
||||
const fs = require('fs');
|
||||
const { getHeapSnapshotOptionTests, recordState } = require('../common/heap');
|
||||
|
||||
class ReadStream {
|
||||
constructor(filename) {
|
||||
this._content = fs.readFileSync(filename, 'utf-8');
|
||||
}
|
||||
pause() {}
|
||||
read() { return this._content; }
|
||||
}
|
||||
|
||||
const tests = getHeapSnapshotOptionTests();
|
||||
if (process.argv[2] === 'child') {
|
||||
const { writeHeapSnapshot } = require('v8');
|
||||
require(tests.fixtures);
|
||||
const { options, expected } = tests.cases[parseInt(process.argv[3])];
|
||||
const filename = writeHeapSnapshot(undefined, options);
|
||||
const snapshot = recordState(new ReadStream(filename));
|
||||
console.log('Snapshot nodes', snapshot.snapshot.length);
|
||||
console.log('Searching for', expected[0].children);
|
||||
tests.check(snapshot, expected);
|
||||
delete globalThis.obj; // To pass the leaked global tests.
|
||||
return;
|
||||
}
|
||||
|
||||
const { spawnSync } = require('child_process');
|
||||
const assert = require('assert');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
tmpdir.refresh();
|
||||
|
||||
// Start child processes to prevent the heap from growing too big.
|
||||
for (let i = 0; i < tests.cases.length; ++i) {
|
||||
const child = spawnSync(
|
||||
process.execPath,
|
||||
['--expose-internals', __filename, 'child', i + ''],
|
||||
{
|
||||
cwd: tmpdir.path
|
||||
});
|
||||
const stderr = child.stderr.toString();
|
||||
const stdout = child.stdout.toString();
|
||||
console.log('[STDERR]', stderr);
|
||||
console.log('[STDOUT]', stdout);
|
||||
assert.strictEqual(child.status, 0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user