From 990aeaeaed68222236dfc6024aab2ca6a849799c 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 a6add66a9eb80a..e1745c3ddbb71e 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1759,6 +1759,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 ff9fd0cf0d2113..199047c7c1eaac 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -794,6 +794,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 700ce9ae7a2079..878685a25d9e20 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -220,6 +220,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); + }); +});