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); |
+} |