Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import 'package:vm_snapshot_analysis/utils.dart';
import 'package:vm_snapshot_analysis/v8_profile.dart';

import '../../shared/charts/treemap.dart';
import '../../shared/feature_flags.dart';
import '../../shared/framework/screen.dart';
import '../../shared/framework/screen_controllers.dart';
import '../../shared/primitives/utils.dart';
import '../../shared/table/table.dart';
Expand Down Expand Up @@ -85,6 +87,9 @@ class DiffTreeMap {
/// `screenControllers`. The `dispose` method is called by `screenControllers`
/// when DevTools is destroying a set of DevTools screen controllers.
class AppSizeController extends DevToolsScreenController {
@override
final screenId = ScreenMetaData.appSize.id;

static const unsupportedFileTypeError =
'Failed to load size analysis file: file type not supported.\n\n'
'The app size tool supports Dart AOT v8 snapshots, instruction sizes, '
Expand Down Expand Up @@ -766,6 +771,16 @@ class AppSizeController extends DevToolsScreenController {
_processingNotifier.dispose();
super.dispose();
}

@override
void releaseMemory({bool partial = false}) {
if (FeatureFlags.memoryObserver) {
// This behavior is the same regardless of the value of `partial`. We can
// implement a partial clearing if it becomes necessary.
clear(AppSizeScreen.analysisTabKey);
clear(AppSizeScreen.diffTabKey);
}
}
}

extension AppSizeJsonFileExtension on DevToolsJsonFile {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import '../../shared/diagnostics/primitives/source_location.dart';
import '../../shared/diagnostics/tree_builder.dart';
import '../../shared/feature_flags.dart';
import '../../shared/framework/routing.dart';
import '../../shared/framework/screen.dart';
import '../../shared/framework/screen_controllers.dart';
import '../../shared/globals.dart';
import '../../shared/primitives/message_bus.dart';
Expand Down Expand Up @@ -58,6 +59,9 @@ class DebuggerController extends DevToolsScreenController
}
}

@override
final screenId = ScreenMetaData.debugger.id;

@override
void init() {
super.init();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import '../../shared/analytics/analytics.dart' as ga;
import '../../shared/analytics/constants.dart' as gac;
import '../../shared/analytics/metrics.dart';
import '../../shared/feature_flags.dart';
import '../../shared/framework/screen.dart';
import '../../shared/framework/screen_controllers.dart';
import '../../shared/globals.dart';
import '../../shared/primitives/utils.dart';
Expand Down Expand Up @@ -158,6 +159,9 @@ class DisplayOptions {
/// when DevTools is destroying a set of DevTools screen controllers.
class DeepLinksController extends DevToolsScreenController
with AutoDisposeControllerMixin {
@override
final screenId = ScreenMetaData.deepLinks.id;

@override
void dispose() {
_selectedAndroidVariantIndex.dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import '../../shared/analytics/metrics.dart';
import '../../shared/console/primitives/simple_items.dart';
import '../../shared/framework/screen.dart';
import '../../shared/framework/screen_controllers.dart';
import '../inspector/inspector_controller.dart' as legacy;
import '../inspector/inspector_tree_controller.dart' as legacy;
Expand All @@ -21,6 +22,9 @@ import '../inspector_v2/inspector_tree_controller.dart' as v2;
/// `screenControllers`. The `dispose` method is called by `screenControllers`
/// when DevTools is destroying a set of DevTools screen controllers.
class InspectorScreenController extends DevToolsScreenController {
@override
final screenId = ScreenMetaData.inspector.id;

late v2.InspectorController v2InspectorController;
late v2.InspectorTreeController v2InspectorTreeController;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import 'package:vm_service/vm_service.dart';
import '../../service/vm_service_wrapper.dart';
import '../../shared/diagnostics/diagnostics_node.dart';
import '../../shared/diagnostics/inspector_service.dart';
import '../../shared/feature_flags.dart';
import '../../shared/framework/app_error_handling.dart' as error_handling;
import '../../shared/framework/screen.dart';
import '../../shared/framework/screen_controllers.dart';
import '../../shared/globals.dart';
import '../../shared/primitives/byte_utils.dart';
Expand Down Expand Up @@ -98,6 +100,9 @@ class LoggingController extends DevToolsScreenController
SearchControllerMixin<LogData>,
FilterControllerMixin<LogData>,
AutoDisposeControllerMixin {
@override
final screenId = ScreenMetaData.logging.id;

static const _minLogLevelFilterId = 'min-log-level';
static const _verboseFlutterFrameworkFilterId = 'verbose-flutter-framework';
static const _verboseFlutterServiceFilterId = 'verbose-flutter-service';
Expand Down Expand Up @@ -787,6 +792,18 @@ class LoggingController extends DevToolsScreenController
data.where((log) => includeLogForFilter(log, filter: filter)).toList(),
);
}

@override
void releaseMemory({bool partial = false}) {
if (FeatureFlags.memoryObserver) {
if (partial) {
// Trim logs from the front so that the oldest logs are removed.
_updateData(data.sublist(data.length ~/ 2));
} else {
clear();
}
}
}
}

extension type _LogRecord(Map<String, dynamic> json) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'dart:async';
import 'package:devtools_app_shared/utils.dart';
import 'package:flutter/foundation.dart';

import '../../../shared/feature_flags.dart';
import '../../../shared/framework/screen.dart';
import '../../../shared/framework/screen_controllers.dart';
import '../../../shared/globals.dart';
Expand Down Expand Up @@ -39,6 +40,9 @@ class MemoryController extends DevToolsScreenController
with
AutoDisposeControllerMixin,
OfflineScreenControllerMixin<OfflineMemoryData> {
@override
final screenId = ScreenMetaData.memory.id;

Future<void> get initialized => _initialized.future;
final _initialized = Completer<void>();

Expand Down Expand Up @@ -210,4 +214,14 @@ class MemoryController extends DevToolsScreenController
_gcing.value = false;
}
}

@override
FutureOr<void> releaseMemory({bool partial = false}) async {
if (FeatureFlags.memoryObserver) {
diff.clearSnapshots(partial: partial);
// Clear all allocation traces since the traces form a single tracing
// profile.
await trace?.clear();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,24 @@ class DiffPaneController extends DisposableController with Serializable {
}
}

void clearSnapshots() {
void clearSnapshots({bool partial = false}) {
const offsetForInstructionSnapshot = 1;
final snapshots = core._snapshots;
for (var i = 1; i < snapshots.value.length; i++) {
final snapshotsToRemove = max(
offsetForInstructionSnapshot,
(snapshots.value.length - offsetForInstructionSnapshot) ~/
(partial ? 2 : 1),
);
final endIndexToRemoveExclusive =
offsetForInstructionSnapshot + snapshotsToRemove;
for (
var i = offsetForInstructionSnapshot;
i < endIndexToRemoveExclusive;
i++
) {
snapshots.value[i].dispose();
}
snapshots.removeRange(1, snapshots.value.length);
snapshots.removeRange(1, endIndexToRemoveExclusive);
core._selectedSnapshotIndex.value = 0;
derived._updateValues();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import 'package:vm_service/vm_service.dart';

import '../../shared/config_specific/import_export/import_export.dart';
import '../../shared/config_specific/logger/allowed_error.dart';
import '../../shared/feature_flags.dart';
import '../../shared/framework/screen.dart';
import '../../shared/framework/screen_controllers.dart';
import '../../shared/globals.dart';
import '../../shared/http/http_request_data.dart';
Expand Down Expand Up @@ -61,6 +63,9 @@ class NetworkController extends DevToolsScreenController
FilterControllerMixin<NetworkRequest>,
OfflineScreenControllerMixin,
AutoDisposeControllerMixin {
@override
final screenId = ScreenMetaData.network.id;

List<DartIOHttpRequestData>? _httpRequests;

Future<String?> exportAsHarFile() async {
Expand Down Expand Up @@ -392,9 +397,11 @@ class NetworkController extends DevToolsScreenController

/// Clears the HTTP profile and socket profile from the vm, and resets the
/// last refresh timestamp to the current time.
Future<void> clear() async {
await networkService.clearData();
_currentNetworkRequests.clear();
Future<void> clear({bool partial = false}) async {
if (!partial) {
await networkService.clearData();
}
_currentNetworkRequests.clear(partial: partial);
_filterAndRefreshSearchMatches();
_updateSelection();
}
Expand Down Expand Up @@ -507,6 +514,13 @@ class NetworkController extends DevToolsScreenController
.whereType<DartIOHttpRequestData>()
.map((item) => item.getFullRequestData())
.wait;

@override
FutureOr<void> releaseMemory({bool partial = false}) async {
if (FeatureFlags.memoryObserver) {
await clear(partial: partial);
}
}
}

/// Class for managing the set of all current sockets, and
Expand Down Expand Up @@ -589,9 +603,19 @@ class CurrentNetworkRequests extends ValueNotifier<List<NetworkRequest>> {
}
}

void clear() {
_requestsById.clear();
value = [];
notifyListeners();
void clear({bool partial = false}) {
if (partial) {
_requestsById.keys
.toList()
// Trim requests from the front so that the oldest data is removed.
.sublist(0, _requestsById.keys.length ~/ 2)
.forEach(_requestsById.remove);
value = value.sublist(value.length ~/ 2);
notifyListeners();
} else {
_requestsById.clear();
value = [];
notifyListeners();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,13 @@ class FlutterFramesController extends PerformanceFeatureController {
}

@override
void clearData() {
_flutterFrames.clear();
void clearData({bool partial = false}) {
if (partial) {
// Trim frames from the front so that the oldest logs are removed.
_flutterFrames.trimToSublist(_flutterFrames.value.length ~/ 2);
} else {
_flutterFrames.clear();
}
_unassignedFlutterFrames.clear();
firstWellFormedFrameMicros = null;
_selectedFrameNotifier.value = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class RebuildStatsController extends PerformanceFeatureController {
RebuildStatsController(super.performanceController);

@override
FutureOr<void> clearData() {}
void clearData({bool partial = false}) {}

@override
void handleSelectedFrame(FlutterFrame frame) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,15 +513,24 @@ class TimelineEventsController extends PerformanceFeatureController
}

@override
Future<void> clearData() async {
_unprocessedTrackEvents.clear();
traceRingBuffer.clear();
_trackDescriptors.clear();
_unassignedFlutterTimelineEvents.clear();

_refreshWorkTracker.clear();
_status.value = EventsControllerStatus.empty;
await perfettoController.clear();
Future<void> clearData({bool partial = false}) async {
if (partial) {
if (traceRingBuffer.chunksLength <= 1) {
traceRingBuffer.clear();
} else {
// Trim from the front so that the oldest data is removed.
traceRingBuffer.trimToSublist(traceRingBuffer.chunksLength ~/ 2);
}
unawaited(loadPerfettoTrace());
} else {
traceRingBuffer.clear();
_unprocessedTrackEvents.clear();
_trackDescriptors.clear();
_unassignedFlutterTimelineEvents.clear();
_refreshWorkTracker.clear();
_status.value = EventsControllerStatus.empty;
await perfettoController.clear();
}
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:vm_service/vm_service.dart';
import '../../service/service_registrations.dart' as registrations;
import '../../shared/diagnostics/inspector_service.dart';
import '../../shared/feature_flags.dart';
import '../../shared/framework/screen.dart';
import '../../shared/framework/screen_controllers.dart';
import '../../shared/globals.dart';
import '../../shared/offline/offline_data.dart';
Expand Down Expand Up @@ -41,6 +42,9 @@ class PerformanceController extends DevToolsScreenController
with
AutoDisposeControllerMixin,
OfflineScreenControllerMixin<OfflinePerformanceData> {
@override
final screenId = ScreenMetaData.performance.id;

late final FlutterFramesController flutterFramesController;

late final TimelineEventsController timelineEventsController;
Expand Down Expand Up @@ -240,13 +244,17 @@ class PerformanceController extends DevToolsScreenController

/// Clears the timeline data currently stored by the controller as well the
/// VM timeline if a connected app is present.
Future<void> clearData() async {
if (serviceConnection.serviceManager.connectedAppInitialized) {
Future<void> clearData({
bool partial = false,
bool clearVmTimeline = true,
}) async {
if (serviceConnection.serviceManager.connectedAppInitialized &&
clearVmTimeline) {
await serviceConnection.serviceManager.service!.clearVMTimeline();
}
offlinePerformanceData = null;
serviceConnection.errorBadgeManager.clearErrors(PerformanceScreen.id);
await _applyToFeatureControllersAsync((c) => c.clearData());
await _applyToFeatureControllersAsync((c) => c.clearData(partial: partial));
}

@override
Expand All @@ -269,6 +277,13 @@ class PerformanceController extends DevToolsScreenController
displayRefreshRate: flutterFramesController.displayRefreshRate.value,
).toJson(),
);

@override
FutureOr<void> releaseMemory({bool partial = false}) async {
if (FeatureFlags.memoryObserver) {
await clearData(partial: partial, clearVmTimeline: !partial);
}
}
}

abstract class PerformanceFeatureController extends DisposableController {
Expand All @@ -294,7 +309,7 @@ abstract class PerformanceFeatureController extends DisposableController {

Future<void> setOfflineData(OfflinePerformanceData offlineData);

FutureOr<void> clearData();
FutureOr<void> clearData({bool partial = false});

void handleSelectedFrame(FlutterFrame frame);
}
Loading