Index: runtime/observatory/lib/object_graph.dart |
diff --git a/runtime/observatory/lib/object_graph.dart b/runtime/observatory/lib/object_graph.dart |
index c25db8ad27e043b4d3ecf5ec98f26d15013c9fdb..65fb824e7f2c28b33b98ea98c110b2f3c6e89b73 100644 |
--- a/runtime/observatory/lib/object_graph.dart |
+++ b/runtime/observatory/lib/object_graph.dart |
@@ -444,6 +444,9 @@ class ObjectGraph { |
MergedObjectVertex get mergedRoot => new MergedObjectVertex._(ROOT, this); |
Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); |
+ int get numCids => _numCids; |
+ int getOwnedByCid(int cid) => _ownedSizesByCid[cid]; |
+ |
Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { |
List<ObjectVertex> _mostRetained = |
new List<ObjectVertex>.from(vertices.where((u) => !u.isRoot)); |
@@ -468,27 +471,30 @@ class ObjectGraph { |
controller.add(["Remapping $_N objects...", 0.0]); |
await new Future(() => _remapNodes()); |
- controller.add(["Remapping $_E references...", 15.0]); |
+ controller.add(["Remapping $_E references...", 10.0]); |
await new Future(() => _remapEdges()); |
_addrToId = null; |
_chunks = null; |
- controller.add(["Finding depth-first order...", 30.0]); |
+ controller.add(["Finding depth-first order...", 20.0]); |
await new Future(() => _dfs()); |
- controller.add(["Finding predecessors...", 40.0]); |
+ controller.add(["Finding predecessors...", 30.0]); |
await new Future(() => _buildPredecessors()); |
- controller.add(["Finding dominators...", 50.0]); |
+ controller.add(["Finding dominators...", 40.0]); |
await new Future(() => _buildDominators()); |
- _firstPreds = null; |
- _preds = null; |
- |
_semi = null; |
_parent = null; |
+ controller.add(["Finding in-degree(1) groups...", 50.0]); |
+ await new Future(() => _buildOwnedSizes()); |
+ |
+ _firstPreds = null; |
+ _preds = null; |
+ |
controller.add(["Finding retained sizes...", 60.0]); |
await new Future(() => _calculateRetainedSizes()); |
@@ -513,6 +519,8 @@ class ObjectGraph { |
int _kObjectAlignment; |
int _kStackCid; |
+ int _kFieldCid; |
+ int _numCids; |
int _N; // Objects in the snapshot. |
int _Nconnected; // Objects reachable from root. |
int _E; // References in the snapshot. |
@@ -542,6 +550,7 @@ class ObjectGraph { |
Uint32List _retainedSizes; |
Uint32List _mergedDomHead; |
Uint32List _mergedDomNext; |
+ Uint32List _ownedSizesByCid; |
void _remapNodes() { |
var N = _N; |
@@ -559,6 +568,10 @@ class ObjectGraph { |
_kObjectAlignment = stream.clampedUint32; |
stream.readUnsigned(); |
_kStackCid = stream.clampedUint32; |
+ stream.readUnsigned(); |
+ _kFieldCid = stream.clampedUint32; |
+ stream.readUnsigned(); |
+ _numCids = stream.clampedUint32; |
var id = ROOT; |
while (id <= N) { |
@@ -613,6 +626,8 @@ class ObjectGraph { |
var stream = new ReadStream(_chunks); |
stream.skipUnsigned(); // kObjectAlignment |
stream.skipUnsigned(); // kStackCid |
+ stream.skipUnsigned(); // kFieldCid |
+ stream.skipUnsigned(); // numCids |
var id = 1, edge = 0; |
while (id <= N) { |
@@ -791,6 +806,75 @@ class ObjectGraph { |
_preds = preds; |
} |
+ // Fold the size of any object with in-degree(1) into its parent. |
+ // Requires the DFS numbering and predecessor lists. |
+ void _buildOwnedSizes() { |
+ var N = _N; |
+ var Nconnected = _Nconnected; |
+ var kStackCid = _kStackCid; |
+ var kFieldCid = _kFieldCid; |
+ |
+ var cids = _cids; |
+ var shallowSizes = _shallowSizes; |
+ var externalSizes = _externalSizes; |
+ var vertex = _vertex; |
+ var firstPreds = _firstPreds; |
+ var preds = _preds; |
+ |
+ var ownedSizes = new Uint32List(N + 1); |
+ for (var i = 1; i <= Nconnected; i++) { |
+ var v = vertex[i]; |
+ ownedSizes[v] = shallowSizes[v] + externalSizes[v]; |
+ assert((ownedSizes[v] != 0) || cids[v] == kStackCid || v == ROOT); |
+ } |
+ |
+ for (var i = Nconnected; i > 1; i--) { |
+ var w = vertex[i]; |
+ assert(w != ROOT); |
+ |
+ var onlyPred = SENTINEL; |
+ |
+ var startPred = firstPreds[w]; |
+ var limitPred = firstPreds[w + 1]; |
+ for (var predIndex = startPred; predIndex < limitPred; predIndex++) { |
+ var v = preds[predIndex]; |
+ if (v == w) { |
+ // Ignore self-predecessor. |
+ } else if (onlyPred == SENTINEL) { |
+ onlyPred = v; |
+ } else if (onlyPred == v) { |
+ // Repeated predecessor. |
+ } else { |
+ // Multiple-predecessors. |
+ onlyPred = SENTINEL; |
+ break; |
+ } |
+ } |
+ |
+ // If this object has a single precessor which is not a Field, Stack or |
+ // the root, blame its size against the precessor. |
+ if ((onlyPred != SENTINEL) && |
+ (onlyPred != ROOT) && |
+ (cids[onlyPred] != kStackCid) && |
+ (cids[onlyPred] != kFieldCid)) { |
+ assert(ownedSizes[w] != 0); |
+ ownedSizes[onlyPred] += ownedSizes[w]; |
+ ownedSizes[w] = 0; |
+ } |
+ } |
+ |
+ // TODO(rmacnak): Maybe keep the per-objects sizes to be able to provide |
+ // examples of large owners for each class. |
+ var ownedSizesByCid = new Uint32List(_numCids); |
+ for (var i = 1; i <= Nconnected; i++) { |
+ var v = vertex[i]; |
+ var cid = cids[v]; |
+ ownedSizesByCid[cid] += ownedSizes[v]; |
+ } |
+ |
+ _ownedSizesByCid = ownedSizesByCid; |
+ } |
+ |
static int _eval(int v, Uint32List ancestor, Uint32List semi, |
Uint32List label, Uint32List stackNode, Uint8List stackState) { |
if (ancestor[v] == SENTINEL) { |