Skip to content
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

async stack traces #34

Open
trusktr opened this issue Jul 10, 2020 · 13 comments
Open

async stack traces #34

trusktr opened this issue Jul 10, 2020 · 13 comments

Comments

@trusktr
Copy link

trusktr commented Jul 10, 2020

It would be convenient to get async stack traces, like what Chrome Devtools shows us in the sources tab.

F.e.

// main.js
function foo() {
  setTimeout(function bar() {
    const stack = getAsyncStackString()
    console.log(stack)
  }, 1000)
}
foo()

/*
Eventually logs the trace
  at bar (main.js:4)
  at foo (main.js:3)
  (main.js:7)
*/

Or similar; not sure if I got that right but you get the idea.

Use case:

I'd like to detect the async stack trace of any fetch call in an app (including 3rd-party modules from node_modules).

I would like to only have to monkey-patch fetch() so that in each call I can have it get the stack trace. However this only gives non-async traces.

To achieve this currently, I would need to instrument the code which is more complicated.

@mk-pmb
Copy link

mk-pmb commented Jul 10, 2020

Can this be solved by loading a custom Promise library that supports long stack traces? IIRC bluebird advertized this as one of its features.

@ljharb
Copy link
Member

ljharb commented Jul 11, 2020

The content of stack traces is up to the implementation. I'm not really clear on what you're asking for here.

@trusktr
Copy link
Author

trusktr commented Jul 11, 2020

Can this be solved by loading a custom Promise library that supports long stack traces? IIRC bluebird advertized this as one of its features.

@mk-pmb How do I tell fetch to return a custom Promise that isn't the one built into the browser? Does fetch rely on the Promise global?

The content of stack traces is up to the implementation. I'm not really clear on what you're asking for here.

@ljharb I thought the purpose of this proposal was to make it not up to the implementation, but up to a standard.

What I'm hoping for is a function that returns a full async stack trace, not just a synchronous one in the current task.

For example, the output of the following,

const originalFetch = globalThis.fetch

globalThis.fetch = function(...args) {
	const stack = new Error().stack
	console.log(stack)
	return originalFetch.apply(globalThis, args)
}

const sleep = t => new Promise(r => setTimeout(r, t))

async function one() {
	console.log('1')
	await sleep(10)
	two()
}

async function two() {
	console.log('2')
	await sleep(10)
	three()
}

async function three() {
	console.log('3')
	await sleep(10)
	return await fetch('https://unpkg.com/three@0.118.3')
}

async function main() {
	await one()
}

main()

is

1
2
3
Error
    at globalThis.fetch (pen.js:5)
    at three (pen.js:27)

but I was hoping it would be more like

1
2
3
Error
    at globalThis.fetch (pen.js:5)
    at three (pen.js:27)
    at two (...)
    at one (...)
    at main (...)

Live example: https://codepen.io/trusktr/pen/b8fd92752b4671268f516ad3804869e4?editors=1010

@ljharb
Copy link
Member

ljharb commented Jul 11, 2020

The purpose of this proposal is to specify the structure, not the contents, which will remain implementation-defined.

@mk-pmb
Copy link

mk-pmb commented Jul 11, 2020

Then as I understand the original request, it is out of scope of this spec.

@kaizhu256
Copy link

out-of-loop-traces are expensive to bookkeep. i could envision performance-hit to programs written by someone naively using promises/await-calls at every opportunity with this feature.

we could instead standardize entry-point-traces for a few debug-use-cases where it makes sense:

  1. tracking-down entry-point for http-requests.
    relevant bodies/nodejs could possibly hash-out standard to preserve entry-point-stack
    in XMLHttpRequest.prototype.send, fetch, and node's [http/https/http2].request

  2. tracking-down entry-point for Promise.all, Promise.race.
    similar to 1, with standards-body being tc39.

  3. tracking-down entry-point where event/message was emitted.
    there's nothing a standards body can do here. its up to userland to include stack-trace in emitted message-payload.

standardizing 1 and 2 might seem like warts to language-purists, but they're outsized impact to debugging could justify it.

@mk-pmb
Copy link

mk-pmb commented Jul 11, 2020

tracking-down entry-point for http-requests.

This sounds arbitrary and overly specific. Why should one special method of communication receive more attention than others, e.g. reading from standard input, or a window manager's notification received by a GUI application?

@kaizhu256
Copy link

if you can do empirical-survey of what javascript-entry-points people find most-useful-to-know when debugging, i'm all for it.

i don't have insight in desktop-development. my insight in web-development however, is that http-request-entry-points is at top-of-the-list.

providing a standard-trace for http-requests would also prevent me from doing well-intentioned-but-stupid-things like:

let originalFetch = globalThis.fetch;
globalThis.fetch = function (...argList) {
    console.error(new Error().stack);
    return originalFetch(...argList);
};

that could end up being security risks.

@mk-pmb
Copy link

mk-pmb commented Jul 11, 2020

The spec should have reasons that make sense independent of popular vote and fashions. We should build mechanisms that empower all developers to more easily debug whatever they're working on. In your fetch example case one solution might be to have browser vendors implement flags to enable a security-preserving version of long stack traces on parts of their API.

@rdking
Copy link

rdking commented Jun 21, 2021

While I agree with the OP on this, I understand that this proposal is about form and not content. On that note, can there not be an another accessor property with the same setup as stack, but guaranteed to return a complete-with-async-frames stack trace? The trace returned by stack can remain as it is, with content decided by the vendors, but there does need to be a guaranteed approach to getting a complete stack trace that's not missing the async frames.

I have run into issues because of situations like this. For instance, in Firefox, as long as the developer tools are open, some code with functionality that probes the stack trace works as intended. However, without the developer tools, the same code breaks because they stop adding the async frames to the stack trace.

@mk-pmb
Copy link

mk-pmb commented Jul 1, 2021

can there not be an another accessor property with the same setup as stack, but guaranteed to return a complete-with-async-frames stack trace?

I see no obstacle. Someone should make a proposal for that.

I'm not sure I understood the example though. Whether and in which circumstances a specific web browser does things one way or another, seems to be a product-specific problem.

@ljharb
Copy link
Member

ljharb commented Jul 1, 2021

@mk-pmb it can not be an accessor property, only a static method, so it can be shadowed per-compartment. .stack, when specified, will be marked "legacy" so that it can be absent in spec-compliant engines.

@rdking
Copy link

rdking commented Jul 8, 2021

I'd be happy even if it were as static method. Just so long as there is a consistent way to get it, that's good enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants