Skip to content

Commit e08c008

Browse files
addaleaxtargos
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 1aeca6d commit e08c008

17 files changed

+111
-0
lines changed

doc/api/cli.md

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

778+
### `--trace-uncaught`
779+
<!-- YAML
780+
added: REPLACEME
781+
-->
782+
783+
Print stack traces for uncaught exceptions; usually, the stack trace associated
784+
with the creation of an `Error` is printed, whereas this makes Node.js also
785+
print the stack trace associated with throwing the value (which does not need
786+
to be an `Error` instance).
787+
788+
Enabling this option may affect garbage collection behavior negatively.
789+
778790
### `--trace-warnings`
779791
<!-- YAML
780792
added: v6.0.0
@@ -1089,6 +1101,7 @@ Node.js options that are allowed are:
10891101
* `--trace-events-enabled`
10901102
* `--trace-sync-io`
10911103
* `--trace-tls`
1104+
* `--trace-uncaught`
10921105
* `--trace-warnings`
10931106
* `--track-heap-objects`
10941107
* `--unhandled-rejections`

doc/node.1

+12
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,18 @@ Print a stack trace whenever synchronous I/O is detected after the first turn of
346346
.It Fl -trace-tls
347347
Prints TLS packet trace information to stderr.
348348
.
349+
.It Fl -trace-uncaught
350+
Print stack traces for uncaught exceptions; usually, the stack trace associated
351+
with the creation of an
352+
.Sy Error
353+
is printed, whereas this makes Node.js also
354+
print the stack trace associated with throwing the value (which does not need
355+
to be an
356+
.Sy Error
357+
instance).
358+
.Pp
359+
Enabling this option may affect garbage collection behavior negatively.
360+
.
349361
.It Fl -trace-warnings
350362
Print stack traces for process warnings (including deprecations).
351363
.

src/node.cc

+2
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ int Environment::InitializeInspector(
232232
void Environment::InitializeDiagnostics() {
233233
isolate_->GetHeapProfiler()->AddBuildEmbedderGraphCallback(
234234
Environment::BuildEmbedderGraph, this);
235+
if (options_->trace_uncaught)
236+
isolate_->SetCaptureStackTraceForUncaughtExceptions(true);
235237

236238
#if defined HAVE_DTRACE || defined HAVE_ETW
237239
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
@@ -494,6 +494,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
494494
"prints TLS packet trace information to stderr",
495495
&EnvironmentOptions::trace_tls,
496496
kAllowedInEnvironment);
497+
AddOption("--trace-uncaught",
498+
"show stack traces for the `throw` behind uncaught exceptions",
499+
&EnvironmentOptions::trace_uncaught,
500+
kAllowedInEnvironment);
497501
AddOption("--trace-warnings",
498502
"show stack traces on process warnings",
499503
&EnvironmentOptions::trace_warnings,

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class EnvironmentOptions : public Options {
142142
bool trace_deprecation = false;
143143
bool trace_sync_io = false;
144144
bool trace_tls = false;
145+
bool trace_uncaught = false;
145146
bool trace_warnings = false;
146147
std::string unhandled_rejections;
147148
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
let ______________________________________________; throw 10
6868
^
6969
10
70+
(Use `node --trace-uncaught ...` to show where the exception was thrown)
7071

7172
[stdin]:1
7273
let ______________________________________________; 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)