report: refactor configuration management

This commit removes process.report.setOptions(). Instead of
using complex configuration synchronization between C++ and
JS, this commit introduces individual getters and setters.

PR-URL: https://github.com/nodejs/node/pull/26414
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Reviewed-By: Wyatt Preul <wpreul@gmail.com>
This commit is contained in:
cjihrig 2019-03-03 12:05:38 -05:00
parent c78788a021
commit 30ee27784c
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
11 changed files with 381 additions and 268 deletions

View File

@ -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'] });
// Change the default path and filename of the report.
process.report.setOptions({ filename: 'foo.json', path: '/home' });
// 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' });
console.log(`Report filename is ${process.report.filename}`);
```
Signal based report generation is not supported on Windows.
## process.report.reportOnFatalError
<!-- YAML
added: REPLACEME
-->
Additional documentation is available in the [report documentation][].
* {boolean}
If `true`, a diagnostic report is generated on fatal errors, such as out of
memory errors or failed C++ assertions.
```js
console.log(`Report on fatal error: ${process.report.reportOnFatalError}`);
```
## process.report.reportOnSignal
<!-- YAML
added: REPLACEME
-->
* {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.

View File

@ -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`.
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.
`reportOnSignal` triggers diagnostic reporting on signal when `true`. This is
not supported on Windows. Defaults to `false`.
`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

View File

@ -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() {

View File

@ -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.
}

View File

@ -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
};

View File

@ -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 "

View File

@ -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();
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());
}
CHECK_EQ(info.Length(), 2);
Local<Object> obj;
if (!info[0]->ToObject(context).ToLocal(&obj)) return;
bool sync = info[1].As<Boolean>()->Value();
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;
}
// 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>();
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());
}
// 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>();
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;
}
Utf8Value signalstr(env->isolate(), signal);
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());
}
// 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>();
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;
}
Utf8Value filestr(env->isolate(), file);
static void ShouldReportOnFatalError(const FunctionCallbackInfo<Value>& info) {
Environment* env = Environment::GetCurrent(info);
info.GetReturnValue().Set(
env->isolate_data()->options()->report_on_fatalerror);
}
// 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>();
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();
}
Utf8Value pathstr(env->isolate(), path);
static void ShouldReportOnSignal(const FunctionCallbackInfo<Value>& info) {
Environment* env = Environment::GetCurrent(info);
info.GetReturnValue().Set(env->isolate_data()->options()->report_on_signal);
}
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;
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();
}
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;
static void ShouldReportOnUncaughtException(
const FunctionCallbackInfo<Value>& info) {
Environment* env = Environment::GetCurrent(info);
info.GetReturnValue().Set(
env->isolate_data()->options()->report_uncaught_exception);
}
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;
}
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

View File

@ -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);
}

View File

@ -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';

View File

@ -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);
}

View File

@ -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);