| 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'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 import 'dart:typed_data'; | 9 import 'dart:typed_data'; |
| 10 | 10 |
| 11 class _JenkinsSmiHash { | 11 class _JenkinsSmiHash { |
| 12 static int combine(int hash, int value) { | 12 static int combine(int hash, int value) { |
| 13 hash = 0x1fffffff & (hash + value); | 13 hash = 0x1fffffff & (hash + value); |
| 14 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | 14 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); |
| 15 return hash ^ (hash >> 6); | 15 return hash ^ (hash >> 6); |
| 16 } | 16 } |
| 17 | 17 |
| 18 static int finish(int hash) { | 18 static int finish(int hash) { |
| 19 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | 19 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); |
| 20 hash = hash ^ (hash >> 11); | 20 hash = hash ^ (hash >> 11); |
| 21 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | 21 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); |
| 22 } | 22 } |
| 23 | 23 |
| 24 static int hash3(a, b, c) => finish(combine(combine(combine(0, a), b), c)); | 24 static int hash3(a, b, c) => finish(combine(combine(combine(0, a), b), c)); |
| 25 } | 25 } |
| 26 | 26 |
| 27 // Map<[uint32, uint32, uint32], uint32> | 27 // Map<[uint32, uint32, uint32], uint32> |
| 28 class AddressMapper { | 28 class AddressMapper { |
| 29 final Uint32List _table; | 29 final Uint32List _table; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 61 throw new Exception("Internal error: attempt to overwrite key"); | 61 throw new Exception("Internal error: attempt to overwrite key"); |
| 62 } | 62 } |
| 63 _table[index] = high; | 63 _table[index] = high; |
| 64 _table[index + 1] = mid; | 64 _table[index + 1] = mid; |
| 65 _table[index + 2] = low; | 65 _table[index + 2] = low; |
| 66 _table[index + 3] = id; | 66 _table[index + 3] = id; |
| 67 return id; | 67 return id; |
| 68 } | 68 } |
| 69 } | 69 } |
| 70 | 70 |
| 71 | |
| 72 // Port of dart::ReadStream from vm/datastream.h. | 71 // Port of dart::ReadStream from vm/datastream.h. |
| 73 // | 72 // |
| 74 // The heap snapshot is a series of variable-length unsigned integers. For | 73 // The heap snapshot is a series of variable-length unsigned integers. For |
| 75 // each byte in the stream, the high bit marks the last byte of an integer and | 74 // each byte in the stream, the high bit marks the last byte of an integer and |
| 76 // the low 7 bits are the payload. The payloads are sent in little endian | 75 // the low 7 bits are the payload. The payloads are sent in little endian |
| 77 // order. | 76 // order. |
| 78 // The largest values used are 64-bit addresses. | 77 // The largest values used are 64-bit addresses. |
| 79 // We read in 4 payload chunks (28-bits) to stay in Smi range on Javascript. | 78 // We read in 4 payload chunks (28-bits) to stay in Smi range on Javascript. |
| 80 // We read them into instance variables ('low', 'mid' and 'high') to avoid | 79 // We read them into instance variables ('low', 'mid' and 'high') to avoid |
| 81 // allocating a container. | 80 // allocating a container. |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 247 // without intermediate values exceeding int32. | 246 // without intermediate values exceeding int32. |
| 248 | 247 |
| 249 var strAddr = ""; | 248 var strAddr = ""; |
| 250 var carry = 0; | 249 var carry = 0; |
| 251 combine4(nibble) { | 250 combine4(nibble) { |
| 252 nibble = nibble * _graph._kObjectAlignment + carry; | 251 nibble = nibble * _graph._kObjectAlignment + carry; |
| 253 carry = nibble >> 4; | 252 carry = nibble >> 4; |
| 254 nibble = nibble & 0xF; | 253 nibble = nibble & 0xF; |
| 255 strAddr = nibble.toRadixString(16) + strAddr; | 254 strAddr = nibble.toRadixString(16) + strAddr; |
| 256 } | 255 } |
| 256 |
| 257 combine32(thirtyTwoBits) { | 257 combine32(thirtyTwoBits) { |
| 258 for (int shift = 0; shift < 32; shift += 4) { | 258 for (int shift = 0; shift < 32; shift += 4) { |
| 259 combine4((thirtyTwoBits >> shift) & 0xF); | 259 combine4((thirtyTwoBits >> shift) & 0xF); |
| 260 } | 260 } |
| 261 } | 261 } |
| 262 |
| 262 combine32(low32); | 263 combine32(low32); |
| 263 combine32(high32); | 264 combine32(high32); |
| 264 return strAddr; | 265 return strAddr; |
| 265 } | 266 } |
| 266 | 267 |
| 267 List<ObjectVertex> dominatorTreeChildren() { | 268 List<ObjectVertex> dominatorTreeChildren() { |
| 268 var N = _graph._N; | 269 var N = _graph._N; |
| 269 var doms = _graph._doms; | 270 var doms = _graph._doms; |
| 270 | 271 |
| 271 var parentId = _id; | 272 var parentId = _id; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 330 | 331 |
| 331 bool moveNext() { | 332 bool moveNext() { |
| 332 if (_nextId == _graph._N) return false; | 333 if (_nextId == _graph._N) return false; |
| 333 current = new ObjectVertex._(_nextId++, _graph); | 334 current = new ObjectVertex._(_nextId++, _graph); |
| 334 return true; | 335 return true; |
| 335 } | 336 } |
| 336 } | 337 } |
| 337 | 338 |
| 338 class ObjectGraph { | 339 class ObjectGraph { |
| 339 ObjectGraph(List<ByteData> chunks, int nodeCount) | 340 ObjectGraph(List<ByteData> chunks, int nodeCount) |
| 340 : this._chunks = chunks | 341 : this._chunks = chunks, |
| 341 , this._N = nodeCount; | 342 this._N = nodeCount; |
| 342 | 343 |
| 343 int get size => _size; | 344 int get size => _size; |
| 344 int get vertexCount => _N; | 345 int get vertexCount => _N; |
| 345 int get edgeCount => _E; | 346 int get edgeCount => _E; |
| 346 | 347 |
| 347 ObjectVertex get root => new ObjectVertex._(1, this); | 348 ObjectVertex get root => new ObjectVertex._(1, this); |
| 348 Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); | 349 Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); |
| 349 | 350 |
| 350 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { | 351 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { |
| 351 List<ObjectVertex> _mostRetained = | 352 List<ObjectVertex> _mostRetained = |
| 352 new List<ObjectVertex>.from(vertices.where((u) => !u.isRoot)); | 353 new List<ObjectVertex>.from(vertices.where((u) => !u.isRoot)); |
| 353 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); | 354 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); |
| 354 | 355 |
| 355 var result = _mostRetained; | 356 var result = _mostRetained; |
| 356 if (classId != null) { | 357 if (classId != null) { |
| 357 result = result.where((u) => u.vmCid == classId); | 358 result = result.where((u) => u.vmCid == classId); |
| 358 } | 359 } |
| 359 if (limit != null) { | 360 if (limit != null) { |
| 360 result = result.take(limit); | 361 result = result.take(limit); |
| 361 } | 362 } |
| 362 return result; | 363 return result; |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 505 // Reference into VM isolate's heap. | 506 // Reference into VM isolate's heap. |
| 506 } | 507 } |
| 507 stream.readUnsigned(); | 508 stream.readUnsigned(); |
| 508 } | 509 } |
| 509 id++; | 510 id++; |
| 510 } | 511 } |
| 511 firstSuccs[id] = edge; // Extra entry for cheap boundary detection. | 512 firstSuccs[id] = edge; // Extra entry for cheap boundary detection. |
| 512 | 513 |
| 513 assert(id == N + 1); | 514 assert(id == N + 1); |
| 514 assert(edge <= E); // edge is smaller because E was computed before we knew | 515 assert(edge <= E); // edge is smaller because E was computed before we knew |
| 515 // if references pointed into the VM isolate | 516 // if references pointed into the VM isolate |
| 516 | 517 |
| 517 _E = edge; | 518 _E = edge; |
| 518 _firstSuccs = firstSuccs; | 519 _firstSuccs = firstSuccs; |
| 519 _succs = succs; | 520 _succs = succs; |
| 520 } | 521 } |
| 521 | 522 |
| 522 void _dfs() { | 523 void _dfs() { |
| 523 var N = _N; | 524 var N = _N; |
| 524 var firstSuccs = _firstSuccs; | 525 var firstSuccs = _firstSuccs; |
| 525 var succs = _succs; | 526 var succs = _succs; |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 597 var numPreds = new Uint32List(N + 2); | 598 var numPreds = new Uint32List(N + 2); |
| 598 var preds = new Uint32List(E); | 599 var preds = new Uint32List(E); |
| 599 | 600 |
| 600 // Count predecessors of each node. | 601 // Count predecessors of each node. |
| 601 for (var succIndex = 0; succIndex < E; succIndex++) { | 602 for (var succIndex = 0; succIndex < E; succIndex++) { |
| 602 var succId = succs[succIndex]; | 603 var succId = succs[succIndex]; |
| 603 numPreds[succId]++; | 604 numPreds[succId]++; |
| 604 } | 605 } |
| 605 | 606 |
| 606 // Assign indices into predecessors array. | 607 // Assign indices into predecessors array. |
| 607 var firstPreds = numPreds; // Alias. | 608 var firstPreds = numPreds; // Alias. |
| 608 var nextPreds = new Uint32List(N + 1); | 609 var nextPreds = new Uint32List(N + 1); |
| 609 var predIndex = 0; | 610 var predIndex = 0; |
| 610 for (var i = 1; i <= N; i++) { | 611 for (var i = 1; i <= N; i++) { |
| 611 var thisPredIndex = predIndex; | 612 var thisPredIndex = predIndex; |
| 612 predIndex += numPreds[i]; | 613 predIndex += numPreds[i]; |
| 613 firstPreds[i] = thisPredIndex; | 614 firstPreds[i] = thisPredIndex; |
| 614 nextPreds[i] = thisPredIndex; | 615 nextPreds[i] = thisPredIndex; |
| 615 } | 616 } |
| 616 assert(predIndex == E); | 617 assert(predIndex == E); |
| 617 firstPreds[N + 1] = E; // Extra entry for cheap boundary detection. | 618 firstPreds[N + 1] = E; // Extra entry for cheap boundary detection. |
| 618 | 619 |
| 619 // Fill predecessors array. | 620 // Fill predecessors array. |
| 620 for (var i = 1; i <= N; i++) { | 621 for (var i = 1; i <= N; i++) { |
| 621 var startSuccIndex = firstSuccs[i]; | 622 var startSuccIndex = firstSuccs[i]; |
| 622 var limitSuccIndex = firstSuccs[i + 1]; | 623 var limitSuccIndex = firstSuccs[i + 1]; |
| 623 for (var succIndex = startSuccIndex; | 624 for (var succIndex = startSuccIndex; |
| 624 succIndex < limitSuccIndex; | 625 succIndex < limitSuccIndex; |
| 625 succIndex++) { | 626 succIndex++) { |
| 626 var succId = succs[succIndex]; | 627 var succId = succs[succIndex]; |
| 627 var predIndex = nextPreds[succId]++; | 628 var predIndex = nextPreds[succId]++; |
| 628 preds[predIndex] = i; | 629 preds[predIndex] = i; |
| 629 } | 630 } |
| 630 } | 631 } |
| 631 | 632 |
| 632 _firstPreds = firstPreds; | 633 _firstPreds = firstPreds; |
| 633 _preds = preds; | 634 _preds = preds; |
| 634 } | 635 } |
| 635 | 636 |
| 636 static int _eval(int v, | 637 static int _eval(int v, Uint32List ancestor, Uint32List semi, |
| 637 Uint32List ancestor, | 638 Uint32List label, Uint32List stackNode, Uint8List stackState) { |
| 638 Uint32List semi, | |
| 639 Uint32List label, | |
| 640 Uint32List stackNode, | |
| 641 Uint8List stackState) { | |
| 642 if (ancestor[v] == 0) { | 639 if (ancestor[v] == 0) { |
| 643 return label[v]; | 640 return label[v]; |
| 644 } else { | 641 } else { |
| 645 { | 642 { |
| 646 // Inlined 'compress' with an explicit stack to prevent JS stack | 643 // Inlined 'compress' with an explicit stack to prevent JS stack |
| 647 // overflow. | 644 // overflow. |
| 648 var top = 0; | 645 var top = 0; |
| 649 stackNode[top] = v; | 646 stackNode[top] = v; |
| 650 stackState[top] = 0; | 647 stackState[top] = 0; |
| 651 while (top >= 0) { | 648 while (top >= 0) { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 676 if (semi[label[ancestor[v]]] >= semi[label[v]]) { | 673 if (semi[label[ancestor[v]]] >= semi[label[v]]) { |
| 677 return label[v]; | 674 return label[v]; |
| 678 } else { | 675 } else { |
| 679 return label[ancestor[v]]; | 676 return label[ancestor[v]]; |
| 680 } | 677 } |
| 681 } | 678 } |
| 682 } | 679 } |
| 683 | 680 |
| 684 // Note the version in the main text of Lengauer & Tarjan incorrectly | 681 // Note the version in the main text of Lengauer & Tarjan incorrectly |
| 685 // uses parent instead of ancestor. The correct version is in Appendix B. | 682 // uses parent instead of ancestor. The correct version is in Appendix B. |
| 686 static void _link(int v, | 683 static void _link(int v, int w, Uint32List size, Uint32List label, |
| 687 int w, | 684 Uint32List semi, Uint32List child, Uint32List ancestor) { |
| 688 Uint32List size, | |
| 689 Uint32List label, | |
| 690 Uint32List semi, | |
| 691 Uint32List child, | |
| 692 Uint32List ancestor) { | |
| 693 assert(size[0] == 0); | 685 assert(size[0] == 0); |
| 694 assert(label[0] == 0); | 686 assert(label[0] == 0); |
| 695 assert(semi[0] == 0); | 687 assert(semi[0] == 0); |
| 696 var s = w; | 688 var s = w; |
| 697 while (semi[label[w]] < semi[label[child[s]]]) { | 689 while (semi[label[w]] < semi[label[child[s]]]) { |
| 698 if (size[s] + size[child[child[s]]] >= 2 * size[child[s]]) { | 690 if (size[s] + size[child[child[s]]] >= 2 * size[child[s]]) { |
| 699 ancestor[child[s]] = s; | 691 ancestor[child[s]] = s; |
| 700 child[s] = child[child[s]]; | 692 child[s] = child[child[s]]; |
| 701 } else { | 693 } else { |
| 702 size[child[s]] = size[s]; | 694 size[child[s]] = size[s]; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 744 var stackNode = new Uint32List(N + 1); | 736 var stackNode = new Uint32List(N + 1); |
| 745 var stackState = new Uint8List(N + 1); | 737 var stackState = new Uint8List(N + 1); |
| 746 | 738 |
| 747 for (var i = N; i > 1; i--) { | 739 for (var i = N; i > 1; i--) { |
| 748 var w = vertex[i]; | 740 var w = vertex[i]; |
| 749 assert(w != root); | 741 assert(w != root); |
| 750 | 742 |
| 751 // Lengauer & Tarjan Step 2. | 743 // Lengauer & Tarjan Step 2. |
| 752 var startPred = firstPreds[w]; | 744 var startPred = firstPreds[w]; |
| 753 var limitPred = firstPreds[w + 1]; | 745 var limitPred = firstPreds[w + 1]; |
| 754 for (var predIndex = startPred; | 746 for (var predIndex = startPred; predIndex < limitPred; predIndex++) { |
| 755 predIndex < limitPred; | |
| 756 predIndex++) { | |
| 757 var v = preds[predIndex]; | 747 var v = preds[predIndex]; |
| 758 var u = _eval(v, ancestor, semi, label, stackNode, stackState); | 748 var u = _eval(v, ancestor, semi, label, stackNode, stackState); |
| 759 if (semi[u] < semi[w]) { | 749 if (semi[u] < semi[w]) { |
| 760 semi[w] = semi[u]; | 750 semi[w] = semi[u]; |
| 761 } | 751 } |
| 762 } | 752 } |
| 763 | 753 |
| 764 // w.semi.bucket.add(w); | 754 // w.semi.bucket.add(w); |
| 765 var tmp = vertex[semi[w]]; | 755 var tmp = vertex[semi[w]]; |
| 766 if (buckets[tmp] == null) { | 756 if (buckets[tmp] == null) { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 816 for (var i = N; i > 1; i--) { | 806 for (var i = N; i > 1; i--) { |
| 817 var v = vertex[i]; | 807 var v = vertex[i]; |
| 818 assert(v != 1); | 808 assert(v != 1); |
| 819 retainedSizes[doms[i]] += retainedSizes[i]; | 809 retainedSizes[doms[i]] += retainedSizes[i]; |
| 820 } | 810 } |
| 821 | 811 |
| 822 _retainedSizes = retainedSizes; | 812 _retainedSizes = retainedSizes; |
| 823 _size = size; | 813 _size = size; |
| 824 } | 814 } |
| 825 } | 815 } |
| OLD | NEW |