| Index: runtime/observatory/lib/src/heap_snapshot/heap_snapshot.dart
|
| diff --git a/runtime/observatory/lib/src/heap_snapshot/heap_snapshot.dart b/runtime/observatory/lib/src/heap_snapshot/heap_snapshot.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..71f41e4f948d29e813ee2938da7d779abdcead18
|
| --- /dev/null
|
| +++ b/runtime/observatory/lib/src/heap_snapshot/heap_snapshot.dart
|
| @@ -0,0 +1,226 @@
|
| +// Copyright (c) 2016, 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.
|
| +
|
| +part of heap_snapshot;
|
| +
|
| +class HeapSnapshot implements M.HeapSnapshot {
|
| + ObjectGraph graph;
|
| + DateTime timestamp;
|
| + int get objects => graph.vertexCount;
|
| + int get references => graph.edgeCount;
|
| + int get size => graph.size;
|
| + HeapSnapshotDominatorNode dominatorTree;
|
| + List<MergedVertex> classReferences;
|
| +
|
| + static Future sleep([Duration duration = const Duration(microseconds: 0)]) {
|
| + final Completer completer = new Completer();
|
| + new Timer(duration, () => completer.complete() );
|
| + return completer.future;
|
| + }
|
| +
|
| + Stream<List> loadProgress(S.Isolate isolate, S.RawHeapSnapshot raw) {
|
| + final progress = new StreamController<List>.broadcast();
|
| + progress.add(['', 0.0]);
|
| + graph = new ObjectGraph(raw.chunks, raw.count);
|
| + var signal = (String description, double p) {
|
| + progress.add([description, p]);
|
| + return sleep();
|
| + };
|
| + (() async {
|
| + timestamp = new DateTime.now();
|
| + final stream = graph.process();
|
| + stream.listen((status) {
|
| + status[1] /= 2.0;
|
| + progress.add(status);
|
| + });
|
| + await stream.last;
|
| + dominatorTree = new HeapSnapshotDominatorNode(isolate, graph.root);
|
| + classReferences = await buildMergedVertices(isolate, graph, signal);
|
| + progress.close();
|
| + }());
|
| + return progress.stream;
|
| + }
|
| +
|
| + Future<List<MergedVertex>> buildMergedVertices(S.Isolate isolate,
|
| + ObjectGraph graph,
|
| + signal) async {
|
| + final cidToMergedVertex = {};
|
| +
|
| + int count = 0;
|
| + final Stopwatch watch = new Stopwatch();
|
| + watch.start();
|
| + var needToUpdate = () {
|
| + count++;
|
| + if (((count % 256) == 0) && (watch.elapsedMilliseconds > 16)) {
|
| + watch.reset();
|
| + return true;
|
| + }
|
| + return false;
|
| + };
|
| + final length = graph.vertices.length;
|
| + for (final vertex in graph.vertices) {
|
| + if (vertex.vmCid == 0) {
|
| + continue;
|
| + }
|
| + if (needToUpdate()) {
|
| + await signal('', count * 50.0 / length + 50);
|
| + }
|
| + final cid = vertex.vmCid;
|
| + MergedVertex source = cidToMergedVertex[cid];
|
| + if (source == null) {
|
| + cidToMergedVertex[cid] = source = new MergedVertex(isolate, cid);
|
| + }
|
| +
|
| + source.instances++;
|
| + source.shallowSize += vertex.shallowSize ?? 0;
|
| +
|
| + for (final vertex2 in vertex.successors) {
|
| + if (vertex2.vmCid == 0) {
|
| + continue;
|
| + }
|
| + final cid2 = vertex2.vmCid;
|
| + MergedEdge edge = source.outgoingEdges[cid2];
|
| + if (edge == null) {
|
| + MergedVertex target = cidToMergedVertex[cid2];
|
| + if (target == null) {
|
| + cidToMergedVertex[cid2] = target = new MergedVertex(isolate, cid2);
|
| + }
|
| + edge = new MergedEdge(source, target);
|
| + source.outgoingEdges[cid2] = edge;
|
| + target.incomingEdges.add(edge);
|
| + }
|
| + edge.count++;
|
| + // An over-estimate if there are multiple references to the same object.
|
| + edge.shallowSize += vertex2.shallowSize ?? 0;
|
| + }
|
| + }
|
| + return cidToMergedVertex.values.toList();
|
| + }
|
| +
|
| + List<Future<S.ServiceObject>> getMostRetained(S.Isolate isolate,
|
| + {int classId, int limit}) {
|
| + var result = [];
|
| + for (ObjectVertex v in graph.getMostRetained(classId: classId,
|
| + limit: limit)) {
|
| + result.add(isolate.getObjectByAddress(v.address)
|
| + .then((S.ServiceObject obj) {
|
| + if (obj is S.HeapObject) {
|
| + obj.retainedSize = v.retainedSize;
|
| + } else {
|
| + print("${obj.runtimeType} should be a HeapObject");
|
| + }
|
| + return obj;
|
| + }));
|
| + }
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +class HeapSnapshotDominatorNode implements M.HeapSnapshotDominatorNode {
|
| + final ObjectVertex v;
|
| + final S.Isolate isolate;
|
| + S.HeapObject _preloaded;
|
| +
|
| + Future<S.HeapObject> get object {
|
| + if (_preloaded != null) {
|
| + return new Future.value(_preloaded);
|
| + } else {
|
| + return isolate.getObjectByAddress(v.address).then((S.HeapObject obj) {
|
| + return _preloaded = obj;
|
| + });
|
| + }
|
| + }
|
| + Iterable<HeapSnapshotDominatorNode> _children;
|
| + Iterable<HeapSnapshotDominatorNode> get children {
|
| + if (_children != null) {
|
| + return _children;
|
| + } else {
|
| + return _children = new List.unmodifiable(
|
| + v.dominatorTreeChildren().map((v) {
|
| + return new HeapSnapshotDominatorNode(isolate, v);
|
| + })
|
| + );
|
| + }
|
| + }
|
| + int get retainedSize => v.retainedSize;
|
| + int get shallowSize => v.shallowSize;
|
| +
|
| + HeapSnapshotDominatorNode(S.Isolate isolate, ObjectVertex vertex)
|
| + : isolate = isolate,
|
| + v = vertex;
|
| +}
|
| +
|
| +class MergedEdge {
|
| + final MergedVertex sourceVertex;
|
| + final MergedVertex targetVertex;
|
| + int count = 0;
|
| + int shallowSize = 0;
|
| + int retainedSize = 0;
|
| +
|
| + MergedEdge(this.sourceVertex, this.targetVertex);
|
| +}
|
| +
|
| +class MergedVertex implements M.HeapSnapshotClassReferences {
|
| + final int cid;
|
| + final S.Isolate isolate;
|
| + S.Class get clazz => isolate.getClassByCid(cid);
|
| + int instances = 0;
|
| + int shallowSize = 0;
|
| + int retainedSize = 0;
|
| +
|
| + List<MergedEdge> incomingEdges = new List<MergedEdge>();
|
| + Map<int, MergedEdge> outgoingEdges = new Map<int, MergedEdge>();
|
| +
|
| + Iterable<HeapSnapshotClassInbound> _inbounds;
|
| + Iterable<HeapSnapshotClassInbound> get inbounds {
|
| + if (_inbounds != null) {
|
| + return _inbounds;
|
| + } else {
|
| + return _inbounds = new List.unmodifiable(
|
| + incomingEdges.map((edge) {
|
| + return new HeapSnapshotClassInbound(this, edge);
|
| + })
|
| + );
|
| + }
|
| + }
|
| +
|
| + Iterable<HeapSnapshotClassOutbound> _outbounds;
|
| + Iterable<HeapSnapshotClassOutbound> get outbounds {
|
| + if (_outbounds != null) {
|
| + return _outbounds;
|
| + } else {
|
| + return _outbounds = new List.unmodifiable(
|
| + outgoingEdges.values.map((edge) {
|
| + return new HeapSnapshotClassInbound(this, edge);
|
| + })
|
| + );
|
| + }
|
| + }
|
| +
|
| + MergedVertex(this.isolate, this.cid);
|
| +}
|
| +
|
| +class HeapSnapshotClassInbound implements M.HeapSnapshotClassInbound {
|
| + final MergedVertex vertex;
|
| + final MergedEdge edge;
|
| + S.Class get source => edge.sourceVertex != vertex ? edge.sourceVertex.clazz
|
| + : edge.targetVertex.clazz;
|
| + int get count => edge.count;
|
| + int get shallowSize => edge.shallowSize;
|
| + int get retainedSize => edge.retainedSize;
|
| +
|
| + HeapSnapshotClassInbound(this.vertex, this.edge);
|
| +}
|
| +
|
| +class HeapSnapshotClassOutbound implements M.HeapSnapshotClassOutbound {
|
| + final MergedVertex vertex;
|
| + final MergedEdge edge;
|
| + S.Class get target => edge.sourceVertex != vertex ? edge.sourceVertex.clazz
|
| + : edge.targetVertex.clazz;
|
| + int get count => edge.count;
|
| + int get shallowSize => edge.shallowSize;
|
| + int get retainedSize => edge.retainedSize;
|
| +
|
| + HeapSnapshotClassOutbound(this.vertex, this.edge);
|
| +}
|
|
|