blob: 4cf4511b51cc1480f1d1f5960c6f55b1a63e6262 [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:collection/collection.dart';
import 'package:test/expect.dart';
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart';
import 'common/test_helper.dart';
class Foo {
@pragma('vm:entry-point')
dynamic left;
@pragma('vm:entry-point')
dynamic right;
}
late Foo r;
late List lst;
Function deepEquality = const DeepCollectionEquality().equals;
void script() {
// Create 3 instances of Foo, with out-degrees
// 0 (for b), 1 (for a), and 2 (for staticFoo).
r = Foo();
final a = Foo();
final b = Foo();
r.left = a;
r.right = b;
a.left = b;
lst = List.filled(2, null);
lst[0] = lst; // Self-loop.
// Larger than any other fixed-size list in a fresh heap.
lst[1] = List.filled(1234569, null);
}
final tests = <IsolateTest>[
(VmService service, IsolateRef isolate) async {
// HeapSnapshotGraph serializes.
final graph = await HeapSnapshotGraph.getSnapshot(service, isolate);
_verifySerialization(graph);
},
(VmService service, IsolateRef isolate) async {
// Snap shot serializes with opted out information.
final graph = await HeapSnapshotGraph.getSnapshot(
service,
isolate,
calculateReferrers: true,
decodeExternalProperties: false,
decodeIdentityHashCodes: false,
decodeObjectData: false,
);
expect(graph.objects[10].data, isA<HeapSnapshotObjectNoData>());
expect(graph.objects[10].identityHashCode, 0);
expect(graph.externalProperties, isEmpty);
expect(graph.objects[10].referrers, isNotNull);
_verifySerialization(graph);
},
(VmService service, IsolateRef isolate) async {
// Referrers are calculated by default.
final snapshotGraph = await HeapSnapshotGraph.getSnapshot(service, isolate);
expect(snapshotGraph.objects[10].referrers, isNotNull);
},
(VmService service, IsolateRef isolate) async {
// Referrers are not calculated if opted out.
final snapshotGraph = await HeapSnapshotGraph.getSnapshot(
service,
isolate,
calculateReferrers: false,
);
final object = snapshotGraph.objects[10];
expect(() => object.referrers, throwsStateError);
},
(VmService service, IsolateRef isolate) async {
final snapshotGraph = await HeapSnapshotGraph.getSnapshot(service, isolate);
expect(snapshotGraph.name, 'main');
expect(snapshotGraph.flags, isNotNull);
expect(snapshotGraph.objects, isNotNull);
expect(snapshotGraph.objects, isNotEmpty);
int actualShallowSize = 0;
int actualRefCount = 0;
for (final o in snapshotGraph.objects) {
// -1 is the CID used by the sentinel.
expect(o.classId >= -1, isTrue);
expect(o.data, isNotNull);
expect(o.references, isNotNull);
actualShallowSize += o.shallowSize;
actualRefCount += o.references.length;
}
// Some accounting differences in the VM result in the global shallow size
// often being greater than the sum of the object shallow sizes.
expect(snapshotGraph.shallowSize >= actualShallowSize, isTrue);
expect(snapshotGraph.shallowSize <= snapshotGraph.capacity, isTrue);
expect(snapshotGraph.referenceCount >= actualRefCount, isTrue);
int actualExternalSize = 0;
expect(snapshotGraph.externalProperties, isNotEmpty);
for (var e in snapshotGraph.externalProperties) {
actualExternalSize += e.externalSize;
expect(e.object >= 0, isTrue);
expect(e.name, isNotNull);
}
expect(snapshotGraph.externalSize, actualExternalSize);
expect(snapshotGraph.classes, isNotEmpty);
for (var c in snapshotGraph.classes) {
expect(c.name, isNotNull);
expect(c.libraryName, isNotNull);
expect(c.libraryUri, isNotNull);
expect(c.fields, isNotNull);
}
// We have the class "Foo".
int foosFound = 0;
int fooClassId = -1;
for (int i = 0; i < snapshotGraph.classes.length; i++) {
final HeapSnapshotClass c = snapshotGraph.classes[i];
if (c.name == 'Foo' &&
c.libraryUri.toString().endsWith('heap_snapshot_graph_test.dart')) {
foosFound++;
fooClassId = i;
}
}
expect(foosFound, equals(1));
// It knows about "Foo" objects.
foosFound = 0;
for (final o in snapshotGraph.objects) {
if (o.classId == 0) continue;
if (o.classId == fooClassId) {
foosFound++;
}
}
expect(foosFound, equals(3));
// Check that we can get another snapshot.
final snapshotGraph2 =
await HeapSnapshotGraph.getSnapshot(service, isolate);
expect(snapshotGraph2.name, 'main');
},
];
void _verifySerialization(HeapSnapshotGraph graph) {
final chunks = graph.toChunks();
final graphCopy = HeapSnapshotGraph.fromChunks(chunks);
expect(graphCopy.name, graph.name);
expect(graphCopy.flags.bitLength, graph.flags.bitLength);
expect(graphCopy.objects.length, graph.objects.length);
expect(graphCopy.classes.length, graph.classes.length);
expect(
graphCopy.externalProperties.length,
graph.externalProperties.length,
);
expect(graphCopy.externalSize, graph.externalSize);
expect(graphCopy.shallowSize, graph.shallowSize);
expect(graphCopy.capacity, graph.capacity);
expect(graphCopy.referenceCount, graph.referenceCount);
for (var i = 0; i < graph.objects.length; i++) {
_verifyObjectsAreEqual(graph.objects[i], graphCopy.objects[i]);
}
for (var i = 0; i < graph.classes.length; i++) {
_verifyClassesAreEqual(graph.classes[i], graphCopy.classes[i]);
}
}
void _verifyObjectsAreEqual(HeapSnapshotObject a, HeapSnapshotObject b) {
expect(a.classId, b.classId);
expect(a.shallowSize, b.shallowSize);
expect(a.data.runtimeType, b.data.runtimeType);
expect(a.identityHashCode, b.identityHashCode);
expect(deepEquality(a.references, b.references), true);
expect(deepEquality(a.referrers, b.referrers), true);
}
void _verifyClassesAreEqual(HeapSnapshotClass a, HeapSnapshotClass b) {
expect(a.name, b.name);
expect(a.libraryName, b.libraryName);
expect(a.libraryUri, b.libraryUri);
expect(a.fields, hasLength(b.fields.length));
for (var i = 0; i < a.fields.length; i++) {
_verifyFieldsAreEqual(a.fields[i], b.fields[i]);
}
}
void _verifyFieldsAreEqual(HeapSnapshotField a, HeapSnapshotField b) {
expect(a.name, b.name);
expect(a.index, b.index);
expect(a.toString(), b.toString());
}
void main([args = const <String>[]]) => runIsolateTests(
args,
tests,
'heap_snapshot_graph_test.dart',
testeeBefore: script,
);