Skip to content

Commit 44f225f

Browse files
committed
Add pin rows to Profile Memory table
1 parent 5122355 commit 44f225f

6 files changed

Lines changed: 287 additions & 54 deletions

File tree

packages/devtools_app/lib/src/screens/memory/panes/profile/model.dart

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,19 @@ class AdaptedProfile with Serializable {
3434
factory AdaptedProfile.fromAllocationProfile(
3535
AllocationProfile profile,
3636
ClassFilter filter,
37-
String? rootPackage,
38-
) {
37+
String? rootPackage, {
38+
Set<String> pinnedClassFullNames = const {},
39+
}) {
3940
final adaptedProfile = AdaptedProfile._(
4041
total: ProfileRecord.total(profile),
4142
items: (profile.members ?? [])
4243
.where((e) => (e.instancesCurrent ?? 0) > 0)
43-
.map((e) => ProfileRecord.fromClassHeapStats(e))
44+
.map((e) => ProfileRecord.fromClassHeapStats(
45+
e,
46+
userPinned: pinnedClassFullNames.contains(
47+
HeapClassName.fromClassRef(e.classRef).fullName,
48+
),
49+
))
4450
.toList(),
4551
newSpaceGCStats: profile.newSpaceGCStats,
4652
oldSpaceGCStats: profile.oldSpaceGCStats,
@@ -70,6 +76,29 @@ class AdaptedProfile with Serializable {
7076
);
7177
}
7278

79+
/// Returns a copy of [profile] with [pinnedClassFullNames] applied to items.
80+
factory AdaptedProfile.withPinnedClasses(
81+
AdaptedProfile profile,
82+
Set<String> pinnedClassFullNames,
83+
String? rootPackage,
84+
) {
85+
final itemsWithPins = profile._items
86+
.map(
87+
(record) => record.copyWith(
88+
userPinned: pinnedClassFullNames.contains(record.heapClass.fullName),
89+
),
90+
)
91+
.toList();
92+
final updated = AdaptedProfile._(
93+
total: profile._total,
94+
items: itemsWithPins,
95+
newSpaceGCStats: profile.newSpaceGCStats,
96+
oldSpaceGCStats: profile.oldSpaceGCStats,
97+
totalGCStats: profile.totalGCStats,
98+
);
99+
return AdaptedProfile.withNewFilter(updated, profile.filter, rootPackage);
100+
}
101+
73102
factory AdaptedProfile.fromJson(Map<String, dynamic> json) {
74103
return AdaptedProfile._(
75104
total: ProfileRecord.fromJson(json[_ProfileJson.total]),
@@ -117,6 +146,7 @@ class AdaptedProfile with Serializable {
117146
class _RecordJson {
118147
static const isTotal = 'it';
119148
static const heapClass = 'c';
149+
static const userPinned = 'p';
120150
static const totalInstances = 'ti';
121151
static const totalSize = 'ts';
122152
static const totalDartHeapSize = 'tds';
@@ -135,6 +165,7 @@ class ProfileRecord with PinnableListEntry, Serializable {
135165
ProfileRecord._({
136166
required this.isTotal,
137167
required this.heapClass,
168+
this.userPinned = false,
138169
required this.totalInstances,
139170
required this.totalSize,
140171
required this.totalDartHeapSize,
@@ -151,14 +182,18 @@ class ProfileRecord with PinnableListEntry, Serializable {
151182
_verifyIntegrity();
152183
}
153184

154-
factory ProfileRecord.fromClassHeapStats(ClassHeapStats stats) {
185+
factory ProfileRecord.fromClassHeapStats(
186+
ClassHeapStats stats, {
187+
bool userPinned = false,
188+
}) {
155189
assert(
156190
stats.bytesCurrent! == stats.newSpace.size + stats.oldSpace.size,
157191
'${stats.bytesCurrent}, ${stats.newSpace.size}, ${stats.oldSpace.size}',
158192
);
159193
return ProfileRecord._(
160194
isTotal: false,
161195
heapClass: HeapClassName.fromClassRef(stats.classRef),
196+
userPinned: userPinned,
162197
totalInstances: stats.instancesCurrent ?? 0,
163198
totalSize:
164199
stats.bytesCurrent! +
@@ -180,6 +215,7 @@ class ProfileRecord with PinnableListEntry, Serializable {
180215

181216
ProfileRecord.total(AllocationProfile profile)
182217
: isTotal = true,
218+
userPinned = false,
183219
heapClass = HeapClassName.fromPath(className: 'All Classes', library: ''),
184220
totalInstances = null,
185221
totalSize =
@@ -202,6 +238,7 @@ class ProfileRecord with PinnableListEntry, Serializable {
202238
return ProfileRecord._(
203239
isTotal: json[_RecordJson.isTotal] as bool,
204240
heapClass: HeapClassName.fromJson(json[_RecordJson.heapClass]),
241+
userPinned: json[_RecordJson.userPinned] as bool? ?? false,
205242
totalInstances: json[_RecordJson.totalInstances] as int?,
206243
totalSize: json[_RecordJson.totalSize] as int,
207244
totalDartHeapSize: json[_RecordJson.totalDartHeapSize] as int,
@@ -217,11 +254,32 @@ class ProfileRecord with PinnableListEntry, Serializable {
217254
);
218255
}
219256

257+
ProfileRecord copyWith({bool? userPinned}) {
258+
return ProfileRecord._(
259+
isTotal: isTotal,
260+
heapClass: heapClass,
261+
userPinned: userPinned ?? this.userPinned,
262+
totalInstances: totalInstances,
263+
totalSize: totalSize,
264+
totalDartHeapSize: totalDartHeapSize,
265+
totalExternalSize: totalExternalSize,
266+
newSpaceInstances: newSpaceInstances,
267+
newSpaceSize: newSpaceSize,
268+
newSpaceDartHeapSize: newSpaceDartHeapSize,
269+
newSpaceExternalSize: newSpaceExternalSize,
270+
oldSpaceInstances: oldSpaceInstances,
271+
oldSpaceSize: oldSpaceSize,
272+
oldSpaceDartHeapSize: oldSpaceDartHeapSize,
273+
oldSpaceExternalSize: oldSpaceExternalSize,
274+
);
275+
}
276+
220277
@override
221278
Map<String, dynamic> toJson() {
222279
return {
223280
_RecordJson.isTotal: isTotal,
224281
_RecordJson.heapClass: heapClass,
282+
_RecordJson.userPinned: userPinned,
225283
_RecordJson.totalInstances: totalInstances,
226284
_RecordJson.totalSize: totalSize,
227285
_RecordJson.totalDartHeapSize: totalDartHeapSize,
@@ -239,6 +297,8 @@ class ProfileRecord with PinnableListEntry, Serializable {
239297

240298
final bool isTotal;
241299

300+
final bool userPinned;
301+
242302
final HeapClassName heapClass;
243303

244304
final int? totalInstances;
@@ -257,7 +317,7 @@ class ProfileRecord with PinnableListEntry, Serializable {
257317
final int? oldSpaceExternalSize;
258318

259319
@override
260-
bool get pinToTop => isTotal;
320+
bool get pinToTop => isTotal || userPinned;
261321

262322
void _verifyIntegrity() {
263323
assert(() {

packages/devtools_app/lib/src/screens/memory/panes/profile/profile_pane_controller.dart

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,32 @@ import 'package:vm_service/vm_service.dart';
1111

1212
import '../../../../shared/config_specific/import_export/import_export.dart';
1313
import '../../../../shared/globals.dart';
14+
import '../../../../shared/memory/class_name.dart';
1415
import '../../shared/heap/class_filter.dart';
1516
import 'model.dart';
1617

1718
@visibleForTesting
18-
enum Json { profile, rootPackage }
19+
enum Json { profile, rootPackage, pinnedClasses }
1920

2021
class ProfilePaneController extends DisposableController
2122
with AutoDisposeControllerMixin, Serializable {
22-
ProfilePaneController({required this.rootPackage, AdaptedProfile? profile}) {
23+
ProfilePaneController({
24+
required this.rootPackage,
25+
AdaptedProfile? profile,
26+
Set<String>? pinnedClassFullNames,
27+
}) {
28+
if (pinnedClassFullNames != null) {
29+
_pinnedClassFullNames.addAll(pinnedClassFullNames);
30+
}
2331
// [profile] should only be non-null when loading offline data.
2432
if (profile != null) {
25-
_currentAllocationProfile.value = AdaptedProfile.withNewFilter(
26-
profile,
27-
classFilter.value,
33+
_currentAllocationProfile.value = AdaptedProfile.withPinnedClasses(
34+
AdaptedProfile.withNewFilter(
35+
profile,
36+
classFilter.value,
37+
rootPackage,
38+
),
39+
_pinnedClassFullNames,
2840
rootPackage,
2941
);
3042
}
@@ -34,6 +46,9 @@ class ProfilePaneController extends DisposableController
3446
return ProfilePaneController(
3547
profile: deserialize(json[Json.profile.name], AdaptedProfile.fromJson),
3648
rootPackage: json[Json.rootPackage.name],
49+
pinnedClassFullNames: (json[Json.pinnedClasses.name] as List?)
50+
?.cast<String>()
51+
.toSet(),
3752
);
3853
}
3954

@@ -42,11 +57,43 @@ class ProfilePaneController extends DisposableController
4257
return {
4358
Json.profile.name: _currentAllocationProfile.value,
4459
Json.rootPackage.name: rootPackage,
60+
Json.pinnedClasses.name: _pinnedClassFullNames.toList(),
4561
};
4662
}
4763

4864
bool _initialized = false;
4965

66+
final _pinnedClassFullNames = <String>{};
67+
68+
/// Classes pinned to the top of the Profile Memory table.
69+
ValueListenable<Set<String>> get pinnedClassFullNames =>
70+
_pinnedClassFullNamesListenable;
71+
final _pinnedClassFullNamesListenable = ValueNotifier<Set<String>>({});
72+
73+
bool isPinned(HeapClassName heapClass) =>
74+
_pinnedClassFullNames.contains(heapClass.fullName);
75+
76+
void togglePin(HeapClassName heapClass) {
77+
final key = heapClass.fullName;
78+
if (_pinnedClassFullNames.contains(key)) {
79+
_pinnedClassFullNames.remove(key);
80+
} else {
81+
_pinnedClassFullNames.add(key);
82+
}
83+
_pinnedClassFullNamesListenable.value = Set.of(_pinnedClassFullNames);
84+
_reapplyPinnedState();
85+
}
86+
87+
void _reapplyPinnedState() {
88+
final currentProfile = _currentAllocationProfile.value;
89+
if (currentProfile == null) return;
90+
_currentAllocationProfile.value = AdaptedProfile.withPinnedClasses(
91+
currentProfile,
92+
_pinnedClassFullNames,
93+
rootPackage,
94+
);
95+
}
96+
5097
/// Initializes the controller if it is not initialized yet.
5198
@override
5299
void init() {
@@ -84,6 +131,7 @@ class ProfilePaneController extends DisposableController
84131
profile,
85132
classFilter.value,
86133
rootPackage,
134+
pinnedClassFullNames: _pinnedClassFullNames,
87135
);
88136
_initializeSelection();
89137
}
@@ -105,9 +153,13 @@ class ProfilePaneController extends DisposableController
105153
_classFilter.value = filter;
106154
final currentProfile = _currentAllocationProfile.value;
107155
if (currentProfile == null) return;
108-
_currentAllocationProfile.value = AdaptedProfile.withNewFilter(
109-
currentProfile,
110-
classFilter.value,
156+
_currentAllocationProfile.value = AdaptedProfile.withPinnedClasses(
157+
AdaptedProfile.withNewFilter(
158+
currentProfile,
159+
classFilter.value,
160+
rootPackage,
161+
),
162+
_pinnedClassFullNames,
111163
rootPackage,
112164
);
113165
}
@@ -210,6 +262,7 @@ class ProfilePaneController extends DisposableController
210262
_currentAllocationProfile.dispose();
211263
_classFilter.dispose();
212264
_refreshOnGc.dispose();
265+
_pinnedClassFullNamesListenable.dispose();
213266
selection.dispose();
214267
super.dispose();
215268
}

0 commit comments

Comments
 (0)