From e114b4fed024cfd0b35af33330e6b1f727e95b99 Mon Sep 17 00:00:00 2001 From: Ethan Arrowood Date: Thu, 8 Feb 2024 21:07:29 -0800 Subject: [PATCH] report: add `--report-exclude-network` option New option `--report-exclude-network`, also available as `report.excludeNetwork`, enables the user to exclude networking interfaces in their diagnostic report. On some systems, this can cause the report to take minutes to generate so this option can be used to optimize that. Fixes: https://github.com/nodejs/node/issues/46060 PR-URL: https://github.com/nodejs/node/pull/51645 --- doc/api/cli.md | 10 ++++++ doc/api/report.md | 15 ++++++++ lib/internal/process/report.js | 7 ++++ src/node_options.cc | 6 ++++ src/node_options.h | 2 ++ src/node_report.cc | 40 +++++++++++++++------ src/node_report_module.cc | 15 ++++++++ test/report/test-report-config.js | 11 ++++++ test/report/test-report-exclude-network.js | 41 ++++++++++++++++++++++ 9 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 test/report/test-report-exclude-network.js diff --git a/doc/api/cli.md b/doc/api/cli.md index 5ba337276597b9..1e85fcc9ec0f9d 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1749,6 +1749,15 @@ Enables report to be generated when the process exits due to an uncaught exception. Useful when inspecting the JavaScript stack in conjunction with native stack and other runtime environment data. +### `--report-exclude-network` + + + +Exclude `header.networkInterfaces` from the diagnostic report. Default is +`false`. + ### `-r`, `--require module` + + Delivers a JSON-formatted diagnostic summary, written to a file. The report is intended for development, test, and production use, to capture @@ -452,6 +459,9 @@ meaning of `SIGUSR2` for the said purposes. * `--report-signal` Sets or resets the signal for report generation (not supported on Windows). Default signal is `SIGUSR2`. +* `--report-exclude-network` Exclude `header.networkInterfaces` from the + diagnostic report. Default is `false`. + A report can also be triggered via an API call from a JavaScript application: ```js @@ -571,6 +581,8 @@ timestamp, PID, and sequence number. written. URLs are not supported. Defaults to the current working directory of the Node.js process. +`excludeNetwork` excludes `header.networkInterfaces` from the diagnostic report. + ```js // Trigger report only on uncaught exceptions. process.report.reportOnFatalError = false; @@ -587,6 +599,9 @@ process.report.reportOnFatalError = false; process.report.reportOnUncaughtException = false; process.report.reportOnSignal = true; process.report.signal = 'SIGQUIT'; + +// Disable network interfaces reporting +process.report.excludeNetwork = true; ``` Configuration on module initialization is also available via diff --git a/lib/internal/process/report.js b/lib/internal/process/report.js index 9889f913c3f81f..1639142dff0eb7 100644 --- a/lib/internal/process/report.js +++ b/lib/internal/process/report.js @@ -60,6 +60,13 @@ const report = { validateBoolean(b, 'compact'); nr.setCompact(b); }, + get excludeNetwork() { + return nr.getExcludeNetwork(); + }, + set excludeNetwork(b) { + validateBoolean(b, 'excludeNetwork'); + nr.setExcludeNetwork(b); + }, get signal() { return nr.getSignal(); }, diff --git a/src/node_options.cc b/src/node_options.cc index 7b5152172c5ce7..97ae9b41a2f3ef 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -785,6 +785,12 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "set default TLS maximum to TLSv1.3 (default: TLSv1.3)", &EnvironmentOptions::tls_max_v1_3, kAllowedInEnvvar); + + AddOption("--report-exclude-network", + "exclude network interface diagnostics." + " (default: false)", + &EnvironmentOptions::report_exclude_network, + kAllowedInEnvvar); } PerIsolateOptionsParser::PerIsolateOptionsParser( diff --git a/src/node_options.h b/src/node_options.h index 915151b7dc2904..e70e5079870aaa 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -218,6 +218,8 @@ class EnvironmentOptions : public Options { std::vector user_argv; + bool report_exclude_network = false; + inline DebugOptions* get_debug_options() { return &debug_options_; } inline const DebugOptions& debug_options() const { return debug_options_; } diff --git a/src/node_report.cc b/src/node_report.cc index 54121cb6b48210..23bbdad186940d 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -61,8 +61,10 @@ static void WriteNodeReport(Isolate* isolate, const std::string& filename, std::ostream& out, Local error, - bool compact); -static void PrintVersionInformation(JSONWriter* writer); + bool compact, + bool exclude_network = false); +static void PrintVersionInformation(JSONWriter* writer, + bool exclude_network = false); static void PrintJavaScriptErrorStack(JSONWriter* writer, Isolate* isolate, Local error, @@ -93,7 +95,8 @@ static void WriteNodeReport(Isolate* isolate, const std::string& filename, std::ostream& out, Local error, - bool compact) { + bool compact, + bool exclude_network) { // Obtain the current time and the pid. TIME_TYPE tm_struct; DiagnosticFilename::LocalTime(&tm_struct); @@ -174,7 +177,7 @@ static void WriteNodeReport(Isolate* isolate, } // Report Node.js and OS version information - PrintVersionInformation(&writer); + PrintVersionInformation(&writer, exclude_network); writer.json_objectend(); if (isolate != nullptr) { @@ -256,7 +259,7 @@ static void WriteNodeReport(Isolate* isolate, } // Report Node.js version, OS version and machine information. -static void PrintVersionInformation(JSONWriter* writer) { +static void PrintVersionInformation(JSONWriter* writer, bool exclude_network) { std::ostringstream buf; // Report Node version buf << "v" << NODE_VERSION_STRING; @@ -300,7 +303,7 @@ static void PrintVersionInformation(JSONWriter* writer) { } PrintCpuInfo(writer); - PrintNetworkInterfaceInfo(writer); + if (!exclude_network) PrintNetworkInterfaceInfo(writer); char host[UV_MAXHOSTNAMESIZE]; size_t host_size = sizeof(host); @@ -917,8 +920,19 @@ std::string TriggerNodeReport(Isolate* isolate, compact = per_process::cli_options->report_compact; } - report::WriteNodeReport( - isolate, env, message, trigger, filename, *outstream, error, compact); + bool exclude_network = env != nullptr ? env->options()->report_exclude_network + : per_process::cli_options->per_isolate + ->per_env->report_exclude_network; + + report::WriteNodeReport(isolate, + env, + message, + trigger, + filename, + *outstream, + error, + compact, + exclude_network); // Do not close stdout/stderr, only close files we opened. if (outfile.is_open()) { @@ -969,8 +983,11 @@ void GetNodeReport(Isolate* isolate, if (isolate != nullptr) { env = Environment::GetCurrent(isolate); } + bool exclude_network = env != nullptr ? env->options()->report_exclude_network + : per_process::cli_options->per_isolate + ->per_env->report_exclude_network; report::WriteNodeReport( - isolate, env, message, trigger, "", out, error, false); + isolate, env, message, trigger, "", out, error, false, exclude_network); } // External function to trigger a report, writing to a supplied stream. @@ -983,8 +1000,11 @@ void GetNodeReport(Environment* env, if (env != nullptr) { isolate = env->isolate(); } + bool exclude_network = env != nullptr ? env->options()->report_exclude_network + : per_process::cli_options->per_isolate + ->per_env->report_exclude_network; report::WriteNodeReport( - isolate, env, message, trigger, "", out, error, false); + isolate, env, message, trigger, "", out, error, false, exclude_network); } } // namespace node diff --git a/src/node_report_module.cc b/src/node_report_module.cc index 58963fd5150b04..09ac89fc528d95 100644 --- a/src/node_report_module.cc +++ b/src/node_report_module.cc @@ -84,6 +84,17 @@ static void SetCompact(const FunctionCallbackInfo& info) { per_process::cli_options->report_compact = compact; } +static void GetExcludeNetwork(const FunctionCallbackInfo& info) { + Environment* env = Environment::GetCurrent(info); + info.GetReturnValue().Set(env->options()->report_exclude_network); +} + +static void SetExcludeNetwork(const FunctionCallbackInfo& info) { + Environment* env = Environment::GetCurrent(info); + CHECK(info[0]->IsBoolean()); + env->options()->report_exclude_network = info[0]->IsTrue(); +} + static void GetDirectory(const FunctionCallbackInfo& info) { Mutex::ScopedLock lock(per_process::cli_options_mutex); Environment* env = Environment::GetCurrent(info); @@ -174,6 +185,8 @@ static void Initialize(Local exports, SetMethod(context, exports, "getReport", GetReport); SetMethod(context, exports, "getCompact", GetCompact); SetMethod(context, exports, "setCompact", SetCompact); + SetMethod(context, exports, "getExcludeNetwork", GetExcludeNetwork); + SetMethod(context, exports, "setExcludeNetwork", SetExcludeNetwork); SetMethod(context, exports, "getDirectory", GetDirectory); SetMethod(context, exports, "setDirectory", SetDirectory); SetMethod(context, exports, "getFilename", GetFilename); @@ -200,6 +213,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(GetReport); registry->Register(GetCompact); registry->Register(SetCompact); + registry->Register(GetExcludeNetwork); + registry->Register(SetExcludeNetwork); registry->Register(GetDirectory); registry->Register(SetDirectory); registry->Register(GetFilename); diff --git a/test/report/test-report-config.js b/test/report/test-report-config.js index dce58c5bfcd954..55a4371fd32d79 100644 --- a/test/report/test-report-config.js +++ b/test/report/test-report-config.js @@ -66,6 +66,17 @@ assert.throws(() => { }, { code: 'ERR_INVALID_ARG_TYPE' }); assert.strictEqual(process.report.compact, true); +// Verify that process.report.excludeNetwork behaves properly. +assert.strictEqual(process.report.excludeNetwork, false); +process.report.excludeNetwork = true; +assert.strictEqual(process.report.excludeNetwork, true); +process.report.excludeNetwork = false; +assert.strictEqual(process.report.excludeNetwork, false); +assert.throws(() => { + process.report.excludeNetwork = {}; +}, { code: 'ERR_INVALID_ARG_TYPE' }); +assert.strictEqual(process.report.excludeNetwork, false); + if (!common.isWindows) { // Verify that process.report.signal behaves properly. assert.strictEqual(process.report.signal, 'SIGUSR2'); diff --git a/test/report/test-report-exclude-network.js b/test/report/test-report-exclude-network.js new file mode 100644 index 00000000000000..c5e50135482f1a --- /dev/null +++ b/test/report/test-report-exclude-network.js @@ -0,0 +1,41 @@ +'use strict'; +require('../common'); +const assert = require('node:assert'); +const { spawnSync } = require('node:child_process'); +const tmpdir = require('../common/tmpdir'); +const { describe, it, before } = require('node:test'); +const fs = require('node:fs'); +const helper = require('../common/report'); + +function validate(pid) { + const reports = helper.findReports(pid, tmpdir.path); + assert.strictEqual(reports.length, 1); + let report = fs.readFileSync(reports[0], { encoding: 'utf8' }); + report = JSON.parse(report); + assert.strictEqual(report.header.networkInterfaces, undefined); + fs.unlinkSync(reports[0]); +} + +describe('report exclude network option', () => { + before(() => { + tmpdir.refresh(); + process.report.directory = tmpdir.path; + }); + + it('should be configurable with --report-exclude-network', () => { + const args = ['--report-exclude-network', '-e', 'process.report.writeReport()']; + const child = spawnSync(process.execPath, args, { cwd: tmpdir.path }); + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + validate(child.pid); + }); + + it('should be configurable with report.excludeNetwork', () => { + process.report.excludeNetwork = true; + process.report.writeReport(); + validate(process.pid); + + const report = process.report.getReport(); + assert.strictEqual(report.header.networkInterfaces, undefined); + }); +});