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 |
(...skipping 426 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
437 | 437 |
438 int get internalSize => _internalSize; | 438 int get internalSize => _internalSize; |
439 int get externalSize => _externalSize; | 439 int get externalSize => _externalSize; |
440 int get vertexCount => _N; | 440 int get vertexCount => _N; |
441 int get edgeCount => _E; | 441 int get edgeCount => _E; |
442 | 442 |
443 ObjectVertex get root => new ObjectVertex._(ROOT, this); | 443 ObjectVertex get root => new ObjectVertex._(ROOT, this); |
444 MergedObjectVertex get mergedRoot => new MergedObjectVertex._(ROOT, this); | 444 MergedObjectVertex get mergedRoot => new MergedObjectVertex._(ROOT, this); |
445 Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); | 445 Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); |
446 | 446 |
| 447 int get numCids => _numCids; |
| 448 int getOwnedByCid(int cid) => _ownedSizesByCid[cid]; |
| 449 |
447 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { | 450 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { |
448 List<ObjectVertex> _mostRetained = | 451 List<ObjectVertex> _mostRetained = |
449 new List<ObjectVertex>.from(vertices.where((u) => !u.isRoot)); | 452 new List<ObjectVertex>.from(vertices.where((u) => !u.isRoot)); |
450 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); | 453 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); |
451 | 454 |
452 var result = _mostRetained; | 455 var result = _mostRetained; |
453 if (classId != null) { | 456 if (classId != null) { |
454 result = result.where((u) => u.vmCid == classId); | 457 result = result.where((u) => u.vmCid == classId); |
455 } | 458 } |
456 if (limit != null) { | 459 if (limit != null) { |
457 result = result.take(limit); | 460 result = result.take(limit); |
458 } | 461 } |
459 return result; | 462 return result; |
460 } | 463 } |
461 | 464 |
462 Stream<List> process() { | 465 Stream<List> process() { |
463 final controller = new StreamController<List>.broadcast(); | 466 final controller = new StreamController<List>.broadcast(); |
464 (() async { | 467 (() async { |
465 // We build futures here instead of marking the steps as async to avoid th
e | 468 // We build futures here instead of marking the steps as async to avoid th
e |
466 // heavy lifting being inside a transformed method. | 469 // heavy lifting being inside a transformed method. |
467 | 470 |
468 controller.add(["Remapping $_N objects...", 0.0]); | 471 controller.add(["Remapping $_N objects...", 0.0]); |
469 await new Future(() => _remapNodes()); | 472 await new Future(() => _remapNodes()); |
470 | 473 |
471 controller.add(["Remapping $_E references...", 15.0]); | 474 controller.add(["Remapping $_E references...", 10.0]); |
472 await new Future(() => _remapEdges()); | 475 await new Future(() => _remapEdges()); |
473 | 476 |
474 _addrToId = null; | 477 _addrToId = null; |
475 _chunks = null; | 478 _chunks = null; |
476 | 479 |
477 controller.add(["Finding depth-first order...", 30.0]); | 480 controller.add(["Finding depth-first order...", 20.0]); |
478 await new Future(() => _dfs()); | 481 await new Future(() => _dfs()); |
479 | 482 |
480 controller.add(["Finding predecessors...", 40.0]); | 483 controller.add(["Finding predecessors...", 30.0]); |
481 await new Future(() => _buildPredecessors()); | 484 await new Future(() => _buildPredecessors()); |
482 | 485 |
483 controller.add(["Finding dominators...", 50.0]); | 486 controller.add(["Finding dominators...", 40.0]); |
484 await new Future(() => _buildDominators()); | 487 await new Future(() => _buildDominators()); |
485 | 488 |
| 489 _semi = null; |
| 490 _parent = null; |
| 491 |
| 492 controller.add(["Finding in-degree(1) groups...", 50.0]); |
| 493 await new Future(() => _buildOwnedSizes()); |
| 494 |
486 _firstPreds = null; | 495 _firstPreds = null; |
487 _preds = null; | 496 _preds = null; |
488 | 497 |
489 _semi = null; | |
490 _parent = null; | |
491 | |
492 controller.add(["Finding retained sizes...", 60.0]); | 498 controller.add(["Finding retained sizes...", 60.0]); |
493 await new Future(() => _calculateRetainedSizes()); | 499 await new Future(() => _calculateRetainedSizes()); |
494 | 500 |
495 _vertex = null; | 501 _vertex = null; |
496 | 502 |
497 controller.add(["Linking dominator tree children...", 70.0]); | 503 controller.add(["Linking dominator tree children...", 70.0]); |
498 await new Future(() => _linkDominatorChildren()); | 504 await new Future(() => _linkDominatorChildren()); |
499 | 505 |
500 controller.add(["Sorting dominator tree children...", 80.0]); | 506 controller.add(["Sorting dominator tree children...", 80.0]); |
501 await new Future(() => _sortDominatorChildren()); | 507 await new Future(() => _sortDominatorChildren()); |
502 | 508 |
503 controller.add(["Merging dominator tree siblings...", 90.0]); | 509 controller.add(["Merging dominator tree siblings...", 90.0]); |
504 await new Future(() => _mergeDominatorSiblings()); | 510 await new Future(() => _mergeDominatorSiblings()); |
505 | 511 |
506 controller.add(["Processed", 100.0]); | 512 controller.add(["Processed", 100.0]); |
507 controller.close(); | 513 controller.close(); |
508 }()); | 514 }()); |
509 return controller.stream; | 515 return controller.stream; |
510 } | 516 } |
511 | 517 |
512 List<ByteData> _chunks; | 518 List<ByteData> _chunks; |
513 | 519 |
514 int _kObjectAlignment; | 520 int _kObjectAlignment; |
515 int _kStackCid; | 521 int _kStackCid; |
| 522 int _kFieldCid; |
| 523 int _numCids; |
516 int _N; // Objects in the snapshot. | 524 int _N; // Objects in the snapshot. |
517 int _Nconnected; // Objects reachable from root. | 525 int _Nconnected; // Objects reachable from root. |
518 int _E; // References in the snapshot. | 526 int _E; // References in the snapshot. |
519 int _internalSize; | 527 int _internalSize; |
520 int _externalSize; | 528 int _externalSize; |
521 | 529 |
522 // Indexed by node id, with id 0 representing invalid/uninitialized. | 530 // Indexed by node id, with id 0 representing invalid/uninitialized. |
523 // From snapshot. | 531 // From snapshot. |
524 Uint16List _cids; | 532 Uint16List _cids; |
525 Uint32List _shallowSizes; | 533 Uint32List _shallowSizes; |
526 Uint32List _externalSizes; | 534 Uint32List _externalSizes; |
527 Uint32List _firstSuccs; | 535 Uint32List _firstSuccs; |
528 Uint32List _succs; | 536 Uint32List _succs; |
529 Uint32List _addressesLow; // No Uint64List in Javascript. | 537 Uint32List _addressesLow; // No Uint64List in Javascript. |
530 Uint32List _addressesHigh; | 538 Uint32List _addressesHigh; |
531 | 539 |
532 // Intermediates. | 540 // Intermediates. |
533 AddressMapper _addrToId; | 541 AddressMapper _addrToId; |
534 Uint32List _vertex; | 542 Uint32List _vertex; |
535 Uint32List _parent; | 543 Uint32List _parent; |
536 Uint32List _semi; | 544 Uint32List _semi; |
537 Uint32List _firstPreds; // Offset into preds. | 545 Uint32List _firstPreds; // Offset into preds. |
538 Uint32List _preds; | 546 Uint32List _preds; |
539 | 547 |
540 // Outputs. | 548 // Outputs. |
541 Uint32List _doms; | 549 Uint32List _doms; |
542 Uint32List _retainedSizes; | 550 Uint32List _retainedSizes; |
543 Uint32List _mergedDomHead; | 551 Uint32List _mergedDomHead; |
544 Uint32List _mergedDomNext; | 552 Uint32List _mergedDomNext; |
| 553 Uint32List _ownedSizesByCid; |
545 | 554 |
546 void _remapNodes() { | 555 void _remapNodes() { |
547 var N = _N; | 556 var N = _N; |
548 var E = 0; | 557 var E = 0; |
549 var addrToId = new AddressMapper(N); | 558 var addrToId = new AddressMapper(N); |
550 | 559 |
551 var addressesHigh = new Uint32List(N + 1); | 560 var addressesHigh = new Uint32List(N + 1); |
552 var addressesLow = new Uint32List(N + 1); | 561 var addressesLow = new Uint32List(N + 1); |
553 var shallowSizes = new Uint32List(N + 1); | 562 var shallowSizes = new Uint32List(N + 1); |
554 var externalSizes = new Uint32List(N + 1); | 563 var externalSizes = new Uint32List(N + 1); |
555 var cids = new Uint16List(N + 1); | 564 var cids = new Uint16List(N + 1); |
556 | 565 |
557 var stream = new ReadStream(_chunks); | 566 var stream = new ReadStream(_chunks); |
558 stream.readUnsigned(); | 567 stream.readUnsigned(); |
559 _kObjectAlignment = stream.clampedUint32; | 568 _kObjectAlignment = stream.clampedUint32; |
560 stream.readUnsigned(); | 569 stream.readUnsigned(); |
561 _kStackCid = stream.clampedUint32; | 570 _kStackCid = stream.clampedUint32; |
| 571 stream.readUnsigned(); |
| 572 _kFieldCid = stream.clampedUint32; |
| 573 stream.readUnsigned(); |
| 574 _numCids = stream.clampedUint32; |
562 | 575 |
563 var id = ROOT; | 576 var id = ROOT; |
564 while (id <= N) { | 577 while (id <= N) { |
565 stream.readUnsigned(); // addr | 578 stream.readUnsigned(); // addr |
566 addrToId.put(stream.high, stream.mid, stream.low, id); | 579 addrToId.put(stream.high, stream.mid, stream.low, id); |
567 addressesHigh[id] = stream.highUint32; | 580 addressesHigh[id] = stream.highUint32; |
568 addressesLow[id] = stream.lowUint32; | 581 addressesLow[id] = stream.lowUint32; |
569 stream.readUnsigned(); // shallowSize | 582 stream.readUnsigned(); // shallowSize |
570 shallowSizes[id] = stream.clampedUint32; | 583 shallowSizes[id] = stream.clampedUint32; |
571 stream.readUnsigned(); // cid | 584 stream.readUnsigned(); // cid |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
606 var N = _N; | 619 var N = _N; |
607 var E = _E; | 620 var E = _E; |
608 var addrToId = _addrToId; | 621 var addrToId = _addrToId; |
609 | 622 |
610 var firstSuccs = new Uint32List(N + 2); | 623 var firstSuccs = new Uint32List(N + 2); |
611 var succs = new Uint32List(E); | 624 var succs = new Uint32List(E); |
612 | 625 |
613 var stream = new ReadStream(_chunks); | 626 var stream = new ReadStream(_chunks); |
614 stream.skipUnsigned(); // kObjectAlignment | 627 stream.skipUnsigned(); // kObjectAlignment |
615 stream.skipUnsigned(); // kStackCid | 628 stream.skipUnsigned(); // kStackCid |
| 629 stream.skipUnsigned(); // kFieldCid |
| 630 stream.skipUnsigned(); // numCids |
616 | 631 |
617 var id = 1, edge = 0; | 632 var id = 1, edge = 0; |
618 while (id <= N) { | 633 while (id <= N) { |
619 stream.skipUnsigned(); // addr | 634 stream.skipUnsigned(); // addr |
620 stream.skipUnsigned(); // shallowSize | 635 stream.skipUnsigned(); // shallowSize |
621 stream.skipUnsigned(); // cid | 636 stream.skipUnsigned(); // cid |
622 | 637 |
623 firstSuccs[id] = edge; | 638 firstSuccs[id] = edge; |
624 | 639 |
625 stream.readUnsigned(); | 640 stream.readUnsigned(); |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
784 var predIndex = nextPreds[succId]++; | 799 var predIndex = nextPreds[succId]++; |
785 preds[predIndex] = i; | 800 preds[predIndex] = i; |
786 } | 801 } |
787 } | 802 } |
788 } | 803 } |
789 | 804 |
790 _firstPreds = firstPreds; | 805 _firstPreds = firstPreds; |
791 _preds = preds; | 806 _preds = preds; |
792 } | 807 } |
793 | 808 |
| 809 // Fold the size of any object with in-degree(1) into its parent. |
| 810 // Requires the DFS numbering and predecessor lists. |
| 811 void _buildOwnedSizes() { |
| 812 var N = _N; |
| 813 var Nconnected = _Nconnected; |
| 814 var kStackCid = _kStackCid; |
| 815 var kFieldCid = _kFieldCid; |
| 816 |
| 817 var cids = _cids; |
| 818 var shallowSizes = _shallowSizes; |
| 819 var externalSizes = _externalSizes; |
| 820 var vertex = _vertex; |
| 821 var firstPreds = _firstPreds; |
| 822 var preds = _preds; |
| 823 |
| 824 var ownedSizes = new Uint32List(N + 1); |
| 825 for (var i = 1; i <= Nconnected; i++) { |
| 826 var v = vertex[i]; |
| 827 ownedSizes[v] = shallowSizes[v] + externalSizes[v]; |
| 828 assert(ownedSizes[v] != 0); |
| 829 } |
| 830 |
| 831 for (var i = Nconnected; i > 1; i--) { |
| 832 var w = vertex[i]; |
| 833 assert(w != ROOT); |
| 834 |
| 835 var onlyPred = SENTINEL; |
| 836 |
| 837 var startPred = firstPreds[w]; |
| 838 var limitPred = firstPreds[w + 1]; |
| 839 for (var predIndex = startPred; predIndex < limitPred; predIndex++) { |
| 840 var v = preds[predIndex]; |
| 841 if (v == w) { |
| 842 // Ignore self-predecessor. |
| 843 } else if (onlyPred == SENTINEL) { |
| 844 onlyPred = v; |
| 845 } else if (onlyPred == v) { |
| 846 // Repeated predecessor. |
| 847 } else { |
| 848 // Multiple-predecessors. |
| 849 onlyPred = SENTINEL; |
| 850 break; |
| 851 } |
| 852 } |
| 853 |
| 854 // If this object has a single precessor which is not a Field, Stack or |
| 855 // the root, blame its size against the precessor. |
| 856 if ((onlyPred != SENTINEL) && |
| 857 (onlyPred != ROOT) && |
| 858 (cids[onlyPred] != kStackCid) && |
| 859 (cids[onlyPred] != kFieldCid)) { |
| 860 assert(ownedSizes[w] != 0); |
| 861 ownedSizes[onlyPred] += ownedSizes[w]; |
| 862 ownedSizes[w] = 0; |
| 863 } |
| 864 } |
| 865 |
| 866 // TODO(rmacnak): Maybe keep the per-objects sizes to be able to provide |
| 867 // examples of large owners for each class. |
| 868 var ownedSizesByCid = new Uint32List(_numCids); |
| 869 for (var i = 1; i <= Nconnected; i++) { |
| 870 var v = vertex[i]; |
| 871 var cid = cids[v]; |
| 872 ownedSizesByCid[cid] += ownedSizes[v]; |
| 873 } |
| 874 |
| 875 _ownedSizesByCid = ownedSizesByCid; |
| 876 } |
| 877 |
794 static int _eval(int v, Uint32List ancestor, Uint32List semi, | 878 static int _eval(int v, Uint32List ancestor, Uint32List semi, |
795 Uint32List label, Uint32List stackNode, Uint8List stackState) { | 879 Uint32List label, Uint32List stackNode, Uint8List stackState) { |
796 if (ancestor[v] == SENTINEL) { | 880 if (ancestor[v] == SENTINEL) { |
797 return label[v]; | 881 return label[v]; |
798 } else { | 882 } else { |
799 { | 883 { |
800 // Inlined 'compress' with an explicit stack to prevent JS stack | 884 // Inlined 'compress' with an explicit stack to prevent JS stack |
801 // overflow. | 885 // overflow. |
802 var top = 0; | 886 var top = 0; |
803 stackNode[top] = v; | 887 stackNode[top] = v; |
(...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1131 | 1215 |
1132 // From all the siblings between child and after, take their children, | 1216 // From all the siblings between child and after, take their children, |
1133 // merge them and given to child. | 1217 // merge them and given to child. |
1134 mergeChildrenAndSort(child, after); | 1218 mergeChildrenAndSort(child, after); |
1135 | 1219 |
1136 child = after; | 1220 child = after; |
1137 } | 1221 } |
1138 } | 1222 } |
1139 } | 1223 } |
1140 } | 1224 } |
OLD | NEW |