-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
process: split routines used to enhance fatal exception stack traces #28308
Conversation
Previously the enhancement were done right after emitting `'uncaughtException'`, which meant by the time we knew the exception was fatal in C++, the error.stack had already been patched. This patch moves those routines to be called later during the fatal exception handling, and split them into two stages: before and after the inspector is notified by the invocation of `V8Inspector::exceptionThrown`. We now expand the stack to include additional informations about unhandled 'error' events before the inspector is notified, but delay the highlighting of the frames until after the inspector is notified, so that the ANSI escape sequences won't show up in the inspector console.
try { | ||
// Set the error.stack here so it gets picked up by the | ||
// inspector. | ||
error.stack = error[kEnhanceStackBeforeInspector](); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally we probably should not even have to do this, instead we can just return a string to some API and leave the error.stack
intact, because patching error.stack
(which is just a randomly formatted string) over and over is error-prone, especially in the presence of Error.prepareStackTrace
...but there does not seem to be a hook to customize the stack trace in the console message logged by the inspector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah and error.stack might not be writeable, (should be fine in this case)
try { | ||
return inspect(error, { colors }); | ||
} catch { | ||
return originalStack; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that here we don't patch error.stack
at all - patching error.stack
is only one of the many ways to pass some metadata around along with the error. The cleanest way, however, is to just wrap those metadata in something else instead of polluting error
itself over and over, which is also what V8 does in its embedder API (the source of truth of the stack trace is message->GetStackTrace()
, not error->Get(context, stack_string)
, and there are many other structured metadata in Message
)
Ideally we could also just pass structured data (e.g. error.code
, error.requireStack
) back to the caller and it knows how to format and print those out without relying on error.stack
at all
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we do this in a WeakMap? Their point is extending things from the outside after all
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need a WeakMap for this, as this is kind of a one-off thing so there is no mapping necessary. We only need to pass a separate object around, the association between the error and the object can already be known from how they are passed around, and we probably don't need an equivalent of Exception::CreateMessage
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Also now that you mentioned it...I believe V8 still does not have embedder API to interface with WeakMaps? That's why we still have to create our ESM module wrap map in JS land)
Very nice, I'm ok with closing my PR in favor of this, but these patches still don't work on Windows. Tested on Windows 8.1 Pro 6.3.9600, got the following output in the console: C:\> node-60e3f81.exe -e "throw new Error"
[eval]:1
throw new Error
^
Error
at [eval]:1:7
←[90m at Script.runInThisContext (vm.js:123:20)←[39m
←[90m at Object.runInThisContext (vm.js:313:38)←[39m
at Object.<anonymous> ([eval]-wrapper:9:26)
←[90m at Module._compile (internal/modules/cjs/loader.js:779:30)←[39m
←[90m at evalScript (internal/process/execution.js:80:25)←[39m
←[90m at internal/main/eval_string.js:23:3←[39m Or is this not supposed to work on Windows? |
@Hakerh400 See #28291 (comment) - that requires a different fix in |
} = lazyInternalUtilInspect(); | ||
const colors = internalBinding('util').guessHandleType(2) === 'TTY' && | ||
require('internal/tty').hasColors() || | ||
defaultColors; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the windows issue: we could temporarily disable the coloring by adding process.plaform === 'win32'
to the conditions here, though I would prefer an alternative fix with changing the implementation of PrintErrorString()
to use uv_write
for TTY instead. I have a WIP in https://github.com/joyeecheung/node/tree/print-uv-write which still fails a few tests that I need to investigate. Either way that's kind of orthognal to this PR as this only tries to fix #28287
Is it worth the effort to add a test for the changed behavior? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a feeling I've seen some of those changes before in another PR, code runs fine and changes seem positive and reasonable.
A test would be great.
@Trott This is testing behaviors after the error thrown is identified as fatal, which means we cannot count on any notification to the test about the changed behavior when they are in the same process. We could test that reliably if um...we have an out-of-process WebSocket client in this code base, but I don't think the test is worth that dependency people have been arguing about for years :S We might consider reporting to inspector even when the error is handled by |
Landed in 9445492 |
Previously the enhancement were done right after emitting `'uncaughtException'`, which meant by the time we knew the exception was fatal in C++, the error.stack had already been patched. This patch moves those routines to be called later during the fatal exception handling, and split them into two stages: before and after the inspector is notified by the invocation of `V8Inspector::exceptionThrown`. We now expand the stack to include additional informations about unhandled 'error' events before the inspector is notified, but delay the highlighting of the frames until after the inspector is notified, so that the ANSI escape sequences won't show up in the inspector console. PR-URL: #28308 Fixes: #28287 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Previously the enhancement were done right after emitting `'uncaughtException'`, which meant by the time we knew the exception was fatal in C++, the error.stack had already been patched. This patch moves those routines to be called later during the fatal exception handling, and split them into two stages: before and after the inspector is notified by the invocation of `V8Inspector::exceptionThrown`. We now expand the stack to include additional informations about unhandled 'error' events before the inspector is notified, but delay the highlighting of the frames until after the inspector is notified, so that the ANSI escape sequences won't show up in the inspector console. PR-URL: #28308 Fixes: #28287 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Previously the enhancement were done right after emitting `'uncaughtException'`, which meant by the time we knew the exception was fatal in C++, the error.stack had already been patched. This patch moves those routines to be called later during the fatal exception handling, and split them into two stages: before and after the inspector is notified by the invocation of `V8Inspector::exceptionThrown`. We now expand the stack to include additional informations about unhandled 'error' events before the inspector is notified, but delay the highlighting of the frames until after the inspector is notified, so that the ANSI escape sequences won't show up in the inspector console. PR-URL: #28308 Fixes: #28287 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Previously the enhancement were done right after emitting
'uncaughtException'
, which meant by the time we knew theexception was fatal in C++, the error.stack had already been
patched.
This patch moves those routines to be called later during the
fatal exception handling, and split them into two stages:
before and after the inspector is notified by the invocation of
V8Inspector::exceptionThrown
. We now expand the stack to includeadditional informations about unhandled 'error' events before
the inspector is notified, but delay the highlighting of the
frames until after the inspector is notified, so that the
ANSI escape sequences won't show up in the inspector console.
Fixes: #28287
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes