Skip to content
Open
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 @@ -34,13 +34,19 @@ class AdaptedProfile with Serializable {
factory AdaptedProfile.fromAllocationProfile(
AllocationProfile profile,
ClassFilter filter,
String? rootPackage,
) {
String? rootPackage, {
Set<String> pinnedClassFullNames = const {},
}) {
final adaptedProfile = AdaptedProfile._(
total: ProfileRecord.total(profile),
items: (profile.members ?? [])
.where((e) => (e.instancesCurrent ?? 0) > 0)
.map((e) => ProfileRecord.fromClassHeapStats(e))
.map((e) => ProfileRecord.fromClassHeapStats(
e,
userPinned: pinnedClassFullNames.contains(
HeapClassName.fromClassRef(e.classRef).fullName,
),
))
.toList(),
newSpaceGCStats: profile.newSpaceGCStats,
oldSpaceGCStats: profile.oldSpaceGCStats,
Expand Down Expand Up @@ -70,6 +76,29 @@ class AdaptedProfile with Serializable {
);
}

/// Returns a copy of [profile] with [pinnedClassFullNames] applied to items.
factory AdaptedProfile.withPinnedClasses(
AdaptedProfile profile,
Set<String> pinnedClassFullNames,
String? rootPackage,
) {
final itemsWithPins = profile._items
.map(
(record) => record.copyWith(
userPinned: pinnedClassFullNames.contains(record.heapClass.fullName),
),
)
.toList();
final updated = AdaptedProfile._(
total: profile._total,
items: itemsWithPins,
newSpaceGCStats: profile.newSpaceGCStats,
oldSpaceGCStats: profile.oldSpaceGCStats,
totalGCStats: profile.totalGCStats,
);
return AdaptedProfile.withNewFilter(updated, profile.filter, rootPackage);
}

factory AdaptedProfile.fromJson(Map<String, dynamic> json) {
return AdaptedProfile._(
total: ProfileRecord.fromJson(json[_ProfileJson.total]),
Expand Down Expand Up @@ -117,6 +146,7 @@ class AdaptedProfile with Serializable {
class _RecordJson {
static const isTotal = 'it';
static const heapClass = 'c';
static const userPinned = 'p';
static const totalInstances = 'ti';
static const totalSize = 'ts';
static const totalDartHeapSize = 'tds';
Expand All @@ -135,6 +165,7 @@ class ProfileRecord with PinnableListEntry, Serializable {
ProfileRecord._({
required this.isTotal,
required this.heapClass,
this.userPinned = false,
required this.totalInstances,
required this.totalSize,
required this.totalDartHeapSize,
Expand All @@ -151,14 +182,18 @@ class ProfileRecord with PinnableListEntry, Serializable {
_verifyIntegrity();
}

factory ProfileRecord.fromClassHeapStats(ClassHeapStats stats) {
factory ProfileRecord.fromClassHeapStats(
ClassHeapStats stats, {
bool userPinned = false,
}) {
assert(
stats.bytesCurrent! == stats.newSpace.size + stats.oldSpace.size,
'${stats.bytesCurrent}, ${stats.newSpace.size}, ${stats.oldSpace.size}',
);
return ProfileRecord._(
isTotal: false,
heapClass: HeapClassName.fromClassRef(stats.classRef),
userPinned: userPinned,
totalInstances: stats.instancesCurrent ?? 0,
totalSize:
stats.bytesCurrent! +
Expand All @@ -180,6 +215,7 @@ class ProfileRecord with PinnableListEntry, Serializable {

ProfileRecord.total(AllocationProfile profile)
: isTotal = true,
userPinned = false,
heapClass = HeapClassName.fromPath(className: 'All Classes', library: ''),
totalInstances = null,
totalSize =
Expand All @@ -202,6 +238,7 @@ class ProfileRecord with PinnableListEntry, Serializable {
return ProfileRecord._(
isTotal: json[_RecordJson.isTotal] as bool,
heapClass: HeapClassName.fromJson(json[_RecordJson.heapClass]),
userPinned: json[_RecordJson.userPinned] as bool? ?? false,
totalInstances: json[_RecordJson.totalInstances] as int?,
totalSize: json[_RecordJson.totalSize] as int,
totalDartHeapSize: json[_RecordJson.totalDartHeapSize] as int,
Expand All @@ -217,11 +254,32 @@ class ProfileRecord with PinnableListEntry, Serializable {
);
}

ProfileRecord copyWith({bool? userPinned}) {
return ProfileRecord._(
isTotal: isTotal,
heapClass: heapClass,
userPinned: userPinned ?? this.userPinned,
totalInstances: totalInstances,
totalSize: totalSize,
totalDartHeapSize: totalDartHeapSize,
totalExternalSize: totalExternalSize,
newSpaceInstances: newSpaceInstances,
newSpaceSize: newSpaceSize,
newSpaceDartHeapSize: newSpaceDartHeapSize,
newSpaceExternalSize: newSpaceExternalSize,
oldSpaceInstances: oldSpaceInstances,
oldSpaceSize: oldSpaceSize,
oldSpaceDartHeapSize: oldSpaceDartHeapSize,
oldSpaceExternalSize: oldSpaceExternalSize,
);
}

@override
Map<String, dynamic> toJson() {
return {
_RecordJson.isTotal: isTotal,
_RecordJson.heapClass: heapClass,
_RecordJson.userPinned: userPinned,
_RecordJson.totalInstances: totalInstances,
_RecordJson.totalSize: totalSize,
_RecordJson.totalDartHeapSize: totalDartHeapSize,
Expand All @@ -239,6 +297,8 @@ class ProfileRecord with PinnableListEntry, Serializable {

final bool isTotal;

final bool userPinned;

final HeapClassName heapClass;

final int? totalInstances;
Expand All @@ -257,7 +317,7 @@ class ProfileRecord with PinnableListEntry, Serializable {
final int? oldSpaceExternalSize;

@override
bool get pinToTop => isTotal;
bool get pinToTop => isTotal || userPinned;

void _verifyIntegrity() {
assert(() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,34 @@ import 'package:vm_service/vm_service.dart';

import '../../../../shared/config_specific/import_export/import_export.dart';
import '../../../../shared/globals.dart';
import '../../../../shared/memory/class_name.dart';
import '../../shared/heap/class_filter.dart';
import 'model.dart';

@visibleForTesting
enum Json { profile, rootPackage }
enum Json { profile, rootPackage, pinnedClasses }

class ProfilePaneController extends DisposableController
with AutoDisposeControllerMixin, Serializable {
ProfilePaneController({required this.rootPackage, AdaptedProfile? profile}) {
ProfilePaneController({
required this.rootPackage,
AdaptedProfile? profile,
Set<String>? pinnedClassFullNames,
}) {
if (pinnedClassFullNames != null) {
_pinnedClassFullNames.addAll(pinnedClassFullNames);
_pinnedClassFullNamesListenable.value = pinnedClassFullNames;
}
Comment thread
khanak0509 marked this conversation as resolved.
_pinnedClassFullNamesListenable.value = Set.of(_pinnedClassFullNames);
// [profile] should only be non-null when loading offline data.
if (profile != null) {
_currentAllocationProfile.value = AdaptedProfile.withNewFilter(
profile,
classFilter.value,
_currentAllocationProfile.value = AdaptedProfile.withPinnedClasses(
AdaptedProfile.withNewFilter(
profile,
classFilter.value,
rootPackage,
),
_pinnedClassFullNames,
rootPackage,
);
}
Expand All @@ -34,6 +48,9 @@ class ProfilePaneController extends DisposableController
return ProfilePaneController(
profile: deserialize(json[Json.profile.name], AdaptedProfile.fromJson),
rootPackage: json[Json.rootPackage.name],
pinnedClassFullNames: (json[Json.pinnedClasses.name] as List?)
?.cast<String>()
.toSet(),
);
}

Expand All @@ -42,11 +59,43 @@ class ProfilePaneController extends DisposableController
return {
Json.profile.name: _currentAllocationProfile.value,
Json.rootPackage.name: rootPackage,
Json.pinnedClasses.name: _pinnedClassFullNames.toList(),
};
}

bool _initialized = false;

final _pinnedClassFullNames = <String>{};

/// Classes pinned to the top of the Profile Memory table.
ValueListenable<Set<String>> get pinnedClassFullNames =>
_pinnedClassFullNamesListenable;
final _pinnedClassFullNamesListenable = ValueNotifier<Set<String>>({});

bool isPinned(HeapClassName heapClass) =>
_pinnedClassFullNames.contains(heapClass.fullName);

void togglePin(HeapClassName heapClass) {
final key = heapClass.fullName;
if (_pinnedClassFullNames.contains(key)) {
_pinnedClassFullNames.remove(key);
} else {
_pinnedClassFullNames.add(key);
}
_pinnedClassFullNamesListenable.value = Set.of(_pinnedClassFullNames);
_reapplyPinnedState();
}

void _reapplyPinnedState() {
final currentProfile = _currentAllocationProfile.value;
if (currentProfile == null) return;
_currentAllocationProfile.value = AdaptedProfile.withPinnedClasses(
currentProfile,
_pinnedClassFullNames,
rootPackage,
);
}

/// Initializes the controller if it is not initialized yet.
@override
void init() {
Expand Down Expand Up @@ -84,6 +133,7 @@ class ProfilePaneController extends DisposableController
profile,
classFilter.value,
rootPackage,
pinnedClassFullNames: _pinnedClassFullNames,
);
_initializeSelection();
}
Expand All @@ -105,9 +155,13 @@ class ProfilePaneController extends DisposableController
_classFilter.value = filter;
final currentProfile = _currentAllocationProfile.value;
if (currentProfile == null) return;
_currentAllocationProfile.value = AdaptedProfile.withNewFilter(
currentProfile,
classFilter.value,
_currentAllocationProfile.value = AdaptedProfile.withPinnedClasses(
AdaptedProfile.withNewFilter(
currentProfile,
classFilter.value,
rootPackage,
),
_pinnedClassFullNames,
rootPackage,
);
}
Expand Down Expand Up @@ -210,6 +264,7 @@ class ProfilePaneController extends DisposableController
_currentAllocationProfile.dispose();
_classFilter.dispose();
_refreshOnGc.dispose();
_pinnedClassFullNamesListenable.dispose();
selection.dispose();
super.dispose();
}
Expand Down
Loading