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 |