summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/process.md101
-rw-r--r--doc/api/report.md40
-rw-r--r--lib/internal/bootstrap/pre_execution.js19
-rw-r--r--lib/internal/process/execution.js17
-rw-r--r--lib/internal/process/report.js161
-rw-r--r--src/node_options.cc8
-rw-r--r--src/node_report_module.cc223
-rw-r--r--test/report/test-report-config.js87
-rw-r--r--test/report/test-report-signal.js1
-rw-r--r--test/report/test-report-triggerreport.js8
-rw-r--r--test/report/test-report-uncaught-exception.js2
11 files changed, 390 insertions, 277 deletions
diff --git a/doc/api/process.md b/doc/api/process.md
index d88c89b5c3..36fefe73d5 100644
--- a/doc/api/process.md
+++ b/doc/api/process.md
@@ -1669,6 +1669,21 @@ added: v11.8.0
reports for the current process. Additional documentation is available in the
[report documentation][].
+## process.report.directory
+<!-- YAML
+added: REPLACEME
+-->
+
+* {string}
+
+Directory where the report is written. The default value is the empty string,
+indicating that reports are written to the current working directory of the
+Node.js process.
+
+```js
+console.log(`Report directory is ${process.report.directory}`);
+```
+
### process.report.getReport([err])
<!-- YAML
added: v11.8.0
@@ -1687,43 +1702,75 @@ console.log(data);
Additional documentation is available in the [report documentation][].
-### process.report.setOptions([options]);
+## process.report.filename
<!-- YAML
-added: v11.8.0
+added: REPLACEME
-->
-* `options` {Object}
- * `events` {string[]}
- * `signal`: Generate a report in response to a signal raised on the process.
- * `exception`: Generate a report on unhandled exceptions.
- * `fatalerror`: Generate a report on internal fault
- (such as out of memory errors or native assertions).
- * `signal` {string} Sets or resets the signal for report generation
- (not supported on Windows). **Default:** `'SIGUSR2'`.
- * `filename` {string} Name of the file where the report is written.
- * `path` {string} Directory where the report is written.
- **Default:** the current working directory of the Node.js process.
+* {string}
-Configures the diagnostic reporting behavior. Upon invocation, the runtime
-is reconfigured to generate reports based on `options`. Several usage examples
-are shown below.
+Filename where the report is written. If set to the empty string, the output
+filename will be comprised of a timestamp, PID, and sequence number. The default
+value is the empty string.
```js
-// Trigger a report on uncaught exceptions or fatal errors.
-process.report.setOptions({ events: ['exception', 'fatalerror'] });
+console.log(`Report filename is ${process.report.filename}`);
+```
+
+## process.report.reportOnFatalError
+<!-- YAML
+added: REPLACEME
+-->
+
+* {boolean}
-// Change the default path and filename of the report.
-process.report.setOptions({ filename: 'foo.json', path: '/home' });
+If `true`, a diagnostic report is generated on fatal errors, such as out of
+memory errors or failed C++ assertions.
-// Produce the report onto stdout, when generated. Special meaning is attached
-// to `stdout` and `stderr`. Usage of these will result in report being written
-// to the associated standard streams. URLs are not supported.
-process.report.setOptions({ filename: 'stdout' });
+```js
+console.log(`Report on fatal error: ${process.report.reportOnFatalError}`);
```
-Signal based report generation is not supported on Windows.
+## process.report.reportOnSignal
+<!-- YAML
+added: REPLACEME
+-->
-Additional documentation is available in the [report documentation][].
+* {boolean}
+
+If `true`, a diagnostic report is generated when the process receives the
+signal specified by `process.report.signal`.
+
+```js
+console.log(`Report on signal: ${process.report.reportOnSignal}`);
+```
+
+## process.report.reportOnUncaughtException
+<!-- YAML
+added: REPLACEME
+-->
+
+* {boolean}
+
+If `true`, a diagnostic report is generated on uncaught exception.
+
+```js
+console.log(`Report on exception: ${process.report.reportOnUncaughtException}`);
+```
+
+## process.report.signal
+<!-- YAML
+added: REPLACEME
+-->
+
+* {string}
+
+The signal used to trigger the creation of a diagnostic report. Defaults to
+`SIGUSR2`.
+
+```js
+console.log(`Report signal: ${process.report.signal}`);
+```
### process.report.triggerReport([filename][, err])
<!-- YAML
@@ -1732,7 +1779,7 @@ added: v11.8.0
* `filename` {string} Name of the file where the report is written. This
should be a relative path, that will be appended to the directory specified in
- `process.report.setOptions`, or the current working directory of the Node.js
+ `process.report.directory`, or the current working directory of the Node.js
process, if unspecified.
* `err` {Error} A custom error used for reporting the JavaScript stack.
diff --git a/doc/api/report.md b/doc/api/report.md
index 856cd80119..5aff191ee4 100644
--- a/doc/api/report.md
+++ b/doc/api/report.md
@@ -484,21 +484,17 @@ times for the same Node.js process.
## Configuration
-Additional runtime configuration that influences the report generation
-constraints are available using `setOptions()` API.
+Additional runtime configuration of report generation is available via
+the following properties of `process.report`:
-```js
-process.report.setOptions({
- events: ['exception', 'fatalerror', 'signal'],
- signal: 'SIGUSR2',
- filename: 'myreport.json',
- path: '/home/nodeuser'
-});
-```
+`reportOnFatalError` triggers diagnostic reporting on fatal errors when `true`.
+Defaults to `false`.
+
+`reportOnSignal` triggers diagnostic reporting on signal when `true`. This is
+not supported on Windows. Defaults to `false`.
-The `events` array contains one or more of the report triggering options.
-The only valid entries are `'exception'`, `'fatalerror'` and `'signal'`.
-By default, a report is not produced on any of these events.
+`reportOnUncaughtException` triggers diagnostic reporting on uncaught exception
+when `true`. Defaults to `false`.
`signal` specifies the POSIX signal identifier that will be used
to intercept external triggers for report generation. Defaults to
@@ -507,24 +503,30 @@ to intercept external triggers for report generation. Defaults to
`filename` specifies the name of the output file in the file system.
Special meaning is attached to `stdout` and `stderr`. Usage of these
will result in report being written to the associated standard streams.
-In such cases when standard streams are used, value in `'path'` is ignored.
+In cases where standard streams are used, the value in `'directory'` is ignored.
URLs are not supported. Defaults to a composite filename that contains
timestamp, PID and sequence number.
-`path` specifies the filesystem directory where the report will be written to.
+`directory` specifies the filesystem directory where the report will be written.
URLs are not supported. Defaults to the current working directory of the
Node.js process.
```js
// Trigger report only on uncaught exceptions.
-process.report.setOptions({ events: ['exception'] });
+process.report.reportOnFatalError = false;
+process.report.reportOnSignal = false;
+process.report.reportOnUncaughtException = true;
// Trigger report for both internal errors as well as external signal.
-process.report.setOptions({ events: ['fatalerror', 'signal'] });
+process.report.reportOnFatalError = true;
+process.report.reportOnSignal = true;
+process.report.reportOnUncaughtException = false;
// Change the default signal to `SIGQUIT` and enable it.
-process.report.setOptions(
- { events: ['signal'], signal: 'SIGQUIT' });
+process.report.reportOnFatalError = false;
+process.report.reportOnUncaughtException = false;
+process.report.reportOnSignal = true;
+process.report.signal = 'SIGQUIT';
```
Configuration on module initialization is also available via
diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js
index 3578e7c112..b6e606cf9d 100644
--- a/lib/internal/bootstrap/pre_execution.js
+++ b/lib/internal/bootstrap/pre_execution.js
@@ -40,14 +40,8 @@ function initializeReport() {
if (!getOptionValue('--experimental-report')) {
return;
}
- const {
- config,
- report,
- syncConfig
- } = require('internal/process/report');
+ const { report } = require('internal/process/report');
process.report = report;
- // Download the CLI / ENV config into JS land.
- syncConfig(config, false);
}
function setupSignalHandlers() {
@@ -68,13 +62,10 @@ function initializeReportSignalHandlers() {
if (!getOptionValue('--experimental-report')) {
return;
}
- const {
- config,
- handleSignal
- } = require('internal/process/report');
- if (config.events.includes('signal')) {
- process.on(config.signal, handleSignal);
- }
+
+ const { addSignalHandler } = require('internal/process/report');
+
+ addSignalHandler();
}
function setupTraceCategoryState() {
diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js
index eb06dc0480..310acc14b4 100644
--- a/lib/internal/process/execution.js
+++ b/lib/internal/process/execution.js
@@ -109,18 +109,11 @@ function createFatalException() {
if (er == null || er.domain == null) {
try {
const report = internalBinding('report');
- if (report != null &&
- require('internal/options')
- .getOptionValue('--experimental-report')) {
- const config = {};
- report.syncConfig(config, false);
- if (Array.isArray(config.events) &&
- config.events.includes('exception')) {
- report.triggerReport(er ? er.message : 'Exception',
- 'Exception',
- null,
- er ? er.stack : undefined);
- }
+ if (report != null && report.shouldReportOnUncaughtException()) {
+ report.triggerReport(er ? er.message : 'Exception',
+ 'Exception',
+ null,
+ er ? er.stack : undefined);
}
} catch {} // Ignore the exception. Diagnostic reporting is unavailable.
}
diff --git a/lib/internal/process/report.js b/lib/internal/process/report.js
index daf948258c..96729b2ee3 100644
--- a/lib/internal/process/report.js
+++ b/lib/internal/process/report.js
@@ -7,72 +7,9 @@ const {
ERR_INVALID_ARG_TYPE,
ERR_SYNTHETIC
} = require('internal/errors').codes;
-
-// If report is enabled, extract the binding and
-// wrap the APIs with thin layers, with some error checks.
-// user options can come in from CLI / ENV / API.
-// CLI and ENV is intercepted in C++ and the API call here (JS).
-// So sync up with both sides as appropriate - initially from
-// C++ to JS and from JS to C++ whenever the API is called.
-// Some events are controlled purely from JS (signal | exception)
-// and some from C++ (fatalerror) so this sync-up is essential for
-// correct behavior and alignment with the supplied tunables.
+const { validateString } = require('internal/validators');
const nr = internalBinding('report');
-
-// Keep it un-exposed; lest programs play with it
-// leaving us with a lot of unwanted sanity checks.
-let config = {
- events: [],
- signal: 'SIGUSR2',
- filename: '',
- path: ''
-};
const report = {
- setOptions(options) {
- emitExperimentalWarning('report');
- const previousConfig = config;
- const newConfig = {};
-
- if (options === null || typeof options !== 'object')
- options = {};
-
- if (Array.isArray(options.events))
- newConfig.events = options.events.slice();
- else if (options.events === undefined)
- newConfig.events = [];
- else
- throw new ERR_INVALID_ARG_TYPE('events', 'Array', options.events);
-
- if (typeof options.filename === 'string')
- newConfig.filename = options.filename;
- else if (options.filename === undefined)
- newConfig.filename = '';
- else
- throw new ERR_INVALID_ARG_TYPE('filename', 'string', options.filename);
-
- if (typeof options.path === 'string')
- newConfig.path = options.path;
- else if (options.path === undefined)
- newConfig.path = '';
- else
- throw new ERR_INVALID_ARG_TYPE('path', 'string', options.path);
-
- if (typeof options.signal === 'string')
- newConfig.signal = convertToValidSignal(options.signal);
- else if (options.signal === undefined)
- newConfig.signal = 'SIGUSR2';
- else
- throw new ERR_INVALID_ARG_TYPE('signal', 'string', options.signal);
-
- if (previousConfig.signal)
- process.removeListener(previousConfig.signal, handleSignal);
-
- if (newConfig.events.includes('signal'))
- process.on(newConfig.signal, handleSignal);
-
- config = newConfig;
- nr.syncConfig(config, true);
- },
triggerReport(file, err) {
emitExperimentalWarning('report');
@@ -98,18 +35,98 @@ const report = {
throw new ERR_INVALID_ARG_TYPE('err', 'Object', err);
return nr.getReport(err.stack);
+ },
+ get directory() {
+ emitExperimentalWarning('report');
+ return nr.getDirectory();
+ },
+ set directory(dir) {
+ emitExperimentalWarning('report');
+ validateString(dir, 'directory');
+ return nr.setDirectory(dir);
+ },
+ get filename() {
+ emitExperimentalWarning('report');
+ return nr.getFilename();
+ },
+ set filename(name) {
+ emitExperimentalWarning('report');
+ validateString(name, 'filename');
+ return nr.setFilename(name);
+ },
+ get signal() {
+ emitExperimentalWarning('report');
+ return nr.getSignal();
+ },
+ set signal(sig) {
+ emitExperimentalWarning('report');
+ validateString(sig, 'signal');
+ convertToValidSignal(sig); // Validate that the signal is recognized.
+ removeSignalHandler();
+ addSignalHandler(sig);
+ return nr.setSignal(sig);
+ },
+ get reportOnFatalError() {
+ emitExperimentalWarning('report');
+ return nr.shouldReportOnFatalError();
+ },
+ set reportOnFatalError(trigger) {
+ emitExperimentalWarning('report');
+
+ if (typeof trigger !== 'boolean')
+ throw new ERR_INVALID_ARG_TYPE('trigger', 'boolean', trigger);
+
+ return nr.setReportOnFatalError(trigger);
+ },
+ get reportOnSignal() {
+ emitExperimentalWarning('report');
+ return nr.shouldReportOnSignal();
+ },
+ set reportOnSignal(trigger) {
+ emitExperimentalWarning('report');
+
+ if (typeof trigger !== 'boolean')
+ throw new ERR_INVALID_ARG_TYPE('trigger', 'boolean', trigger);
+
+ nr.setReportOnSignal(trigger);
+ removeSignalHandler();
+ addSignalHandler();
+ },
+ get reportOnUncaughtException() {
+ emitExperimentalWarning('report');
+ return nr.shouldReportOnUncaughtException();
+ },
+ set reportOnUncaughtException(trigger) {
+ emitExperimentalWarning('report');
+
+ if (typeof trigger !== 'boolean')
+ throw new ERR_INVALID_ARG_TYPE('trigger', 'boolean', trigger);
+
+ return nr.setReportOnUncaughtException(trigger);
}
};
-function handleSignal(signo) {
- if (typeof signo !== 'string')
- signo = config.signal;
- nr.triggerReport(signo, 'Signal', null, '');
+function addSignalHandler(sig) {
+ if (nr.shouldReportOnSignal()) {
+ if (typeof sig !== 'string')
+ sig = nr.getSignal();
+
+ process.on(sig, signalHandler);
+ }
+}
+
+function removeSignalHandler() {
+ const sig = nr.getSignal();
+
+ if (sig)
+ process.removeListener(sig, signalHandler);
+}
+
+function signalHandler(sig) {
+ nr.triggerReport(sig, 'Signal', null, '');
}
module.exports = {
- config,
- handleSignal,
- report,
- syncConfig: nr.syncConfig
+ addSignalHandler,
+ report
};
diff --git a/src/node_options.cc b/src/node_options.cc
index 7107549bd4..f2961b2147 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -57,8 +57,14 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors) {
void PerIsolateOptions::CheckOptions(std::vector<std::string>* errors) {
per_env->CheckOptions(errors);
#ifdef NODE_REPORT
- if (per_env->experimental_report)
+ if (per_env->experimental_report) {
+ // Assign the report_signal default value here. Once the
+ // --experimental-report flag is dropped, move this initialization to
+ // node_options.h, where report_signal is declared.
+ if (report_signal.empty())
+ report_signal = "SIGUSR2";
return;
+ }
if (!report_directory.empty()) {
errors->push_back("--diagnostic-report-directory option is valid only when "
diff --git a/src/node_report_module.cc b/src/node_report_module.cc
index cf17db62db..202730d3b6 100644
--- a/src/node_report_module.cc
+++ b/src/node_report_module.cc
@@ -18,10 +18,7 @@
namespace report {
using node::Environment;
-using node::FIXED_ONE_BYTE_STRING;
-using node::PerIsolateOptions;
using node::Utf8Value;
-using v8::Array;
using v8::Boolean;
using v8::Context;
using v8::Function;
@@ -31,7 +28,6 @@ using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
-using v8::V8;
using v8::Value;
// External JavaScript API for triggering a report
@@ -75,129 +71,89 @@ void GetReport(const FunctionCallbackInfo<Value>& info) {
.ToLocalChecked());
}
-// A method to sync up data elements in the JS land with its
-// corresponding elements in the C++ world. Required because
-// (i) the tunables are first intercepted through the CLI but
-// later modified via APIs. (ii) the report generation events
-// are controlled partly from C++ and partly from JS.
-void SyncConfig(const FunctionCallbackInfo<Value>& info) {
+static void GetDirectory(const FunctionCallbackInfo<Value>& info) {
Environment* env = Environment::GetCurrent(info);
- Local<Context> context = env->context();
- std::shared_ptr<PerIsolateOptions> options = env->isolate_data()->options();
-
- CHECK_EQ(info.Length(), 2);
- Local<Object> obj;
- if (!info[0]->ToObject(context).ToLocal(&obj)) return;
- bool sync = info[1].As<Boolean>()->Value();
-
- // Events array
- Local<String> eventskey = FIXED_ONE_BYTE_STRING(env->isolate(), "events");
- Local<Value> events_unchecked;
- if (!obj->Get(context, eventskey).ToLocal(&events_unchecked)) return;
- Local<Array> events;
- if (events_unchecked->IsUndefined() || events_unchecked->IsNull()) {
- events_unchecked = Array::New(env->isolate(), 0);
- if (obj->Set(context, eventskey, events_unchecked).IsNothing()) return;
- }
- events = events_unchecked.As<Array>();
-
- // Signal
- Local<String> signalkey = env->signal_string();
- Local<Value> signal_unchecked;
- if (!obj->Get(context, signalkey).ToLocal(&signal_unchecked)) return;
- Local<String> signal;
- if (signal_unchecked->IsUndefined() || signal_unchecked->IsNull())
- signal_unchecked = signalkey;
- signal = signal_unchecked.As<String>();
-
- Utf8Value signalstr(env->isolate(), signal);
-
- // Report file
- Local<String> filekey = FIXED_ONE_BYTE_STRING(env->isolate(), "filename");
- Local<Value> file_unchecked;
- if (!obj->Get(context, filekey).ToLocal(&file_unchecked)) return;
- Local<String> file;
- if (file_unchecked->IsUndefined() || file_unchecked->IsNull())
- file_unchecked = filekey;
- file = file_unchecked.As<String>();
-
- Utf8Value filestr(env->isolate(), file);
-
- // Report file path
- Local<String> pathkey = FIXED_ONE_BYTE_STRING(env->isolate(), "path");
- Local<Value> path_unchecked;
- if (!obj->Get(context, pathkey).ToLocal(&path_unchecked)) return;
- Local<String> path;
- if (path_unchecked->IsUndefined() || path_unchecked->IsNull())
- path_unchecked = pathkey;
- path = path_unchecked.As<String>();
-
- Utf8Value pathstr(env->isolate(), path);
-
- if (sync) {
- static const std::string e = "exception";
- static const std::string s = "signal";
- static const std::string f = "fatalerror";
- for (uint32_t i = 0; i < events->Length(); i++) {
- Local<Value> v;
- if (!events->Get(context, i).ToLocal(&v)) return;
- Local<String> elem;
- if (!v->ToString(context).ToLocal(&elem)) return;
- String::Utf8Value buf(env->isolate(), elem);
- if (*buf == e) {
- options->report_uncaught_exception = true;
- } else if (*buf == s) {
- options->report_on_signal = true;
- } else if (*buf == f) {
- options->report_on_fatalerror = true;
- }
- }
- CHECK_NOT_NULL(*signalstr);
- options->report_signal = *signalstr;
- CHECK_NOT_NULL(*filestr);
- options->report_filename = *filestr;
- CHECK_NOT_NULL(*pathstr);
- options->report_directory = *pathstr;
- } else {
- int i = 0;
- if (options->report_uncaught_exception &&
- events
- ->Set(context,
- i++,
- FIXED_ONE_BYTE_STRING(env->isolate(), "exception"))
- .IsNothing())
- return;
- if (options->report_on_signal &&
- events
- ->Set(context, i++, FIXED_ONE_BYTE_STRING(env->isolate(), "signal"))
- .IsNothing())
- return;
- if (options->report_on_fatalerror &&
- events
- ->Set(
- context, i, FIXED_ONE_BYTE_STRING(env->isolate(), "fatalerror"))
- .IsNothing())
- return;
-
- Local<Value> signal_value;
- Local<Value> file_value;
- Local<Value> path_value;
- std::string signal = options->report_signal;
- if (signal.empty()) signal = "SIGUSR2";
- if (!node::ToV8Value(context, signal).ToLocal(&signal_value))
- return;
- if (!obj->Set(context, signalkey, signal_value).FromJust()) return;
-
- if (!node::ToV8Value(context, options->report_filename)
- .ToLocal(&file_value))
- return;
- if (!obj->Set(context, filekey, file_value).FromJust()) return;
-
- if (!node::ToV8Value(context, options->report_directory)
- .ToLocal(&path_value))
- return;
- if (!obj->Set(context, pathkey, path_value).FromJust()) return;
- }
+ std::string directory = env->isolate_data()->options()->report_directory;
+ auto result = String::NewFromUtf8(env->isolate(),
+ directory.c_str(),
+ v8::NewStringType::kNormal);
+ info.GetReturnValue().Set(result.ToLocalChecked());
+}
+
+static void SetDirectory(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ CHECK(info[0]->IsString());
+ Utf8Value dir(env->isolate(), info[0].As<String>());
+ env->isolate_data()->options()->report_directory = *dir;
+}
+
+static void GetFilename(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ std::string filename = env->isolate_data()->options()->report_filename;
+ auto result = String::NewFromUtf8(env->isolate(),
+ filename.c_str(),
+ v8::NewStringType::kNormal);
+ info.GetReturnValue().Set(result.ToLocalChecked());
+}
+
+static void SetFilename(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ CHECK(info[0]->IsString());
+ Utf8Value name(env->isolate(), info[0].As<String>());
+ env->isolate_data()->options()->report_filename = *name;
+}
+
+static void GetSignal(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ std::string signal = env->isolate_data()->options()->report_signal;
+ auto result = String::NewFromUtf8(env->isolate(),
+ signal.c_str(),
+ v8::NewStringType::kNormal);
+ info.GetReturnValue().Set(result.ToLocalChecked());
+}
+
+static void SetSignal(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ CHECK(info[0]->IsString());
+ Utf8Value signal(env->isolate(), info[0].As<String>());
+ env->isolate_data()->options()->report_signal = *signal;
+}
+
+static void ShouldReportOnFatalError(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ info.GetReturnValue().Set(
+ env->isolate_data()->options()->report_on_fatalerror);
+}
+
+static void SetReportOnFatalError(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ CHECK(info[0]->IsBoolean());
+ env->isolate_data()->options()->report_on_fatalerror = info[0]->IsTrue();
+}
+
+static void ShouldReportOnSignal(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ info.GetReturnValue().Set(env->isolate_data()->options()->report_on_signal);
+}
+
+static void SetReportOnSignal(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ CHECK(info[0]->IsBoolean());
+ env->isolate_data()->options()->report_on_signal = info[0]->IsTrue();
+}
+
+static void ShouldReportOnUncaughtException(
+ const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ info.GetReturnValue().Set(
+ env->isolate_data()->options()->report_uncaught_exception);
+}
+
+static void SetReportOnUncaughtException(
+ const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ CHECK(info[0]->IsBoolean());
+ env->isolate_data()->options()->report_uncaught_exception = info[0]->IsTrue();
}
static void Initialize(Local<Object> exports,
@@ -207,7 +163,20 @@ static void Initialize(Local<Object> exports,
env->SetMethod(exports, "triggerReport", TriggerReport);
env->SetMethod(exports, "getReport", GetReport);
- env->SetMethod(exports, "syncConfig", SyncConfig);
+ env->SetMethod(exports, "getDirectory", GetDirectory);
+ env->SetMethod(exports, "setDirectory", SetDirectory);
+ env->SetMethod(exports, "getFilename", GetFilename);
+ env->SetMethod(exports, "setFilename", SetFilename);
+ env->SetMethod(exports, "getSignal", GetSignal);
+ env->SetMethod(exports, "setSignal", SetSignal);
+ env->SetMethod(exports, "shouldReportOnFatalError", ShouldReportOnFatalError);
+ env->SetMethod(exports, "setReportOnFatalError", SetReportOnFatalError);
+ env->SetMethod(exports, "shouldReportOnSignal", ShouldReportOnSignal);
+ env->SetMethod(exports, "setReportOnSignal", SetReportOnSignal);
+ env->SetMethod(exports, "shouldReportOnUncaughtException",
+ ShouldReportOnUncaughtException);
+ env->SetMethod(exports, "setReportOnUncaughtException",
+ SetReportOnUncaughtException);
}
} // namespace report
diff --git a/test/report/test-report-config.js b/test/report/test-report-config.js
new file mode 100644
index 0000000000..796f63d2b8
--- /dev/null
+++ b/test/report/test-report-config.js
@@ -0,0 +1,87 @@
+// Flags: --experimental-report --diagnostic-report-on-fatalerror --diagnostic-report-on-signal --diagnostic-report-uncaught-exception
+'use strict';
+const common = require('../common');
+common.skipIfReportDisabled();
+const assert = require('assert');
+
+common.expectWarning('ExperimentalWarning',
+ 'report is an experimental feature. This feature could ' +
+ 'change at any time');
+
+// Verify that process.report.directory behaves properly.
+assert.strictEqual(process.report.directory, '');
+process.report.directory = __dirname;
+assert.strictEqual(process.report.directory, __dirname);
+common.expectsError(() => {
+ process.report.directory = {};
+}, { code: 'ERR_INVALID_ARG_TYPE' });
+assert.strictEqual(process.report.directory, __dirname);
+
+// Verify that process.report.filename behaves properly.
+assert.strictEqual(process.report.filename, '');
+process.report.filename = 'test-report.json';
+assert.strictEqual(process.report.filename, 'test-report.json');
+common.expectsError(() => {
+ process.report.filename = {};
+}, { code: 'ERR_INVALID_ARG_TYPE' });
+assert.strictEqual(process.report.filename, 'test-report.json');
+
+// Verify that process.report.reportOnFatalError behaves properly.
+assert.strictEqual(process.report.reportOnFatalError, true);
+process.report.reportOnFatalError = false;
+assert.strictEqual(process.report.reportOnFatalError, false);
+process.report.reportOnFatalError = true;
+assert.strictEqual(process.report.reportOnFatalError, true);
+common.expectsError(() => {
+ process.report.reportOnFatalError = {};
+}, { code: 'ERR_INVALID_ARG_TYPE' });
+assert.strictEqual(process.report.reportOnFatalError, true);
+
+
+// Verify that process.report.reportOnUncaughtException behaves properly.
+assert.strictEqual(process.report.reportOnUncaughtException, true);
+process.report.reportOnUncaughtException = false;
+assert.strictEqual(process.report.reportOnUncaughtException, false);
+process.report.reportOnUncaughtException = true;
+assert.strictEqual(process.report.reportOnUncaughtException, true);
+common.expectsError(() => {
+ process.report.reportOnUncaughtException = {};
+}, { code: 'ERR_INVALID_ARG_TYPE' });
+assert.strictEqual(process.report.reportOnUncaughtException, true);
+
+// Verify that process.report.reportOnSignal behaves properly.
+assert.strictEqual(process.report.reportOnSignal, true);
+process.report.reportOnSignal = false;
+assert.strictEqual(process.report.reportOnSignal, false);
+process.report.reportOnSignal = true;
+assert.strictEqual(process.report.reportOnSignal, true);
+common.expectsError(() => {
+ process.report.reportOnSignal = {};
+}, { code: 'ERR_INVALID_ARG_TYPE' });
+assert.strictEqual(process.report.reportOnSignal, true);
+
+if (!common.isWindows) {
+ // Verify that process.report.signal behaves properly.
+ assert.strictEqual(process.report.signal, 'SIGUSR2');
+ common.expectsError(() => {
+ process.report.signal = {};
+ }, { code: 'ERR_INVALID_ARG_TYPE' });
+ common.expectsError(() => {
+ process.report.signal = 'foo';
+ }, { code: 'ERR_UNKNOWN_SIGNAL' });
+ assert.strictEqual(process.report.signal, 'SIGUSR2');
+ process.report.signal = 'SIGUSR1';
+ assert.strictEqual(process.report.signal, 'SIGUSR1');
+
+ // Verify that the interaction between reportOnSignal and signal is correct.
+ process.report.signal = 'SIGUSR2';
+ process.report.reportOnSignal = false;
+ assert.strictEqual(process.listenerCount('SIGUSR2'), 0);
+ process.report.reportOnSignal = true;
+ assert.strictEqual(process.listenerCount('SIGUSR2'), 1);
+ process.report.signal = 'SIGUSR1';
+ assert.strictEqual(process.listenerCount('SIGUSR2'), 0);
+ assert.strictEqual(process.listenerCount('SIGUSR1'), 1);
+ process.report.reportOnSignal = false;
+ assert.strictEqual(process.listenerCount('SIGUSR1'), 0);
+}
diff --git a/test/report/test-report-signal.js b/test/report/test-report-signal.js
index 129933b546..68f5c25402 100644
--- a/test/report/test-report-signal.js
+++ b/test/report/test-report-signal.js
@@ -64,6 +64,7 @@ if (process.argv[2] === 'child') {
});
child.on('exit', common.mustCall((code, signal) => {
console.log('child exited');
+ console.log(stderr);
const report_msg = 'No reports found';
const process_msg = 'Process exited unexpectedly';
const signal_msg = 'Process exited with unexpected signal';
diff --git a/test/report/test-report-triggerreport.js b/test/report/test-report-triggerreport.js
index 4ac8f1a9a5..c9ad8cdee0 100644
--- a/test/report/test-report-triggerreport.js
+++ b/test/report/test-report-triggerreport.js
@@ -14,7 +14,7 @@ common.expectWarning('ExperimentalWarning',
'report is an experimental feature. This feature could ' +
'change at any time');
tmpdir.refresh();
-process.report.setOptions({ path: tmpdir.path });
+process.report.directory = tmpdir.path;
function validate() {
const reports = helper.findReports(process.pid, tmpdir.path);
@@ -59,11 +59,11 @@ function validate() {
{
// Test with a filename option.
- const filename = path.join(tmpdir.path, 'custom-name-3.json');
- process.report.setOptions({ filename });
+ process.report.filename = 'custom-name-3.json';
const file = process.report.triggerReport();
assert.strictEqual(helper.findReports(process.pid, tmpdir.path).length, 0);
- assert.strictEqual(file, filename);
+ const filename = path.join(process.report.directory, 'custom-name-3.json');
+ assert.strictEqual(file, process.report.filename);
helper.validate(filename);
fs.unlinkSync(filename);
}
diff --git a/test/report/test-report-uncaught-exception.js b/test/report/test-report-uncaught-exception.js
index b3da7c4244..34e4759ea2 100644
--- a/test/report/test-report-uncaught-exception.js
+++ b/test/report/test-report-uncaught-exception.js
@@ -12,7 +12,7 @@ common.expectWarning('ExperimentalWarning',
'report is an experimental feature. This feature could ' +
'change at any time');
tmpdir.refresh();
-process.report.setOptions({ path: tmpdir.path });
+process.report.directory = tmpdir.path;
process.on('uncaughtException', common.mustCall((err) => {
assert.strictEqual(err, error);