Skip to content

Commit 0478e40

Browse files
joyeecheungRafaelGSS
authored andcommittedNov 10, 2022
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: #44989 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent ba45373 commit 0478e40

16 files changed

+322
-33
lines changed
 

‎doc/api/v8.md

+21-3
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,23 @@ following properties:
6161
}
6262
```
6363

64-
## `v8.getHeapSnapshot()`
64+
## `v8.getHeapSnapshot([options])`
6565

6666
<!-- YAML
6767
added: v11.13.0
68+
changes:
69+
- version: REPLACEME
70+
pr-url: https://github.com/nodejs/node/pull/44989
71+
description: Support options to configure the heap snapshot.
6872
-->
6973

70-
* Returns: {stream.Readable} A Readable Stream containing the V8 heap snapshot
74+
* `options` {Object}
75+
* `exposeInternals` {boolean} If true, expose internals in the heap snapshot.
76+
**Default:** `false`.
77+
* `exposeNumericValues` {boolean} If true, expose numeric values in
78+
artificial fields. **Default:** `false`.
79+
80+
* Returns: {stream.Readable} A Readable containing the V8 heap snapshot.
7181

7282
Generates a snapshot of the current V8 heap and returns a Readable
7383
Stream that may be used to read the JSON serialized representation.
@@ -289,11 +299,14 @@ by [`NODE_V8_COVERAGE`][].
289299
When the process is about to exit, one last coverage will still be written to
290300
disk unless [`v8.stopCoverage()`][] is invoked before the process exits.
291301

292-
## `v8.writeHeapSnapshot([filename])`
302+
## `v8.writeHeapSnapshot([filename[,options]])`
293303

294304
<!-- YAML
295305
added: v11.13.0
296306
changes:
307+
- version: REPLACEME
308+
pr-url: https://github.com/nodejs/node/pull/44989
309+
description: Support options to configure the heap snapshot.
297310
- version: v18.0.0
298311
pr-url: https://github.com/nodejs/node/pull/41373
299312
description: An exception will now be thrown if the file could not be written.
@@ -308,6 +321,11 @@ changes:
308321
generated, where `{pid}` will be the PID of the Node.js process,
309322
`{thread_id}` will be `0` when `writeHeapSnapshot()` is called from
310323
the main Node.js thread or the id of a worker thread.
324+
* `options` {Object}
325+
* `exposeInternals` {boolean} If true, expose internals in the heap snapshot.
326+
**Default:** `false`.
327+
* `exposeNumericValues` {boolean} If true, expose numeric values in
328+
artificial fields. **Default:** `false`.
311329
* Returns: {string} The filename where the snapshot was saved.
312330

313331
Generates a snapshot of the current V8 heap and writes it to a JSON

‎doc/api/worker_threads.md

+11-2
Original file line numberDiff line numberDiff line change
@@ -1067,14 +1067,23 @@ added: v10.5.0
10671067
The `'online'` event is emitted when the worker thread has started executing
10681068
JavaScript code.
10691069

1070-
### `worker.getHeapSnapshot()`
1070+
### `worker.getHeapSnapshot([options])`
10711071

10721072
<!-- YAML
10731073
added:
10741074
- v13.9.0
10751075
- v12.17.0
1076+
changes:
1077+
- version: REPLACEME
1078+
pr-url: https://github.com/nodejs/node/pull/44989
1079+
description: Support options to configure the heap snapshot.
10761080
-->
10771081

1082+
* `options` {Object}
1083+
* `exposeInternals` {boolean} If true, expose internals in the heap snapshot.
1084+
**Default:** `false`.
1085+
* `exposeNumericValues` {boolean} If true, expose numeric values in
1086+
artificial fields. **Default:** `false`.
10781087
* Returns: {Promise} A promise for a Readable Stream containing
10791088
a V8 heap snapshot
10801089

@@ -1379,7 +1388,7 @@ thread spawned will spawn another until the application crashes.
13791388
[`require('node:worker_threads').threadId`]: #workerthreadid
13801389
[`require('node:worker_threads').workerData`]: #workerworkerdata
13811390
[`trace_events`]: tracing.md
1382-
[`v8.getHeapSnapshot()`]: v8.md#v8getheapsnapshot
1391+
[`v8.getHeapSnapshot()`]: v8.md#v8getheapsnapshotoptions
13831392
[`vm`]: vm.md
13841393
[`worker.SHARE_ENV`]: #workershare_env
13851394
[`worker.on('message')`]: #event-message_1

‎lib/internal/heap_utils.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
'use strict';
22
const {
3-
Symbol
3+
Symbol,
4+
Uint8Array,
45
} = primordials;
56
const {
67
kUpdateTimer,
78
onStreamRead,
89
} = require('internal/stream_base_commons');
910
const { owner_symbol } = require('internal/async_hooks').symbols;
1011
const { Readable } = require('stream');
12+
const { validateObject, validateBoolean } = require('internal/validators');
13+
const { kEmptyObject } = require('internal/util');
1114

1215
const kHandle = Symbol('kHandle');
1316

17+
function getHeapSnapshotOptions(options = kEmptyObject) {
18+
validateObject(options, 'options');
19+
const {
20+
exposeInternals = false,
21+
exposeNumericValues = false,
22+
} = options;
23+
validateBoolean(exposeInternals, 'options.exposeInternals');
24+
validateBoolean(exposeNumericValues, 'options.exposeNumericValues');
25+
return new Uint8Array([+exposeInternals, +exposeNumericValues]);
26+
}
27+
1428
class HeapSnapshotStream extends Readable {
1529
constructor(handle) {
1630
super({ autoDestroy: true });
@@ -37,5 +51,6 @@ class HeapSnapshotStream extends Readable {
3751
}
3852

3953
module.exports = {
40-
HeapSnapshotStream
54+
getHeapSnapshotOptions,
55+
HeapSnapshotStream,
4156
};

‎lib/internal/worker.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,16 @@ class Worker extends EventEmitter {
416416
return makeResourceLimits(this[kHandle].getResourceLimits());
417417
}
418418

419-
getHeapSnapshot() {
420-
const heapSnapshotTaker = this[kHandle] && this[kHandle].takeHeapSnapshot();
419+
getHeapSnapshot(options) {
420+
const {
421+
HeapSnapshotStream,
422+
getHeapSnapshotOptions
423+
} = require('internal/heap_utils');
424+
const optionsArray = getHeapSnapshotOptions(options);
425+
const heapSnapshotTaker = this[kHandle]?.takeHeapSnapshot(optionsArray);
421426
return new Promise((resolve, reject) => {
422427
if (!heapSnapshotTaker) return reject(new ERR_WORKER_NOT_RUNNING());
423428
heapSnapshotTaker.ondone = (handle) => {
424-
const { HeapSnapshotStream } = require('internal/heap_utils');
425429
resolve(new HeapSnapshotStream(handle));
426430
};
427431
});

‎lib/v8.js

+18-5
Original file line numberDiff line numberDiff line change
@@ -57,31 +57,44 @@ const {
5757
createHeapSnapshotStream,
5858
triggerHeapSnapshot
5959
} = internalBinding('heap_utils');
60-
const { HeapSnapshotStream } = require('internal/heap_utils');
60+
const {
61+
HeapSnapshotStream,
62+
getHeapSnapshotOptions
63+
} = require('internal/heap_utils');
6164
const promiseHooks = require('internal/promise_hooks');
6265
const { getOptionValue } = require('internal/options');
6366

6467
/**
6568
* Generates a snapshot of the current V8 heap
6669
* and writes it to a JSON file.
6770
* @param {string} [filename]
71+
* @param {{
72+
* exposeInternals?: boolean,
73+
* exposeNumericValues?: boolean
74+
* }} [options]
6875
* @returns {string}
6976
*/
70-
function writeHeapSnapshot(filename) {
77+
function writeHeapSnapshot(filename, options) {
7178
if (filename !== undefined) {
7279
filename = getValidatedPath(filename);
7380
filename = toNamespacedPath(filename);
7481
}
75-
return triggerHeapSnapshot(filename);
82+
const optionArray = getHeapSnapshotOptions(options);
83+
return triggerHeapSnapshot(filename, optionArray);
7684
}
7785

7886
/**
7987
* Generates a snapshot of the current V8 heap
8088
* and returns a Readable Stream.
89+
* @param {{
90+
* exposeInternals?: boolean,
91+
* exposeNumericValues?: boolean
92+
* }} [options]
8193
* @returns {import('./stream.js').Readable}
8294
*/
83-
function getHeapSnapshot() {
84-
const handle = createHeapSnapshotStream();
95+
function getHeapSnapshot(options) {
96+
const optionArray = getHeapSnapshotOptions(options);
97+
const handle = createHeapSnapshotStream(optionArray);
8598
assert(handle);
8699
return new HeapSnapshotStream(handle);
87100
}

‎src/env.cc

+5-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ using v8::EscapableHandleScope;
3939
using v8::Function;
4040
using v8::FunctionTemplate;
4141
using v8::HandleScope;
42+
using v8::HeapProfiler;
4243
using v8::HeapSpaceStatistics;
4344
using v8::Integer;
4445
using v8::Isolate;
@@ -1790,7 +1791,10 @@ size_t Environment::NearHeapLimitCallback(void* data,
17901791

17911792
Debug(env, DebugCategory::DIAGNOSTICS, "Start generating %s...\n", *name);
17921793

1793-
heap::WriteSnapshot(env, filename.c_str());
1794+
HeapProfiler::HeapSnapshotOptions options;
1795+
options.numerics_mode = HeapProfiler::NumericsMode::kExposeNumericValues;
1796+
options.snapshot_mode = HeapProfiler::HeapSnapshotMode::kExposeInternals;
1797+
heap::WriteSnapshot(env, filename.c_str(), options);
17941798
env->heap_limit_snapshot_taken_ += 1;
17951799

17961800
Debug(env,

‎src/heap_utils.cc

+35-12
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ using v8::FunctionCallbackInfo;
2525
using v8::FunctionTemplate;
2626
using v8::Global;
2727
using v8::HandleScope;
28+
using v8::HeapProfiler;
2829
using v8::HeapSnapshot;
2930
using v8::Isolate;
3031
using v8::JustVoid;
@@ -36,6 +37,7 @@ using v8::Number;
3637
using v8::Object;
3738
using v8::ObjectTemplate;
3839
using v8::String;
40+
using v8::Uint8Array;
3941
using v8::Value;
4042

4143
namespace node {
@@ -340,15 +342,19 @@ class HeapSnapshotStream : public AsyncWrap,
340342
HeapSnapshotPointer snapshot_;
341343
};
342344

343-
inline void TakeSnapshot(Environment* env, v8::OutputStream* out) {
344-
HeapSnapshotPointer snapshot {
345-
env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
345+
inline void TakeSnapshot(Environment* env,
346+
v8::OutputStream* out,
347+
HeapProfiler::HeapSnapshotOptions options) {
348+
HeapSnapshotPointer snapshot{
349+
env->isolate()->GetHeapProfiler()->TakeHeapSnapshot(options)};
346350
snapshot->Serialize(out, HeapSnapshot::kJSON);
347351
}
348352

349353
} // namespace
350354

351-
Maybe<void> WriteSnapshot(Environment* env, const char* filename) {
355+
Maybe<void> WriteSnapshot(Environment* env,
356+
const char* filename,
357+
HeapProfiler::HeapSnapshotOptions options) {
352358
uv_fs_t req;
353359
int err;
354360

@@ -365,7 +371,7 @@ Maybe<void> WriteSnapshot(Environment* env, const char* filename) {
365371
}
366372

367373
FileOutputStream stream(fd, &req);
368-
TakeSnapshot(env, &stream);
374+
TakeSnapshot(env, &stream, options);
369375
if ((err = stream.status()) < 0) {
370376
env->ThrowUVException(err, "write", nullptr, filename);
371377
return Nothing<void>();
@@ -410,10 +416,28 @@ BaseObjectPtr<AsyncWrap> CreateHeapSnapshotStream(
410416
return MakeBaseObject<HeapSnapshotStream>(env, std::move(snapshot), obj);
411417
}
412418

419+
HeapProfiler::HeapSnapshotOptions GetHeapSnapshotOptions(
420+
Local<Value> options_value) {
421+
CHECK(options_value->IsUint8Array());
422+
Local<Uint8Array> arr = options_value.As<Uint8Array>();
423+
uint8_t* options =
424+
static_cast<uint8_t*>(arr->Buffer()->Data()) + arr->ByteOffset();
425+
HeapProfiler::HeapSnapshotOptions result;
426+
result.snapshot_mode = options[0]
427+
? HeapProfiler::HeapSnapshotMode::kExposeInternals
428+
: HeapProfiler::HeapSnapshotMode::kRegular;
429+
result.numerics_mode = options[1]
430+
? HeapProfiler::NumericsMode::kExposeNumericValues
431+
: HeapProfiler::NumericsMode::kHideNumericValues;
432+
return result;
433+
}
434+
413435
void CreateHeapSnapshotStream(const FunctionCallbackInfo<Value>& args) {
414436
Environment* env = Environment::GetCurrent(args);
415-
HeapSnapshotPointer snapshot {
416-
env->isolate()->GetHeapProfiler()->TakeHeapSnapshot() };
437+
CHECK_EQ(args.Length(), 1);
438+
auto options = GetHeapSnapshotOptions(args[0]);
439+
HeapSnapshotPointer snapshot{
440+
env->isolate()->GetHeapProfiler()->TakeHeapSnapshot(options)};
417441
CHECK(snapshot);
418442
BaseObjectPtr<AsyncWrap> stream =
419443
CreateHeapSnapshotStream(env, std::move(snapshot));
@@ -424,13 +448,13 @@ void CreateHeapSnapshotStream(const FunctionCallbackInfo<Value>& args) {
424448
void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
425449
Environment* env = Environment::GetCurrent(args);
426450
Isolate* isolate = args.GetIsolate();
427-
451+
CHECK_EQ(args.Length(), 2);
428452
Local<Value> filename_v = args[0];
453+
auto options = GetHeapSnapshotOptions(args[1]);
429454

430455
if (filename_v->IsUndefined()) {
431456
DiagnosticFilename name(env, "Heap", "heapsnapshot");
432-
if (WriteSnapshot(env, *name).IsNothing())
433-
return;
457+
if (WriteSnapshot(env, *name, options).IsNothing()) return;
434458
if (String::NewFromUtf8(isolate, *name).ToLocal(&filename_v)) {
435459
args.GetReturnValue().Set(filename_v);
436460
}
@@ -439,8 +463,7 @@ void TriggerHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
439463

440464
BufferValue path(isolate, filename_v);
441465
CHECK_NOT_NULL(*path);
442-
if (WriteSnapshot(env, *path).IsNothing())
443-
return;
466+
if (WriteSnapshot(env, *path, options).IsNothing()) return;
444467
return args.GetReturnValue().Set(filename_v);
445468
}
446469

‎src/node_internals.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,9 @@ class DiagnosticFilename {
382382
};
383383

384384
namespace heap {
385-
v8::Maybe<void> WriteSnapshot(Environment* env, const char* filename);
385+
v8::Maybe<void> WriteSnapshot(Environment* env,
386+
const char* filename,
387+
v8::HeapProfiler::HeapSnapshotOptions options);
386388
}
387389

388390
namespace heap {
@@ -423,6 +425,12 @@ std::ostream& operator<<(std::ostream& output,
423425
}
424426

425427
bool linux_at_secure();
428+
429+
namespace heap {
430+
v8::HeapProfiler::HeapSnapshotOptions GetHeapSnapshotOptions(
431+
v8::Local<v8::Value> options);
432+
} // namespace heap
433+
426434
} // namespace node
427435

428436
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

‎src/node_worker.cc

+5-3
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,8 @@ class WorkerHeapSnapshotTaker : public AsyncWrap {
778778
void Worker::TakeHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
779779
Worker* w;
780780
ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
781+
CHECK_EQ(args.Length(), 1);
782+
auto options = heap::GetHeapSnapshotOptions(args[0]);
781783

782784
Debug(w, "Worker %llu taking heap snapshot", w->thread_id_.id);
783785

@@ -797,10 +799,10 @@ void Worker::TakeHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
797799

798800
// Interrupt the worker thread and take a snapshot, then schedule a call
799801
// on the parent thread that turns that snapshot into a readable stream.
800-
bool scheduled = w->RequestInterrupt([taker = std::move(taker),
801-
env](Environment* worker_env) mutable {
802+
bool scheduled = w->RequestInterrupt([taker = std::move(taker), env, options](
803+
Environment* worker_env) mutable {
802804
heap::HeapSnapshotPointer snapshot{
803-
worker_env->isolate()->GetHeapProfiler()->TakeHeapSnapshot()};
805+
worker_env->isolate()->GetHeapProfiler()->TakeHeapSnapshot(options)};
804806
CHECK(snapshot);
805807

806808
// Here, the worker thread temporarily owns the WorkerHeapSnapshotTaker

‎test/common/heap.js

+33-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,39 @@ function validateSnapshotNodes(...args) {
211211
return recordState().validateSnapshotNodes(...args);
212212
}
213213

214+
function getHeapSnapshotOptionTests() {
215+
const fixtures = require('../common/fixtures');
216+
const cases = [
217+
{
218+
options: { exposeInternals: true },
219+
expected: [{
220+
children: [
221+
// We don't have anything special to test here yet
222+
// because we don't use cppgc or embedder heap tracer.
223+
{ edge_name: 'nonNumeric', node_name: 'test' },
224+
]
225+
}]
226+
},
227+
{
228+
options: { exposeNumericValues: true },
229+
expected: [{
230+
children: [
231+
{ edge_name: 'numeric', node_name: 'smi number' },
232+
]
233+
}]
234+
},
235+
];
236+
return {
237+
fixtures: fixtures.path('klass-with-fields.js'),
238+
check(snapshot, expected) {
239+
snapshot.validateSnapshot('Klass', expected, { loose: true });
240+
},
241+
cases,
242+
};
243+
}
244+
214245
module.exports = {
215246
recordState,
216-
validateSnapshotNodes
247+
validateSnapshotNodes,
248+
getHeapSnapshotOptionTests
217249
};

‎test/fixtures/klass-with-fields.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
const {
4+
parentPort,
5+
isMainThread
6+
} = require('node:worker_threads');
7+
8+
class Klass {
9+
numeric = 1234;
10+
nonNumeric = 'test';
11+
}
12+
13+
globalThis.obj = new Klass();
14+
15+
if (!isMainThread) {
16+
parentPort.postMessage('ready');
17+
setInterval(() => {}, 100);
18+
}

‎test/parallel/test-worker-heapdump-failure.js

+15
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,18 @@ const { once } = require('events');
1313
code: 'ERR_WORKER_NOT_RUNNING'
1414
});
1515
})().then(common.mustCall());
16+
17+
(async function() {
18+
const worker = new Worker('setInterval(() => {}, 1000);', { eval: true });
19+
await once(worker, 'online');
20+
21+
[1, true, [], null, Infinity, NaN].forEach((i) => {
22+
assert.throws(() => worker.getHeapSnapshot(i), {
23+
code: 'ERR_INVALID_ARG_TYPE',
24+
name: 'TypeError',
25+
message: 'The "options" argument must be of type object.' +
26+
common.invalidArgTypeHelper(i)
27+
});
28+
});
29+
await worker.terminate();
30+
})().then(common.mustCall());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
3+
// Flags: --expose-internals
4+
5+
require('../common');
6+
7+
const { getHeapSnapshotOptionTests, recordState } = require('../common/heap');
8+
9+
const tests = getHeapSnapshotOptionTests();
10+
if (process.argv[2] === 'child') {
11+
const { getHeapSnapshot } = require('v8');
12+
require(tests.fixtures);
13+
const { options, expected } = tests.cases[parseInt(process.argv[3])];
14+
const snapshot = recordState(getHeapSnapshot(options));
15+
console.log('Snapshot nodes', snapshot.snapshot.length);
16+
console.log('Searching for', expected[0].children);
17+
tests.check(snapshot, expected);
18+
delete globalThis.obj; // To pass the leaked global tests.
19+
return;
20+
}
21+
22+
const { spawnSync } = require('child_process');
23+
const assert = require('assert');
24+
const tmpdir = require('../common/tmpdir');
25+
tmpdir.refresh();
26+
27+
for (let i = 0; i < tests.cases.length; ++i) {
28+
const child = spawnSync(
29+
process.execPath,
30+
['--expose-internals', __filename, 'child', i + ''],
31+
{
32+
cwd: tmpdir.path
33+
});
34+
const stderr = child.stderr.toString();
35+
const stdout = child.stdout.toString();
36+
console.log('[STDERR]', stderr);
37+
console.log('[STDOUT]', stdout);
38+
assert.strictEqual(child.status, 0);
39+
}

‎test/sequential/test-heapdump.js

+18
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,24 @@ process.chdir(tmpdir.path);
4747
});
4848
});
4949

50+
[1, true, [], null, Infinity, NaN].forEach((i) => {
51+
assert.throws(() => writeHeapSnapshot('test.heapsnapshot', i), {
52+
code: 'ERR_INVALID_ARG_TYPE',
53+
name: 'TypeError',
54+
message: 'The "options" argument must be of type object.' +
55+
common.invalidArgTypeHelper(i)
56+
});
57+
});
58+
59+
[1, true, [], null, Infinity, NaN].forEach((i) => {
60+
assert.throws(() => getHeapSnapshot(i), {
61+
code: 'ERR_INVALID_ARG_TYPE',
62+
name: 'TypeError',
63+
message: 'The "options" argument must be of type object.' +
64+
common.invalidArgTypeHelper(i)
65+
});
66+
});
67+
5068
{
5169
let data = '';
5270
const snapshot = getHeapSnapshot();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
const common = require('../common');
4+
const { recordState, getHeapSnapshotOptionTests } = require('../common/heap');
5+
const { Worker } = require('worker_threads');
6+
const { once } = require('events');
7+
8+
(async function() {
9+
const tests = getHeapSnapshotOptionTests();
10+
const w = new Worker(tests.fixtures);
11+
12+
await once(w, 'message');
13+
14+
for (const { options, expected } of tests.cases) {
15+
const stream = await w.getHeapSnapshot(options);
16+
const snapshot = recordState(stream);
17+
tests.check(snapshot, expected);
18+
}
19+
20+
await w.terminate();
21+
})().then(common.mustCall());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use strict';
2+
3+
// Flags: --expose-internals
4+
5+
require('../common');
6+
7+
const fs = require('fs');
8+
const { getHeapSnapshotOptionTests, recordState } = require('../common/heap');
9+
10+
class ReadStream {
11+
constructor(filename) {
12+
this._content = fs.readFileSync(filename, 'utf-8');
13+
}
14+
pause() {}
15+
read() { return this._content; }
16+
}
17+
18+
const tests = getHeapSnapshotOptionTests();
19+
if (process.argv[2] === 'child') {
20+
const { writeHeapSnapshot } = require('v8');
21+
require(tests.fixtures);
22+
const { options, expected } = tests.cases[parseInt(process.argv[3])];
23+
const filename = writeHeapSnapshot(undefined, options);
24+
const snapshot = recordState(new ReadStream(filename));
25+
console.log('Snapshot nodes', snapshot.snapshot.length);
26+
console.log('Searching for', expected[0].children);
27+
tests.check(snapshot, expected);
28+
delete globalThis.obj; // To pass the leaked global tests.
29+
return;
30+
}
31+
32+
const { spawnSync } = require('child_process');
33+
const assert = require('assert');
34+
const tmpdir = require('../common/tmpdir');
35+
tmpdir.refresh();
36+
37+
// Start child processes to prevent the heap from growing too big.
38+
for (let i = 0; i < tests.cases.length; ++i) {
39+
const child = spawnSync(
40+
process.execPath,
41+
['--expose-internals', __filename, 'child', i + ''],
42+
{
43+
cwd: tmpdir.path
44+
});
45+
const stderr = child.stderr.toString();
46+
const stdout = child.stdout.toString();
47+
console.log('[STDERR]', stderr);
48+
console.log('[STDOUT]', stdout);
49+
assert.strictEqual(child.status, 0);
50+
}

0 commit comments

Comments
 (0)
Please sign in to comment.