Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,10 @@ class _SecondaryPerformanceControls extends StatelessWidget {
const SizedBox(width: denseSpacing),
OpenSaveButtonGroup(
screenId: ScreenMetaData.performance.id,
onSave: (_) => controller.exportData(),
onSave: (_) async {
await controller.fetchFullDataBeforeExport();
controller.exportData();
},
),
if (isFlutterApp) ...[
const SizedBox(width: denseSpacing),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,18 @@ class TimelineEventsController extends PerformanceFeatureController
await loadPerfettoTrace();
}

/// Pulls any timeline events from the VM that have not been received yet.
///
/// Unlike [forceRefresh], this does not reload the Perfetto trace viewer.
Future<void> pullLatestTimelineEvents() async {
try {
await _pullPerfettoVmTimeline();
} catch (e, st) {
// Don't block the export of the data we already have.
_log.warning('Failed to pull the latest timeline events.', e, st);
}
}

void processTrackEvents() {
if (!_isFlutterAppHelper()) {
debugTraceCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,20 @@ class PerformanceController extends DevToolsScreenController
).toJson(),
);

/// Pulls the latest timeline events so that they are included in the export.
///
/// Timeline events are only polled periodically, so the most recent events
/// would otherwise be missing from the export.
Future<void> fetchFullDataBeforeExport() async {
final serviceManager = serviceConnection.serviceManager;
if (offlineDataController.showingOfflineData.value ||
!serviceManager.connectedState.value.connected ||
(serviceManager.connectedApp?.isDartWebAppNow ?? false)) {
return;
}
await timelineEventsController.pullLatestTimelineEvents();
}

@override
FutureOr<void> releaseMemory({bool partial = false}) async {
await clearData(partial: partial, clearVmTimeline: !partial);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,17 @@ void main() {
expect(controller.flutterFramesController.isActiveFeature, isTrue);
expect(controller.timelineEventsController.isActiveFeature, isTrue);
});

test('fetchFullDataBeforeExport does not pull timeline events when showing '
'offline data', () async {
final mockServiceManager =
mockServiceConnection.serviceManager as MockServiceManager;
clearInteractions(mockServiceManager);

await controller.fetchFullDataBeforeExport();

verifyNever(mockServiceManager.service);
expect(controller.timelineEventsController.fullPerfettoTrace, isEmpty);
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,32 @@ void main() {
);
});

group('pullLatestTimelineEvents', () {
setUp(() {
setGlobal(PreferencesController, PreferencesController());
});

test('adds the latest VM timeline to the trace', () async {
expect(eventsController.fullPerfettoTrace, isEmpty);

await eventsController.pullLatestTimelineEvents();

expect(eventsController.fullPerfettoTrace, isNotEmpty);
});

test('completes without throwing when the pull fails', () async {
// This fake service throws when the VM timeline is requested because
// it was not provided any timeline data.
setGlobal(ServiceConnectionManager, FakeServiceConnectionManager());

await expectLater(
eventsController.pullLatestTimelineEvents(),
completes,
);
expect(eventsController.fullPerfettoTrace, isEmpty);
});
});

group('can clearData', () {
setUp(() async {
// Set some data in the controller. It does not matter if it is connected
Expand Down
Loading