8000 [Flight] Eagerly parse stack traces in DebugNode by sebmarkbage · Pull Request #33589 · facebook/react · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[Flight] Eagerly parse stack traces in DebugNode #33589

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.

Alre 8000 ady on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 22, 2025

Conversation

sebmarkbage
Copy link
Collaborator
@sebmarkbage sebmarkbage commented Jun 21, 2025

There's a memory leak in DebugNode where the Error objects that we instantiate retains their callstacks which can have Promises on them. In fact, it's very likely since the current callsite has the "resource" on it which is the Promise itself. If those Promises are retained then their destroy async hook is never fired which doesn't clean up our map which can contains the Error object. Creating a cycle that can't be cleaned up.

This fix is just eagerly reifying and parsing the stacks.

I totally expect this to be crazy slow since there's so many Promises that we end up not needing to visit otherwise. We'll need to optimize it somehow. Perhaps by being smarter about which ones we might need stacks for. However, at least it doesn't leak indefinitely.

@sebmarkbage sebmarkbage requested a review from eps1lon June 21, 2025 22:18
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Jun 21, 2025
@react-sizebot
Copy link

Comparing: 6c7b1a1...33197f4

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB +0.11% 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 530.57 kB 530.57 kB = 93.67 kB 93.67 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB +0.05% 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 651.66 kB 651.66 kB = 114.78 kB 114.78 kB
facebook-www/ReactDOM-prod.classic.js = 674.81 kB 674.81 kB = 118.78 kB 118.78 kB
facebook-www/ReactDOM-prod.modern.js = 665.30 kB 665.30 kB = 117.19 kB 117.19 kB

Significant size changes

Includes any change greater than 0.2%:

(No significant changes)

Generated by 🚫 dangerJS against 33197f4

@sebmarkbage sebmarkbage merged commit d70ee32 into facebook:main Jun 22, 2025
247 checks passed
sebmarkbage added a commit that referenced this pull request Jun 23, 2025
…nformation (#33592)

Stacked on #33588, #33589 and #33590.

This lets us automatically show the resolved value in the UI.

<img width="863" alt="Screenshot 2025-06-22 at 12 54 41 AM"
src="https://github.com/user-attachments/assets/a66d1d5e-0513-4767-910c-5c7169fc2df4"
/>

We can also show rejected I/O that may or may not have been handled with
the error message.

<img width="838" alt="Screenshot 2025-06-22 at 12 55 06 AM"
src="https://github.com/user-attachments/assets/e0a8b6ae-08ba-46d8-8cc5-efb60956a1d1"
/>

To get this working we need to keep the Promise around for longer so
that we can access it once we want to emit an async sequence. I do this
by storing the WeakRefs but to ensure that the Promise doesn't get
garbage collected, I keep a WeakMap of Promise to the Promise that it
depended on. This lets the VM still clean up any Promise chains that
have leaves that are cleaned up. So this makes Promises live until the
last Promise downstream is done. At that point we can go back up the
chain to read the values out of them.

Additionally, to get the best possible value we don't want to get a
Promise that's used by internals of a third-party function. We want the
value that the first party gets to observe. To do this I had to change
the logic for which "await" to use, to be the one that is the first
await that happened in user space. It's not enough that the await has
any first party at all on the stack - it has to be the very first frame.
This is a little sketchy because it relies on the `.then()` call or
`await` call not having any third party wrappers. But it gives the best
object since it hides all the internals. For example when you call
`fetch()` we now log that actual `Response` object.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants
0