-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
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:
Start main. #0 main (file:///Users/jacobr/git/stack_trace_repro/console-simple/bin/console_simple.dart:54:35)
#1 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:281:32)
#2 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
nonAsyncFunc. Stack trace correctly includes handleTimeout.
It is correct this stacktrace does not include main as Timer was used rather than
aysnc.
#0 nonAsyncFunc (file:///Users/jacobr/git/stack_trace_repro/console-simple/bin/console_simple.dart:15:18)
#1 handleTimeout (file:///Users/jacobr/git/stack_trace_repro/console-simple/bin/console_simple.dart:7:3)
#2 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
#3 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395:19)
#4 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426:5)
#5 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
Func1. Stack trace should be:
func1
<asynchronous suspension>
nonAsyncFunc
useATimerToGetOnTheEventLoopWithoutAsync
...
But is instead:
#0 func1 (file:///Users/jacobr/git/stack_trace_repro/console-simple/bin/console_simple.dart:47:16)
<asynchronous suspension>
Func2. Stack trace should be:
func2
<asynchronous suspension>
func1
<asynchronous suspension>
nonAsyncFunc
useATimerToGetOnTheEventLoopWithoutAsync
...
But is instead:
#0 func2 (file:///Users/jacobr/git/stack_trace_repro/console-simple/bin/console_simple.dart:31:16)
<asynchronous suspension>
#1 func1 (file:///Users/jacobr/git/stack_trace_repro/console-simple/bin/console_simple.dart:50:3)
<asynchronous suspension>