Unverified Commit 2004afae authored by Jason Simmons's avatar Jason Simmons Committed by GitHub

Use async timeline events for the phases of the scheduler binding (#88825)

parent 7736b9ee
...@@ -37,11 +37,11 @@ Future<void> saveDurationsHistogram(List<Map<String, dynamic>> events, String ou ...@@ -37,11 +37,11 @@ Future<void> saveDurationsHistogram(List<Map<String, dynamic>> events, String ou
} else if (startEvent != null && eventName == 'Frame') { } else if (startEvent != null && eventName == 'Frame') {
final String phase = event['ph'] as String; final String phase = event['ph'] as String;
final int timestamp = event['ts'] as int; final int timestamp = event['ts'] as int;
if (phase == 'B') { if (phase == 'B' || phase == 'b') {
assert(frameStart == null); assert(frameStart == null);
frameStart = timestamp; frameStart = timestamp;
} else { } else {
assert(phase == 'E'); assert(phase == 'E' || phase == 'e');
final String routeName = (startEvent['args'] as Map<String, dynamic>)['to'] as String; final String routeName = (startEvent['args'] as Map<String, dynamic>)['to'] as String;
durations[routeName] ??= <int>[]; durations[routeName] ??= <int>[];
durations[routeName]!.add(timestamp - frameStart!); durations[routeName]!.add(timestamp - frameStart!);
......
...@@ -298,7 +298,7 @@ abstract class BindingBase { ...@@ -298,7 +298,7 @@ abstract class BindingBase {
/// The [Future] returned by the `callback` argument is returned by [lockEvents]. /// The [Future] returned by the `callback` argument is returned by [lockEvents].
@protected @protected
Future<void> lockEvents(Future<void> Function() callback) { Future<void> lockEvents(Future<void> Function() callback) {
developer.Timeline.startSync('Lock events'); final developer.TimelineTask timelineTask = developer.TimelineTask()..start('Lock events');
assert(callback != null); assert(callback != null);
_lockCount += 1; _lockCount += 1;
...@@ -307,7 +307,7 @@ abstract class BindingBase { ...@@ -307,7 +307,7 @@ abstract class BindingBase {
future.whenComplete(() { future.whenComplete(() {
_lockCount -= 1; _lockCount -= 1;
if (!locked) { if (!locked) {
developer.Timeline.finishSync(); timelineTask.finish();
unlocked(); unlocked();
} }
}); });
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:developer' show Flow, Timeline; import 'dart:developer' show Flow, Timeline, TimelineTask;
import 'dart:ui' show AppLifecycleState, FramePhase, FrameTiming, TimingsCallback, PlatformDispatcher; import 'dart:ui' show AppLifecycleState, FramePhase, FrameTiming, TimingsCallback, PlatformDispatcher;
import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue; import 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue;
...@@ -851,7 +851,7 @@ mixin SchedulerBinding on BindingBase { ...@@ -851,7 +851,7 @@ mixin SchedulerBinding on BindingBase {
return; return;
_warmUpFrame = true; _warmUpFrame = true;
Timeline.startSync('Warm-up frame'); final TimelineTask timelineTask = TimelineTask()..start('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame; final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between. // We use timers here to ensure that microtasks flush in between.
Timer.run(() { Timer.run(() {
...@@ -879,7 +879,7 @@ mixin SchedulerBinding on BindingBase { ...@@ -879,7 +879,7 @@ mixin SchedulerBinding on BindingBase {
// scheduled frame has finished. // scheduled frame has finished.
lockEvents(() async { lockEvents(() async {
await endOfFrame; await endOfFrame;
Timeline.finishSync(); timelineTask.finish();
}); });
} }
...@@ -996,6 +996,9 @@ mixin SchedulerBinding on BindingBase { ...@@ -996,6 +996,9 @@ mixin SchedulerBinding on BindingBase {
handleDrawFrame(); handleDrawFrame();
} }
TimelineTask? _frameTimelineTask;
TimelineTask? _animateTimelineTask;
/// Called by the engine to prepare the framework to produce a new frame. /// Called by the engine to prepare the framework to produce a new frame.
/// ///
/// This function calls all the transient frame callbacks registered by /// This function calls all the transient frame callbacks registered by
...@@ -1020,7 +1023,7 @@ mixin SchedulerBinding on BindingBase { ...@@ -1020,7 +1023,7 @@ mixin SchedulerBinding on BindingBase {
/// statements printed during a frame from those printed between frames (e.g. /// statements printed during a frame from those printed between frames (e.g.
/// in response to events or timers). /// in response to events or timers).
void handleBeginFrame(Duration? rawTimeStamp) { void handleBeginFrame(Duration? rawTimeStamp) {
Timeline.startSync('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent); _frameTimelineTask = TimelineTask()..start('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);
_firstRawTimeStampInEpoch ??= rawTimeStamp; _firstRawTimeStampInEpoch ??= rawTimeStamp;
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp); _currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
if (rawTimeStamp != null) if (rawTimeStamp != null)
...@@ -1047,7 +1050,7 @@ mixin SchedulerBinding on BindingBase { ...@@ -1047,7 +1050,7 @@ mixin SchedulerBinding on BindingBase {
_hasScheduledFrame = false; _hasScheduledFrame = false;
try { try {
// TRANSIENT FRAME CALLBACKS // TRANSIENT FRAME CALLBACKS
Timeline.startSync('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent); _animateTimelineTask = TimelineTask()..start('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
_schedulerPhase = SchedulerPhase.transientCallbacks; _schedulerPhase = SchedulerPhase.transientCallbacks;
final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks; final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
_transientCallbacks = <int, _FrameCallbackEntry>{}; _transientCallbacks = <int, _FrameCallbackEntry>{};
...@@ -1072,7 +1075,8 @@ mixin SchedulerBinding on BindingBase { ...@@ -1072,7 +1075,8 @@ mixin SchedulerBinding on BindingBase {
/// useful when working with frame callbacks. /// useful when working with frame callbacks.
void handleDrawFrame() { void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks); assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the "Animate" phase _animateTimelineTask?.finish(); // end the "Animate" phase
_animateTimelineTask = null;
try { try {
// PERSISTENT FRAME CALLBACKS // PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks; _schedulerPhase = SchedulerPhase.persistentCallbacks;
...@@ -1088,7 +1092,8 @@ mixin SchedulerBinding on BindingBase { ...@@ -1088,7 +1092,8 @@ mixin SchedulerBinding on BindingBase {
_invokeFrameCallback(callback, _currentFrameTimeStamp!); _invokeFrameCallback(callback, _currentFrameTimeStamp!);
} finally { } finally {
_schedulerPhase = SchedulerPhase.idle; _schedulerPhase = SchedulerPhase.idle;
Timeline.finishSync(); // end the Frame _frameTimelineTask?.finish(); // end the Frame
_frameTimelineTask = null;
assert(() { assert(() {
if (debugPrintEndFrameBanner) if (debugPrintEndFrameBanner)
debugPrint('▀' * _debugBanner!.length); debugPrint('▀' * _debugBanner!.length);
......
...@@ -351,7 +351,7 @@ class TimelineSummary { ...@@ -351,7 +351,7 @@ class TimelineSummary {
// Timeline does not guarantee that the first event is the "begin" event. // Timeline does not guarantee that the first event is the "begin" event.
TimelineEvent? begin; TimelineEvent? begin;
for (final TimelineEvent event in events) { for (final TimelineEvent event in events) {
if (event.phase == 'B') { if (event.phase == 'B' || event.phase == 'b') {
begin = event; begin = event;
} else { } else {
if (begin != null) { if (begin != null) {
......
...@@ -79,5 +79,26 @@ void main() { ...@@ -79,5 +79,26 @@ void main() {
expect(response.json['value'], 'Brightness.light'); expect(response.json['value'], 'Brightness.light');
} }
timer.cancel(); timer.cancel();
// Verify that all duration events on the timeline are properly nested.
final Response response = await vmService.callServiceExtension('getVMTimeline');
final List<TimelineEvent> events = (response as Timeline).traceEvents;
final Map<int, List<String>> threadDurationEventStack = <int, List<String>>{};
for (final TimelineEvent e in events) {
final Map<String, dynamic> event = e.json;
final String phase = event['ph'] as String;
final int tid = event['tid'] as int;
final String name = event['name'] as String;
final List<String> stack = threadDurationEventStack.putIfAbsent(tid, () => <String>[]);
if (phase == 'B') {
stack.add(name);
} else if (phase == 'E') {
// The downloaded part of the timeline may contain an end event whose
// corresponding begin event happened before the start of the timeline.
if (stack.isNotEmpty) {
expect(stack.removeLast(), name);
}
}
}
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment