Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

report: skip report if uncaught exception is handled #44208

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 0 additions & 21 deletions lib/internal/process/execution.js
Expand Up @@ -151,27 +151,6 @@ function createOnGlobalUncaughtException() {
// call that threw and was never cleared. So clear it now.
clearDefaultTriggerAsyncId();

// If diagnostic reporting is enabled, call into its handler to see
// whether it is interested in handling the situation.
// Ignore if the error is scoped inside a domain.
// use == in the checks as we want to allow for null and undefined
if (er == null || er.domain == null) {
try {
const report = internalBinding('report');
if (report != null && report.shouldReportOnUncaughtException()) {
report.writeReport(
typeof er?.message === 'string' ?
er.message :
'Exception',
'Exception',
null,
er ?? {});
}
} catch {
// Ignore the exception. Diagnostic reporting is unavailable.
}
}

const type = fromPromise ? 'unhandledRejection' : 'uncaughtException';
process.emit('uncaughtExceptionMonitor', er, type);
if (exceptionHandlerState.captureFn !== null) {
Expand Down
8 changes: 8 additions & 0 deletions src/node_errors.cc
Expand Up @@ -392,6 +392,7 @@ static void ReportFatalException(Environment* env,
}

node::Utf8Value trace(env->isolate(), stack_trace);
std::string report_message = "Exception";

// range errors have a trace member set to undefined
if (trace.length() > 0 && !stack_trace->IsUndefined()) {
Expand Down Expand Up @@ -424,6 +425,8 @@ static void ReportFatalException(Environment* env,
} else {
node::Utf8Value name_string(env->isolate(), name.ToLocalChecked());
node::Utf8Value message_string(env->isolate(), message.ToLocalChecked());
// Update the report message if it is an object has message property.
report_message = message_string.ToString();

if (arrow.IsEmpty() || !arrow->IsString() || decorated) {
FPrintF(stderr, "%s: %s\n", name_string, message_string);
Expand All @@ -445,6 +448,11 @@ static void ReportFatalException(Environment* env,
}
}

if (env->isolate_data()->options()->report_uncaught_exception) {
report::TriggerNodeReport(
isolate, env, report_message.c_str(), "Exception", "", error);
}

if (env->options()->trace_uncaught) {
Local<StackTrace> trace = message->GetStackTrace();
if (!trace.IsEmpty()) {
Expand Down
35 changes: 31 additions & 4 deletions test/report/test-report-uncaught-exception-compat.js
@@ -1,5 +1,32 @@
// Flags: --experimental-report --report-uncaught-exception --report-compact
'use strict';
// Test producing a compact report on uncaught exception.
require('../common');
require('./test-report-uncaught-exception.js');
// Test producing a report on uncaught exception.
const common = require('../common');
const assert = require('assert');
const childProcess = require('child_process');
const helper = require('../common/report');
const tmpdir = require('../common/tmpdir');

if (process.argv[2] === 'child') {
throw new Error('test error');
}

tmpdir.refresh();
const child = childProcess.spawn(process.execPath, [
'--report-uncaught-exception',
'--report-compact',
__filename,
'child',
], {
cwd: tmpdir.path
});
child.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 1);
const reports = helper.findReports(child.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);

helper.validate(reports[0], [
['header.event', 'Exception'],
['header.trigger', 'Exception'],
['javascriptStack.message', 'Error: test error'],
]);
}));
23 changes: 23 additions & 0 deletions test/report/test-report-uncaught-exception-handled.js
@@ -0,0 +1,23 @@
// Flags: --report-uncaught-exception
'use strict';
// Test report is suppressed on uncaught exception hook.
const common = require('../common');
const assert = require('assert');
const helper = require('../common/report');
const tmpdir = require('../common/tmpdir');
const error = new Error('test error');

tmpdir.refresh();
process.report.directory = tmpdir.path;

// Make sure the uncaughtException listener is called.
process.on('uncaughtException', common.mustCall());

process.on('exit', (code) => {
assert.strictEqual(code, 0);
// Make sure no reports are generated.
const reports = helper.findReports(process.pid, tmpdir.path);
assert.strictEqual(reports.length, 0);
});

throw error;
4 changes: 1 addition & 3 deletions test/report/test-report-uncaught-exception-override.js
Expand Up @@ -12,9 +12,7 @@ process.report.directory = tmpdir.path;

// First, install an uncaught exception hook.
process.setUncaughtExceptionCaptureCallback(common.mustCall());

// Make sure this is ignored due to the above override.
process.on('uncaughtException', common.mustNotCall());
// Do not install process uncaughtException handler.

process.on('exit', (code) => {
assert.strictEqual(code, 0);
Expand Down
27 changes: 17 additions & 10 deletions test/report/test-report-uncaught-exception-primitives.js
@@ -1,25 +1,32 @@
// Flags: --report-uncaught-exception
'use strict';
// Test producing a report on uncaught exception.
const common = require('../common');
const assert = require('assert');
const childProcess = require('child_process');
const helper = require('../common/report');
const tmpdir = require('../common/tmpdir');

const exception = 1;
if (process.argv[2] === 'child') {
// eslint-disable-next-line no-throw-literal
throw 1;
}

tmpdir.refresh();
process.report.directory = tmpdir.path;

process.on('uncaughtException', common.mustCall((err) => {
assert.strictEqual(err, exception);
const reports = helper.findReports(process.pid, tmpdir.path);
const child = childProcess.spawn(process.execPath, [
'--report-uncaught-exception',
__filename,
'child',
], {
cwd: tmpdir.path,
});
child.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 1);
const reports = helper.findReports(child.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);

helper.validate(reports[0], [
['header.event', 'Exception'],
['javascriptStack.message', `${exception}`],
['header.trigger', 'Exception'],
['javascriptStack.message', '1'],
]);
}));

throw exception;
24 changes: 15 additions & 9 deletions test/report/test-report-uncaught-exception-symbols.js
@@ -1,25 +1,31 @@
// Flags: --report-uncaught-exception
'use strict';
// Test producing a report on uncaught exception.
const common = require('../common');
const assert = require('assert');
const childProcess = require('child_process');
const helper = require('../common/report');
const tmpdir = require('../common/tmpdir');

const exception = Symbol('foobar');
if (process.argv[2] === 'child') {
throw Symbol('foobar');
}

tmpdir.refresh();
process.report.directory = tmpdir.path;

process.on('uncaughtException', common.mustCall((err) => {
assert.strictEqual(err, exception);
const reports = helper.findReports(process.pid, tmpdir.path);
const child = childProcess.spawn(process.execPath, [
'--report-uncaught-exception',
__filename,
'child',
], {
cwd: tmpdir.path,
});
child.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 1);
const reports = helper.findReports(child.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);

helper.validate(reports[0], [
['header.event', 'Exception'],
['header.trigger', 'Exception'],
['javascriptStack.message', 'Symbol(foobar)'],
]);
}));

throw exception;
31 changes: 21 additions & 10 deletions test/report/test-report-uncaught-exception.js
@@ -1,20 +1,31 @@
// Flags: --report-uncaught-exception
'use strict';
// Test producing a report on uncaught exception.
const common = require('../common');
const assert = require('assert');
const childProcess = require('child_process');
const helper = require('../common/report');
const tmpdir = require('../common/tmpdir');
const error = new Error('test error');

tmpdir.refresh();
process.report.directory = tmpdir.path;
if (process.argv[2] === 'child') {
throw new Error('test error');
}

process.on('uncaughtException', common.mustCall((err) => {
assert.strictEqual(err, error);
const reports = helper.findReports(process.pid, tmpdir.path);
tmpdir.refresh();
const child = childProcess.spawn(process.execPath, [
'--report-uncaught-exception',
__filename,
'child',
], {
cwd: tmpdir.path,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

});
child.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 1);
const reports = helper.findReports(child.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);
helper.validate(reports[0]);
}));

throw error;
helper.validate(reports[0], [
['header.event', 'Exception'],
['header.trigger', 'Exception'],
['javascriptStack.message', 'Error: test error'],
]);
}));