Skip to content

Commit

Permalink
cli: add --trace-uncaught flag
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
addaleax authored and targos committed Jan 8, 2020
1 parent 1aeca6d commit e08c008
Show file tree
Hide file tree
Showing 17 changed files with 111 additions and 0 deletions.
13 changes: 13 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,18 @@ added: v12.2.0
Prints TLS packet trace information to `stderr`. This can be used to debug TLS
connection problems.

### `--trace-uncaught`
<!-- YAML
added: REPLACEME
-->

Print stack traces for uncaught exceptions; usually, the stack trace associated
with the creation of an `Error` is printed, whereas this makes Node.js also
print the stack trace associated with throwing the value (which does not need
to be an `Error` instance).

Enabling this option may affect garbage collection behavior negatively.

### `--trace-warnings`
<!-- YAML
added: v6.0.0
Expand Down Expand Up @@ -1089,6 +1101,7 @@ Node.js options that are allowed are:
* `--trace-events-enabled`
* `--trace-sync-io`
* `--trace-tls`
* `--trace-uncaught`
* `--trace-warnings`
* `--track-heap-objects`
* `--unhandled-rejections`
Expand Down
12 changes: 12 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,18 @@ Print a stack trace whenever synchronous I/O is detected after the first turn of
.It Fl -trace-tls
Prints TLS packet trace information to stderr.
.
.It Fl -trace-uncaught
Print stack traces for uncaught exceptions; usually, the stack trace associated
with the creation of an
.Sy Error
is printed, whereas this makes Node.js also
print the stack trace associated with throwing the value (which does not need
to be an
.Sy Error
instance).
.Pp
Enabling this option may affect garbage collection behavior negatively.
.
.It Fl -trace-warnings
Print stack traces for process warnings (including deprecations).
.
Expand Down
2 changes: 2 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ int Environment::InitializeInspector(
void Environment::InitializeDiagnostics() {
isolate_->GetHeapProfiler()->AddBuildEmbedderGraphCallback(
Environment::BuildEmbedderGraph, this);
if (options_->trace_uncaught)
isolate_->SetCaptureStackTraceForUncaughtExceptions(true);

#if defined HAVE_DTRACE || defined HAVE_ETW
InitDTrace(this);
Expand Down
13 changes: 13 additions & 0 deletions src/node_errors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,19 @@ static void ReportFatalException(Environment* env,
"%s\n%s: %s\n", *arrow_string, *name_string, *message_string);
}
}

if (!env->options()->trace_uncaught) {
PrintErrorString("(Use `node --trace-uncaught ...` to show "
"where the exception was thrown)\n");
}
}

if (env->options()->trace_uncaught) {
Local<StackTrace> trace = message->GetStackTrace();
if (!trace.IsEmpty()) {
PrintErrorString("Thrown at:\n");
PrintStackTrace(env->isolate(), trace);
}
}

fflush(stderr);
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"prints TLS packet trace information to stderr",
&EnvironmentOptions::trace_tls,
kAllowedInEnvironment);
AddOption("--trace-uncaught",
"show stack traces for the `throw` behind uncaught exceptions",
&EnvironmentOptions::trace_uncaught,
kAllowedInEnvironment);
AddOption("--trace-warnings",
"show stack traces on process warnings",
&EnvironmentOptions::trace_warnings,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ class EnvironmentOptions : public Options {
bool trace_deprecation = false;
bool trace_sync_io = false;
bool trace_tls = false;
bool trace_uncaught = false;
bool trace_warnings = false;
std::string unhandled_rejections;
std::string userland_loader;
Expand Down
2 changes: 2 additions & 0 deletions test/message/eval_messages.out
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ ReferenceError: y is not defined
var ______________________________________________; throw 10
^
10
(Use `node --trace-uncaught ...` to show where the exception was thrown)

[eval]:1
var ______________________________________________; throw 10
^
10
(Use `node --trace-uncaught ...` to show where the exception was thrown)
done
2 changes: 2 additions & 0 deletions test/message/stdin_messages.out
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ ReferenceError: y is not defined
let ______________________________________________; throw 10
^
10
(Use `node --trace-uncaught ...` to show where the exception was thrown)

[stdin]:1
let ______________________________________________; throw 10
^
10
(Use `node --trace-uncaught ...` to show where the exception was thrown)
done
1 change: 1 addition & 0 deletions test/message/throw_error_with_getter_throw.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
throw { // eslint-disable-line no-throw-literal
^
[object Object]
(Use `node --trace-uncaught ...` to show where the exception was thrown)
11 changes: 11 additions & 0 deletions test/message/throw_error_with_getter_throw_traced.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Flags: --trace-uncaught
'use strict';
require('../common');
throw { // eslint-disable-line no-throw-literal
get stack() {
throw new Error('weird throw but ok');
},
get name() {
throw new Error('weird throw but ok');
},
};
12 changes: 12 additions & 0 deletions test/message/throw_error_with_getter_throw_traced.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

*:4
throw { // eslint-disable-line no-throw-literal
^
[object Object]
Thrown at:
at *throw_error_with_getter_throw_traced.js:*:*
at Module._compile (internal/modules/cjs/loader.js:*:*)
at Module._extensions..js (internal/modules/cjs/loader.js:*:*)
at Module.load (internal/modules/cjs/loader.js:*:*)
at Module._load (internal/modules/cjs/loader.js:*:*)
at Module.runMain (internal/modules/cjs/loader.js:*:*)
1 change: 1 addition & 0 deletions test/message/throw_null.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
throw null;
^
null
(Use `node --trace-uncaught ...` to show where the exception was thrown)
6 changes: 6 additions & 0 deletions test/message/throw_null_traced.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Flags: --trace-uncaught
'use strict';
require('../common');

// eslint-disable-next-line no-throw-literal
throw null;
12 changes: 12 additions & 0 deletions test/message/throw_null_traced.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

*test*message*throw_null_traced.js:*
throw null;
^
null
Thrown at:
at *throw_null_traced.js:*:*
at Module._compile (internal/modules/cjs/loader.js:*:*)
at Module._extensions..js (internal/modules/cjs/loader.js:*:*)
at Module.load (internal/modules/cjs/loader.js:*:*)
at Module._load (internal/modules/cjs/loader.js:*:*)
at Module.runMain (internal/modules/cjs/loader.js:*:*)
1 change: 1 addition & 0 deletions test/message/throw_undefined.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
throw undefined;
^
undefined
(Use `node --trace-uncaught ...` to show where the exception was thrown)
6 changes: 6 additions & 0 deletions test/message/throw_undefined_traced.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Flags: --trace-uncaught
'use strict';
require('../common');

// eslint-disable-next-line no-throw-literal
throw undefined;
12 changes: 12 additions & 0 deletions test/message/throw_undefined_traced.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

*test*message*throw_undefined_traced.js:*
throw undefined;
^
undefined
Thrown at:
at *throw_undefined_traced.js:*:*
at Module._compile (internal/modules/cjs/loader.js:*:*)
at Module._extensions..js (internal/modules/cjs/loader.js:*:*)
at Module.load (internal/modules/cjs/loader.js:*:*)
at Module._load (internal/modules/cjs/loader.js:*:*)
at Module.runMain (internal/modules/cjs/loader.js:*:*)

0 comments on commit e08c008

Please sign in to comment.