blob: c8f593a9cb48ce2151f1daa59fa652ad0e55a72e [file] [log] [blame]
// Copyright (c) 2022, 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 'dart:developer';
import 'package:expect/expect.dart';
import 'package:path/path.dart' as path;
import 'heap_snapshot_test.dart';
import 'use_flag_test_helper.dart';
@pragma('vm:entry-point')
class FooBar {
@pragma('vm:entry-point')
BarBaz bar;
int value;
@pragma('vm:entry-point')
List<int> bar2;
FooBar(this.bar, this.value, this.bar2);
String toString() => 'FooBar($bar, $value, $bar2)';
}
@pragma('vm:entry-point')
class BarBaz {
final int value;
BarBaz(this.value);
String toString() => 'BarBaz($value)';
}
@pragma('vm:entry-point')
var global;
main() async {
if (const bool.fromEnvironment('dart.vm.product')) return;
await withTempDir('heap_snapshot_test', (String dir) async {
final file = path.join(dir, 'state1.heapsnapshot');
// Closures are the example this is a regresion test for, so ensure there's
// a reachable closure in the heapsnapshot.
global = alwaysTrue ? FooBar(BarBaz(1), 2, List.filled(1, 3)) : null;
NativeRuntime.writeHeapSnapshotToFile(file);
Expect.equals('FooBar(BarBaz(1), 2, [3])', global.toString());
final snapshot = loadHeapSnapshotFromFile(file);
final classes = snapshot.classes;
// Ensure all objects have same number of references as fields, except for
// variable-length lists.
final reachableObjects = findReachableObjects(snapshot);
for (final object in reachableObjects) {
final klass = snapshot.classes[object.classId];
final uri = klass.libraryUri.toString();
final fields = klass.fields;
// Variable-length data may have more references than fields.
if (uri == '') {
// We don't verify non-user-visible objects.
} else if (uri.startsWith('dart') &&
['Array', 'List', 'Record'].any((p) => klass.name.contains(p))) {
Expect.isTrue(fields.length <= object.references.length);
} else {
// For objects with vm-defined layouts, this fails if a new field is
// added but runtime/vm/raw_object_fields.cc is not updated accordingly.
Expect.equals(fields.length, object.references.length, klass.name);
}
}
// Specifically look at layout and references of [FooBar] class.
final fooClass = classes.singleWhere((c) => c.name == 'FooBar');
final barClass = classes.singleWhere((c) => c.name == 'BarBaz');
final listClass = classes.singleWhere((c) => c.name == '_List');
final barField = fooClass.fields.singleWhere((f) => f.name == 'bar');
final bar2Field = fooClass.fields.singleWhere((f) => f.name == 'bar2');
// There should be one instance of [FooBar].
final fooObject = reachableObjects
.where((object) => object.classId == fooClass.classId)
.single;
final fooObjectBarField =
snapshot.objects[fooObject.references[barField.index]];
final fooObjectBar2Field =
snapshot.objects[fooObject.references[bar2Field.index]];
Expect.equals(3, fooObject.references.length);
Expect.equals(0, barField.index);
Expect.equals(2, bar2Field.index);
Expect.equals(barClass.classId, fooObjectBarField.classId);
Expect.equals(listClass.classId, fooObjectBar2Field.classId);
Expect.equals(
listClass.fields.length + 1,
fooObjectBar2Field.references.length,
);
});
}