Skip to content

errors: improve hideStackFrames - #49990

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
Uzlopak:hidestack-frames
Nov 11, 2023
Merged

errors: improve hideStackFrames#49990
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
Uzlopak:hidestack-frames

Conversation

@Uzlopak

@UzlopakUzlopak commented Oct 1, 2023

Copy link
Copy Markdown
Contributor

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 call validateNumber(size, 'size', 0, kMaxLength);.

'use strict'try{Buffer.alloc('1')}catch(err){console.error(err.stack)}

then we get the following stacktrace:

TypeError [ERR_INVALID_ARG_TYPE]: The "size" argument must be of type number. Received type string ('1')
at Function.alloc (node:buffer:393:3)
at Object.<anonymous> (/home/aras/workspace/node/test.js:4:12)
at Module._compile (node:internal/modules/cjs/loader:1369:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1427:10)
at Module.load (node:internal/modules/cjs/loader:1201:32)
at Module._load (node:internal/modules/cjs/loader:1017:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:104:12)
at node:internal/main/run_main_module:23:47

If we remove the hideStackFrame wrapping for demonstration purposes we get:

TypeError[ERR_INVALID_ARG_TYPE]: The"size"argumentmustbeoftypenumber.Receivedtypestring('1')atvalidateNumber(node:internal/validators:177:11)atFunction.alloc(node:buffer:393:3)atObject.<anonymous>(/home/aras/workspace/node/test.js:4:12)atModule._compile(node:internal/modules/cjs/loader:1369:14)atModule._extensions..js(node:internal/modules/cjs/loader:1427:10)atModule.load(node:internal/modules/cjs/loader:1201:32)atModule._load(node:internal/modules/cjs/loader:1017:12)atFunction.executeUserEntryPoint[asrunMain](node:internal/modules/run_main:104:12)at node:internal/main/run_main_module:23:47

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 with captureLargerStackTrace, which sets Error.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.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/http2
  • @nodejs/net

@nodejs-github-botnodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Oct 1, 2023
@mcollina

Copy link
Copy Markdown
Member

what's the benefit/performance diff?

@Uzlopak

Copy link
Copy Markdown
ContributorAuthor

My benchmarks dont show regressions.
But if we go on, I would ensure that all touched code does not contain any perf 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.

Comment threadlib/internal/errors.js Outdated

@benjamingrbenjamingrOct 1, 2023

Copy link
Copy Markdown
Member

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)?

Copy link
Copy Markdown
ContributorAuthor

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.

Copy link
Copy Markdown
Member

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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Uzlopak

Copy link
Copy Markdown
ContributorAuthor

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.

@Uzlopak

Copy link
Copy Markdown
ContributorAuthor

@Uzlopak
Uzlopakforce-pushed the hidestack-frames branch 4 times, most recently from a2134fc to 3f94be4CompareOctober 4, 2023 20:11
@Uzlopak

Copy link
Copy Markdown
ContributorAuthor

@anonrig can you please add the author-ready tag?

I would like to discuss this.

@anonrig

Copy link
Copy Markdown
Member

@anonrig can you please add the author-ready tag?

author-ready requires at least 1 collaborator approval. I don't have enough knowledge to review this. I recommend @benjamingr or @mcollina to leave a review before adding author-ready

@anonriganonrig added the request-ci Add this label to start a Jenkins CI on a PR. label Oct 4, 2023
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Oct 4, 2023
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@tniessentniessen added the review wanted PRs that need reviews. label Oct 4, 2023
@targos

Copy link
Copy Markdown
Member

Also @BridgeAR

@Uzlopak
Uzlopakforce-pushed the hidestack-frames branch 3 times, most recently from da8ff55 to 134ea2bCompareOctober 5, 2023 12:05

@mcollinamcollina left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Comment threadlib/internal/errors.js Outdated
@mcollina

Copy link
Copy Markdown
Member

You are correct

});
if (otherClasses.includes(HideStackFramesError)) {
if (otherClasses.length !== 1) {
otherClasses.forEach((clazz) => {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcollina
@anonrig

Should we use a for loop instead?

otherClasses.forEach((clazz) => {
def[clazz.name] = makeNodeErrorWithCode(clazz, sym);
});
if (otherClasses.includes(HideStackFramesError)) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcollina
@anonrig

Should we use primordials?

Copy link
Copy Markdown
Member

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.

@Uzlopak

Copy link
Copy Markdown
ContributorAuthor

@benjamingr
Can you have a look?

@benjamingrbenjamingr left a comment

Copy link
Copy Markdown
Member

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

@Uzlopak

Uzlopak commented Oct 15, 2023

Copy link
Copy Markdown
ContributorAuthor

@anonrig
Can you now add the author-ready tag, please?

@anonriganonrig added author ready PRs that have at least one approval, no pending requests for changes, and a CI started. request-ci Add this label to start a Jenkins CI on a PR. labels Oct 15, 2023
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Oct 15, 2023
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@Uzlopak

Copy link
Copy Markdown
ContributorAuthor

@anonrig
can you retrigger the failed CI,please?

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

nodejs-github-bot commented Nov 2, 2023

Copy link
Copy Markdown
Collaborator

@mcollinamcollina left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@jasnelljasnell added the commit-queue Add this label to land a pull request using GitHub Actions. label Nov 11, 2023
@nodejs-github-botnodejs-github-bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label Nov 11, 2023
@nodejs-github-bot
nodejs-github-bot merged commit 83e6350 into nodejs:mainNov 11, 2023
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in 83e6350

targos pushed a commit that referenced this pull request Nov 11, 2023
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>
@targostargos mentioned this pull request Nov 12, 2023
@Uzlopak
Uzlopak deleted the hidestack-frames branch November 13, 2023 07:42
targos pushed a commit that referenced this pull request Nov 14, 2023
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>
richardlau pushed a commit that referenced this pull request Mar 25, 2024
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>
@richardlaurichardlau mentioned this pull request Mar 25, 2024
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs that have at least one approval, no pending requests for changes, and a CI started.lib / srcIssues and PRs related to general changes in the lib or src directory.needs-ciPRs that need a full CI run.needs-citgmPRs that need a CITGM CI run.review wantedPRs that need reviews.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants

@Uzlopak@nodejs-github-bot@mcollina@anonrig@targos@Qard@jasnell@benjamingr@RafaelGSS@tniessen