mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 16:41:07 +00:00
Extend HeapSnapshotGraph API to support incoming references in package:vm_service
Currently the HeapSnapshotGraph represents the objects in the Dart VM heap and how they relate to each other. They effectively form a directed graph with a specific root node. The API does allow traversing the graph by following an object's outgoing references. Though in certain situations it can be very useful to know the set of objects referencing a given object (i.e. incoming references). This CL adds such support, thereby allowing traversing edges in both directions: Finding what an object references and who references an object. When loading a snapshot we therefore have to also compute the reverse edges and store them. To offset any additional computation time and memory consumption, we optimize the existing implementation by not using growable List<int> objects for outgoing edges but rather typed-data views into the existing Uint32List of edges. Due to this optimization we make loading of snapshot faster and smaller, in a simple benchmark we seem to get: * JIT: 10% faster, 40% less RAM * AOT: 30% faster, 40% less RAM despite the additional compute & data structures of this API. TEST=vm/dart{,_2}/heap_snapshot_referencees_test Change-Id: Ief30bfb58c70364744eeb7f69967dd1f72ece807 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/260524 Reviewed-by: Tess Strickland <sstrickl@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
affc3392a0
commit
6b1e9bbdfe
|
@ -195,7 +195,12 @@ class HeapSnapshotObject {
|
|||
dynamic get data => _data;
|
||||
|
||||
/// A list of indices into [HeapSnapshotGraph.objects].
|
||||
List<int> get references => _references;
|
||||
Uint32List get references => Uint32List.sublistView(_graph._successors,
|
||||
_graph._firstSuccessors[_oid], _graph._firstSuccessors[_oid + 1]);
|
||||
|
||||
/// A list of indices into [HeapSnapshotGraph.objects].
|
||||
Uint32List get referrers => Uint32List.sublistView(_graph._predecessors,
|
||||
_graph._firstPredecessors[_oid], _graph._firstPredecessors[_oid + 1]);
|
||||
|
||||
/// The identity hash code of this object.
|
||||
///
|
||||
|
@ -222,7 +227,6 @@ class HeapSnapshotObject {
|
|||
int _shallowSize = -1;
|
||||
int _identityHashCode = 0;
|
||||
late final dynamic _data;
|
||||
final List<int> _references = <int>[];
|
||||
|
||||
HeapSnapshotObject._sentinel(this._graph)
|
||||
: _oid = 0,
|
||||
|
@ -234,17 +238,17 @@ class HeapSnapshotObject {
|
|||
_classId = reader.readUnsigned();
|
||||
_shallowSize = reader.readUnsigned();
|
||||
_data = _getNonReferenceData(reader);
|
||||
_graph._firstSuccessors[_oid] = _graph._eid;
|
||||
_populateReferences(reader);
|
||||
}
|
||||
|
||||
void _populateReferences(_ReadStream reader) {
|
||||
_graph._firstSuccessors[_oid] = _graph._eid;
|
||||
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++;
|
||||
final currentOid = _graph._eid++;
|
||||
final childOid = reader.readUnsigned();
|
||||
_graph._successors[currentOid] = childOid;
|
||||
_graph._predecessorCounts[childOid]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -308,8 +312,14 @@ class HeapSnapshotGraph {
|
|||
final List<HeapSnapshotExternalProperty> _externalProperties =
|
||||
<HeapSnapshotExternalProperty>[];
|
||||
|
||||
late Uint32List _firstSuccessors;
|
||||
late Uint32List _successors;
|
||||
late Uint32List _predecessorCounts;
|
||||
|
||||
late final Uint32List _firstSuccessors;
|
||||
late final Uint32List _successors;
|
||||
|
||||
late final Uint32List _firstPredecessors;
|
||||
late final Uint32List _predecessors;
|
||||
|
||||
int _eid = 0;
|
||||
|
||||
/// Requests a heap snapshot for a given isolate and builds a
|
||||
|
@ -356,6 +366,8 @@ class HeapSnapshotGraph {
|
|||
_populateObjects(reader);
|
||||
_populateExternalProperties(reader);
|
||||
_populateIdentityHashCodes(reader);
|
||||
|
||||
_calculatePredecessors();
|
||||
}
|
||||
|
||||
void _populateClasses(_ReadStream reader) {
|
||||
|
@ -370,8 +382,10 @@ class HeapSnapshotGraph {
|
|||
void _populateObjects(_ReadStream reader) {
|
||||
_referenceCount = reader.readUnsigned();
|
||||
final objectCount = reader.readUnsigned();
|
||||
|
||||
_firstSuccessors = _newUint32Array(objectCount + 2);
|
||||
_successors = _newUint32Array(_referenceCount);
|
||||
_predecessorCounts = _newUint32Array(objectCount + 2);
|
||||
|
||||
_objects.add(HeapSnapshotObject._sentinel(this));
|
||||
for (int i = 1; i <= objectCount; ++i) {
|
||||
|
@ -380,6 +394,37 @@ class HeapSnapshotGraph {
|
|||
_firstSuccessors[objectCount + 1] = _eid;
|
||||
}
|
||||
|
||||
void _calculatePredecessors() {
|
||||
final objectCount = _objects.length - 1;
|
||||
|
||||
_firstPredecessors = _newUint32Array(objectCount + 2);
|
||||
_predecessors = _newUint32Array(_referenceCount);
|
||||
|
||||
_firstPredecessors[objectCount + 1] = _eid;
|
||||
|
||||
// We reuse the [_predecessorCounts] array and turn it into the
|
||||
// write cursor array.
|
||||
final predecessorCounts = _predecessorCounts;
|
||||
_predecessorCounts = Uint32List(0);
|
||||
int sum = 0;
|
||||
int totalCount = _referenceCount;
|
||||
for (int i = objectCount; i >= 0; --i) {
|
||||
sum += predecessorCounts[i];
|
||||
final firstPredecessor = totalCount - sum;
|
||||
_firstPredecessors[i] = predecessorCounts[i] = firstPredecessor;
|
||||
}
|
||||
|
||||
final predecessorWriteCursor = predecessorCounts;
|
||||
for (int i = 1; i <= objectCount; ++i) {
|
||||
final from = _firstSuccessors[i];
|
||||
final to = _firstSuccessors[i + 1];
|
||||
for (int j = from; j < to; ++j) {
|
||||
final cursor = predecessorWriteCursor[_successors[j]]++;
|
||||
_predecessors[cursor] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _populateExternalProperties(_ReadStream reader) {
|
||||
final propertiesCount = reader.readUnsigned();
|
||||
for (int i = 0; i < propertiesCount; ++i) {
|
||||
|
|
42
runtime/tests/vm/dart/heap_snapshot_referencees_test.dart
Normal file
42
runtime/tests/vm/dart/heap_snapshot_referencees_test.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
// 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:_internal';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'heap_snapshot_test.dart';
|
||||
import 'use_flag_test_helper.dart';
|
||||
|
||||
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');
|
||||
VMInternalsForTesting.writeHeapSnapshotToFile(file);
|
||||
final graph = loadHeapSnapshotFromFile(file);
|
||||
final reachable = findReachableObjects(graph);
|
||||
|
||||
for (int id = 0; id < graph.objects.length; ++id) {
|
||||
final object = graph.objects[id];
|
||||
|
||||
// Ensure all `references` appear in `referrers`.
|
||||
for (final rid in object.references) {
|
||||
final users = graph.objects[rid].referrers;
|
||||
if (!users.contains(id)) {
|
||||
throw 'Object $id references $rid, but is not in referrers';
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure all `referrers` appear in `references`.
|
||||
for (final uid in object.referrers) {
|
||||
final refs = graph.objects[uid].references;
|
||||
if (!refs.contains(id)) {
|
||||
throw 'Object $id is referenced by $uid, but is not in references.';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
44
runtime/tests/vm/dart_2/heap_snapshot_referencees_test.dart
Normal file
44
runtime/tests/vm/dart_2/heap_snapshot_referencees_test.dart
Normal file
|
@ -0,0 +1,44 @@
|
|||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
import 'dart:_internal';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'heap_snapshot_test.dart';
|
||||
import 'use_flag_test_helper.dart';
|
||||
|
||||
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');
|
||||
VMInternalsForTesting.writeHeapSnapshotToFile(file);
|
||||
final graph = loadHeapSnapshotFromFile(file);
|
||||
final reachable = findReachableObjects(graph);
|
||||
|
||||
for (int id = 0; id < graph.objects.length; ++id) {
|
||||
final object = graph.objects[id];
|
||||
|
||||
// Ensure all `references` appear in `referrers`.
|
||||
for (final rid in object.references) {
|
||||
final users = graph.objects[rid].referrers;
|
||||
if (!users.contains(id)) {
|
||||
throw 'Object $id references $rid, but is not in referrers';
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure all `referrers` appear in `references`.
|
||||
for (final uid in object.referrers) {
|
||||
final refs = graph.objects[uid].references;
|
||||
if (!refs.contains(id)) {
|
||||
throw 'Object $id is referenced by $uid, but is not in references.';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue