in packages/devtools_app/lib/src/performance/performance_controller.dart [359:463]
Future<void> toggleSelectedFrame(FlutterFrame frame) async {
if (frame == null || data == null) {
return;
}
_currentFrameBeingSelected = frame;
// Unselect [frame] if is already selected.
if (data.selectedFrame == frame) {
data.selectedFrame = null;
_selectedFrameNotifier.value = null;
return;
}
data.selectedFrame = frame;
_selectedFrameNotifier.value = frame;
// Default to viewing the timeline events flame chart when a new frame is
// selected.
_selectedAnalysisTab.value = null;
if (!offlineController.offlineMode.value) {
final bool frameBeforeFirstWellFormedFrame =
firstWellFormedFrameMicros != null &&
frame.timeFromFrameTiming.start.inMicroseconds <
firstWellFormedFrameMicros;
if (!frame.isWellFormed && !frameBeforeFirstWellFormedFrame) {
// Only try to pull timeline events for frames that are after the first
// well formed frame. Timeline events that occurred before this frame will
// have already fallen out of the buffer.
await processAvailableEvents();
}
if (_currentFrameBeingSelected != frame) return;
// If the frame is still not well formed after processing all available
// events, wait a short delay and try to process events again after the
// VM has been polled one more time.
if (!frame.isWellFormed && !frameBeforeFirstWellFormedFrame) {
assert(!_processing.value);
_processing.value = true;
await Future.delayed(timelinePollingInterval, () async {
if (_currentFrameBeingSelected != frame) return;
await processTraceEvents(allTraceEvents);
_processing.value = false;
});
}
if (_currentFrameBeingSelected != frame) return;
}
// We do not need to pull the CPU profile because we will pull the profile
// for the entire frame. The order of selecting the timeline event and
// pulling the CPU profile for the frame (directly below) matters here.
// If the selected timeline event is null, the event details section will
// not show the progress bar while we are processing the CPU profile.
await selectTimelineEvent(
frame.timelineEventData.uiEvent,
updateProfiler: false,
);
if (_currentFrameBeingSelected != frame) return;
final storedProfileForFrame = cpuProfilerController.cpuProfileStore
.lookupProfile(time: frame.timeFromEventFlows);
if (storedProfileForFrame == null) {
cpuProfilerController.reset();
if (!offlineController.offlineMode.value &&
frame.timeFromEventFlows.isWellFormed) {
await cpuProfilerController.pullAndProcessProfile(
startMicros: frame.timeFromEventFlows.start.inMicroseconds,
extentMicros: frame.timeFromEventFlows.duration.inMicroseconds,
processId: 'Flutter frame ${frame.id}',
);
}
if (_currentFrameBeingSelected != frame) return;
data.cpuProfileData = cpuProfilerController.dataNotifier.value;
} else {
if (!storedProfileForFrame.processed) {
await cpuProfilerController.transformer.processData(
storedProfileForFrame,
processId: 'Flutter frame ${frame.id} - stored profile ',
);
}
if (_currentFrameBeingSelected != frame) return;
data.cpuProfileData = storedProfileForFrame;
cpuProfilerController.loadProcessedData(
storedProfileForFrame,
storeAsUserTagNone: true,
);
}
if (debugTimeline) {
final buf = StringBuffer();
buf.writeln('UI timeline event for frame ${frame.id}:');
frame.timelineEventData.uiEvent.format(buf, ' ');
buf.writeln('\nUI trace for frame ${frame.id}');
frame.timelineEventData.uiEvent.writeTraceToBuffer(buf);
buf.writeln('\Raster timeline event frame ${frame.id}:');
frame.timelineEventData.rasterEvent.format(buf, ' ');
buf.writeln('\nRaster trace for frame ${frame.id}');
frame.timelineEventData.rasterEvent.writeTraceToBuffer(buf);
log(buf.toString());
}
}