From 29f09f05f7e7acb57a783b2a3c3ccce28a93699a Mon Sep 17 00:00:00 2001 From: Ethan Arrowood Date: Fri, 1 Mar 2024 14:50:04 -0700 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 Co-authored-by: Joyee Cheung Reviewed-By: Yagiz Nizipli Reviewed-By: Joyee Cheung --- doc/api/cli.md | 10 ++++++ doc/api/report.md | 16 +++++++++ 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, 138 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 ca7b8ed31d75aa..1afd124f6232e2 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1702,6 +1702,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. By default +this is not set and the network interfaces are included. + ### `-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,10 @@ 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. By default this is not set and the network interfaces + are included. + A report can also be triggered via an API call from a JavaScript application: ```js @@ -571,6 +582,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 +600,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 fa09d4503980e1..26add55ce7c7c9 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -780,6 +780,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 293642d79fa168..a62d0ae32c44d1 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -217,6 +217,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); + }); +});