Index: runtime/observatory/lib/object_graph.dart |
diff --git a/runtime/observatory/lib/object_graph.dart b/runtime/observatory/lib/object_graph.dart |
index c2b4169e7d8cbf6ba61e2647b57ec074b0eb8fdd..f070165ecf54f18ba043b122d5bc1a534dc2c332 100644 |
--- a/runtime/observatory/lib/object_graph.dart |
+++ b/runtime/observatory/lib/object_graph.dart |
@@ -214,14 +214,19 @@ class ReadStream { |
static const int maxUnsignedDataPerByte = byteMask; |
} |
+// Node indices for the root and sentinel nodes. Note that using 0 as the |
+// sentinel means a newly allocated typed array comes initialized with all |
+// elements as the sentinel. |
+const ROOT = 1; |
+const SENTINEL = 0; |
+ |
class ObjectVertex { |
- // 0 represents invalid/uninitialized, 1 is the root. |
final int _id; |
final ObjectGraph _graph; |
ObjectVertex._(this._id, this._graph); |
- bool get isRoot => _id == 1; |
+ bool get isRoot => ROOT == _id; |
bool operator ==(other) => _id == other._id && _graph == other._graph; |
int get hashCode => _id; |
@@ -272,7 +277,7 @@ class ObjectVertex { |
var parentId = _id; |
var domChildren = []; |
- for (var childId = 1; childId <= N; childId++) { |
+ for (var childId = ROOT; childId <= N; childId++) { |
if (doms[childId] == parentId) { |
domChildren.add(new ObjectVertex._(childId, _graph)); |
} |
@@ -282,6 +287,81 @@ class ObjectVertex { |
} |
} |
+// A node in the dominator tree where siblings with the same class are merged. |
+// That is, a set of objects with the same cid whose parent chains in the |
+// dominator tree have the same cids at each level. [id_] is the representative |
+// object of this set. The other members of the set are found by walking the |
+// mergedDomNext links until finding the sentinel node or a node with a |
+// different class. |
+class MergedObjectVertex { |
+ final int _id; |
+ final ObjectGraph _graph; |
+ |
+ MergedObjectVertex._(this._id, this._graph); |
+ |
+ bool get isRoot => ROOT == _id; |
+ |
+ bool operator ==(other) => _id == other._id && _graph == other._graph; |
+ int get hashCode => _id; |
+ |
+ int get vmCid => _graph._cids[_id]; |
+ |
+ int get shallowSize { |
+ var cids = _graph._cids; |
+ var size = 0; |
+ var sibling = _id; |
+ while (sibling != SENTINEL && |
+ cids[sibling] == cids[_id]) { |
+ size += _graph._shallowSizes[sibling]; |
+ sibling = _graph._mergedDomNext[sibling]; |
+ } |
+ return size; |
+ } |
+ int get retainedSize { |
+ var cids = _graph._cids; |
+ var size = 0; |
+ var sibling = _id; |
+ while (sibling != SENTINEL && |
+ cids[sibling] == cids[_id]) { |
+ size += _graph._retainedSizes[sibling]; |
+ sibling = _graph._mergedDomNext[sibling]; |
+ } |
+ return size; |
+ } |
+ int get instanceCount { |
+ var cids = _graph._cids; |
+ var count = 0; |
+ var sibling = _id; |
+ while (sibling != SENTINEL && |
+ cids[sibling] == cids[_id]) { |
+ count++; |
+ sibling = _graph._mergedDomNext[sibling]; |
+ } |
+ return count; |
+ } |
+ |
+ List<MergedObjectVertex> dominatorTreeChildren() { |
+ var next = _graph._mergedDomNext; |
+ var cids = _graph._cids; |
+ |
+ var domChildren = []; |
+ var prev = SENTINEL; |
+ var child = _graph._mergedDomHead[_id]; |
+ // Walk the list of children and look for the representative objects, i.e. |
+ // the first sibling of each cid. |
+ while (child != SENTINEL) { |
+ if (prev == SENTINEL || |
+ cids[prev] != cids[child]) { |
+ domChildren.add(new MergedObjectVertex._(child, _graph)); |
+ } |
+ prev = child; |
+ child = next[child]; |
+ } |
+ |
+ return domChildren; |
+ } |
+} |
+ |
class _SuccessorsIterable extends IterableBase<ObjectVertex> { |
final ObjectGraph _graph; |
final int _id; |
@@ -345,7 +425,8 @@ class ObjectGraph { |
int get vertexCount => _N; |
int get edgeCount => _E; |
- ObjectVertex get root => new ObjectVertex._(1, this); |
+ ObjectVertex get root => new ObjectVertex._(ROOT, this); |
+ MergedObjectVertex get mergedRoot => new MergedObjectVertex._(ROOT, this); |
Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); |
Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { |
@@ -381,10 +462,10 @@ class ObjectGraph { |
controller.add(["Finding depth-first order...", 30.0]); |
await new Future(() => _dfs()); |
- controller.add(["Finding predecessors...", 45.0]); |
+ controller.add(["Finding predecessors...", 40.0]); |
await new Future(() => _buildPredecessors()); |
- controller.add(["Finding dominators...", 60.0]); |
+ controller.add(["Finding dominators...", 50.0]); |
await new Future(() => _buildDominators()); |
_firstPreds = null; |
@@ -393,12 +474,21 @@ class ObjectGraph { |
_semi = null; |
_parent = null; |
- controller.add(["Finding retained sizes...", 75.0]); |
+ controller.add(["Finding retained sizes...", 60.0]); |
await new Future(() => _calculateRetainedSizes()); |
_vertex = null; |
- controller.add(["Loaded", 100.0]); |
+ controller.add(["Linking dominator tree children...", 70.0]); |
+ await new Future(() => _linkDominatorChildren()); |
+ |
+ controller.add(["Sorting dominator tree children...", 80.0]); |
+ await new Future(() => _sortDominatorChildren()); |
+ |
+ controller.add(["Merging dominator tree siblings...", 90.0]); |
+ await new Future(() => _mergeDominatorSiblings()); |
+ |
+ controller.add(["Processed", 100.0]); |
controller.close(); |
}()); |
return controller.stream; |
@@ -431,6 +521,8 @@ class ObjectGraph { |
// Outputs. |
Uint32List _doms; |
Uint32List _retainedSizes; |
+ Uint32List _mergedDomHead; |
+ Uint32List _mergedDomNext; |
void _remapNodes() { |
var N = _N; |
@@ -446,7 +538,7 @@ class ObjectGraph { |
stream.readUnsigned(); |
_kObjectAlignment = stream.clampedUint32; |
- var id = 1; |
+ var id = ROOT; |
while (stream.pendingBytes > 0) { |
stream.readUnsigned(); // addr |
addrToId.put(stream.high, stream.mid, stream.low, id); |
@@ -466,8 +558,7 @@ class ObjectGraph { |
} |
assert(id == (N + 1)); |
- var root = addrToId.get(0, 0, 0); |
- assert(root == 1); |
+ assert(ROOT == addrToId.get(0, 0, 0)); |
_E = E; |
_addrToId = addrToId; |
@@ -534,11 +625,10 @@ class ObjectGraph { |
var dfsNumber = 0; |
var stackTop = 0; |
- var root = 1; |
// Push root. |
- stackNodes[0] = root; |
- stackCurrentEdgePos[0] = firstSuccs[root]; |
+ stackNodes[0] = ROOT; |
+ stackCurrentEdgePos[0] = firstSuccs[ROOT]; |
while (stackTop >= 0) { |
var v = stackNodes[stackTop]; |
@@ -571,12 +661,12 @@ class ObjectGraph { |
} |
assert(dfsNumber == N); |
- for (var i = 1; i <= N; i++) { |
- assert(semi[i] != 0); |
+ for (var i = ROOT; i <= N; i++) { |
+ assert(semi[i] != SENTINEL); |
} |
- assert(parent[1] == 0); |
- for (var i = 2; i <= N; i++) { |
- assert(parent[i] != 0); |
+ assert(parent[ROOT] == SENTINEL); |
+ for (var i = ROOT + 1; i <= N; i++) { |
+ assert(parent[i] != SENTINEL); |
} |
_vertex = vertex; |
@@ -636,7 +726,7 @@ class ObjectGraph { |
static int _eval(int v, Uint32List ancestor, Uint32List semi, |
Uint32List label, Uint32List stackNode, Uint8List stackState) { |
- if (ancestor[v] == 0) { |
+ if (ancestor[v] == SENTINEL) { |
return label[v]; |
} else { |
{ |
@@ -719,7 +809,6 @@ class ObjectGraph { |
var firstPreds = _firstPreds; |
var preds = _preds; |
- var root = 1; |
var dom = new Uint32List(N + 1); |
var ancestor = new Uint32List(N + 1); |
@@ -738,7 +827,7 @@ class ObjectGraph { |
for (var i = N; i > 1; i--) { |
var w = vertex[i]; |
- assert(w != root); |
+ assert(w != ROOT); |
// Lengauer & Tarjan Step 2. |
var startPred = firstPreds[w]; |
@@ -771,7 +860,7 @@ class ObjectGraph { |
} |
} |
} |
- for (var i = 1; i <= N; i++) { |
+ for (var i = ROOT; i <= N; i++) { |
assert(buckets[i] == null); |
} |
// Lengauer & Tarjan Step 4. |
@@ -794,7 +883,7 @@ class ObjectGraph { |
var doms = _doms; |
// Sum shallow sizes. |
- for (var i = 1; i < N; i++) { |
+ for (var i = ROOT; i < N; i++) { |
size += shallowSizes[i]; |
} |
@@ -805,11 +894,172 @@ class ObjectGraph { |
// size, skipping root. |
for (var i = N; i > 1; i--) { |
var v = vertex[i]; |
- assert(v != 1); |
+ assert(v != ROOT); |
retainedSizes[doms[i]] += retainedSizes[i]; |
} |
_retainedSizes = retainedSizes; |
_size = size; |
} |
+ |
+ // Build linked lists of the children for each node in the dominator tree. |
+ void _linkDominatorChildren() { |
+ var N = _N; |
+ var doms = _doms; |
+ var head = new Uint32List(N + 1); |
+ var next = new Uint32List(N + 1); |
+ |
+ for (var child = ROOT; child <= N; child++) { |
+ var parent = doms[child]; |
+ next[child] = head[parent]; |
+ head[parent] = child; |
+ } |
+ |
+ _mergedDomHead = head; |
+ _mergedDomNext = next; |
+ } |
+ |
+ // Merge the given lists according to the given key in ascending order. |
+ // Returns the head of the merged list. |
+ static int _mergeSorted(int head1, int head2, |
+ Uint32List next, Uint16List key) { |
+ var head = head1; |
+ var beforeInsert = SENTINEL; |
+ var afterInsert = head1; |
+ var startInsert = head2; |
+ |
+ while (startInsert != SENTINEL) { |
+ while ((afterInsert != SENTINEL) && |
+ (key[afterInsert] <= key[startInsert])) { |
+ beforeInsert = afterInsert; |
+ afterInsert = next[beforeInsert]; |
+ } |
+ |
+ var endInsert = startInsert; |
+ var peek = next[endInsert]; |
+ |
+ while ((peek != SENTINEL) && (key[peek] < key[afterInsert])) { |
+ endInsert = peek; |
+ peek = next[endInsert]; |
+ } |
+ assert(endInsert != SENTINEL); |
+ |
+ if (beforeInsert == SENTINEL) { |
+ head = startInsert; |
+ } else { |
+ next[beforeInsert] = startInsert; |
+ } |
+ next[endInsert] = afterInsert; |
+ |
+ startInsert = peek; |
+ beforeInsert = endInsert; |
+ } |
+ |
+ return head; |
+ } |
+ |
+ void _sortDominatorChildren() { |
+ var N = _N; |
+ var cids = _cids; |
+ var head = _mergedDomHead; |
+ var next = _mergedDomNext; |
+ |
+ // Returns the new head of the sorted list. |
+ int sort(int head) { |
+ if (head == SENTINEL) return SENTINEL; |
+ if (next[head] == SENTINEL) return head; |
+ |
+ // Find the middle of the list. |
+ int head1 = head; |
+ int slow = head; |
+ int fast = head; |
+ while (next[fast] != SENTINEL && |
+ next[next[fast]] != SENTINEL) { |
+ slow = next[slow]; |
+ fast = next[next[fast]]; |
+ } |
+ |
+ // Split the list in half. |
+ int head2 = next[slow]; |
+ next[slow] = SENTINEL; |
+ |
+ // Recursively sort the sublists and merge. |
+ assert(head1 != head2); |
+ int newHead1 = sort(head1); |
+ int newHead2 = sort(head2); |
+ return _mergeSorted(newHead1, newHead2, next, cids); |
+ } |
+ |
+ // Sort all list of dominator tree children by cid. |
+ for (var parent = ROOT; parent <= N; parent++) { |
+ head[parent] = sort(head[parent]); |
+ } |
+ } |
+ |
+ void _mergeDominatorSiblings() { |
+ var N = _N; |
+ var cids = _cids; |
+ var head = _mergedDomHead; |
+ var next = _mergedDomNext; |
+ var workStack = new Uint32List(N); |
+ var workStackTop = 0; |
+ |
+ mergeChildrenAndSort(var parent1, var end) { |
+ assert(parent1 != SENTINEL); |
+ if (next[parent1] == end) return; |
+ |
+ // Find the middle of the list. |
+ int cid = cids[parent1]; |
+ int slow = parent1; |
+ int fast = parent1; |
+ while (next[fast] != end && |
+ next[next[fast]] != end) { |
+ slow = next[slow]; |
+ fast = next[next[fast]]; |
+ } |
+ |
+ int parent2 = next[slow]; |
+ |
+ assert(parent2 != SENTINEL); |
+ assert(parent1 != parent2); |
+ assert(cids[parent1] == cids[parent2]); |
+ |
+ // Recursively sort the sublists. |
+ mergeChildrenAndSort(parent1, parent2); |
+ mergeChildrenAndSort(parent2, end); |
+ |
+ // Merge sorted sublists. |
+ head[parent1] = _mergeSorted(head[parent1], head[parent2], next, cids); |
+ |
+ // Children moved to parent1. |
+ head[parent2] = SENTINEL; |
+ } |
+ |
+ // Push root. |
+ workStack[workStackTop++] = ROOT; |
+ |
+ while (workStackTop > 0) { |
+ var parent = workStack[--workStackTop]; |
+ |
+ var prev = SENTINEL; |
+ var child = head[parent]; |
+ while (child != SENTINEL) { |
+ // Push child. |
+ workStack[workStackTop++] = child; |
+ |
+ // Find next sibling with a different cid. |
+ var after = child; |
+ while (after != SENTINEL && |
+ cids[after] == cids[child]) { |
+ after = next[after]; |
+ } |
+ |
+ // From all the siblings between child and after, take their children, |
+ // merge them and given to child. |
+ mergeChildrenAndSort(child, after); |
+ |
+ child = after; |
+ } |
+ } |
+ } |
} |