Skip to content

Commit 678551c

Browse files
committed
report: skip report if uncaught exception is handled
If the exception is handled by the userland process#uncaughtException handler, reports should not be generated repetitively as the process may continue to run. PR-URL: #44208 Reviewed-By: Rafael Gonzaga <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Stephen Belanger <[email protected]>
1 parent 5e5fb82 commit 678551c

9 files changed

+122
-60
lines changed

doc/api/cli.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,9 @@ Default signal is `SIGUSR2`.
11391139
<!-- YAML
11401140
added: v11.8.0
11411141
changes:
1142+
- version: REPLACEME
1143+
pr-url: https://github.com/nodejs/node/pull/44208
1144+
description: Report is not generated if the uncaught exception is handled.
11421145
- version:
11431146
- v13.12.0
11441147
- v12.17.0
@@ -1150,9 +1153,9 @@ changes:
11501153
`--report-uncaught-exception`.
11511154
-->
11521155

1153-
Enables report to be generated on uncaught exceptions. Useful when inspecting
1154-
the JavaScript stack in conjunction with native stack and other runtime
1155-
environment data.
1156+
Enables report to be generated when the process exits due to an uncaught
1157+
exception. Useful when inspecting the JavaScript stack in conjunction with
1158+
native stack and other runtime environment data.
11561159

11571160
### `--secure-heap=n`
11581161

lib/internal/process/execution.js

-21
Original file line numberDiff line numberDiff line change
@@ -151,27 +151,6 @@ function createOnGlobalUncaughtException() {
151151
// call that threw and was never cleared. So clear it now.
152152
clearDefaultTriggerAsyncId();
153153

154-
// If diagnostic reporting is enabled, call into its handler to see
155-
// whether it is interested in handling the situation.
156-
// Ignore if the error is scoped inside a domain.
157-
// use == in the checks as we want to allow for null and undefined
158-
if (er == null || er.domain == null) {
159-
try {
160-
const report = internalBinding('report');
161-
if (report != null && report.shouldReportOnUncaughtException()) {
162-
report.writeReport(
163-
typeof er?.message === 'string' ?
164-
er.message :
165-
'Exception',
166-
'Exception',
167-
null,
168-
er ?? {});
169-
}
170-
} catch {
171-
// Ignore the exception. Diagnostic reporting is unavailable.
172-
}
173-
}
174-
175154
const type = fromPromise ? 'unhandledRejection' : 'uncaughtException';
176155
process.emit('uncaughtExceptionMonitor', er, type);
177156
if (exceptionHandlerState.captureFn !== null) {

src/node_errors.cc

+8
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ static void ReportFatalException(Environment* env,
392392
}
393393

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

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

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

451+
if (env->isolate_data()->options()->report_uncaught_exception) {
452+
report::TriggerNodeReport(
453+
isolate, env, report_message.c_str(), "Exception", "", error);
454+
}
455+
448456
if (env->options()->trace_uncaught) {
449457
Local<StackTrace> trace = message->GetStackTrace();
450458
if (!trace.IsEmpty()) {
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
1-
// Flags: --experimental-report --report-uncaught-exception --report-compact
21
'use strict';
3-
// Test producing a compact report on uncaught exception.
4-
require('../common');
5-
require('./test-report-uncaught-exception.js');
2+
// Test producing a report on uncaught exception.
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const childProcess = require('child_process');
6+
const helper = require('../common/report');
7+
const tmpdir = require('../common/tmpdir');
8+
9+
if (process.argv[2] === 'child') {
10+
throw new Error('test error');
11+
}
12+
13+
tmpdir.refresh();
14+
const child = childProcess.spawn(process.execPath, [
15+
'--report-uncaught-exception',
16+
'--report-compact',
17+
__filename,
18+
'child',
19+
], {
20+
cwd: tmpdir.path
21+
});
22+
child.on('exit', common.mustCall((code) => {
23+
assert.strictEqual(code, 1);
24+
const reports = helper.findReports(child.pid, tmpdir.path);
25+
assert.strictEqual(reports.length, 1);
26+
27+
helper.validate(reports[0], [
28+
['header.event', 'Exception'],
29+
['header.trigger', 'Exception'],
30+
['javascriptStack.message', 'Error: test error'],
31+
]);
32+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Flags: --report-uncaught-exception
2+
'use strict';
3+
// Test report is suppressed on uncaught exception hook.
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const helper = require('../common/report');
7+
const tmpdir = require('../common/tmpdir');
8+
const error = new Error('test error');
9+
10+
tmpdir.refresh();
11+
process.report.directory = tmpdir.path;
12+
13+
// Make sure the uncaughtException listener is called.
14+
process.on('uncaughtException', common.mustCall());
15+
16+
process.on('exit', (code) => {
17+
assert.strictEqual(code, 0);
18+
// Make sure no reports are generated.
19+
const reports = helper.findReports(process.pid, tmpdir.path);
20+
assert.strictEqual(reports.length, 0);
21+
});
22+
23+
throw error;

test/report/test-report-uncaught-exception-override.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ process.report.directory = tmpdir.path;
1212

1313
// First, install an uncaught exception hook.
1414
process.setUncaughtExceptionCaptureCallback(common.mustCall());
15-
16-
// Make sure this is ignored due to the above override.
17-
process.on('uncaughtException', common.mustNotCall());
15+
// Do not install process uncaughtException handler.
1816

1917
process.on('exit', (code) => {
2018
assert.strictEqual(code, 0);

test/report/test-report-uncaught-exception-primitives.js

+17-10
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
1-
// Flags: --report-uncaught-exception
21
'use strict';
32
// Test producing a report on uncaught exception.
43
const common = require('../common');
54
const assert = require('assert');
5+
const childProcess = require('child_process');
66
const helper = require('../common/report');
77
const tmpdir = require('../common/tmpdir');
88

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

1114
tmpdir.refresh();
12-
process.report.directory = tmpdir.path;
13-
14-
process.on('uncaughtException', common.mustCall((err) => {
15-
assert.strictEqual(err, exception);
16-
const reports = helper.findReports(process.pid, tmpdir.path);
15+
const child = childProcess.spawn(process.execPath, [
16+
'--report-uncaught-exception',
17+
__filename,
18+
'child',
19+
], {
20+
cwd: tmpdir.path,
21+
});
22+
child.on('exit', common.mustCall((code) => {
23+
assert.strictEqual(code, 1);
24+
const reports = helper.findReports(child.pid, tmpdir.path);
1725
assert.strictEqual(reports.length, 1);
1826

1927
helper.validate(reports[0], [
2028
['header.event', 'Exception'],
21-
['javascriptStack.message', `${exception}`],
29+
['header.trigger', 'Exception'],
30+
['javascriptStack.message', '1'],
2231
]);
2332
}));
24-
25-
throw exception;

test/report/test-report-uncaught-exception-symbols.js

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
1-
// Flags: --report-uncaught-exception
21
'use strict';
32
// Test producing a report on uncaught exception.
43
const common = require('../common');
54
const assert = require('assert');
5+
const childProcess = require('child_process');
66
const helper = require('../common/report');
77
const tmpdir = require('../common/tmpdir');
88

9-
const exception = Symbol('foobar');
9+
if (process.argv[2] === 'child') {
10+
throw Symbol('foobar');
11+
}
1012

1113
tmpdir.refresh();
12-
process.report.directory = tmpdir.path;
13-
14-
process.on('uncaughtException', common.mustCall((err) => {
15-
assert.strictEqual(err, exception);
16-
const reports = helper.findReports(process.pid, tmpdir.path);
14+
const child = childProcess.spawn(process.execPath, [
15+
'--report-uncaught-exception',
16+
__filename,
17+
'child',
18+
], {
19+
cwd: tmpdir.path,
20+
});
21+
child.on('exit', common.mustCall((code) => {
22+
assert.strictEqual(code, 1);
23+
const reports = helper.findReports(child.pid, tmpdir.path);
1724
assert.strictEqual(reports.length, 1);
1825

1926
helper.validate(reports[0], [
2027
['header.event', 'Exception'],
28+
['header.trigger', 'Exception'],
2129
['javascriptStack.message', 'Symbol(foobar)'],
2230
]);
2331
}));
24-
25-
throw exception;
+21-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
1-
// Flags: --report-uncaught-exception
21
'use strict';
32
// Test producing a report on uncaught exception.
43
const common = require('../common');
54
const assert = require('assert');
5+
const childProcess = require('child_process');
66
const helper = require('../common/report');
77
const tmpdir = require('../common/tmpdir');
8-
const error = new Error('test error');
98

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

13-
process.on('uncaughtException', common.mustCall((err) => {
14-
assert.strictEqual(err, error);
15-
const reports = helper.findReports(process.pid, tmpdir.path);
13+
tmpdir.refresh();
14+
const child = childProcess.spawn(process.execPath, [
15+
'--report-uncaught-exception',
16+
__filename,
17+
'child',
18+
], {
19+
cwd: tmpdir.path,
20+
});
21+
child.on('exit', common.mustCall((code) => {
22+
assert.strictEqual(code, 1);
23+
const reports = helper.findReports(child.pid, tmpdir.path);
1624
assert.strictEqual(reports.length, 1);
17-
helper.validate(reports[0]);
18-
}));
1925

20-
throw error;
26+
helper.validate(reports[0], [
27+
['header.event', 'Exception'],
28+
['header.trigger', 'Exception'],
29+
['javascriptStack.message', 'Error: test error'],
30+
]);
31+
}));

0 commit comments

Comments
 (0)