| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library object_graph; | 5 library object_graph; |
| 6 | 6 |
| 7 import 'dart:async'; |
| 8 import 'dart:collection'; |
| 7 import 'dart:typed_data'; | 9 import 'dart:typed_data'; |
| 8 | 10 |
| 9 import 'dominator_tree.dart'; | 11 import 'package:logging/logging.dart'; |
| 10 | 12 |
| 11 // Port of dart::ReadStream from vm/datastream.h. | 13 // Port of dart::ReadStream from vm/datastream.h. |
| 12 class ReadStream { | 14 class _ReadStream { |
| 13 int _cur = 0; | 15 int position = 0; |
| 14 final ByteData _data; | 16 int _size = 0; |
| 15 | 17 final List<ByteData> _chunks; |
| 16 ReadStream(this._data); | 18 |
| 17 | 19 _ReadStream(this._chunks) { |
| 18 int get pendingBytes => _data.lengthInBytes - _cur; | 20 int n = _chunks.length; |
| 19 | 21 for (var i = 0; i < n; i++) { |
| 22 var chunk = _chunks[i]; |
| 23 if (i + 1 != n) { |
| 24 assert(chunk.lengthInBytes == (1 << 20)); |
| 25 } |
| 26 _size += chunk.lengthInBytes; |
| 27 } |
| 28 } |
| 29 |
| 30 int get pendingBytes => _size - position; |
| 31 |
| 32 int getUint8(i) { |
| 33 return _chunks[i >> 20].getUint8(i & 0xFFFFF); |
| 34 } |
| 35 |
| 20 int readUnsigned() { | 36 int readUnsigned() { |
| 21 int result = 0; | 37 int result = 0; |
| 22 int shift = 0; | 38 int shift = 0; |
| 23 while (_data.getUint8(_cur) <= maxUnsignedDataPerByte) { | 39 while (getUint8(position) <= maxUnsignedDataPerByte) { |
| 24 result |= _data.getUint8(_cur) << shift; | 40 result |= getUint8(position) << shift; |
| 25 shift += dataBitsPerByte; | 41 shift += dataBitsPerByte; |
| 26 ++_cur; | 42 position++; |
| 27 } | 43 } |
| 28 result |= (_data.getUint8(_cur) & byteMask) << shift; | 44 result |= (getUint8(position) & byteMask) << shift; |
| 29 ++_cur; | 45 position++; |
| 30 return result; | 46 return result; |
| 31 } | 47 } |
| 32 | 48 |
| 33 static const int dataBitsPerByte = 7; | 49 static const int dataBitsPerByte = 7; |
| 34 static const int byteMask = (1 << dataBitsPerByte) - 1; | 50 static const int byteMask = (1 << dataBitsPerByte) - 1; |
| 35 static const int maxUnsignedDataPerByte = byteMask; | 51 static const int maxUnsignedDataPerByte = byteMask; |
| 36 } | 52 } |
| 37 | 53 |
| 38 class ObjectVertex { | 54 class ObjectVertex { |
| 39 // Never null. The isolate root has id 0. | 55 // 0 represents invalid/uninitialized, 1 is the root. |
| 40 final int _id; | 56 final int _id; |
| 41 bool get isRoot => _id == 0; | 57 final ObjectGraph _graph; |
| 42 // TODO(koda): Include units in object graph metadata. | 58 |
| 43 int addressForWordSize(int bytesPerWord) => _id * 2 * bytesPerWord; | 59 ObjectVertex._(this._id, this._graph); |
| 44 // null for VM-heap objects. | 60 |
| 45 int _shallowSize; | 61 bool operator ==(other) => _id == other._id && _graph == other._graph; |
| 46 int get shallowSize => _shallowSize; | 62 int get hashCode => _id; |
| 47 int _retainedSize; | 63 |
| 48 int get retainedSize => _retainedSize; | 64 int get retainedSize => _graph._retainedSizes[_id]; |
| 49 // null for VM-heap objects. | 65 ObjectVertex get dominator => new ObjectVertex._(_graph._doms[_id], _graph); |
| 50 int _classId; | 66 |
| 51 int get classId => _classId; | 67 int get shallowSize { |
| 52 final List<ObjectVertex> succ = new List<ObjectVertex>(); | 68 var stream = new _ReadStream(_graph._chunks); |
| 53 ObjectVertex(this._id) : _retainedSize = 0; | 69 stream.position = _graph._positions[_id]; |
| 54 String toString() => '$_id,$_shallowSize,$succ'; | 70 stream.readUnsigned(); // addr |
| 71 return stream.readUnsigned(); // shallowSize |
| 72 } |
| 73 |
| 74 int get vmCid { |
| 75 var stream = new _ReadStream(_graph._chunks); |
| 76 stream.position = _graph._positions[_id]; |
| 77 stream.readUnsigned(); // addr |
| 78 stream.readUnsigned(); // shallowSize |
| 79 return stream.readUnsigned(); // cid |
| 80 } |
| 81 |
| 82 get successors => new _SuccessorsIterable(_graph, _id); |
| 83 |
| 84 int get address { |
| 85 // Note that everywhere else in this file, "address" really means an address |
| 86 // scaled down by kObjectAlignment. They were scaled down so they would fit |
| 87 // into Smis on the client. |
| 88 var stream = new _ReadStream(_graph._chunks); |
| 89 stream.position = _graph._positions[_id]; |
| 90 var scaledAddr = stream.readUnsigned(); |
| 91 return scaledAddr * _graph._kObjectAlignment; |
| 92 } |
| 93 |
| 94 List<ObjectVertex> dominatorTreeChildren() { |
| 95 var N = _graph._N; |
| 96 var doms = _graph._doms; |
| 97 |
| 98 var parentId = _id; |
| 99 var domChildren = []; |
| 100 |
| 101 for (var childId = 1; childId <= N; childId++) { |
| 102 if (doms[childId] == parentId) { |
| 103 domChildren.add(new ObjectVertex._(childId, _graph)); |
| 104 } |
| 105 } |
| 106 |
| 107 return domChildren; |
| 108 } |
| 55 } | 109 } |
| 56 | 110 |
| 57 // See implementation of ObjectGraph::Serialize for format. | 111 class _SuccessorsIterable extends IterableBase<ObjectVertex> { |
| 58 class ObjectGraph { | 112 final ObjectGraph _graph; |
| 59 final Map<int, ObjectVertex> _idToVertex = new Map<int, ObjectVertex>(); | 113 final int _id; |
| 60 | 114 |
| 61 ObjectVertex _asVertex(int id) { | 115 _SuccessorsIterable(this._graph, this._id); |
| 62 return _idToVertex.putIfAbsent(id, () => new ObjectVertex(id)); | 116 |
| 117 Iterator<ObjectVertex> get iterator => new _SuccessorsIterator(_graph, _id); |
| 118 } |
| 119 |
| 120 class _SuccessorsIterator implements Iterator<ObjectVertex> { |
| 121 final ObjectGraph _graph; |
| 122 _ReadStream _stream; |
| 123 |
| 124 ObjectVertex current; |
| 125 |
| 126 _SuccessorsIterator(this._graph, int id) { |
| 127 _stream = new _ReadStream(this._graph._chunks); |
| 128 _stream.position = _graph._positions[id]; |
| 129 _stream.readUnsigned(); // addr |
| 130 _stream.readUnsigned(); // shallowSize |
| 131 var cid = _stream.readUnsigned(); |
| 132 assert((cid & ~0xFFFF) == 0); // Sanity check: cid's are 16 bit. |
| 63 } | 133 } |
| 64 | 134 |
| 65 void _addFrom(ReadStream stream) { | 135 bool moveNext() { |
| 66 ObjectVertex obj = _asVertex(stream.readUnsigned()); | 136 while (true) { |
| 67 obj._shallowSize = stream.readUnsigned(); | 137 var nextAddr = _stream.readUnsigned(); |
| 68 obj._classId = stream.readUnsigned(); | 138 if (nextAddr == 0) return false; |
| 69 int last = stream.readUnsigned(); | 139 var nextId = _graph._addrToId[nextAddr]; |
| 70 while (last != 0) { | 140 if (nextId == null) continue; // Reference to VM isolate's heap. |
| 71 obj.succ.add(_asVertex(last)); | 141 current = new ObjectVertex._(nextId, _graph); |
| 72 last = stream.readUnsigned(); | 142 return true; |
| 73 } | 143 } |
| 74 } | 144 } |
| 145 } |
| 75 | 146 |
| 76 ObjectGraph(ReadStream reader) { | 147 class _VerticesIterable extends IterableBase<ObjectVertex> { |
| 77 while (reader.pendingBytes > 0) { | 148 final ObjectGraph _graph; |
| 78 _addFrom(reader); | 149 |
| 79 } | 150 _VerticesIterable(this._graph); |
| 80 _computeRetainedSizes(); | 151 |
| 81 _mostRetained = new List<ObjectVertex>.from( | 152 Iterator<ObjectVertex> get iterator => new _VerticesIterator(_graph); |
| 82 vertices.where((u) => !u.isRoot)); | 153 } |
| 154 |
| 155 class _VerticesIterator implements Iterator<ObjectVertex> { |
| 156 final ObjectGraph _graph; |
| 157 |
| 158 int _nextId = 0; |
| 159 ObjectVertex current; |
| 160 |
| 161 _VerticesIterator(this._graph); |
| 162 |
| 163 bool moveNext() { |
| 164 if (_nextId == _graph._N) return false; |
| 165 current = new ObjectVertex._(_nextId++, _graph); |
| 166 return true; |
| 167 } |
| 168 } |
| 169 |
| 170 class ObjectGraph { |
| 171 ObjectGraph(List<ByteData> chunks, int nodeCount) |
| 172 : this._chunks = chunks |
| 173 , this._N = nodeCount; |
| 174 |
| 175 int get size => _size; |
| 176 int get vertexCount => _N; |
| 177 int get edgeCount => _E; |
| 178 |
| 179 ObjectVertex get root => new ObjectVertex._(1, this); |
| 180 Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); |
| 181 |
| 182 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { |
| 183 List<ObjectVertex> _mostRetained = |
| 184 new List<ObjectVertex>.from(vertices.where((u) => !u.isRoot)); |
| 83 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); | 185 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); |
| 84 } | |
| 85 | 186 |
| 86 Iterable<ObjectVertex> get vertices => _idToVertex.values; | |
| 87 List<ObjectVertex> _mostRetained; | |
| 88 | |
| 89 ObjectVertex get root => _asVertex(0); | |
| 90 | |
| 91 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { | |
| 92 var result = _mostRetained; | 187 var result = _mostRetained; |
| 93 if (classId != null) { | 188 if (classId != null) { |
| 94 result = result.where((u) => u.classId == classId); | 189 result = result.where((u) => u.classId == classId); |
| 95 } | 190 } |
| 96 if (limit != null) { | 191 if (limit != null) { |
| 97 result = result.take(limit); | 192 result = result.take(limit); |
| 98 } | 193 } |
| 99 return result; | 194 return result; |
| 100 } | 195 } |
| 101 | 196 |
| 102 void _computeRetainedSizes() { | 197 Future process(statusReporter) async { |
| 103 // The retained size for an object is the sum of the shallow sizes of | 198 // We build futures here instead of marking the steps as async to avoid the |
| 104 // all its descendants in the dominator tree (including itself). | 199 // heavy lifting being inside a transformed method. |
| 105 var d = new Dominator(); | 200 |
| 106 for (ObjectVertex u in vertices) { | 201 statusReporter.add("Finding node positions..."); |
| 107 if (u.shallowSize != null) { | 202 await new Future(() => _buildPositions()); |
| 108 u._retainedSize = u.shallowSize; | 203 |
| 109 d.addEdges(u, u.succ.where((ObjectVertex v) => v.shallowSize != null)); | 204 statusReporter.add("Finding post order..."); |
| 110 } | 205 await new Future(() => _buildPostOrder()); |
| 111 } | 206 |
| 112 d.computeDominatorTree(root); | 207 statusReporter.add("Finding predecessors..."); |
| 113 // Compute all retained sizes "bottom up", starting from the leaves. | 208 await new Future(() => _buildPredecessors()); |
| 114 // Keep track of number of remaining children of each vertex. | 209 |
| 115 var degree = new Map<ObjectVertex, int>(); | 210 statusReporter.add("Finding dominators..."); |
| 116 for (ObjectVertex u in vertices) { | 211 await new Future(() => _buildDominators()); |
| 117 var v = d.dominator(u); | 212 |
| 118 if (v != null) { | 213 _firstPreds = null; |
| 119 degree[v] = 1 + degree.putIfAbsent(v, () => 0); | 214 _preds = null; |
| 120 } | 215 _postOrderIndices = null; |
| 121 } | 216 |
| 122 var leaves = new List<ObjectVertex>(); | 217 statusReporter.add("Finding retained sizes..."); |
| 123 for (ObjectVertex u in vertices) { | 218 await new Future(() => _calculateRetainedSizes()); |
| 124 if (!degree.containsKey(u)) { | 219 |
| 125 leaves.add(u); | 220 _postOrderOrdinals = null; |
| 126 } | 221 |
| 127 } | 222 statusReporter.add("Loaded"); |
| 128 while (!leaves.isEmpty) { | 223 return this; |
| 129 var v = leaves.removeLast(); | 224 } |
| 130 var u = d.dominator(v); | 225 |
| 131 if (u == null) continue; | 226 final List<ByteData> _chunks; |
| 132 u._retainedSize += v._retainedSize; | 227 |
| 133 if (--degree[u] == 0) { | 228 int _kObjectAlignment; |
| 134 leaves.add(u); | 229 int _N; |
| 135 } | 230 int _E; |
| 136 } | 231 int _size; |
| 137 } | 232 |
| 138 } | 233 Map<int, int> _addrToId = new Map<int, int>(); |
| 234 |
| 235 // Indexed by node id, with id 0 representing invalid/uninitialized. |
| 236 Uint32List _positions; // Position of the node in the snapshot. |
| 237 Uint32List _postOrderOrdinals; // post-order index -> id |
| 238 Uint32List _postOrderIndices; // id -> post-order index |
| 239 Uint32List _firstPreds; // Offset into preds. |
| 240 Uint32List _preds; |
| 241 Uint32List _doms; |
| 242 Uint32List _retainedSizes; |
| 243 |
| 244 void _buildPositions() { |
| 245 var N = _N; |
| 246 var addrToId = _addrToId; |
| 247 |
| 248 var positions = new Uint32List(N + 1); |
| 249 |
| 250 var stream = new _ReadStream(_chunks); |
| 251 _kObjectAlignment = stream.readUnsigned(); |
| 252 |
| 253 var id = 1; |
| 254 while (stream.pendingBytes > 0) { |
| 255 positions[id] = stream.position; |
| 256 var addr = stream.readUnsigned(); |
| 257 var shallowSize = stream.readUnsigned(); |
| 258 var cid = stream.readUnsigned(); |
| 259 addrToId[addr] = id; |
| 260 |
| 261 var succAddr = stream.readUnsigned(); |
| 262 while (succAddr != 0) { |
| 263 succAddr = stream.readUnsigned(); |
| 264 } |
| 265 id++; |
| 266 } |
| 267 assert(id == (N + 1)); |
| 268 |
| 269 var root = addrToId[0]; |
| 270 assert(root == 1); |
| 271 |
| 272 _positions = positions; |
| 273 } |
| 274 |
| 275 void _buildPostOrder() { |
| 276 var N = _N; |
| 277 var E = 0; |
| 278 var addrToId = _addrToId; |
| 279 var positions = _positions; |
| 280 |
| 281 var postOrderOrdinals = new Uint32List(N); |
| 282 var postOrderIndices = new Uint32List(N + 1); |
| 283 var stackNodes = new Uint32List(N); |
| 284 var stackCurrentEdgePos = new Uint32List(N); |
| 285 |
| 286 var visited = new Uint8List(N + 1); |
| 287 var postOrderIndex = 0; |
| 288 var stackTop = 0; |
| 289 var root = 1; |
| 290 |
| 291 stackNodes[0] = root; |
| 292 |
| 293 var stream = new _ReadStream(_chunks); |
| 294 stream.position = positions[root]; |
| 295 stream.readUnsigned(); // addr |
| 296 stream.readUnsigned(); // shallowSize |
| 297 stream.readUnsigned(); // cid |
| 298 stackCurrentEdgePos[0] = stream.position; |
| 299 visited[root] = 1; |
| 300 |
| 301 while (stackTop >= 0) { |
| 302 var n = stackNodes[stackTop]; |
| 303 var edgePos = stackCurrentEdgePos[stackTop]; |
| 304 |
| 305 stream.position = edgePos; |
| 306 var childAddr = stream.readUnsigned(); |
| 307 if (childAddr != 0) { |
| 308 stackCurrentEdgePos[stackTop] = stream.position; |
| 309 var childId = addrToId[childAddr]; |
| 310 if (childId == null) continue; // Reference to VM isolate's heap. |
| 311 E++; |
| 312 if (visited[childId] == 1) continue; |
| 313 |
| 314 stackTop++; |
| 315 stackNodes[stackTop] = childId; |
| 316 |
| 317 stream.position = positions[childId]; |
| 318 stream.readUnsigned(); // addr |
| 319 stream.readUnsigned(); // shallowSize |
| 320 stream.readUnsigned(); // cid |
| 321 stackCurrentEdgePos[stackTop] = stream.position; // i.e., first edge |
| 322 visited[childId] = 1; |
| 323 } else { |
| 324 // Done with all children. |
| 325 postOrderIndices[n] = postOrderIndex; |
| 326 postOrderOrdinals[postOrderIndex++] = n; |
| 327 stackTop--; |
| 328 } |
| 329 } |
| 330 |
| 331 assert(postOrderIndex == N); |
| 332 assert(postOrderOrdinals[N - 1] == root); |
| 333 |
| 334 _postOrderOrdinals = postOrderOrdinals; |
| 335 _postOrderIndices = postOrderIndices; |
| 336 _E = E; |
| 337 } |
| 338 |
| 339 void _buildPredecessors() { |
| 340 var N = _N; |
| 341 var E = _E; |
| 342 var addrToId = _addrToId; |
| 343 var positions = _positions; |
| 344 |
| 345 // This is first filled with the predecessor counts, then reused to hold the |
| 346 // offset to the first predecessor (see alias below). |
| 347 // + 1 because 0 is a sentinel |
| 348 // + 1 so the number of predecessors can be found from the difference with |
| 349 // the next node's offset. |
| 350 var numPreds = new Uint32List(N + 2); |
| 351 var preds = new Uint32List(E); |
| 352 |
| 353 // Count predecessors of each node. |
| 354 var stream = new _ReadStream(_chunks); |
| 355 for (var i = 1; i <= N; i++) { |
| 356 stream.position = positions[i]; |
| 357 stream.readUnsigned(); // addr |
| 358 stream.readUnsigned(); // shallowSize |
| 359 stream.readUnsigned(); // cid |
| 360 var succAddr = stream.readUnsigned(); |
| 361 while (succAddr != 0) { |
| 362 var succId = addrToId[succAddr]; |
| 363 if (succId != null) { |
| 364 numPreds[succId]++; |
| 365 } else { |
| 366 // Reference to VM isolate's heap. |
| 367 } |
| 368 succAddr = stream.readUnsigned(); |
| 369 } |
| 370 } |
| 371 |
| 372 // Assign indices into predecessors array. |
| 373 var firstPreds = numPreds; // Alias. |
| 374 var nextPreds = new Uint32List(N + 1); |
| 375 var predIndex = 0; |
| 376 for (var i = 1; i <= N; i++) { |
| 377 var thisPredIndex = predIndex; |
| 378 predIndex += numPreds[i]; |
| 379 firstPreds[i] = thisPredIndex; |
| 380 nextPreds[i] = thisPredIndex; |
| 381 } |
| 382 assert(predIndex == E); |
| 383 firstPreds[N + 1] = E; // Extra entry for cheap boundary detection. |
| 384 |
| 385 // Fill predecessors array. |
| 386 for (var i = 1; i <= N; i++) { |
| 387 stream.position = positions[i]; |
| 388 stream.readUnsigned(); // addr |
| 389 stream.readUnsigned(); // shallowSize |
| 390 stream.readUnsigned(); // cid |
| 391 var succAddr = stream.readUnsigned(); |
| 392 while (succAddr != 0) { |
| 393 var succId = addrToId[succAddr]; |
| 394 if (succId != null) { |
| 395 var predIndex = nextPreds[succId]++; |
| 396 preds[predIndex] = i; |
| 397 } else { |
| 398 // Reference to VM isolate's heap. |
| 399 } |
| 400 succAddr = stream.readUnsigned(); |
| 401 } |
| 402 } |
| 403 |
| 404 _firstPreds = firstPreds; |
| 405 _preds = preds; |
| 406 } |
| 407 |
| 408 // "A Simple, Fast Dominance Algorithm" |
| 409 // Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy |
| 410 void _buildDominators() { |
| 411 var N = _N; |
| 412 var E = _E; |
| 413 var addrToId = _addrToId; |
| 414 var postOrder = _postOrderOrdinals; |
| 415 var postOrderIndex = _postOrderIndices; |
| 416 var firstPreds = _firstPreds; |
| 417 var preds = _preds; |
| 418 |
| 419 var root = 1; |
| 420 var rootPostOrderIndex = postOrderIndex[root]; |
| 421 var domByPOI = new Uint32List(N + 1); |
| 422 |
| 423 domByPOI[rootPostOrderIndex] = rootPostOrderIndex; |
| 424 |
| 425 var iteration = 0; |
| 426 var changed = true; |
| 427 while (changed) { |
| 428 changed = false; |
| 429 Logger.root.info("Find dominators iteration $iteration"); |
| 430 iteration++; // dart2js heaps typically converge in 10 iterations. |
| 431 |
| 432 // Visit the nodes, except the root, in reverse post order (top down). |
| 433 for (var curPostOrderIndex = rootPostOrderIndex - 1; |
| 434 curPostOrderIndex > 1; |
| 435 curPostOrderIndex--) { |
| 436 if (domByPOI[curPostOrderIndex] == rootPostOrderIndex) |
| 437 continue; |
| 438 |
| 439 var nodeOrdinal = postOrder[curPostOrderIndex]; |
| 440 var newDomIndex = 0; // 0 = undefined |
| 441 |
| 442 // Intersect the DOM sets of the node's precedessors. |
| 443 var beginPredIndex = firstPreds[nodeOrdinal]; |
| 444 var endPredIndex = firstPreds[nodeOrdinal + 1]; |
| 445 for (var predIndex = beginPredIndex; |
| 446 predIndex < endPredIndex; |
| 447 predIndex++) { |
| 448 var predOrdinal = preds[predIndex]; |
| 449 var predPostOrderIndex = postOrderIndex[predOrdinal]; |
| 450 if (domByPOI[predPostOrderIndex] != 0) { |
| 451 if (newDomIndex == 0) { |
| 452 newDomIndex = predPostOrderIndex; |
| 453 } else { |
| 454 // Note this two finger algorithm to find the DOM intersection |
| 455 // relies on comparing nodes by their post order index. |
| 456 while (predPostOrderIndex != newDomIndex) { |
| 457 while(predPostOrderIndex < newDomIndex) |
| 458 predPostOrderIndex = domByPOI[predPostOrderIndex]; |
| 459 while (newDomIndex < predPostOrderIndex) |
| 460 newDomIndex = domByPOI[newDomIndex]; |
| 461 } |
| 462 } |
| 463 if (newDomIndex == rootPostOrderIndex) { |
| 464 break; |
| 465 } |
| 466 } |
| 467 } |
| 468 if (newDomIndex != 0 && domByPOI[curPostOrderIndex] != newDomIndex) { |
| 469 domByPOI[curPostOrderIndex] = newDomIndex; |
| 470 changed = true; |
| 471 } |
| 472 } |
| 473 } |
| 474 |
| 475 // Reindex doms by id instead of post order index so we can throw away |
| 476 // the post order arrays. |
| 477 var domById = new Uint32List(N + 1); |
| 478 for (var id = 1; id <= N; id++) { |
| 479 domById[id] = postOrder[domByPOI[postOrderIndex[id]]]; |
| 480 } |
| 481 |
| 482 domById[root] = 0; |
| 483 |
| 484 _doms = domById; |
| 485 } |
| 486 |
| 487 void _calculateRetainedSizes() { |
| 488 var N = _N; |
| 489 var E = _E; |
| 490 |
| 491 var size = 0; |
| 492 var positions = _positions; |
| 493 var postOrderOrdinals = _postOrderOrdinals; |
| 494 var doms = _doms; |
| 495 var retainedSizes = new Uint32List(N + 1); |
| 496 |
| 497 // Start with retained size as shallow size. |
| 498 var reader = new _ReadStream(_chunks); |
| 499 for (var i = 1; i <= N; i++) { |
| 500 reader.position = positions[i]; |
| 501 reader.readUnsigned(); // addr |
| 502 var shallowSize = reader.readUnsigned(); |
| 503 retainedSizes[i] = shallowSize; |
| 504 size += shallowSize; |
| 505 } |
| 506 |
| 507 // In post order (bottom up), add retained size to dominator's retained |
| 508 // size, skipping root. |
| 509 for (var o = 0; o < (N - 1); o++) { |
| 510 var i = postOrderOrdinals[o]; |
| 511 assert(i != 1); |
| 512 retainedSizes[doms[i]] += retainedSizes[i]; |
| 513 } |
| 514 |
| 515 _retainedSizes = retainedSizes; |
| 516 _size = size; |
| 517 } |
| 518 } |
| OLD | NEW |