Skip to content

Commit 9b6b567

Browse files
committed
lib,src,doc: add --heapsnapshot-signal CLI flag
This flag allows heap snapshots to be captured without modifying application code. PR-URL: #27133 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Sam Roberts <[email protected]> Reviewed-By: Richard Lau <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 8cf3af1 commit 9b6b567

File tree

6 files changed

+74
-0
lines changed

6 files changed

+74
-0
lines changed

doc/api/cli.md

+9
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,14 @@ https://github.com/tc39/ecma262/pull/1320.
230230

231231
Both of the above may change in future updates, which will be breaking changes.
232232

233+
### `--heapsnapshot-signal=signal`
234+
<!-- YAML
235+
added: REPLACEME
236+
-->
237+
238+
Generates a heap snapshot each time the process receives the specified signal.
239+
`signal` must be a valid signal name. Disabled by default.
240+
233241
### `--http-parser=library`
234242
<!-- YAML
235243
added: v11.4.0
@@ -765,6 +773,7 @@ Node.js options that are allowed are:
765773
- `--experimental-vm-modules`
766774
- `--force-fips`
767775
- `--frozen-intrinsics`
776+
- `--heapsnapshot-signal`
768777
- `--icu-data-dir`
769778
- `--inspect`
770779
- `--inspect-brk`

doc/node.1

+3
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ Same requirements as
156156
.It Fl -frozen-intrinsics
157157
Enable experimental frozen intrinsics support.
158158
.
159+
.It Fl -heapsnapshot-signal Ns = Ns Ar signal
160+
Generate heap snapshot on specified signal.
161+
.
159162
.It Fl -http-parser Ns = Ns Ar library
160163
Chooses an HTTP parser library. Available values are
161164
.Sy llhttp

lib/internal/bootstrap/pre_execution.js

+16
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ function prepareMainThreadExecution(expandArgv1 = false) {
2929
initializeReport();
3030
initializeReportSignalHandlers(); // Main-thread-only.
3131

32+
initializeHeapSnapshotSignalHandlers();
33+
3234
// If the process is spawned with env NODE_CHANNEL_FD, it's probably
3335
// spawned by our child_process module, then initialize IPC.
3436
// This attaches some internal event listeners and creates:
@@ -166,6 +168,20 @@ function initializeReportSignalHandlers() {
166168
addSignalHandler();
167169
}
168170

171+
function initializeHeapSnapshotSignalHandlers() {
172+
const signal = getOptionValue('--heapsnapshot-signal');
173+
174+
if (!signal)
175+
return;
176+
177+
require('internal/validators').validateSignalName(signal);
178+
const { writeHeapSnapshot } = require('v8');
179+
180+
process.on(signal, () => {
181+
writeHeapSnapshot();
182+
});
183+
}
184+
169185
function setupTraceCategoryState() {
170186
const { isTraceCategoryEnabled } = internalBinding('trace_events');
171187
const { toggleTraceCategoryState } = require('internal/process/per_thread');

src/node_options.cc

+4
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
273273
"experimental frozen intrinsics support",
274274
&EnvironmentOptions::frozen_intrinsics,
275275
kAllowedInEnvironment);
276+
AddOption("--heapsnapshot-signal",
277+
"Generate heap snapshot on specified signal",
278+
&EnvironmentOptions::heap_snapshot_signal,
279+
kAllowedInEnvironment);
276280
AddOption("--http-parser",
277281
"Select which HTTP parser to use; either 'legacy' or 'llhttp' "
278282
"(default: llhttp).",

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class EnvironmentOptions : public Options {
100100
bool experimental_vm_modules = false;
101101
bool expose_internals = false;
102102
bool frozen_intrinsics = false;
103+
std::string heap_snapshot_signal;
103104
std::string http_parser = "llhttp";
104105
bool no_deprecation = false;
105106
bool no_force_async_hooks_checks = false;

test/sequential/test-heapdump-flag.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict';
2+
const common = require('../common');
3+
4+
if (common.isWindows)
5+
common.skip('test not supported on Windows');
6+
7+
const assert = require('assert');
8+
9+
if (process.argv[2] === 'child') {
10+
const fs = require('fs');
11+
12+
assert.strictEqual(process.listenerCount('SIGUSR2'), 1);
13+
process.kill(process.pid, 'SIGUSR2');
14+
process.kill(process.pid, 'SIGUSR2');
15+
16+
// Asynchronously wait for the snapshot. Use an async loop to be a bit more
17+
// robust in case platform or machine differences throw off the timing.
18+
(function validate() {
19+
const files = fs.readdirSync(process.cwd());
20+
21+
if (files.length === 0)
22+
return setImmediate(validate);
23+
24+
assert.strictEqual(files.length, 2);
25+
26+
for (let i = 0; i < files.length; i++) {
27+
assert(/^Heap\..+\.heapsnapshot$/.test(files[i]));
28+
JSON.parse(fs.readFileSync(files[i]));
29+
}
30+
})();
31+
} else {
32+
const { spawnSync } = require('child_process');
33+
const tmpdir = require('../common/tmpdir');
34+
35+
tmpdir.refresh();
36+
const args = ['--heapsnapshot-signal', 'SIGUSR2', __filename, 'child'];
37+
const child = spawnSync(process.execPath, args, { cwd: tmpdir.path });
38+
39+
assert.strictEqual(child.status, 0);
40+
assert.strictEqual(child.signal, null);
41+
}

0 commit comments

Comments
 (0)