-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
errors: improve hideStackFrames #49990
Conversation
Review requested:
|
what's the benefit/performance diff? |
My benchmarks dont show regressions. The benefit is, that the stack trace will be generated propery, without the need to call captureLargerStackTrace. So we wont be forced to keep infinity amount of CallSites just to kick some. On the long run, i want to try to remove prepareStackTrace. Currently i investigate what this overrideStackTrace Map is doing, and how we can avoid it. |
@@ -372,6 +356,23 @@ function makeSystemErrorWithCode(key) { | |||
}; | |||
} | |||
|
|||
function makeNodeErrorForHideStackFrame(Base) { | |||
class HideStackFramesError extends Base { |
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.
Won't this mess with the .name
of the error and how stack traces look (vs the previous implementation)?
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.
The only reason i created this class, was because to avoid to patch everywhere the logic for Error.stackTraceLimit over and over.
The name should be the same imho. But i can tackle it.
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.
Thanks let me know when ready
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.
Does this test cover the issue?
// Test that validator errors have the correct name and message. |
cd3b2c9
to
a12adde
Compare
The existing tests pass. Next step is to remove the captureLargerStackTrace function and ensure that the logic is equivalent. Imho this is an exciting PR for me. If done right, this should improve the performance of basically every error case. And tbh it seems to be a smart solution (patting on my own shoulder). Also it will cover all the validateX functions.so it should expose less unnecessary stacks. |
note to myself: Investigate https://docs.google.com/document/d/13Sy_kBIJGP0XT34V1CV3nkWya4TwYx9L3Yv45LdGB6Q/edit#heading=h.9ss45aibqpw2 |
a2134fc
to
3f94be4
Compare
@anonrig can you please add the author-ready tag? I would like to discuss this. |
|
Also @BridgeAR |
da8ff55
to
134ea2b
Compare
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.
lgtm
You are correct |
}); | ||
if (otherClasses.includes(HideStackFramesError)) { | ||
if (otherClasses.length !== 1) { | ||
otherClasses.forEach((clazz) => { |
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.
otherClasses.forEach((clazz) => { | ||
def[clazz.name] = makeNodeErrorWithCode(clazz, sym); | ||
}); | ||
if (otherClasses.includes(HideStackFramesError)) { |
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.
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 would recommend we do.
@benjamingr |
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.
Don't have capacity, sorry, unblocking so others can progress
@anonrig |
@anonrig |
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.
lgtm
Landed in 83e6350 |
PR-URL: #49990 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com>
PR-URL: #49990 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com>
PR-URL: #49990 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This PR improves the hideStackFrames logic.
hideStackFrames is used to hide function calls from stracktraces of Errors. E.g. we call
Buffer.alloc(size)
, then we check if the parameter size is an integer and callvalidateNumber(size, 'size', 0, kMaxLength);
.then we get the following stacktrace:
If we remove the hideStackFrame wrapping for demonstration purposes we get:
So we have the additional
at validateNumber (node:internal/validators:177:11)
entry.We want to hide this frame, as it is some node internal information, and does not give any benefit for the developer.
Javascript has no
inline
specifier, so we have to hide it differently. In main branch this is solved by prefixing the function name with__node_internal_
and then generating the whole stacktrace withcaptureLargerStackTrace
, which setsError.stackTraceLimit = Infinity
. When we then call Error.stack, prepareStackTrace will filter out the all frames, which start with__node_internal_
.But we dont need the whole stackTrace. We only need a limited number, by default 10 frames.
To achieve this we just generate the necessary stackTrace.
So hideStackFrames will wrap the original function and will capture the correct stack trace if an error is thrown. Functions which are wrapped have to call the HideStackFramesError Errors and wont have a Stack trace.
An edge case is when a function, which uses hideStackFrames calls another function which is wrapped with hideStackFrames. To avoid double generation of the stack, a function which is wrapped with hideStackFrames exposes a function as attribute of the wrapped function called
.withoutStackTrace
. The outer function will then get the necessary stack trace.Usually double wrapped functions are validators. I would have liked to extract them all into validators.js or into module/validators.js. But this would have reduced the readability further.