Skip to content

Commit 31217a8

Browse files
addaleaxdanbev
authored andcommitted
cli: add --trace-uncaught flag
Add a flag that makes Node.js print the stack trace at the time of *throwing* uncaught exceptions, rather than at the creation of the `Error` object, if there is any. This is disabled by default because it affects GC behavior. PR-URL: #30025 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Richard Lau <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent 71b342f commit 31217a8

17 files changed

+111
-0
lines changed

doc/api/cli.md

+13
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,18 @@ added: v12.2.0
747747
Prints TLS packet trace information to `stderr`. This can be used to debug TLS
748748
connection problems.
749749

750+
### `--trace-uncaught`
751+
<!-- YAML
752+
added: REPLACEME
753+
-->
754+
755+
Print stack traces for uncaught exceptions; usually, the stack trace associated
756+
with the creation of an `Error` is printed, whereas this makes Node.js also
757+
print the stack trace associated with throwing the value (which does not need
758+
to be an `Error` instance).
759+
760+
Enabling this option may affect garbage collection behavior negatively.
761+
750762
### `--trace-warnings`
751763
<!-- YAML
752764
added: v6.0.0
@@ -1044,6 +1056,7 @@ Node.js options that are allowed are:
10441056
* `--trace-events-enabled`
10451057
* `--trace-sync-io`
10461058
* `--trace-tls`
1059+
* `--trace-uncaught`
10471060
* `--trace-warnings`
10481061
* `--track-heap-objects`
10491062
* `--unhandled-rejections`

doc/node.1

+12
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,18 @@ Print a stack trace whenever synchronous I/O is detected after the first turn of
337337
.It Fl -trace-tls
338338
Prints TLS packet trace information to stderr.
339339
.
340+
.It Fl -trace-uncaught
341+
Print stack traces for uncaught exceptions; usually, the stack trace associated
342+
with the creation of an
343+
.Sy Error
344+
is printed, whereas this makes Node.js also
345+
print the stack trace associated with throwing the value (which does not need
346+
to be an
347+
.Sy Error
348+
instance).
349+
.Pp
350+
Enabling this option may affect garbage collection behavior negatively.
351+
.
340352
.It Fl -trace-warnings
341353
Print stack traces for process warnings (including deprecations).
342354
.

src/node.cc

+2
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ int Environment::InitializeInspector(
265265
void Environment::InitializeDiagnostics() {
266266
isolate_->GetHeapProfiler()->AddBuildEmbedderGraphCallback(
267267
Environment::BuildEmbedderGraph, this);
268+
if (options_->trace_uncaught)
269+
isolate_->SetCaptureStackTraceForUncaughtExceptions(true);
268270

269271
#if defined HAVE_DTRACE || defined HAVE_ETW
270272
InitDTrace(this);

src/node_errors.cc

+13
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,19 @@ static void ReportFatalException(Environment* env,
380380
"%s\n%s: %s\n", *arrow_string, *name_string, *message_string);
381381
}
382382
}
383+
384+
if (!env->options()->trace_uncaught) {
385+
PrintErrorString("(Use `node --trace-uncaught ...` to show "
386+
"where the exception was thrown)\n");
387+
}
388+
}
389+
390+
if (env->options()->trace_uncaught) {
391+
Local<StackTrace> trace = message->GetStackTrace();
392+
if (!trace.IsEmpty()) {
393+
PrintErrorString("Thrown at:\n");
394+
PrintStackTrace(env->isolate(), trace);
395+
}
383396
}
384397

385398
fflush(stderr);

src/node_options.cc

+4
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
476476
"prints TLS packet trace information to stderr",
477477
&EnvironmentOptions::trace_tls,
478478
kAllowedInEnvironment);
479+
AddOption("--trace-uncaught",
480+
"show stack traces for the `throw` behind uncaught exceptions",
481+
&EnvironmentOptions::trace_uncaught,
482+
kAllowedInEnvironment);
479483
AddOption("--trace-warnings",
480484
"show stack traces on process warnings",
481485
&EnvironmentOptions::trace_warnings,

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class EnvironmentOptions : public Options {
140140
bool trace_deprecation = false;
141141
bool trace_sync_io = false;
142142
bool trace_tls = false;
143+
bool trace_uncaught = false;
143144
bool trace_warnings = false;
144145
std::string unhandled_rejections;
145146
std::string userland_loader;

test/message/eval_messages.out

+2
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ ReferenceError: y is not defined
5555
var ______________________________________________; throw 10
5656
^
5757
10
58+
(Use `node --trace-uncaught ...` to show where the exception was thrown)
5859

5960
[eval]:1
6061
var ______________________________________________; throw 10
6162
^
6263
10
64+
(Use `node --trace-uncaught ...` to show where the exception was thrown)
6365
done

test/message/stdin_messages.out

+2
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ ReferenceError: y is not defined
6767
var ______________________________________________; throw 10
6868
^
6969
10
70+
(Use `node --trace-uncaught ...` to show where the exception was thrown)
7071

7172
[stdin]:1
7273
var ______________________________________________; throw 10
7374
^
7475
10
76+
(Use `node --trace-uncaught ...` to show where the exception was thrown)
7577
done

test/message/throw_error_with_getter_throw.out

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
throw { // eslint-disable-line no-throw-literal
44
^
55
[object Object]
6+
(Use `node --trace-uncaught ...` to show where the exception was thrown)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Flags: --trace-uncaught
2+
'use strict';
3+
require('../common');
4+
throw { // eslint-disable-line no-throw-literal
5+
get stack() {
6+
throw new Error('weird throw but ok');
7+
},
8+
get name() {
9+
throw new Error('weird throw but ok');
10+
},
11+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
*:4
3+
throw { // eslint-disable-line no-throw-literal
4+
^
5+
[object Object]
6+
Thrown at:
7+
at *throw_error_with_getter_throw_traced.js:*:*
8+
at Module._compile (internal/modules/cjs/loader.js:*:*)
9+
at Module._extensions..js (internal/modules/cjs/loader.js:*:*)
10+
at Module.load (internal/modules/cjs/loader.js:*:*)
11+
at Module._load (internal/modules/cjs/loader.js:*:*)
12+
at Module.runMain (internal/modules/cjs/loader.js:*:*)

test/message/throw_null.out

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
throw null;
44
^
55
null
6+
(Use `node --trace-uncaught ...` to show where the exception was thrown)

test/message/throw_null_traced.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Flags: --trace-uncaught
2+
'use strict';
3+
require('../common');
4+
5+
// eslint-disable-next-line no-throw-literal
6+
throw null;

test/message/throw_null_traced.out

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
*test*message*throw_null_traced.js:*
3+
throw null;
4+
^
5+
null
6+
Thrown at:
7+
at *throw_null_traced.js:*:*
8+
at Module._compile (internal/modules/cjs/loader.js:*:*)
9+
at Module._extensions..js (internal/modules/cjs/loader.js:*:*)
10+
at Module.load (internal/modules/cjs/loader.js:*:*)
11+
at Module._load (internal/modules/cjs/loader.js:*:*)
12+
at Module.runMain (internal/modules/cjs/loader.js:*:*)

test/message/throw_undefined.out

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
throw undefined;
44
^
55
undefined
6+
(Use `node --trace-uncaught ...` to show where the exception was thrown)
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Flags: --trace-uncaught
2+
'use strict';
3+
require('../common');
4+
5+
// eslint-disable-next-line no-throw-literal
6+
throw undefined;
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
*test*message*throw_undefined_traced.js:*
3+
throw undefined;
4+
^
5+
undefined
6+
Thrown at:
7+
at *throw_undefined_traced.js:*:*
8+
at Module._compile (internal/modules/cjs/loader.js:*:*)
9+
at Module._extensions..js (internal/modules/cjs/loader.js:*:*)
10+
at Module.load (internal/modules/cjs/loader.js:*:*)
11+
at Module._load (internal/modules/cjs/loader.js:*:*)
12+
at Module.runMain (internal/modules/cjs/loader.js:*:*)

0 commit comments

Comments
 (0)