Skip to content
Merged
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 @@ -83,8 +83,8 @@ class _HeapCouple {
class DiffHeapClasses extends HeapClasses<DiffClassStats>
with FilterableHeapClasses<DiffClassStats> {
DiffHeapClasses(_HeapCouple couple)
: before = couple.younger.data,
after = couple.older.data {
: before = couple.older.data,
after = couple.younger.data {
classesByName = subtractMaps<HeapClassName, SingleClassStats,
SingleClassStats, DiffClassStats>(
from: couple.younger.classes.classesByName,
Expand Down Expand Up @@ -133,7 +133,10 @@ class DiffClassStats extends ClassStats {

final result = DiffClassStats._(
heapClass: heapClass,
total: ObjectSetDiff(before: before?.objects, after: after?.objects),
total: ObjectSetDiff(
setBefore: before?.objects,
setAfter: after?.objects,
),
statsByPath: subtractMaps<ClassOnlyHeapPath, ObjectSetStats,
ObjectSetStats, ObjectSetStats>(
from: after?.statsByPath,
Expand All @@ -152,44 +155,42 @@ class DiffClassStats extends ClassStats {

/// Comparison between two sets of objects.
class ObjectSetDiff {
ObjectSetDiff({ObjectSet? before, ObjectSet? after}) {
before ??= ObjectSet.empty;
after ??= ObjectSet.empty;
ObjectSetDiff({ObjectSet? setBefore, ObjectSet? setAfter}) {
setBefore ??= ObjectSet.empty;
setAfter ??= ObjectSet.empty;

final codesBefore = before.objectsByCodes.keys.toSet();
final codesAfter = after.objectsByCodes.keys.toSet();
final allCodes = _unionCodes(setBefore, setAfter);

final allCodes = codesBefore.union(codesAfter);
for (var code in allCodes) {
final inBefore = codesBefore.contains(code);
final inAfter = codesAfter.contains(code);

final object = before.objectsByCodes[code] ?? after.objectsByCodes[code]!;
final before = setBefore.objectsByCodes[code];
final after = setAfter.objectsByCodes[code];

if (inAfter && inBefore) {
// We assume that state 'after' is what is most interesting for user
if (before != null && after != null) {
// When an object exists both before and after
// the state 'after' is more interesting for user
// about the retained size.
final excludeFromRetained =
after.objectsExcludedFromRetainedSize.contains(object.code);
setAfter.objectsExcludedFromRetainedSize.contains(after.code);
persisted.countInstance(
object,
after,
excludeFromRetained: excludeFromRetained,
);
continue;
}

if (inBefore) {
if (before != null) {
final excludeFromRetained =
before.objectsExcludedFromRetainedSize.contains(object.code);
deleted.countInstance(object, excludeFromRetained: excludeFromRetained);
delta.uncountInstance(object, excludeFromRetained: excludeFromRetained);
setBefore.objectsExcludedFromRetainedSize.contains(before.code);
deleted.countInstance(before, excludeFromRetained: excludeFromRetained);
delta.uncountInstance(before, excludeFromRetained: excludeFromRetained);
continue;
}
if (inAfter) {

if (after != null) {
final excludeFromRetained =
after.objectsExcludedFromRetainedSize.contains(object.code);
created.countInstance(object, excludeFromRetained: excludeFromRetained);
delta.countInstance(object, excludeFromRetained: excludeFromRetained);
setAfter.objectsExcludedFromRetainedSize.contains(after.code);
created.countInstance(after, excludeFromRetained: excludeFromRetained);
delta.countInstance(after, excludeFromRetained: excludeFromRetained);
continue;
}

Expand All @@ -204,6 +205,13 @@ class ObjectSetDiff {
);
}

static Set<IdentityHashCode> _unionCodes(ObjectSet set1, ObjectSet set2) {
final codesBefore = set1.objectsByCodes.keys.toSet();
final codesAfter = set2.objectsByCodes.keys.toSet();

return codesBefore.union(codesAfter);
}

final created = ObjectSet();
final deleted = ObjectSet();
final persisted = ObjectSet();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import '../../../shared/heap/heap.dart';
import '../../../shared/primitives/instance_set_button.dart';

class HeapClassSampler extends ClassSampler {
HeapClassSampler(this.objects, this.heap, this.heapClass);
HeapClassSampler(this.objects, this.heap, this.heapClass)
: assert(objects.objectsByCodes.isNotEmpty);

final HeapClassName heapClass;
final ObjectSet objects;
Expand Down Expand Up @@ -77,4 +78,17 @@ class HeapClassSampler extends ClassSampler {
isolateRef: _mainIsolateRef,
);
}

@override
Future<void> oneStaticToConsole() async {
final heapObject = objects.objectsByCodes.values.first;
final heapSelection = HeapObjectSelection(heap, object: heapObject);

// drop to console
serviceManager.consoleService.appendBrowsableInstance(
instanceRef: null,
isolateRef: _mainIsolateRef,
heapSelection: heapSelection,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ class _InstanceColumn extends ColumnData<DiffClassStats>
data.heapClass,
isSelected: isRowSelected,
gaContext: gac.MemoryAreas.snapshotDiff,
liveItemsEnabled: dataPart != _DataPart.deleted,
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class InstanceTableCell extends StatelessWidget {
HeapClassName heapClass, {
required this.isSelected,
required this.gaContext,
this.liveItemsEnabled = true,
}) : _showMenu = _shouldShowMenu(isSelected, objects),
_sampleObtainer = _shouldShowMenu(isSelected, objects)
? HeapClassSampler(objects, heap, heapClass)
Expand All @@ -37,6 +38,7 @@ class InstanceTableCell extends StatelessWidget {
final bool isSelected;
final MemoryAreas gaContext;
final int _count;
final bool liveItemsEnabled;

@override
Widget build(BuildContext context) {
Expand All @@ -52,6 +54,7 @@ class InstanceTableCell extends StatelessWidget {
gaContext: gaContext,
sampleObtainer: _sampleObtainer,
showMenu: _showMenu,
liveItemsEnabled: liveItemsEnabled,
),
],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ abstract class ClassSampler {
/// Drop one variable, which exists in static set and still alive in app, to console.
Future<void> oneLiveStaticToConsole();

/// Drop one variable from static set, to console.
Future<void> oneStaticToConsole();

/// Drop all live instances to console.
Future<void> manyLiveToConsole();

Expand All @@ -31,6 +34,7 @@ class InstanceSetButton extends StatelessWidget {
required this.sampleObtainer,
required this.showMenu,
required this.gaContext,
required this.liveItemsEnabled,
}) : assert(showMenu == (sampleObtainer != null)),
assert(count >= 0);

Expand All @@ -40,6 +44,9 @@ class InstanceSetButton extends StatelessWidget {
final TextStyle? textStyle;
final MemoryAreas gaContext;

/// If true, menu items that show live objects, will be enabled.
final bool liveItemsEnabled;

@override
Widget build(BuildContext context) {
final shouldShowMenu = showMenu && count > 0;
Expand All @@ -53,48 +60,69 @@ class InstanceSetButton extends StatelessWidget {
if (shouldShowMenu)
ContextMenuButton(
style: textStyle,
menu: _menu(sampleObtainer!),
menu: _menu(
sampleObtainer!,
liveItemsEnabled: liveItemsEnabled,
),
),
if (!shouldShowMenu) const SizedBox(width: ContextMenuButton.width),
],
);
}
}

class _StoreAsVariableMenu extends StatelessWidget {
const _StoreAsVariableMenu(this.sampleObtainer);
class _StoreAsOneVariableMenu extends StatelessWidget {
const _StoreAsOneVariableMenu(
this.sampleObtainer, {
required this.liveItemsEnabled,
});

final ClassSampler sampleObtainer;
final bool liveItemsEnabled;

@override
Widget build(BuildContext context) {
final enabled = sampleObtainer.isEvalEnabled;
const menuText = 'Store as a console variable';
final limit = preferences.memory.refLimit.value;
const menuText = 'Store one instance as a console variable';

if (!enabled) {
return const MenuItemButton(child: Text(menuText));
}

return SubmenuButton(
// TODO(polina-c): change structure and review texts before opening the feature.
menuChildren: <Widget>[
MenuItemButton(
onPressed: sampleObtainer.oneLiveStaticToConsole,
onPressed: sampleObtainer.oneStaticToConsole,
child: const Text(
'One instance that exists in snapshot, and is alive in application',
'Any from snapshot',
),
),
MenuItemButton(
onPressed: sampleObtainer.manyLiveToConsole,
child: Text('Up to $limit instances, currently alive in application'),
onPressed:
liveItemsEnabled ? sampleObtainer.oneLiveStaticToConsole : null,
child: const Text(
'Any from snapshot, not garbage collected',
),
),
],
child: const Text(menuText),
);
}
}

List<Widget> _menu(ClassSampler sampleObtainer) => [
_StoreAsVariableMenu(sampleObtainer),
];
// TODO(polina-c): review structure/texts and add ga, before opening the feature.
List<Widget> _menu(
ClassSampler sampleObtainer, {
required bool liveItemsEnabled,
}) {
final limit = preferences.memory.refLimit.value;
return [
_StoreAsOneVariableMenu(sampleObtainer, liveItemsEnabled: liveItemsEnabled),
MenuItemButton(
onPressed: sampleObtainer.manyLiveToConsole,
child: Text(
'Store up to $limit instances, currently alive in application',
),
),
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ class DartObjectNode extends TreeNode<DartObjectNode> {
if (treeInitializeComplete || children.isNotEmpty || childCount > 0) {
return children.isNotEmpty || childCount > 0;
}
if (ref?.heapSelection != null) return true;
final diagnostic = ref?.diagnostic;
if (diagnostic != null &&
((diagnostic.inlineProperties.isNotEmpty) || diagnostic.hasChildren))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,6 @@ Future<void> _addInstanceRefItems(
);

if (result is Instance) {
if (FeatureFlags.evalAndBrowse &&
ref != null &&
ref.heapSelection != null) {
addReferencesRoot(variable, ref);
}

await _addChildrenToInstanceVariable(
variable: variable,
value: result,
Expand Down Expand Up @@ -406,6 +400,12 @@ Future<void> buildVariablesTree(
variable.addChild(DartObjectNode.text('error: $ex\n$stack'));
}

if (FeatureFlags.evalAndBrowse &&
ref.heapSelection != null &&
ref is! ObjectReferences) {
addReferencesRoot(variable, ref);
}

await _addDiagnosticChildrenIfNeeded(
variable,
diagnostic,
Expand Down