Chromium Code Reviews| 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..33a727aea258ff17d8db55190a2bf0fecf903620 100644 |
| --- a/runtime/observatory/lib/object_graph.dart |
| +++ b/runtime/observatory/lib/object_graph.dart |
| @@ -282,6 +282,75 @@ class ObjectVertex { |
| } |
| } |
| + |
| +class MergedObjectVertex { |
|
Cutch
2016/11/14 17:24:13
Doc comment explaining what this represents at a h
rmacnak
2016/11/15 00:44:15
Added comment. This class does *not* implement Obj
|
| + // 0 represents invalid/uninitialized, 1 is the root. |
|
Cutch
2016/11/14 17:24:13
Let's use a constant for 0 where you mean the inva
|
| + final int _id; |
| + final ObjectGraph _graph; |
| + |
| + MergedObjectVertex._(this._id, this._graph); |
| + |
| + bool get isRoot => _id == 1; |
| + |
| + 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 != 0 && |
| + 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 != 0 && |
|
Cutch
2016/11/14 17:24:13
here and elsewhere != invalidNodeIndex
|
| + 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 != 0 && |
| + cids[sibling] == cids[_id]) { |
| + count++; |
| + sibling = _graph._mergedDomNext[sibling]; |
| + } |
| + return count; |
| + } |
| + |
| + List<ObjectVertex> dominatorTreeChildren() { |
| + var next = _graph._mergedDomNext; |
| + var cids = _graph._cids; |
| + |
| + var domChildren = []; |
| + var prev = 0; |
| + var child = _graph._mergedDomHead[_id]; |
| + while (child != 0) { |
|
Cutch
2016/11/14 17:24:13
Scan the list of siblings, creating MergedObjectVe
rmacnak
2016/11/15 00:44:15
Done.
|
| + if (prev == 0 || |
| + 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; |
| @@ -346,6 +415,7 @@ class ObjectGraph { |
| int get edgeCount => _E; |
| ObjectVertex get root => new ObjectVertex._(1, this); |
| + MergedObjectVertex get mergedRoot => new MergedObjectVertex._(1, this); |
| Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); |
| Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { |
| @@ -381,10 +451,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 +463,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 +510,8 @@ class ObjectGraph { |
| // Outputs. |
| Uint32List _doms; |
| Uint32List _retainedSizes; |
| + Uint32List _mergedDomHead; |
| + Uint32List _mergedDomNext; |
| void _remapNodes() { |
| var N = _N; |
| @@ -812,4 +893,150 @@ class ObjectGraph { |
| _retainedSizes = retainedSizes; |
| _size = size; |
| } |
| + |
| + void _linkDominatorChildren() { |
|
Cutch
2016/11/14 17:24:13
// Build a set of linked lists of children dominat
|
| + var N = _N; |
|
Cutch
2016/11/14 17:24:13
Let's start typing local variables ....
rmacnak
2016/11/15 00:44:15
Not while we can still write Dart.
|
| + var doms = _doms; |
| + var head = new Uint32List(N + 1); |
|
Cutch
2016/11/14 17:24:13
We need to start commenting at a high level what i
|
| + var next = new Uint32List(N + 1); |
| + |
| + for (var child = 1; child <= N; child++) { |
| + var parent = doms[child]; |
| + next[child] = head[parent]; |
| + head[parent] = child; |
| + } |
| + |
| + _mergedDomHead = head; |
| + _mergedDomNext = next; |
| + } |
| + |
| + static int _mergeSorted(int head1, int head2, |
| + Uint32List next, Uint16List key) { |
| + var head = head1; |
| + var beforeInsert = 0; |
| + var afterInsert = head1; |
| + var startInsert = head2; |
| + |
| + while (startInsert != 0) { |
| + while ((afterInsert != 0) && |
| + (key[afterInsert] <= key[startInsert])) { |
| + beforeInsert = afterInsert; |
| + afterInsert = next[beforeInsert]; |
| + } |
| + |
| + var endInsert = startInsert; |
| + var peek = next[endInsert]; |
| + |
| + while ((peek != 0) && (key[peek] < key[afterInsert])) { |
| + endInsert = peek; |
| + peek = next[endInsert]; |
| + } |
| + assert(endInsert != 0); |
| + |
| + if (beforeInsert == 0) { |
| + 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; |
| + |
| + int sort(int head) { |
|
Cutch
2016/11/14 17:24:13
/// Returns the first node index in the list after
|
| + if (head == 0) return 0; |
|
Cutch
2016/11/14 17:24:13
... == invalidNodeIndex) {
return invalidNodeInd
|
| + if (next[head] == 0) return head; |
| + |
|
Cutch
2016/11/14 17:24:13
// Determine the mid and end point of the list by
|
| + int head1 = head; |
| + int slow = head; |
| + int fast = head; |
| + while (next[fast] != 0 && |
| + next[next[fast]] != 0) { |
| + slow = next[slow]; |
| + fast = next[next[fast]]; |
| + } |
| + |
| + int head2 = next[slow]; |
|
Cutch
2016/11/14 17:24:13
// Split the list in half.
|
| + next[slow] = 0; |
| + |
| + assert(head1 != head2); |
| + int newHead1 = sort(head1); |
|
Cutch
2016/11/14 17:24:13
Recursively sort each of the sublists
|
| + int newHead2 = sort(head2); |
| + return _mergeSorted(newHead1, newHead2, next, cids); |
|
Cutch
2016/11/14 17:24:13
Merge them
|
| + } |
| + |
| + for (var parent = 1; parent <= N; parent++) { |
|
Cutch
2016/11/14 17:24:13
Sort all of the merged dominator sibling lists so
|
| + 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 != 0); |
| + if (next[parent1] == end) return; |
| + |
| + int cid = cids[parent1]; |
| + int slow = parent1; |
| + int fast = parent1; |
| + while (next[fast] != end && |
| + next[next[fast]] != end) { |
|
Cutch
2016/11/14 17:24:13
this should be factored out and reused with the co
|
| + slow = next[slow]; |
| + fast = next[next[fast]]; |
| + } |
| + |
| + int parent2 = next[slow]; |
| + |
| + assert(parent2 != 0); |
| + assert(parent1 != parent2); |
| + assert(cids[parent1] == cids[parent2]); |
| + |
| + mergeChildrenAndSort(parent1, parent2); |
| + mergeChildrenAndSort(parent2, end); |
| + |
| + head[parent1] = _mergeSorted(head[parent1], head[parent2], next, cids); |
| + head[parent2] = 0; |
|
Cutch
2016/11/14 17:24:12
// We have merged the siblings of parent2 into par
|
| + } |
| + |
| + // Push root. |
| + workStack[workStackTop++] = 1; |
| + |
| + while (workStackTop > 0) { |
| + var parent = workStack[--workStackTop]; |
| + |
| + var prev = 0; |
| + var child = head[parent]; |
| + while (child != 0) { |
| + // Push child. |
| + workStack[workStackTop++] = child; |
| + |
| + // Next sibling with a different cid. |
| + var after = child; |
| + while (after != 0 && |
| + cids[after] == cids[child]) { |
| + after = next[after]; |
| + } |
| + |
| + mergeChildrenAndSort(child, after); |
|
Cutch
2016/11/14 17:24:13
// child is the first node with cid A and after is
|
| + |
| + child = after; |
| + } |
| + } |
| + } |
| } |