5

According to Zero-cost async stack traces V8 uses the Promise reference in the microtask to reconstruct an "async stacktrace". I assume similar techniques are also used in the debugger, which also shows an "async stack". However when debugging a "pseudorecursive function", like e.g. this one using requestAnimationFrame:

(function tick() { requestAnimationFrame(tick); })();

then Chrome also shows some "async stack" as shown in this question, although no Promise is present. How does V8 recover this stack? Does it collect information on how the microtask was scheduled? And if so, what prevents this from leaking memory?

Kaiido
  • 123,334
  • 13
  • 219
  • 285
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • 1
    This is not specific to rAF, the same also happens with "recursive" setTimeout – Kaiido Jul 14 '21 at 13:26
  • 2
    And [here](https://bugs.chromium.org/p/chromium/issues/detail?id=1069425) are some relevant info. No time for a proper answer right now, but basically, they symbolize a fresh stack of every async events **when the dev-tools are open**. You can control this behavior by typing `Do not capture async stack traces` in the command menu (ctrl + shift + P from the dev-tools). – Kaiido Jul 14 '21 at 14:42

1 Answers1

3

It cheats basically. When the devtools is opened a debugger speaking the chrome devtools protocol is connected to v8.

It in turn calls setAsyncCallStackDepth which will "stitch" the stack whenever an async call is done (you can see how in v8/src/inspector/v8-debugger.cc in the chromium repo).

It is expensive which is why you only get it in devtools.

You can think of it as:

  • Whenever an async function is called, keep track of it by taking a stack trace.
  • When an error is thrown, get its stack trace and "stitch" the two together.

Note this was done in userland libraries before (like bluebird) and is quite useful but not great to rely on in production apps since it only works with a debugger connected.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504