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
} else if (startEvent != null && eventName == 'Frame') {
final String phase = event['ph'] as String;
final int timestamp = event['ts'] as int;
if (phase == 'B') {
if (phase == 'B' || phase == 'b') {
assert(frameStart == null);
frameStart = timestamp;
} else {
assert(phase == 'E');
assert(phase == 'E' || phase == 'e');
final String routeName = (startEvent['args'] as Map<String, dynamic>)['to'] as String;
durations[routeName] ??= <int>[];
durations[routeName]!.add(timestamp - frameStart!);
......
......@@ -298,7 +298,7 @@ abstract class BindingBase {
/// The [Future] returned by the `callback` argument is returned by [lockEvents].
@protected
Future<void> lockEvents(Future<void> Function() callback) {
developer.Timeline.startSync('Lock events');
final developer.TimelineTask timelineTask = developer.TimelineTask()..start('Lock events');
assert(callback != null);
_lockCount += 1;
......@@ -307,7 +307,7 @@ abstract class BindingBase {
future.whenComplete(() {
_lockCount -= 1;
if (!locked) {
developer.Timeline.finishSync();
timelineTask.finish();
unlocked();
}
});
......
......@@ -4,7 +4,7 @@
import 'dart:async';
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 'package:collection/collection.dart' show PriorityQueue, HeapPriorityQueue;
......@@ -851,7 +851,7 @@ mixin SchedulerBinding on BindingBase {
return;
_warmUpFrame = true;
Timeline.startSync('Warm-up frame');
final TimelineTask timelineTask = TimelineTask()..start('Warm-up frame');
final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() {
......@@ -879,7 +879,7 @@ mixin SchedulerBinding on BindingBase {
// scheduled frame has finished.
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
timelineTask.finish();
});
}
......@@ -996,6 +996,9 @@ mixin SchedulerBinding on BindingBase {
handleDrawFrame();
}
TimelineTask? _frameTimelineTask;
TimelineTask? _animateTimelineTask;
/// Called by the engine to prepare the framework to produce a new frame.
///
/// This function calls all the transient frame callbacks registered by
......@@ -1020,7 +1023,7 @@ mixin SchedulerBinding on BindingBase {
/// statements printed during a frame from those printed between frames (e.g.
/// in response to events or timers).
void handleBeginFrame(Duration? rawTimeStamp) {
Timeline.startSync('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);
_frameTimelineTask = TimelineTask()..start('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);
_firstRawTimeStampInEpoch ??= rawTimeStamp;
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
if (rawTimeStamp != null)
......@@ -1047,7 +1050,7 @@ mixin SchedulerBinding on BindingBase {
_hasScheduledFrame = false;
try {
// TRANSIENT FRAME CALLBACKS
Timeline.startSync('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
_animateTimelineTask = TimelineTask()..start('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
_schedulerPhase = SchedulerPhase.transientCallbacks;
final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
_transientCallbacks = <int, _FrameCallbackEntry>{};
......@@ -1072,7 +1075,8 @@ mixin SchedulerBinding on BindingBase {
/// useful when working with frame callbacks.
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the "Animate" phase
_animateTimelineTask?.finish(); // end the "Animate" phase
_animateTimelineTask = null;
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;
......@@ -1088,7 +1092,8 @@ mixin SchedulerBinding on BindingBase {
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
} finally {
_schedulerPhase = SchedulerPhase.idle;
Timeline.finishSync(); // end the Frame
_frameTimelineTask?.finish(); // end the Frame
_frameTimelineTask = null;
assert(() {
if (debugPrintEndFrameBanner)
debugPrint('▀' * _debugBanner!.length);
......
......@@ -351,7 +351,7 @@ class TimelineSummary {
// Timeline does not guarantee that the first event is the "begin" event.
TimelineEvent? begin;
for (final TimelineEvent event in events) {
if (event.phase == 'B') {
if (event.phase == 'B' || event.phase == 'b') {
begin = event;
} else {
if (begin != null) {
......
......@@ -79,5 +79,26 @@ void main() {
expect(response.json['value'], 'Brightness.light');
}
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