Skip to content

Async stack traces are missing frames before the first async gap for all typical Flutter use cases #46318

@jacob314

Description

@jacob314

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
Screen Shot 2021-06-08 at 12 14 29 PM
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>

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.type-bugIncorrect behavior (everything from a crash to more subtle misbehavior)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions