Skip to content

Commit

Permalink
lib,src,doc: add --heapsnapshot-signal CLI flag
Browse files Browse the repository at this point in the history
This flag allows heap snapshots to be captured without
modifying application code.

PR-URL: #27133
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information
cjihrig committed Apr 12, 2019
1 parent 8cf3af1 commit 9b6b567
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 0 deletions.
9 changes: 9 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ https://github.com/tc39/ecma262/pull/1320.

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

### `--heapsnapshot-signal=signal`
<!-- YAML
added: REPLACEME
-->

Generates a heap snapshot each time the process receives the specified signal.
`signal` must be a valid signal name. Disabled by default.

### `--http-parser=library`
<!-- YAML
added: v11.4.0
Expand Down Expand Up @@ -765,6 +773,7 @@ Node.js options that are allowed are:
- `--experimental-vm-modules`
- `--force-fips`
- `--frozen-intrinsics`
- `--heapsnapshot-signal`
- `--icu-data-dir`
- `--inspect`
- `--inspect-brk`
Expand Down
3 changes: 3 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ Same requirements as
.It Fl -frozen-intrinsics
Enable experimental frozen intrinsics support.
.
.It Fl -heapsnapshot-signal Ns = Ns Ar signal
Generate heap snapshot on specified signal.
.
.It Fl -http-parser Ns = Ns Ar library
Chooses an HTTP parser library. Available values are
.Sy llhttp
Expand Down
16 changes: 16 additions & 0 deletions lib/internal/bootstrap/pre_execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ function prepareMainThreadExecution(expandArgv1 = false) {
initializeReport();
initializeReportSignalHandlers(); // Main-thread-only.

initializeHeapSnapshotSignalHandlers();

// If the process is spawned with env NODE_CHANNEL_FD, it's probably
// spawned by our child_process module, then initialize IPC.
// This attaches some internal event listeners and creates:
Expand Down Expand Up @@ -166,6 +168,20 @@ function initializeReportSignalHandlers() {
addSignalHandler();
}

function initializeHeapSnapshotSignalHandlers() {
const signal = getOptionValue('--heapsnapshot-signal');

if (!signal)
return;

require('internal/validators').validateSignalName(signal);
const { writeHeapSnapshot } = require('v8');

process.on(signal, () => {
writeHeapSnapshot();
});
}

function setupTraceCategoryState() {
const { isTraceCategoryEnabled } = internalBinding('trace_events');
const { toggleTraceCategoryState } = require('internal/process/per_thread');
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"experimental frozen intrinsics support",
&EnvironmentOptions::frozen_intrinsics,
kAllowedInEnvironment);
AddOption("--heapsnapshot-signal",
"Generate heap snapshot on specified signal",
&EnvironmentOptions::heap_snapshot_signal,
kAllowedInEnvironment);
AddOption("--http-parser",
"Select which HTTP parser to use; either 'legacy' or 'llhttp' "
"(default: llhttp).",
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class EnvironmentOptions : public Options {
bool experimental_vm_modules = false;
bool expose_internals = false;
bool frozen_intrinsics = false;
std::string heap_snapshot_signal;
std::string http_parser = "llhttp";
bool no_deprecation = false;
bool no_force_async_hooks_checks = false;
Expand Down
41 changes: 41 additions & 0 deletions test/sequential/test-heapdump-flag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict';
const common = require('../common');

if (common.isWindows)
common.skip('test not supported on Windows');

const assert = require('assert');

if (process.argv[2] === 'child') {
const fs = require('fs');

assert.strictEqual(process.listenerCount('SIGUSR2'), 1);
process.kill(process.pid, 'SIGUSR2');
process.kill(process.pid, 'SIGUSR2');

// Asynchronously wait for the snapshot. Use an async loop to be a bit more
// robust in case platform or machine differences throw off the timing.
(function validate() {
const files = fs.readdirSync(process.cwd());

if (files.length === 0)
return setImmediate(validate);

assert.strictEqual(files.length, 2);

for (let i = 0; i < files.length; i++) {
assert(/^Heap\..+\.heapsnapshot$/.test(files[i]));
JSON.parse(fs.readFileSync(files[i]));
}
})();
} else {
const { spawnSync } = require('child_process');
const tmpdir = require('../common/tmpdir');

tmpdir.refresh();
const args = ['--heapsnapshot-signal', 'SIGUSR2', __filename, 'child'];
const child = spawnSync(process.execPath, args, { cwd: tmpdir.path });

assert.strictEqual(child.status, 0);
assert.strictEqual(child.signal, null);
}

0 comments on commit 9b6b567

Please sign in to comment.