[ package:vm_service ] Prepare for 6.0.1 stable null-safe release
There is no diff between this version and 6.0.1-nullsafety.1.
This temporarily reverts commit 8d99d295da.
TEST=None
Change-Id: I3597b3543c9ea9122865604b9ba07c99683a5355
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182580
Reviewed-by: Nate Bosch <nbosch@google.com>
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## 6.1.0-nullsafety.0
|
||||
- Added `identityHashCode` property to `HeapSnapshotObject`, which can be used to compare
|
||||
objects across heap snapshots.
|
||||
- Added `successors` iterable to `HeapSnapshotObject`, which provides a convenient way to
|
||||
access children of a given object.
|
||||
- Added `klass` getter to `HeapSnapshotObject`.
|
||||
## 6.0.1
|
||||
- Stable null-safe release.
|
||||
|
||||
## 6.0.1-nullsafety.1
|
||||
- Fix issue where some `Instance` properties were not being populated correctly.
|
||||
@@ -16,6 +12,7 @@
|
||||
## 6.0.0-nullsafety.4
|
||||
- Fixed issue where response parsing could fail for `SourceReportRange.coverage`
|
||||
if no coverage information was provided.
|
||||
|
||||
## 6.0.0-nullsafety.3
|
||||
- Fixed issue where `Response.type` and classes which override `Response.type` were
|
||||
returning the name of the `package:vm_service` reference object (e.g., InstanceRef) instead of
|
||||
|
||||
@@ -15,9 +15,6 @@ class _ReadStream {
|
||||
|
||||
_ReadStream(this._chunks);
|
||||
|
||||
bool get atEnd => ((_byteIndex >= _chunks[_chunkIndex].lengthInBytes) &&
|
||||
(_chunkIndex + 1 >= _chunks.length));
|
||||
|
||||
int readByte() {
|
||||
while (_byteIndex >= _chunks[_chunkIndex].lengthInBytes) {
|
||||
_chunkIndex++;
|
||||
@@ -120,9 +117,6 @@ class HeapSnapshotField {
|
||||
|
||||
/// A representation of a class type captured in a memory snapshot.
|
||||
class HeapSnapshotClass {
|
||||
/// The class ID representing this type.
|
||||
int get classId => _classId;
|
||||
|
||||
/// The simple (not qualified) name of the class.
|
||||
String get name => _name;
|
||||
|
||||
@@ -135,13 +129,12 @@ class HeapSnapshotClass {
|
||||
/// The list of fields in the class.
|
||||
List<HeapSnapshotField> get fields => _fields;
|
||||
|
||||
final int _classId;
|
||||
String _name = '';
|
||||
String _libraryName = '';
|
||||
late final Uri _libraryUri;
|
||||
final List<HeapSnapshotField> _fields = <HeapSnapshotField>[];
|
||||
|
||||
HeapSnapshotClass._read(this._classId, _ReadStream reader) {
|
||||
HeapSnapshotClass._read(_ReadStream reader) {
|
||||
// flags (reserved).
|
||||
reader.readUnsigned();
|
||||
|
||||
@@ -155,18 +148,6 @@ class HeapSnapshotClass {
|
||||
_populateFields(reader);
|
||||
}
|
||||
|
||||
HeapSnapshotClass._root()
|
||||
: _classId = 0,
|
||||
_name = 'Root',
|
||||
_libraryName = '',
|
||||
_libraryUri = Uri();
|
||||
|
||||
HeapSnapshotClass._sentinel()
|
||||
: _classId = 0,
|
||||
_name = 'Sentinel',
|
||||
_libraryName = '',
|
||||
_libraryUri = Uri();
|
||||
|
||||
void _populateFields(_ReadStream reader) {
|
||||
final fieldCount = reader.readUnsigned();
|
||||
for (int i = 0; i < fieldCount; ++i) {
|
||||
@@ -180,14 +161,6 @@ class HeapSnapshotObject {
|
||||
/// The class ID representing the type of this object.
|
||||
int get classId => _classId;
|
||||
|
||||
/// The class representing the type of this object.
|
||||
HeapSnapshotClass get klass {
|
||||
if (_classId <= 0) {
|
||||
return HeapSnapshotClass._sentinel();
|
||||
}
|
||||
return _graph._classes[_classId];
|
||||
}
|
||||
|
||||
/// The space used by this object in bytes.
|
||||
int get shallowSize => _shallowSize;
|
||||
|
||||
@@ -197,54 +170,22 @@ class HeapSnapshotObject {
|
||||
/// A list of 1-origin indicies into [HeapSnapshotGraph.objects].
|
||||
List<int> get references => _references;
|
||||
|
||||
/// The identity hash code of this object.
|
||||
///
|
||||
/// If `identityHashCode` is 0, either the snapshot did not contain the list
|
||||
/// of identity hash codes or this object cannot be compared across
|
||||
/// snapshots.
|
||||
int get identityHashCode => _identityHashCode;
|
||||
|
||||
Iterable<HeapSnapshotObject> get successors sync* {
|
||||
final startSuccessorIndex = _graph._firstSuccessors[_oid];
|
||||
final limitSuccessorIndex = _graph._firstSuccessors[_oid + 1];
|
||||
|
||||
for (int nextSuccessorIndex = startSuccessorIndex;
|
||||
nextSuccessorIndex < limitSuccessorIndex;
|
||||
++nextSuccessorIndex) {
|
||||
final successorId = _graph._successors[nextSuccessorIndex];
|
||||
yield _graph.objects[successorId];
|
||||
}
|
||||
}
|
||||
|
||||
final HeapSnapshotGraph _graph;
|
||||
final int _oid;
|
||||
int _classId = -1;
|
||||
int _shallowSize = -1;
|
||||
int _identityHashCode = 0;
|
||||
late final dynamic _data;
|
||||
final List<int> _references = <int>[];
|
||||
|
||||
HeapSnapshotObject._sentinel(this._graph)
|
||||
: _oid = 0,
|
||||
_data = HeapSnapshotObjectNoData() {
|
||||
_graph._firstSuccessors[_oid] = _graph._eid;
|
||||
}
|
||||
|
||||
HeapSnapshotObject._read(this._graph, this._oid, _ReadStream reader) {
|
||||
HeapSnapshotObject._read(_ReadStream reader) {
|
||||
_classId = reader.readUnsigned();
|
||||
_shallowSize = reader.readUnsigned();
|
||||
_data = _getNonReferenceData(reader);
|
||||
_graph._firstSuccessors[_oid] = _graph._eid;
|
||||
_populateReferences(reader);
|
||||
}
|
||||
|
||||
void _populateReferences(_ReadStream reader) {
|
||||
final referencesCount = reader.readUnsigned();
|
||||
for (int i = 0; i < referencesCount; ++i) {
|
||||
int childOid = reader.readUnsigned();
|
||||
_references.add(childOid);
|
||||
_graph._successors[_graph._eid] = childOid;
|
||||
_graph._eid++;
|
||||
_references.add(reader.readUnsigned());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,10 +249,6 @@ class HeapSnapshotGraph {
|
||||
final List<HeapSnapshotExternalProperty> _externalProperties =
|
||||
<HeapSnapshotExternalProperty>[];
|
||||
|
||||
late Uint32List _firstSuccessors;
|
||||
late Uint32List _successors;
|
||||
int _eid = 0;
|
||||
|
||||
/// Requests a heap snapshot for a given isolate and builds a
|
||||
/// [HeapSnapshotGraph].
|
||||
///
|
||||
@@ -353,31 +290,23 @@ class HeapSnapshotGraph {
|
||||
_capacity = reader.readUnsigned();
|
||||
_externalSize = reader.readUnsigned();
|
||||
_populateClasses(reader);
|
||||
_referenceCount = reader.readUnsigned();
|
||||
_populateObjects(reader);
|
||||
_populateExternalProperties(reader);
|
||||
_populateIdentityHashCodes(reader);
|
||||
}
|
||||
|
||||
void _populateClasses(_ReadStream reader) {
|
||||
final classCount = reader.readUnsigned();
|
||||
_classes.add(HeapSnapshotClass._root());
|
||||
for (int i = 1; i <= classCount; ++i) {
|
||||
final klass = HeapSnapshotClass._read(i, reader);
|
||||
_classes.add(klass);
|
||||
for (int i = 0; i < classCount; ++i) {
|
||||
_classes.add(HeapSnapshotClass._read(reader));
|
||||
}
|
||||
}
|
||||
|
||||
void _populateObjects(_ReadStream reader) {
|
||||
_referenceCount = reader.readUnsigned();
|
||||
final objectCount = reader.readUnsigned();
|
||||
_firstSuccessors = _newUint32Array(objectCount + 2);
|
||||
_successors = _newUint32Array(_referenceCount);
|
||||
|
||||
_objects.add(HeapSnapshotObject._sentinel(this));
|
||||
for (int i = 1; i <= objectCount; ++i) {
|
||||
_objects.add(HeapSnapshotObject._read(this, i, reader));
|
||||
for (int i = 0; i < objectCount; ++i) {
|
||||
_objects.add(HeapSnapshotObject._read(reader));
|
||||
}
|
||||
_firstSuccessors[objectCount + 1] = _eid;
|
||||
}
|
||||
|
||||
void _populateExternalProperties(_ReadStream reader) {
|
||||
@@ -386,29 +315,6 @@ class HeapSnapshotGraph {
|
||||
_externalProperties.add(HeapSnapshotExternalProperty._read(reader));
|
||||
}
|
||||
}
|
||||
|
||||
void _populateIdentityHashCodes(_ReadStream reader) {
|
||||
if (reader.atEnd) {
|
||||
// Older VMs don't include identity hash codes.
|
||||
return;
|
||||
}
|
||||
final objectCount = _objects.length;
|
||||
for (int i = 1; i < objectCount; ++i) {
|
||||
_objects[i]._identityHashCode = reader.readUnsigned();
|
||||
}
|
||||
}
|
||||
|
||||
Uint32List _newUint32Array(int size) {
|
||||
try {
|
||||
return Uint32List(size);
|
||||
} on ArgumentError {
|
||||
// JS throws a misleading invalid argument error. Convert to a more
|
||||
// user-friendly message.
|
||||
throw Exception(
|
||||
'OutOfMemoryError: Not enough memory available to analyze the snapshot.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _kNoData = 0;
|
||||
|
||||
@@ -3,7 +3,7 @@ description: >-
|
||||
A library to communicate with a service implementing the Dart VM
|
||||
service protocol.
|
||||
|
||||
version: 6.1.0-nullsafety.0
|
||||
version: 6.0.1
|
||||
|
||||
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
|
||||
|
||||
@@ -13,10 +13,10 @@ environment:
|
||||
dependencies:
|
||||
|
||||
dev_dependencies:
|
||||
async: ^2.5.0-nullsafety.3
|
||||
async: ^2.5.0
|
||||
markdown: ^4.0.0-nullsafety.0
|
||||
mockito: ^5.0.0-nullsafety.1
|
||||
path: ^1.8.0-nullsafety.3
|
||||
path: ^1.8.0
|
||||
pedantic: ^1.10.0-nullsafety.3
|
||||
pub_semver: ^2.0.0-nullsafety.0
|
||||
test: ^1.16.0-nullsafety.13
|
||||
|
||||
@@ -133,7 +133,7 @@ class _ServiceTesteeLauncher {
|
||||
fullArgs.add('--pause-isolates-on-start');
|
||||
}
|
||||
if (pause_on_exit) {
|
||||
fullArgs.add('--pause-isolates-on-exit');
|
||||
fullArgs.add('--pause-isolates-on-io.exit');
|
||||
}
|
||||
if (!useAuthToken) {
|
||||
fullArgs.add('--disable-service-auth-codes');
|
||||
|
||||
@@ -44,8 +44,7 @@ var tests = <IsolateTest>[
|
||||
int actualShallowSize = 0;
|
||||
int actualRefCount = 0;
|
||||
snapshotGraph.objects.forEach((HeapSnapshotObject o) {
|
||||
// -1 is the CID used by the sentinel.
|
||||
expect(o.classId >= -1, isTrue);
|
||||
expect(o.classId >= 0, isTrue);
|
||||
expect(o.data, isNotNull);
|
||||
expect(o.references, isNotNull);
|
||||
actualShallowSize += o.shallowSize;
|
||||
@@ -92,7 +91,7 @@ var tests = <IsolateTest>[
|
||||
foosFound = 0;
|
||||
snapshotGraph.objects.forEach((HeapSnapshotObject o) {
|
||||
if (o.classId == 0) return;
|
||||
if (o.classId == fooClassId) {
|
||||
if (o.classId - 1 == fooClassId) {
|
||||
foosFound++;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
// Copyright (c) 2021, 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:collection';
|
||||
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'common/service_test_common.dart';
|
||||
import 'common/test_helper.dart';
|
||||
|
||||
class Foo {}
|
||||
|
||||
class Bar {}
|
||||
|
||||
class Container1 {
|
||||
@pragma("vm:entry-point")
|
||||
Foo foo = Foo();
|
||||
@pragma("vm:entry-point")
|
||||
Bar bar = Bar();
|
||||
}
|
||||
|
||||
class Container2 {
|
||||
Container2(this.foo);
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
Foo foo;
|
||||
@pragma("vm:entry-point")
|
||||
Bar bar = Bar();
|
||||
}
|
||||
|
||||
class Container3 {
|
||||
@pragma("vm:entry-point")
|
||||
int number = 42;
|
||||
@pragma("vm:entry-point")
|
||||
double doub = 3.14;
|
||||
@pragma("vm:entry-point")
|
||||
String foo = 'foobar';
|
||||
@pragma("vm:entry-point")
|
||||
bool bar = false;
|
||||
@pragma("vm:entry-point")
|
||||
late Map baz;
|
||||
@pragma("vm:entry-point")
|
||||
late LinkedHashMap linkedBaz;
|
||||
@pragma("vm:entry-point")
|
||||
late List list;
|
||||
@pragma("vm:entry-point")
|
||||
late List unmodifiableList;
|
||||
|
||||
Container3() {
|
||||
baz = {
|
||||
'a': 'b',
|
||||
};
|
||||
linkedBaz = LinkedHashMap.from(baz);
|
||||
list = [1, 2, 3];
|
||||
unmodifiableList = List.empty();
|
||||
}
|
||||
}
|
||||
|
||||
late Container1 c1;
|
||||
late Container2 c2;
|
||||
late Container3 c3;
|
||||
|
||||
void script() {
|
||||
c1 = Container1();
|
||||
c2 = Container2(c1.foo);
|
||||
c3 = Container3();
|
||||
}
|
||||
|
||||
late HeapSnapshotGraph snapshot1;
|
||||
late HeapSnapshotObject snapshot1Foo;
|
||||
late HeapSnapshotObject snapshot1Bar;
|
||||
|
||||
late HeapSnapshotGraph snapshot2;
|
||||
late HeapSnapshotObject snapshot2Foo;
|
||||
late HeapSnapshotObject snapshot2Bar;
|
||||
|
||||
late HeapSnapshotGraph snapshot3;
|
||||
|
||||
final tests = <IsolateTest>[
|
||||
(VmService service, IsolateRef isolate) async {
|
||||
snapshot1 = await HeapSnapshotGraph.getSnapshot(service, isolate);
|
||||
|
||||
Iterable<HeapSnapshotObject> container1s = snapshot1.objects.where(
|
||||
(HeapSnapshotObject obj) => obj.klass.name == 'Container1',
|
||||
);
|
||||
expect(container1s.length, 1);
|
||||
|
||||
final c1Obj = container1s.first;
|
||||
|
||||
snapshot1Foo = c1Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Foo',
|
||||
);
|
||||
expect(
|
||||
snapshot1Foo.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
|
||||
snapshot1Bar = c1Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Bar',
|
||||
);
|
||||
expect(
|
||||
snapshot1Bar.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
},
|
||||
(VmService service, IsolateRef isolate) async {
|
||||
snapshot2 = await HeapSnapshotGraph.getSnapshot(service, isolate);
|
||||
;
|
||||
Iterable<HeapSnapshotObject> container2s = snapshot2.objects.where(
|
||||
(HeapSnapshotObject obj) => obj.klass.name == 'Container2',
|
||||
);
|
||||
expect(container2s.length, 1);
|
||||
|
||||
final c2Obj = container2s.first;
|
||||
|
||||
snapshot2Foo = c2Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Foo',
|
||||
);
|
||||
expect(
|
||||
snapshot2Foo.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
snapshot1Foo.identityHashCode == snapshot2Foo.identityHashCode,
|
||||
true,
|
||||
);
|
||||
|
||||
snapshot2Bar = c2Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Bar',
|
||||
);
|
||||
expect(
|
||||
snapshot2Bar.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
snapshot1Bar.identityHashCode != snapshot2Bar.identityHashCode,
|
||||
true,
|
||||
);
|
||||
},
|
||||
(VmService service, IsolateRef isolate) async {
|
||||
snapshot3 = await HeapSnapshotGraph.getSnapshot(service, isolate);
|
||||
Iterable<HeapSnapshotObject> container3s = snapshot3.objects.where(
|
||||
(HeapSnapshotObject obj) => obj.klass.name == 'Container3',
|
||||
);
|
||||
expect(container3s.length, 1);
|
||||
final c3Obj = container3s.first;
|
||||
for (final successor in c3Obj.successors) {
|
||||
expect(successor.identityHashCode, 0);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
main(args) => runIsolateTests(
|
||||
args,
|
||||
tests,
|
||||
testeeBefore: script,
|
||||
pause_on_exit: true,
|
||||
);
|
||||
@@ -202,16 +202,11 @@ const _SENTINEL = 0;
|
||||
|
||||
/// An object in a heap snapshot.
|
||||
abstract class SnapshotObject {
|
||||
/// The identity hash code of this object, used to compare objects across
|
||||
/// snapshots. If [identityHashCode] is 0, this object cannot be compared to
|
||||
/// other objects.
|
||||
int get identityHashCode;
|
||||
|
||||
/// If this object has been obtained from [successors] or [predecessors], the
|
||||
/// name of slot. Otherwise, the empty string.
|
||||
// If this object has been obtained from [successors] or [predecessors], the
|
||||
// name of slot. Otherwise, the empty string.
|
||||
String get label;
|
||||
|
||||
/// The value for primitives. Otherwise, the class name.
|
||||
// The value for primitives. Otherwise, the class name.
|
||||
String get description;
|
||||
|
||||
/// [internalSize] + [externalSize].
|
||||
@@ -260,12 +255,10 @@ abstract class SnapshotObject {
|
||||
|
||||
class _SnapshotObject implements SnapshotObject {
|
||||
final int _id;
|
||||
final int identityHashCode;
|
||||
final _SnapshotGraph _graph;
|
||||
final String label;
|
||||
|
||||
_SnapshotObject._new(this._id, this._graph, this.label)
|
||||
: identityHashCode = _graph._identityHashes![_id];
|
||||
_SnapshotObject._new(this._id, this._graph, this.label);
|
||||
|
||||
bool operator ==(Object other) {
|
||||
if (other is _SnapshotObject) {
|
||||
@@ -351,7 +344,6 @@ class _SyntheticSnapshotObject implements SnapshotObject {
|
||||
late SnapshotObject _parent;
|
||||
late List<SnapshotObject> _children;
|
||||
|
||||
int get identityHashCode => 0;
|
||||
String get label => "";
|
||||
String get description => _description;
|
||||
SnapshotClass get klass => _klass;
|
||||
@@ -813,9 +805,6 @@ class _SnapshotGraph implements SnapshotGraph {
|
||||
onProgress.add("Loading external properties...");
|
||||
await new Future(() => _readExternalProperties(stream!));
|
||||
|
||||
onProgress.add("Loading object identity hash codes...");
|
||||
await new Future(() => _readObjectIdentityHashes(stream!));
|
||||
|
||||
stream = null;
|
||||
|
||||
onProgress.add("Compute class table...");
|
||||
@@ -888,7 +877,6 @@ class _SnapshotGraph implements SnapshotGraph {
|
||||
Uint32List? _externalSizes;
|
||||
Uint32List? _firstSuccs;
|
||||
Uint32List? _succs;
|
||||
Uint32List? _identityHashes;
|
||||
|
||||
// Intermediates.
|
||||
Uint32List? _vertex;
|
||||
@@ -1048,15 +1036,6 @@ class _SnapshotGraph implements SnapshotGraph {
|
||||
_externalSizes = externalSizes;
|
||||
}
|
||||
|
||||
void _readObjectIdentityHashes(_ReadStream stream) {
|
||||
final N = _N!;
|
||||
final identityHashes = _newUint32Array(N + 1);
|
||||
for (int oid = 1; oid <= N; ++oid) {
|
||||
identityHashes[oid] = stream.readUnsigned();
|
||||
}
|
||||
_identityHashes = identityHashes;
|
||||
}
|
||||
|
||||
void _computeClassTable() {
|
||||
final N = _N!;
|
||||
final classes = _classes!;
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
// Copyright (c) 2021, 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/object_graph.dart';
|
||||
import 'package:observatory/service_io.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'service_test_common.dart';
|
||||
import 'test_helper.dart';
|
||||
|
||||
class Foo {}
|
||||
|
||||
class Bar {}
|
||||
|
||||
class Container1 {
|
||||
@pragma("vm:entry-point")
|
||||
Foo foo = Foo();
|
||||
@pragma("vm:entry-point")
|
||||
Bar bar = Bar();
|
||||
}
|
||||
|
||||
class Container2 {
|
||||
Container2(this.foo);
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
Foo foo;
|
||||
@pragma("vm:entry-point")
|
||||
Bar bar = Bar();
|
||||
}
|
||||
|
||||
class Container3 {
|
||||
@pragma("vm:entry-point")
|
||||
int number = 42;
|
||||
@pragma("vm:entry-point")
|
||||
double doub = 3.14;
|
||||
@pragma("vm:entry-point")
|
||||
String foo = 'foobar';
|
||||
@pragma("vm:entry-point")
|
||||
bool bar = false;
|
||||
@pragma("vm:entry-point")
|
||||
late Map baz;
|
||||
@pragma("vm:entry-point")
|
||||
late List list;
|
||||
@pragma("vm:entry-point")
|
||||
late List unmodifiableList;
|
||||
|
||||
Container3() {
|
||||
baz = {
|
||||
'a': 'b',
|
||||
};
|
||||
list = [1, 2, 3];
|
||||
unmodifiableList = List.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
late Container1 c1;
|
||||
@pragma("vm:entry-point")
|
||||
late Container2 c2;
|
||||
@pragma("vm:entry-point")
|
||||
late Container3 c3;
|
||||
|
||||
void script() {
|
||||
c1 = Container1();
|
||||
c2 = Container2(c1.foo);
|
||||
c3 = Container3();
|
||||
}
|
||||
|
||||
late SnapshotGraph snapshot1;
|
||||
late SnapshotObject snapshot1Foo;
|
||||
late SnapshotObject snapshot1Bar;
|
||||
|
||||
late SnapshotGraph snapshot2;
|
||||
late SnapshotObject snapshot2Foo;
|
||||
late SnapshotObject snapshot2Bar;
|
||||
|
||||
late SnapshotGraph snapshot3;
|
||||
|
||||
final tests = <IsolateTest>[
|
||||
(Isolate isolate) async {
|
||||
snapshot1 = await isolate.fetchHeapSnapshot().done;
|
||||
|
||||
Iterable<SnapshotObject> container1s = snapshot1.objects.where(
|
||||
(SnapshotObject obj) => obj.klass.name == 'Container1',
|
||||
);
|
||||
expect(container1s.length, 1);
|
||||
|
||||
final c1Obj = container1s.first;
|
||||
|
||||
c1Obj.successors.forEach((element) {
|
||||
print(element.klass.name);
|
||||
});
|
||||
snapshot1Foo = c1Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Foo',
|
||||
);
|
||||
expect(
|
||||
snapshot1Foo.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
|
||||
snapshot1Bar = c1Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Bar',
|
||||
);
|
||||
expect(
|
||||
snapshot1Bar.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
},
|
||||
(Isolate isolate) async {
|
||||
snapshot2 = await isolate.fetchHeapSnapshot().done;
|
||||
Iterable<SnapshotObject> container2s = snapshot2.objects.where(
|
||||
(SnapshotObject obj) => obj.klass.name == 'Container2',
|
||||
);
|
||||
expect(container2s.length, 1);
|
||||
|
||||
final c2Obj = container2s.first;
|
||||
|
||||
snapshot2Foo = c2Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Foo',
|
||||
);
|
||||
expect(
|
||||
snapshot2Foo.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
snapshot1Foo.identityHashCode == snapshot2Foo.identityHashCode,
|
||||
true,
|
||||
);
|
||||
|
||||
snapshot2Bar = c2Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Bar',
|
||||
);
|
||||
expect(
|
||||
snapshot2Bar.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
snapshot1Bar.identityHashCode != snapshot2Bar.identityHashCode,
|
||||
true,
|
||||
);
|
||||
},
|
||||
(Isolate isolate) async {
|
||||
snapshot3 = await isolate.fetchHeapSnapshot().done;
|
||||
Iterable<SnapshotObject> container3s = snapshot3.objects.where(
|
||||
(SnapshotObject obj) => obj.klass.name == 'Container3',
|
||||
);
|
||||
expect(container3s.length, 1);
|
||||
final c3Obj = container3s.first;
|
||||
for (final successor in c3Obj.successors) {
|
||||
expect(successor.identityHashCode, 0);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
main(args) => runIsolateTests(
|
||||
args,
|
||||
tests,
|
||||
testeeBefore: script,
|
||||
pause_on_exit: true,
|
||||
);
|
||||
@@ -202,16 +202,11 @@ const _SENTINEL = 0;
|
||||
|
||||
/// An object in a heap snapshot.
|
||||
abstract class SnapshotObject {
|
||||
/// The identity hash code of this object, used to compare objects across
|
||||
/// snapshots. If [identityHashCode] is 0, this object cannot be compared to
|
||||
/// other objects.
|
||||
int get identityHashCode;
|
||||
|
||||
/// If this object has been obtained from [successors] or [predecessors], the
|
||||
/// name of slot. Otherwise, the empty string.
|
||||
// If this object has been obtained from [successors] or [predecessors], the
|
||||
// name of slot. Otherwise, the empty string.
|
||||
String get label;
|
||||
|
||||
/// The value for primitives. Otherwise, the class name.
|
||||
// The value for primitives. Otherwise, the class name.
|
||||
String get description;
|
||||
|
||||
/// [internalSize] + [externalSize].
|
||||
@@ -260,12 +255,10 @@ abstract class SnapshotObject {
|
||||
|
||||
class _SnapshotObject implements SnapshotObject {
|
||||
final int _id;
|
||||
final int identityHashCode;
|
||||
final _SnapshotGraph _graph;
|
||||
final String label;
|
||||
|
||||
_SnapshotObject._new(this._id, this._graph, this.label)
|
||||
: identityHashCode = _graph._identityHashes[_id];
|
||||
_SnapshotObject._new(this._id, this._graph, this.label);
|
||||
|
||||
bool operator ==(Object other) {
|
||||
if (other is _SnapshotObject) {
|
||||
@@ -351,7 +344,6 @@ class _SyntheticSnapshotObject implements SnapshotObject {
|
||||
SnapshotObject _parent;
|
||||
List<SnapshotObject> _children;
|
||||
|
||||
int get identityHashCode => 0;
|
||||
String get label => null;
|
||||
String get description => _description;
|
||||
SnapshotClass get klass => _klass;
|
||||
@@ -802,9 +794,6 @@ class _SnapshotGraph implements SnapshotGraph {
|
||||
onProgress.add("Loading external properties...");
|
||||
await new Future(() => _readExternalProperties(stream));
|
||||
|
||||
onProgress.add("Loading object identity hash codes...");
|
||||
await new Future(() => _readObjectIdentityHashes(stream));
|
||||
|
||||
stream = null;
|
||||
|
||||
onProgress.add("Compute class table...");
|
||||
@@ -877,7 +866,6 @@ class _SnapshotGraph implements SnapshotGraph {
|
||||
Uint32List _externalSizes;
|
||||
Uint32List _firstSuccs;
|
||||
Uint32List _succs;
|
||||
Uint32List _identityHashes;
|
||||
|
||||
// Intermediates.
|
||||
Uint32List _vertex;
|
||||
@@ -1037,15 +1025,6 @@ class _SnapshotGraph implements SnapshotGraph {
|
||||
_externalSizes = externalSizes;
|
||||
}
|
||||
|
||||
void _readObjectIdentityHashes(_ReadStream stream) {
|
||||
final N = _N;
|
||||
final identityHashes = _newUint32Array(N + 1);
|
||||
for (int oid = 1; oid <= N; ++oid) {
|
||||
identityHashes[oid] = stream.readUnsigned();
|
||||
}
|
||||
_identityHashes = identityHashes;
|
||||
}
|
||||
|
||||
void _computeClassTable() {
|
||||
final N = _N;
|
||||
final classes = _classes;
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
// Copyright (c) 2021, 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_2/object_graph.dart';
|
||||
import 'package:observatory_2/service_io.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'service_test_common.dart';
|
||||
import 'test_helper.dart';
|
||||
|
||||
class Foo {}
|
||||
|
||||
class Bar {}
|
||||
|
||||
class Container1 {
|
||||
@pragma("vm:entry-point")
|
||||
Foo foo = Foo();
|
||||
@pragma("vm:entry-point")
|
||||
Bar bar = Bar();
|
||||
}
|
||||
|
||||
class Container2 {
|
||||
Container2(this.foo);
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
Foo foo;
|
||||
@pragma("vm:entry-point")
|
||||
Bar bar = Bar();
|
||||
}
|
||||
|
||||
class Container3 {
|
||||
@pragma("vm:entry-point")
|
||||
int number = 42;
|
||||
@pragma("vm:entry-point")
|
||||
double doub = 3.14;
|
||||
@pragma("vm:entry-point")
|
||||
String foo = 'foobar';
|
||||
@pragma("vm:entry-point")
|
||||
bool bar = false;
|
||||
@pragma("vm:entry-point")
|
||||
Map baz;
|
||||
@pragma("vm:entry-point")
|
||||
List list;
|
||||
@pragma("vm:entry-point")
|
||||
List unmodifiableList;
|
||||
|
||||
Container3() {
|
||||
baz = {
|
||||
'a': 'b',
|
||||
};
|
||||
list = [1, 2, 3];
|
||||
unmodifiableList = List.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
Container1 c1;
|
||||
@pragma("vm:entry-point")
|
||||
Container2 c2;
|
||||
@pragma("vm:entry-point")
|
||||
Container3 c3;
|
||||
|
||||
void script() {
|
||||
c1 = Container1();
|
||||
c2 = Container2(c1.foo);
|
||||
c3 = Container3();
|
||||
}
|
||||
|
||||
SnapshotGraph snapshot1;
|
||||
SnapshotObject snapshot1Foo;
|
||||
SnapshotObject snapshot1Bar;
|
||||
|
||||
SnapshotGraph snapshot2;
|
||||
SnapshotObject snapshot2Foo;
|
||||
SnapshotObject snapshot2Bar;
|
||||
|
||||
SnapshotGraph snapshot3;
|
||||
|
||||
final tests = <IsolateTest>[
|
||||
(Isolate isolate) async {
|
||||
snapshot1 = await isolate.fetchHeapSnapshot().done;
|
||||
|
||||
Iterable<SnapshotObject> container1s = snapshot1.objects.where(
|
||||
(SnapshotObject obj) => obj.klass.name == 'Container1',
|
||||
);
|
||||
expect(container1s.length, 1);
|
||||
|
||||
final c1Obj = container1s.first;
|
||||
|
||||
c1Obj.successors.forEach((element) {
|
||||
print(element.klass.name);
|
||||
});
|
||||
snapshot1Foo = c1Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Foo',
|
||||
);
|
||||
expect(
|
||||
snapshot1Foo.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
|
||||
snapshot1Bar = c1Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Bar',
|
||||
);
|
||||
expect(
|
||||
snapshot1Bar.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
},
|
||||
(Isolate isolate) async {
|
||||
snapshot2 = await isolate.fetchHeapSnapshot().done;
|
||||
Iterable<SnapshotObject> container2s = snapshot2.objects.where(
|
||||
(SnapshotObject obj) => obj.klass.name == 'Container2',
|
||||
);
|
||||
expect(container2s.length, 1);
|
||||
|
||||
final c2Obj = container2s.first;
|
||||
|
||||
snapshot2Foo = c2Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Foo',
|
||||
);
|
||||
expect(
|
||||
snapshot2Foo.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
snapshot1Foo.identityHashCode == snapshot2Foo.identityHashCode,
|
||||
true,
|
||||
);
|
||||
|
||||
snapshot2Bar = c2Obj.successors.firstWhere(
|
||||
(element) => element.klass.name == 'Bar',
|
||||
);
|
||||
expect(
|
||||
snapshot2Bar.identityHashCode != 0,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
snapshot1Bar.identityHashCode != snapshot2Bar.identityHashCode,
|
||||
true,
|
||||
);
|
||||
},
|
||||
(Isolate isolate) async {
|
||||
snapshot3 = await isolate.fetchHeapSnapshot().done;
|
||||
Iterable<SnapshotObject> container3s = snapshot3.objects.where(
|
||||
(SnapshotObject obj) => obj.klass.name == 'Container3',
|
||||
);
|
||||
expect(container3s.length, 1);
|
||||
final c3Obj = container3s.first;
|
||||
for (final successor in c3Obj.successors) {
|
||||
expect(successor.identityHashCode, 0);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
main(args) => runIsolateTests(
|
||||
args,
|
||||
tests,
|
||||
testeeBefore: script,
|
||||
pause_on_exit: true,
|
||||
);
|
||||
@@ -1013,90 +1013,6 @@ class Pass2Visitor : public ObjectVisitor,
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Pass2Visitor);
|
||||
};
|
||||
class Pass3Visitor : public ObjectVisitor {
|
||||
public:
|
||||
explicit Pass3Visitor(HeapSnapshotWriter* writer)
|
||||
: ObjectVisitor(), isolate_(Isolate::Current()), writer_(writer) {}
|
||||
|
||||
void VisitObject(ObjectPtr obj) {
|
||||
if (obj->IsPseudoObject()) {
|
||||
return;
|
||||
}
|
||||
writer_->WriteUnsigned(GetHash(obj));
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t GetHash(ObjectPtr obj) {
|
||||
if (!obj->IsHeapObject()) return 0;
|
||||
intptr_t cid = obj->GetClassId();
|
||||
uint32_t hash = 0;
|
||||
switch (cid) {
|
||||
case kForwardingCorpse:
|
||||
case kFreeListElement:
|
||||
case kSmiCid:
|
||||
UNREACHABLE();
|
||||
case kArrayCid:
|
||||
case kBoolCid:
|
||||
case kCodeSourceMapCid:
|
||||
case kCompressedStackMapsCid:
|
||||
case kDoubleCid:
|
||||
case kExternalOneByteStringCid:
|
||||
case kExternalTwoByteStringCid:
|
||||
case kGrowableObjectArrayCid:
|
||||
case kImmutableArrayCid:
|
||||
case kInstructionsCid:
|
||||
case kInstructionsSectionCid:
|
||||
case kLinkedHashMapCid:
|
||||
case kMintCid:
|
||||
case kNeverCid:
|
||||
case kNullCid:
|
||||
case kObjectPoolCid:
|
||||
case kOneByteStringCid:
|
||||
case kPcDescriptorsCid:
|
||||
case kTwoByteStringCid:
|
||||
case kVoidCid:
|
||||
// Don't provide hash codes for objects with the above CIDs in order
|
||||
// to try and avoid having to initialize identity hash codes for common
|
||||
// primitives and types that don't have hash codes.
|
||||
break;
|
||||
default: {
|
||||
hash = GetHashHelper(obj);
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint32_t GetHashHelper(ObjectPtr obj) {
|
||||
uint32_t hash;
|
||||
#if defined(HASH_IN_OBJECT_HEADER)
|
||||
hash = Object::GetCachedHash(obj);
|
||||
if (hash == 0) {
|
||||
ASSERT(
|
||||
!isolate_->group()->heap()->old_space()->IsObjectFromImagePages(obj));
|
||||
hash = isolate_->random()->NextUInt32();
|
||||
Object::SetCachedHash(obj, hash);
|
||||
hash = Object::GetCachedHash(obj);
|
||||
}
|
||||
#else
|
||||
Heap* heap = isolate_->group()->heap();
|
||||
hash = heap->GetHash(obj);
|
||||
if (hash == 0) {
|
||||
ASSERT(!heap->old_space()->IsObjectFromImagePages(obj));
|
||||
heap->SetHash(obj, isolate_->random()->NextUInt32());
|
||||
hash = heap->GetHash(obj);
|
||||
}
|
||||
#endif
|
||||
return hash;
|
||||
}
|
||||
|
||||
// TODO(dartbug.com/36097): Once the shared class table contains more
|
||||
// information than just the size (i.e. includes an immutable class
|
||||
// descriptor), we can remove this dependency on the current isolate.
|
||||
Isolate* isolate_;
|
||||
HeapSnapshotWriter* const writer_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Pass3Visitor);
|
||||
};
|
||||
|
||||
void HeapSnapshotWriter::Write() {
|
||||
HeapIterationScope iteration(thread());
|
||||
@@ -1265,18 +1181,6 @@ void HeapSnapshotWriter::Write() {
|
||||
isolate()->group()->VisitWeakPersistentHandles(&visitor);
|
||||
}
|
||||
|
||||
{
|
||||
// Identity hash codes
|
||||
Pass3Visitor visitor(this);
|
||||
|
||||
// Handle root object.
|
||||
WriteUnsigned(0);
|
||||
|
||||
// Handle visit rest of the objects.
|
||||
iteration.IterateVMIsolateObjects(&visitor);
|
||||
iteration.IterateObjects(&visitor);
|
||||
}
|
||||
|
||||
ClearObjectIds();
|
||||
Flush(true);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ type SnapshotGraph {
|
||||
// The amount of memory reserved for this heap. At least as large as |shallowSize|.
|
||||
capacity : uleb128,
|
||||
|
||||
// The sum of sizes of all external properties in this graph.
|
||||
// The sum of sizes of all external properites in this graph.
|
||||
externalSize : uleb128,
|
||||
|
||||
classCount : uleb128,
|
||||
@@ -54,14 +54,6 @@ type SnapshotGraph {
|
||||
|
||||
externalPropertyCount : uleb128,
|
||||
externalProperties : SnapshotExternalProperty[externalPropertyCount],
|
||||
|
||||
// The list of identity hash codes corresponding to each entry in objects.
|
||||
// A hash code of zero is invalid and cannot be used to determine equality
|
||||
// between objects. If the same object is included in multiple
|
||||
// HeapSnapshots, it will report the same identityHashCode. The converse is
|
||||
// not true: two different objects may report the same identityHashCode
|
||||
// (with low probability).
|
||||
identityHashCodes: uint32[objectCount],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user