Skip to content

Commit 9092e12

Browse files
jasnelltargos
authored andcommitted
v8: integrate node-heapdump into core
Adds `v8.writeHeapSnapshot(filename)` with impl adapted from the `node-heapdump` module. Also, adds a v8.getHeapSnapshot() alternative that returns a Readable Stream PR-URL: #26501 Reviewed-By: Richard Lau <riclau@uk.ibm.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent d0801a1 commit 9092e12

14 files changed

+537
-15
lines changed

LICENSE

+38-1
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ The externally maintained libraries used by Node.js are:
634634

635635
- OpenSSL, located at deps/openssl, is licensed as follows:
636636
"""
637-
Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved.
637+
Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved.
638638

639639
Redistribution and use in source and binary forms, with or without
640640
modification, are permitted provided that the following conditions
@@ -1445,3 +1445,40 @@ The externally maintained libraries used by Node.js are:
14451445
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
14461446
THE POSSIBILITY OF SUCH DAMAGE.
14471447
"""
1448+
1449+
- node-heapdump, located at src/heap_utils.cc, is licensed as follows:
1450+
"""
1451+
ISC License
1452+
1453+
Copyright (c) 2012, Ben Noordhuis <info@bnoordhuis.nl>
1454+
1455+
Permission to use, copy, modify, and/or distribute this software for any
1456+
purpose with or without fee is hereby granted, provided that the above
1457+
copyright notice and this permission notice appear in all copies.
1458+
1459+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1460+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1461+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1462+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1463+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1464+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1465+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1466+
1467+
=== src/compat.h src/compat-inl.h ===
1468+
1469+
ISC License
1470+
1471+
Copyright (c) 2014, StrongLoop Inc.
1472+
1473+
Permission to use, copy, modify, and/or distribute this software for any
1474+
purpose with or without fee is hereby granted, provided that the above
1475+
copyright notice and this permission notice appear in all copies.
1476+
1477+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1478+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1479+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1480+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1481+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1482+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1483+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1484+
"""

doc/api/v8.md

+71
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,24 @@ The value returned is an array of objects containing the following properties:
8787
]
8888
```
8989

90+
## v8.getHeapSnapshot()
91+
<!-- YAML
92+
added: REPLACEME
93+
-->
94+
95+
* Returns: {stream.Readable} A Readable Stream containing the V8 heap snapshot
96+
97+
Generates a snapshot of the current V8 heap and returns a Readable
98+
Stream that may be used to read the JSON serialized representation.
99+
This JSON stream format is intended to be used with tools such as
100+
Chrome DevTools. The JSON schema is undocumented and specific to the
101+
V8 engine, and may change from one version of V8 to the next.
102+
103+
```js
104+
const stream = v8.getHeapSnapshot();
105+
stream.pipe(process.stdout);
106+
```
107+
90108
## v8.getHeapStatistics()
91109
<!-- YAML
92110
added: v1.0.0
@@ -159,6 +177,58 @@ v8.setFlagsFromString('--trace_gc');
159177
setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3);
160178
```
161179

180+
## v8.writeHeapSnapshot([filename])
181+
<!-- YAML
182+
added: REPLACEME
183+
-->
184+
185+
* `filename` {string} The file path where the V8 heap snapshot is to be
186+
saved. If not specified, a file name with the pattern
187+
`'Heap-${yyyymmdd}-${hhmmss}-${pid}-${thread_id}.heapsnapshot'` will be
188+
generated, where `{pid}` will be the PID of the Node.js process,
189+
`{thread_id}` will be `0` when `writeHeapSnapshot()` is called from
190+
the main Node.js thread or the id of a worker thread.
191+
* Returns: {string} The filename where the snapshot was saved.
192+
193+
Generates a snapshot of the current V8 heap and writes it to a JSON
194+
file. This file is intended to be used with tools such as Chrome
195+
DevTools. The JSON schema is undocumented and specific to the V8
196+
engine, and may change from one version of V8 to the next.
197+
198+
A heap snapshot is specific to a single V8 isolate. When using
199+
[Worker Threads][], a heap snapshot generated from the main thread will
200+
not contain any information about the workers, and vice versa.
201+
202+
```js
203+
const { writeHeapSnapshot } = require('v8');
204+
const {
205+
Worker,
206+
isMainThread,
207+
parentPort
208+
} = require('worker_threads');
209+
210+
if (isMainThread) {
211+
const worker = new Worker(__filename);
212+
213+
worker.once('message', (filename) => {
214+
console.log(`worker heapdump: ${filename}`);
215+
// Now get a heapdump for the main thread.
216+
console.log(`main thread heapdump: ${writeHeapSnapshot()}`);
217+
});
218+
219+
// Tell the worker to create a heapdump.
220+
worker.postMessage('heapdump');
221+
} else {
222+
parentPort.once('message', (message) => {
223+
if (message === 'heapdump') {
224+
// Generate a heapdump for the worker
225+
// and return the filename to the parent.
226+
parentPort.postMessage(writeHeapSnapshot());
227+
}
228+
});
229+
}
230+
```
231+
162232
## Serialization API
163233

164234
> Stability: 1 - Experimental
@@ -417,4 +487,5 @@ A subclass of [`Deserializer`][] corresponding to the format written by
417487
[`vm.Script`]: vm.html#vm_constructor_new_vm_script_code_options
418488
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
419489
[V8]: https://developers.google.com/v8/
490+
[Worker Threads]: worker_threads.html
420491
[here]: https://github.com/thlorenz/v8-flags/blob/master/flags-0.11.md

lib/internal/test/heap.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ process.emitWarning(
44
'These APIs are for internal testing only. Do not use them.',
55
'internal/test/heap');
66

7-
const { createHeapDump, buildEmbedderGraph } = internalBinding('heap_utils');
7+
const {
8+
createHeapSnapshot,
9+
buildEmbedderGraph
10+
} = internalBinding('heap_utils');
811
const assert = require('internal/assert');
912

1013
// This is not suitable for production code. It creates a full V8 heap dump,
1114
// parses it as JSON, and then creates complex objects from it, leading
1215
// to significantly increased memory usage.
13-
function createJSHeapDump() {
14-
const dump = createHeapDump();
16+
function createJSHeapSnapshot() {
17+
const dump = createHeapSnapshot();
1518
const meta = dump.snapshot.meta;
1619

1720
const nodes =
@@ -81,6 +84,6 @@ function readHeapInfo(raw, fields, types, strings) {
8184
}
8285

8386
module.exports = {
84-
createJSHeapDump,
87+
createJSHeapSnapshot,
8588
buildEmbedderGraph
8689
};

lib/v8.js

+59-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,65 @@ const {
2020
Serializer: _Serializer,
2121
Deserializer: _Deserializer
2222
} = internalBinding('serdes');
23+
const assert = require('internal/assert');
2324
const { copy } = internalBinding('buffer');
2425
const { objectToString } = require('internal/util');
2526
const { FastBuffer } = require('internal/buffer');
27+
const { toPathIfFileURL } = require('internal/url');
28+
const { validatePath } = require('internal/fs/utils');
29+
const { toNamespacedPath } = require('path');
30+
const {
31+
createHeapSnapshotStream,
32+
triggerHeapSnapshot
33+
} = internalBinding('heap_utils');
34+
const { Readable } = require('stream');
35+
const { owner_symbol } = require('internal/async_hooks').symbols;
36+
const {
37+
kUpdateTimer,
38+
onStreamRead,
39+
} = require('internal/stream_base_commons');
40+
const kHandle = Symbol('kHandle');
41+
42+
43+
function writeHeapSnapshot(filename) {
44+
if (filename !== undefined) {
45+
filename = toPathIfFileURL(filename);
46+
validatePath(filename);
47+
filename = toNamespacedPath(filename);
48+
}
49+
return triggerHeapSnapshot(filename);
50+
}
51+
52+
class HeapSnapshotStream extends Readable {
53+
constructor(handle) {
54+
super({ autoDestroy: true });
55+
this[kHandle] = handle;
56+
handle[owner_symbol] = this;
57+
handle.onread = onStreamRead;
58+
}
59+
60+
_read() {
61+
if (this[kHandle])
62+
this[kHandle].readStart();
63+
}
64+
65+
_destroy() {
66+
// Release the references on the handle so that
67+
// it can be garbage collected.
68+
this[kHandle][owner_symbol] = undefined;
69+
this[kHandle] = undefined;
70+
}
71+
72+
[kUpdateTimer]() {
73+
// Does nothing
74+
}
75+
}
76+
77+
function getHeapSnapshot() {
78+
const handle = createHeapSnapshotStream();
79+
assert(handle);
80+
return new HeapSnapshotStream(handle);
81+
}
2682

2783
// Calling exposed c++ functions directly throws exception as it expected to be
2884
// called with new operator and caused an assert to fire.
@@ -210,6 +266,7 @@ function deserialize(buffer) {
210266

211267
module.exports = {
212268
cachedDataVersionTag,
269+
getHeapSnapshot,
213270
getHeapStatistics,
214271
getHeapSpaceStatistics,
215272
setFlagsFromString,
@@ -218,5 +275,6 @@ module.exports = {
218275
DefaultSerializer,
219276
DefaultDeserializer,
220277
deserialize,
221-
serialize
278+
serialize,
279+
writeHeapSnapshot
222280
};

src/async_wrap.h

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ namespace node {
4141
V(FSREQPROMISE) \
4242
V(GETADDRINFOREQWRAP) \
4343
V(GETNAMEINFOREQWRAP) \
44+
V(HEAPSNAPSHOT) \
4445
V(HTTP2SESSION) \
4546
V(HTTP2STREAM) \
4647
V(HTTP2PING) \

src/env.h

+1
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
379379
V(script_data_constructor_function, v8::Function) \
380380
V(secure_context_constructor_template, v8::FunctionTemplate) \
381381
V(shutdown_wrap_template, v8::ObjectTemplate) \
382+
V(streambaseoutputstream_constructor_template, v8::ObjectTemplate) \
382383
V(tcp_constructor_template, v8::FunctionTemplate) \
383384
V(tick_callback_function, v8::Function) \
384385
V(timers_callback_function, v8::Function) \

0 commit comments

Comments
 (0)