Skip to content

Handle edge cases for eval and pass needed parameters to HeapObjectGraph. #5059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 18, 2023
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 @@ -11,42 +11,51 @@ import '../../../shared/primitives/class_name.dart';
import '../../../shared/primitives/instance_set_view.dart';

class HeapClassSampler extends ClassSampler {
HeapClassSampler(this.className, this.objects);
HeapClassSampler(this.objects, this.heap);

final HeapClassName className;
final SingleClassStats objects;
final AdaptedHeapData heap;

IsolateRef get _mainIsolateRef =>
serviceManager.isolateManager.mainIsolate.value!;

Future<InstanceRef> _oneInstance() async {
Future<InstanceRef?> _oneInstance() async {
final isolateId = _mainIsolateRef.id!;

// TODO(polina-c): It would be great to find out how to avoid full scan of classes.
final theClass = (await serviceManager.service!.getClassList(isolateId))
.classes!
.firstWhere((ref) => className.matches(ref));
.firstWhere((ref) => objects.heapClass.matches(ref));

final instances = await serviceManager.service!.getInstances(
isolateId,
theClass.id!,
1,
);

return instances.instances!.first as InstanceRef;
final result = instances.instances!.first;

if (result is InstanceRef) return result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what scenario would this not be an InstanceRef? If it's only an error case, consider throwing an exception rather than having a nullable return type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes it is ClassRef


return null;
}

@override
Future<void> oneVariableToConsole() async {
final instance = await _oneInstance();

// drop to console
serviceManager.consoleService.appendInstanceRef(
value: instance,
diagnostic: null,
isolateRef: _mainIsolateRef,
forceScrollIntoView: true,
);
if (instance == null) {
serviceManager.consoleService
.appendStdio('the instance cannot be evaluated');
} else {
// drop to console
serviceManager.consoleService.appendInstanceRef(
value: instance,
diagnostic: null,
isolateRef: _mainIsolateRef,
forceScrollIntoView: true,
);
}

// TODO (polina-c): remove the commented code
// before opening the flag.
Expand All @@ -67,6 +76,17 @@ class HeapClassSampler extends ClassSampler {

@override
Future<void> instanceGraphToConsole() async {
serviceManager.consoleService.appendInstanceGraph(HeapObjectGraph('hello'));
serviceManager.consoleService.appendInstanceGraph(
HeapObjectGraph(
heap,
objects.objects.objectsByCodes.keys.first,
objects.heapClass,
),
);
}

@override
bool get isEvalEnabled =>
objects.heapClass.classType(serviceManager.rootInfoNow().package) !=
ClassType.runtime;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import '../../../../../shared/table/table_data.dart';
import '../../../../../shared/theme.dart';
import '../../../../../shared/utils.dart';
import '../../../shared/heap/heap.dart';
import '../../../shared/heap/model.dart';
import '../../../shared/primitives/instance_set_view.dart';
import '../../../shared/primitives/simple_elements.dart';
import '../../../shared/shared_memory_widgets.dart';
Expand Down Expand Up @@ -79,14 +80,16 @@ class _ClassNameColumn extends ColumnData<SingleClassStats>

class _InstanceColumn extends ColumnData<SingleClassStats>
implements ColumnRenderer<SingleClassStats> {
_InstanceColumn()
_InstanceColumn(this.heap)
: super(
'Instances',
titleTooltip: nonGcableInstancesColumnTooltip,
fixedWidthPx: scaleByFontFactor(180.0),
alignment: ColumnAlignment.right,
);

final AdaptedHeapData heap;

@override
int getValue(SingleClassStats classStats) => classStats.objects.instanceCount;

Expand All @@ -103,6 +106,7 @@ class _InstanceColumn extends ColumnData<SingleClassStats>
if (!FeatureFlags.evalAndBrowse) return null;

final theme = Theme.of(context);
final showMenu = isRowSelected;

return Row(
mainAxisAlignment: MainAxisAlignment.end,
Expand All @@ -112,9 +116,8 @@ class _InstanceColumn extends ColumnData<SingleClassStats>
isRowSelected ? theme.selectedTextStyle : theme.regularTextStyle,
count: getValue(data),
gaContext: gac.MemoryAreas.snapshotSingle,
sampleObtainer:
isRowSelected ? HeapClassSampler(data.heapClass, data) : null,
showMenu: isRowSelected,
sampleObtainer: showMenu ? HeapClassSampler(data, heap) : null,
showMenu: showMenu,
),
],
);
Expand Down Expand Up @@ -178,7 +181,7 @@ class _RetainedSizeColumn extends ColumnData<SingleClassStats> {
}

class _ClassesTableSingleColumns {
_ClassesTableSingleColumns(this.totalSize, this.classFilterButton);
_ClassesTableSingleColumns(this.totalSize, this.classFilterButton, this.heap);

/// Is needed to calculate percentage.
final int totalSize;
Expand All @@ -187,9 +190,11 @@ class _ClassesTableSingleColumns {

late final retainedSizeColumn = _RetainedSizeColumn(totalSize);

final AdaptedHeapData heap;

late final columnList = <ColumnData<SingleClassStats>>[
_ClassNameColumn(classFilterButton),
_InstanceColumn(),
_InstanceColumn(heap),
_ShallowSizeColumn(),
retainedSizeColumn,
];
Expand All @@ -202,21 +207,29 @@ class ClassesTableSingle extends StatelessWidget {
required this.selection,
required this.totalSize,
required this.classFilterButton,
required this.heap,
});

final int totalSize;

final Widget classFilterButton;

final List<SingleClassStats> classes;

final ValueNotifier<SingleClassStats?> selection;

final AdaptedHeapData heap;

@override
Widget build(BuildContext context) {
// We want to preserve the sorting and sort directions for ClassesTableDiff
// no matter what the data passed to it is.
const dataKey = 'ClassesTableSingle';
final columns = _ClassesTableSingleColumns(totalSize, classFilterButton);
final columns = _ClassesTableSingleColumns(
totalSize,
classFilterButton,
heap,
);
return FlatTable<SingleClassStats>(
columns: columns.columnList,
data: classes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class SnapshotView extends StatelessWidget {
selection: controller.derived.selectedSingleClassStats,
totalSize: totalSize!,
classFilterButton: classFilterButton,
heap:
(controller.derived.selectedItem.value as SnapshotInstanceItem)
.heap!
.data,
);
} else if (diffClasses != null) {
classTable = ClassesTableDiff(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,9 @@ mixin Sealable {
class HeapObjectGraph {
//TODO(polina-c): add needed fields

HeapObjectGraph(this.name);
HeapObjectGraph(this.heap, this.identityHashCode, this.className);

final String name;
final int identityHashCode;
final HeapClassName className;
final AdaptedHeapData heap;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import '../../../../shared/primitives/utils.dart';
abstract class ClassSampler {
Future<void> oneVariableToConsole();
void instanceGraphToConsole();
bool get isEvalEnabled;
}

/// A button with label '...' to show near count of instances,
Expand Down Expand Up @@ -53,17 +54,23 @@ class InstanceSetButton extends StatelessWidget {
}

class _StoreAsVariableMenu extends StatelessWidget {
const _StoreAsVariableMenu(this.menuText, this.sampleObtainer);
const _StoreAsVariableMenu(this.sampleObtainer);

final String menuText;
final ClassSampler sampleObtainer;

@override
Widget build(BuildContext context) {
final enabled = sampleObtainer.isEvalEnabled;
const menuText = 'Store as a console variable';

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

return SubmenuButton(
menuChildren: <Widget>[
MenuItemButton(
onPressed: sampleObtainer.oneVariableToConsole,
onPressed: enabled ? sampleObtainer.oneVariableToConsole : null,
child: const Text('One instance'),
),
const MenuItemButton(
Expand All @@ -73,16 +80,13 @@ class _StoreAsVariableMenu extends StatelessWidget {
child: Text('All instances'),
),
],
child: Text(menuText),
child: const Text(menuText),
);
}
}

List<Widget> _menu(ClassSampler sampleObtainer) => [
_StoreAsVariableMenu(
'Store as a console variable',
sampleObtainer,
),
_StoreAsVariableMenu(sampleObtainer),
MenuItemButton(
onPressed: sampleObtainer.instanceGraphToConsole,
child: const Text('Browse references for a single instance in console'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ class _ConsoleOutputState extends State<_ConsoleOutput>
variable: line.variable,
);
} else if (line is GraphConsoleLine) {
return Text(line.graph.name);
return Text(line.graph.toString());
} else {
assert(
false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,6 @@ class GraphConsoleLine extends ConsoleLine {
GraphConsoleLine(this.graph, {bool forceScrollIntoView = false})
: super._(forceScrollIntoView);
final HeapObjectGraph graph;

@override
String toString() {
return graph.name;
}
}

/// Source of truth for the state of the Console including both events from the
Expand Down