-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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 are missing frames before the first async gap for all typical Flutter use cases #46318
Comments
This is a known limitation of the current approach to async stack traces, see go/dart-10x-faster-async Theoretically we could collect a stack traces when we call async function without awaiting, but that is going to cost in terms of performance. /cc @mkustermann @cskau-g |
+100 to collecting the stack trace even when the sync function is not awaited even if there is some performance overhead. @natebosch also filed a bug likely due to the same change in async stack trace behavior. |
100% agree. It should at least be a runtime flag as currently, the stack trace is practically useless for some async code. There is |
I hope we can get full stack trace when the application is running in debug mode at least. |
We do have explicit points in the code when we suspend an async frame. We could let that code test whether the caller is non-async/async* and if so, capture the stack in the async functions state, making it available to the stack walker. Though it would make every async function a bit slower and sync->async calls quite a bit slower (capturing stack trace is expensive). One could avoid always paying the cost and instead make it explicit in the code by adding e.g. a unawaited(foo()); The analyzer even has a unawaited_futures hint that suggests to call /cc @alexmarkov wdyt? |
@mkustermann Stack trace collection during We used to have an option ( |
Our stack trace code does unwind across
Going back to what we had (eagerly capturing stack traces via runtime calls for every single async function invocation) is an absolute no-go. So history will not repeat :-) Capturing stacks eagerly for sync->async transitions (or the subset of cases where the returned |
Our stack trace only scans asynchronous awaiters after reaching the first async. All synchronous calls are omitted from the stack trace after the first async (they are technically already gone at that point). For example (without unawaited futures): void printStackTrace() {
print(StackTrace.current);
}
Future<void> baz() async {
await null;
printStackTrace();
}
Future<void> bar() {
return baz().then((_) {
print('bar');
});
}
Future<void> foo() {
return bar();
}
main() async {
await foo();
}
So, I would not focus solely on supporting stack trace for the |
Correct. The difference can be explained more easily as a semantic difference:
The main usability problem is when there's a truncated stack, which could be solved by handling |
Hi, is there any updates? One awesome feature of Dart is its async/await, and it is used so frequently everywhere in the Dart/Flutter ecosystem. Therefore, it would be great to have this stack trace supported. |
We don't currently have any plans to add the "missing" part of the stack trace - this requires capturing stack traces eagerly on entry to We might try this at some point with a special optimised version of the stack trace capturing code (one that does not need to go into runtime), but right now we have no immediate plans to do that. This might fit into the experiment for sharing stacks for activations of async functions because we might do it by changing calling conventions of the async function to recognise this situation. |
Thanks for the reply and looking forward for the possible future! |
Couldn't this be added with some sort of flag that enables capturing the entire stack? |
It turns out Dart stack traces have always been incomplete for the typical Flutter use case of an async method triggered by code running on the event loop rather than async code triggered from main. This impacts both
Stacktrace.current
and the stacktraces returned by the VMService.Command line Dart app repro:
https://github.com/jacob314/stack_trace_repro/blob/main/console-simple/bin/console_simple.dart
This repro uses
Timer
to simulate running some code on the event loop as in a Flutter application. If you attempt to repro using purely async methods you will not be able to as async stack traces work great for that case.dart run bin/console_simple.dart
Notice that none of the stack traces include methods before the the first .
For Flutter this makes debugging sync code difficult as you cannot see what the code from your Flutter widget was that triggered the async code (e.g. what is the call stack from clicking a button until the first async gap?).
For example, when debugging devtools I often see stack traces that look like
where I have no ide what method in my code called blockWhileInProgress.
Output from the Dart command line repro:
The text was updated successfully, but these errors were encountered: