Skip to content

Add Queued Microtasks tab to Performance View #9239

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

derekxu16
Copy link
Contributor

@derekxu16 derekxu16 commented Jun 3, 2025

This PR adds a Queued Microtasks tab to the Performance View, which allows a user to see details about the microtasks scheduled in an isolate's microtask queue. This tab currently only appears when DevTools is connected to a Flutter or Dart app started with --profile-microtasks.

Screenshot 2025-06-03 at 5 41 29 PM

@derekxu16 derekxu16 force-pushed the queued-microtasks branch 2 times, most recently from b3c3d89 to d85106e Compare June 3, 2025 21:37
@@ -23,7 +23,11 @@ TODO: Remove this section if there are not any general updates.

## Performance updates

TODO: Remove this section if there are not any general updates.
- Added a Queued Microtasks tab to the Performance View, which allows a user to
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how these names should be capaitalized.

@derekxu16 derekxu16 force-pushed the queued-microtasks branch from d85106e to 0f3ea7e Compare June 3, 2025 22:04
@derekxu16 derekxu16 marked this pull request as ready for review June 3, 2025 22:12
@derekxu16 derekxu16 requested review from kenzieschmoll and a team as code owners June 3, 2025 22:12
@kenzieschmoll kenzieschmoll requested a review from bkonyi June 4, 2025 15:59
Comment on lines +32 to +41
final _status = ValueNotifier<QueuedMicrotasksControllerStatus>(
QueuedMicrotasksControllerStatus.empty,
);
ValueListenable<QueuedMicrotasksControllerStatus> get status => _status;

final _queuedMicrotasks = ValueNotifier<QueuedMicrotasks?>(null);
ValueListenable<QueuedMicrotasks?> get queuedMicrotasks => _queuedMicrotasks;

final _selectedMicrotask = ValueNotifier<Microtask?>(null);
ValueListenable<Microtask?> get selectedMicrotask => _selectedMicrotask;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future<void> handleSelectedFrame(FlutterFrame frame) async {}

@override
Future<void> setOfflineData(OfflinePerformanceData offlineData) async {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OfflinePerformanceData should be able to take the data from this tab and serialize it for offline exports of the performance page data. Then this method should set the _queuedMicrotasks and _selectedMicrotask from offline data when this method is called. See the other performance feature controllers for an example.

}
}

class QueuedMicrotasksTabControls extends StatelessWidget {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this helper widget seems unnecessary. Can we just use RefreshQueuedMicrotasksButton directly below where this widget is currently used?

// run earliest is at index 0 of the returned list. We use those indices of the
// returned list to sort the entries of the microtask selector, so that they
// they also appear in ascending order of when they will be dequeued.
typedef IndexedMicrotask = (int, Microtask);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we used named elements in this record so that we don't have to use $1 and $2?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation should also be using ///

final void Function(Microtask?) _setSelectedMicrotask;

@override
Widget build(BuildContext context) => Column(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this column necessary or can it be removed?

const PerformanceScreenBody({
super.key,
// This allows the body to modify the value that gets returned by the
// enclosing [PerformanceScreen]'s `showIsolateSelector` [ValueListenable].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me wonder generally whether this feature belongs in the Performance screen or the CPU profiler screen. Can you explain the user journey of how a user would use this feature to debug a performance problem, and what type of performance problem would the user be encountering?

Copy link
Contributor Author

@derekxu16 derekxu16 Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only known use case of this feature is checking if there's a bug causing an isolate's microtask queue to get stuck. This was requested in b/329176605. The user journey of using this feature would look something like:

  1. The user notices that their app can become unresponsive
  2. The user determines a reproduction that can make their app enter an unresponsive state
  3. The user starts their app with flutter run --profile-microtasks and connects their app to DevTools
  4. The user makes their app enter an unresponsive state
  5. The user navigates to the Queued Microtasks tab
  6. The user selects an isolate in the isolate selector
  7. The user takes a snapshot of the selected isolate's microtask queue
  8. The user notes down the ID of the microtask scheduled to run next
  9. The user waits for some amount of time (microtasks should drain quickly when the queue is not stuck, so waiting for a second is enough)
  10. The user takes another snapshot of the selected isolate's microtask queue
  11. If the ID of the microtask scheduled to run next is the same as the one noted down in step 8, there is a bug causing the microtask queue to be stuck
  12. If a bug was discovered in step 11, report it in the Dart SDK repo. Otherwise, repeat steps 6-12 until all isolates have been checked.

createMockPerformanceControllerWithDefaults(),
);
});

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after adding offline support for this feature, please add tests for setOfflineData here.

});

test('refresh', () async {
await controller.refresh();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add expect statements for what the state should have been prior to calling refresh

Comment on lines +299 to +301
await tester.tap(find.text('Timeline Events'));
await tester.pumpAndSettle();
await tester.tap(find.text('Queued Microtasks'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have to switch tabs? we should just be able to call pumpAndSettle to trigger a rebuild, and if that doesn't work then maybe there is a bug?

@@ -1 +1 @@
36ea2bdeab611e908967b6fa57659998f600a2cb
ead0a01bb9707b60097679a43a6262a8979f4a89
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use a version of Flutter that has been checked into google3. The latest is 7cfbf1b000546a88f7c1a5b0c154a51fd97147f8

.getQueuedMicrotasks(isolateId);
_queuedMicrotasks.value = queuedMicrotasks;

return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary return.

class RefreshQueuedMicrotasksButton extends StatelessWidget {
const RefreshQueuedMicrotasksButton({
super.key,
required QueuedMicrotasksController controller,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd just make _controller public and use required this.controller here.

// run earliest is at index 0 of the returned list. We use those indices of the
// returned list to sort the entries of the microtask selector, so that they
// they also appear in ascending order of when they will be dequeued.
typedef IndexedMicrotask = (int, Microtask);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation should also be using ///

);
}

class StackTraceView extends StatelessWidget {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/StackTraceView/MicrotaskStackTraceView/

@@ -16,6 +16,9 @@ const profiler = 'profiler';
// Defined in SDK: https://github.com/dart-lang/sdk/blob/master/runtime/vm/profiler.cc#L36
const profilePeriod = 'profile_period';

// Defined in SDK: https://github.com/dart-lang/sdk/blob/main/runtime/vm/microtask_mirror_queues.cc#L18-L23.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's worth including the line numbers here, especially if we're using a URL that doesn't point to a specific commit.

vmFlags: [
(
flagName: profileMicrotasks,
value: shouldEnableMicrotaskProfiling ? 'true' : 'false',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: shouldEnableMicrotaskProfiling.toString()

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

Successfully merging this pull request may close these issues.

3 participants