// Copyright (c) 2014, 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:observatory/models.dart' as M; import 'package:observatory/object_graph.dart'; import 'package:observatory/service_io.dart'; import 'package:test/test.dart'; import 'test_helper.dart'; class Foo { // Make sure these fields are not removed by the tree shaker. @pragma("vm:entry-point") dynamic left; @pragma("vm:entry-point") dynamic right; } late Foo r; late List lst; void script() { // Create 3 instances of Foo, with out-degrees // 0 (for b), 1 (for a), and 2 (for staticFoo). r = new Foo(); var a = new Foo(); var b = new Foo(); r.left = a; r.right = b; a.left = b; lst = new List.filled(2, null); lst[0] = lst; // Self-loop. // Larger than any other fixed-size list in a fresh heap. lst[1] = new List.filled(1234569, null); } var tests = [ (Isolate isolate) async { var graph = await isolate.fetchHeapSnapshot().done; Iterable foos = graph.objects.where((SnapshotObject obj) => obj.klass.name == "Foo"); expect(foos.length, equals(3)); SnapshotObject bVertex = foos.singleWhere((SnapshotObject obj) { List successors = obj.successors.toList(); return successors[0].klass.name == "Null" && successors[1].klass.name == "Null"; }); SnapshotObject aVertex = foos.singleWhere((SnapshotObject obj) { List successors = obj.successors.toList(); return successors[0].klass.name == "Foo" && successors[1].klass.name == "Null"; }); SnapshotObject rVertex = foos.singleWhere((SnapshotObject obj) { List successors = obj.successors.toList(); return successors[0].klass.name == "Foo" && successors[1].klass.name == "Foo"; }); // TODO(koda): Check actual byte sizes. expect(aVertex.retainedSize, equals(aVertex.shallowSize)); expect(bVertex.retainedSize, equals(bVertex.shallowSize)); expect( rVertex.retainedSize, equals( aVertex.shallowSize + bVertex.shallowSize + rVertex.shallowSize)); List lists = new List.from( graph.objects.where((SnapshotObject obj) => obj.klass.name == '_List')); expect(lists.length >= 2, isTrue); // Order by decreasing retained size. lists.sort((u, v) => v.retainedSize - u.retainedSize); SnapshotObject first = lists[0]; expect(first.successors.length, greaterThanOrEqualTo(2)); SnapshotObject second = lists[1]; expect(second.successors.length, greaterThanOrEqualTo(1234569)); // Check that the short list retains more than the long list inside. // and specifically, that it retains exactly itself + the long one. expect(first.retainedSize, equals(first.shallowSize + second.shallowSize)); // Verify sizes of classes are the appropriates sums of their instances. // This also verifies that the class instance iterators are visiting the // correct set of objects (e.g., not including dead objects). for (SnapshotClass klass in graph.classes) { int shallowSum = 0; int internalSum = 0; int externalSum = 0; for (SnapshotObject instance in klass.instances) { if (instance == graph.root) { // The root may have 0 self size. expect(instance.internalSize, greaterThanOrEqualTo(0)); expect(instance.externalSize, greaterThanOrEqualTo(0)); expect(instance.shallowSize, greaterThanOrEqualTo(0)); } else { // All other objects are heap objects with positive size. expect(instance.internalSize, greaterThan(0)); expect(instance.externalSize, greaterThanOrEqualTo(0)); expect(instance.shallowSize, greaterThan(0)); } expect(instance.retainedSize, greaterThan(0)); expect(instance.shallowSize, equals(instance.internalSize + instance.externalSize)); shallowSum += instance.shallowSize; internalSum += instance.internalSize; externalSum += instance.externalSize; } expect(shallowSum, equals(klass.shallowSize)); expect(internalSum, equals(klass.internalSize)); expect(externalSum, equals(klass.externalSize)); expect( klass.shallowSize, equals(klass.internalSize + klass.externalSize)); } // Verify sizes of the overall graph are the appropriates sums of all // instances. This also verifies that the all instances iterator is visiting // the correct set of objects (e.g., not including dead objects). int shallowSum = 0; int internalSum = 0; int externalSum = 0; for (SnapshotObject instance in graph.objects) { if (instance == graph.root) { // The root may have 0 self size. expect(instance.internalSize, greaterThanOrEqualTo(0)); expect(instance.externalSize, greaterThanOrEqualTo(0)); expect(instance.shallowSize, greaterThanOrEqualTo(0)); } else { // All other objects are heap objects with positive size. expect(instance.internalSize, greaterThan(0)); expect(instance.externalSize, greaterThanOrEqualTo(0)); expect(instance.shallowSize, greaterThan(0)); } expect(instance.retainedSize, greaterThan(0)); expect(instance.shallowSize, equals(instance.internalSize + instance.externalSize)); shallowSum += instance.shallowSize; internalSum += instance.internalSize; externalSum += instance.externalSize; } expect(shallowSum, equals(graph.size)); expect(internalSum, equals(graph.internalSize)); expect(externalSum, equals(graph.externalSize)); expect(graph.size, equals(graph.internalSize + graph.externalSize)); }, ]; main(args) => runIsolateTests(args, tests, testeeBefore: script);