1 /* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @interface 33 */ 34 WebInspector.HeapSnapshotItem = function() { } 35 36 WebInspector.HeapSnapshotItem.prototype = { 37 /** 38 * @return {number} 39 */ 40 itemIndex: function() { }, 41 42 /** 43 * @return {!Object} 44 */ 45 serialize: function() { } 46 }; 47 48 /** 49 * @constructor 50 * @implements {WebInspector.HeapSnapshotItem} 51 * @param {!WebInspector.HeapSnapshot} snapshot 52 * @param {number=} edgeIndex 53 */ 54 WebInspector.HeapSnapshotEdge = function(snapshot, edgeIndex) 55 { 56 this._snapshot = snapshot; 57 this._edges = snapshot.containmentEdges; 58 this.edgeIndex = edgeIndex || 0; 59 } 60 61 WebInspector.HeapSnapshotEdge.prototype = { 62 /** 63 * @return {!WebInspector.HeapSnapshotEdge} 64 */ 65 clone: function() 66 { 67 return new WebInspector.HeapSnapshotEdge(this._snapshot, this.edgeIndex); 68 }, 69 70 /** 71 * @return {boolean} 72 */ 73 hasStringName: function() 74 { 75 throw new Error("Not implemented"); 76 }, 77 78 /** 79 * @return {string} 80 */ 81 name: function() 82 { 83 throw new Error("Not implemented"); 84 }, 85 86 /** 87 * @return {!WebInspector.HeapSnapshotNode} 88 */ 89 node: function() 90 { 91 return this._snapshot.createNode(this.nodeIndex()); 92 }, 93 94 /** 95 * @return {number} 96 */ 97 nodeIndex: function() 98 { 99 return this._edges[this.edgeIndex + this._snapshot._edgeToNodeOffset]; 100 }, 101 102 /** 103 * @return {string} 104 */ 105 toString: function() 106 { 107 return "HeapSnapshotEdge: " + this.name(); 108 }, 109 110 /** 111 * @return {string} 112 */ 113 type: function() 114 { 115 return this._snapshot._edgeTypes[this._type()]; 116 }, 117 118 /** 119 * @override 120 * @return {number} 121 */ 122 itemIndex: function() 123 { 124 return this.edgeIndex; 125 }, 126 127 /** 128 * @override 129 * @return {!WebInspector.HeapSnapshotCommon.Edge} 130 */ 131 serialize: function() 132 { 133 return new WebInspector.HeapSnapshotCommon.Edge(this.name(), this.node().serialize(), this.type(), this.edgeIndex); 134 }, 135 136 _type: function() 137 { 138 return this._edges[this.edgeIndex + this._snapshot._edgeTypeOffset]; 139 } 140 }; 141 142 143 /** 144 * @interface 145 */ 146 WebInspector.HeapSnapshotItemIterator = function() { } 147 148 WebInspector.HeapSnapshotItemIterator.prototype = { 149 /** 150 * @return {boolean} 151 */ 152 hasNext: function() { }, 153 154 /** 155 * @return {!WebInspector.HeapSnapshotItem} 156 */ 157 item: function() { }, 158 159 next: function() { } 160 }; 161 162 163 /** 164 * @interface 165 */ 166 WebInspector.HeapSnapshotItemIndexProvider = function() { } 167 168 WebInspector.HeapSnapshotItemIndexProvider.prototype = { 169 /** 170 * @param {number} newIndex 171 * @return {!WebInspector.HeapSnapshotItem} 172 */ 173 itemForIndex: function(newIndex) { }, 174 }; 175 176 /** 177 * @constructor 178 * @implements {WebInspector.HeapSnapshotItemIndexProvider} 179 * @param {!WebInspector.HeapSnapshot} snapshot 180 */ 181 WebInspector.HeapSnapshotNodeIndexProvider = function(snapshot) 182 { 183 this._node = snapshot.createNode(); 184 } 185 186 WebInspector.HeapSnapshotNodeIndexProvider.prototype = { 187 /** 188 * @param {number} index 189 * @return {!WebInspector.HeapSnapshotNode} 190 */ 191 itemForIndex: function(index) 192 { 193 this._node.nodeIndex = index; 194 return this._node; 195 } 196 }; 197 198 199 /** 200 * @constructor 201 * @implements {WebInspector.HeapSnapshotItemIndexProvider} 202 * @param {!WebInspector.HeapSnapshot} snapshot 203 */ 204 WebInspector.HeapSnapshotEdgeIndexProvider = function(snapshot) 205 { 206 this._edge = snapshot.createEdge(0); 207 } 208 209 WebInspector.HeapSnapshotEdgeIndexProvider.prototype = { 210 /** 211 * @param {number} index 212 * @return {!WebInspector.HeapSnapshotEdge} 213 */ 214 itemForIndex: function(index) 215 { 216 this._edge.edgeIndex = index; 217 return this._edge; 218 } 219 }; 220 221 222 /** 223 * @constructor 224 * @implements {WebInspector.HeapSnapshotItemIndexProvider} 225 * @param {!WebInspector.HeapSnapshot} snapshot 226 */ 227 WebInspector.HeapSnapshotRetainerEdgeIndexProvider = function(snapshot) 228 { 229 this._retainerEdge = snapshot.createRetainingEdge(0); 230 } 231 232 WebInspector.HeapSnapshotRetainerEdgeIndexProvider.prototype = { 233 /** 234 * @param {number} index 235 * @return {!WebInspector.HeapSnapshotRetainerEdge} 236 */ 237 itemForIndex: function(index) 238 { 239 this._retainerEdge.setRetainerIndex(index); 240 return this._retainerEdge; 241 } 242 }; 243 244 245 /** 246 * @constructor 247 * @implements {WebInspector.HeapSnapshotItemIterator} 248 * @param {!WebInspector.HeapSnapshotNode} node 249 */ 250 WebInspector.HeapSnapshotEdgeIterator = function(node) 251 { 252 this._sourceNode = node; 253 this.edge = node._snapshot.createEdge(node.edgeIndexesStart()); 254 } 255 256 WebInspector.HeapSnapshotEdgeIterator.prototype = { 257 /** 258 * @return {boolean} 259 */ 260 hasNext: function() 261 { 262 return this.edge.edgeIndex < this._sourceNode.edgeIndexesEnd(); 263 }, 264 265 /** 266 * @return {!WebInspector.HeapSnapshotEdge} 267 */ 268 item: function() 269 { 270 return this.edge; 271 }, 272 273 next: function() 274 { 275 this.edge.edgeIndex += this.edge._snapshot._edgeFieldsCount; 276 } 277 }; 278 279 /** 280 * @constructor 281 * @implements {WebInspector.HeapSnapshotItem} 282 * @param {!WebInspector.HeapSnapshot} snapshot 283 * @param {number} retainerIndex 284 */ 285 WebInspector.HeapSnapshotRetainerEdge = function(snapshot, retainerIndex) 286 { 287 this._snapshot = snapshot; 288 this.setRetainerIndex(retainerIndex); 289 } 290 291 WebInspector.HeapSnapshotRetainerEdge.prototype = { 292 /** 293 * @return {!WebInspector.HeapSnapshotRetainerEdge} 294 */ 295 clone: function() 296 { 297 return new WebInspector.HeapSnapshotRetainerEdge(this._snapshot, this.retainerIndex()); 298 }, 299 300 /** 301 * @return {boolean} 302 */ 303 hasStringName: function() 304 { 305 return this._edge().hasStringName(); 306 }, 307 308 /** 309 * @return {string} 310 */ 311 name: function() 312 { 313 return this._edge().name(); 314 }, 315 316 /** 317 * @return {!WebInspector.HeapSnapshotNode} 318 */ 319 node: function() 320 { 321 return this._node(); 322 }, 323 324 /** 325 * @return {number} 326 */ 327 nodeIndex: function() 328 { 329 return this._retainingNodeIndex; 330 }, 331 332 /** 333 * @return {number} 334 */ 335 retainerIndex: function() 336 { 337 return this._retainerIndex; 338 }, 339 340 /** 341 * @param {number} retainerIndex 342 */ 343 setRetainerIndex: function(retainerIndex) 344 { 345 if (retainerIndex === this._retainerIndex) 346 return; 347 this._retainerIndex = retainerIndex; 348 this._globalEdgeIndex = this._snapshot._retainingEdges[retainerIndex]; 349 this._retainingNodeIndex = this._snapshot._retainingNodes[retainerIndex]; 350 this._edgeInstance = null; 351 this._nodeInstance = null; 352 }, 353 354 /** 355 * @param {number} edgeIndex 356 */ 357 set edgeIndex(edgeIndex) 358 { 359 this.setRetainerIndex(edgeIndex); 360 }, 361 362 _node: function() 363 { 364 if (!this._nodeInstance) 365 this._nodeInstance = this._snapshot.createNode(this._retainingNodeIndex); 366 return this._nodeInstance; 367 }, 368 369 _edge: function() 370 { 371 if (!this._edgeInstance) 372 this._edgeInstance = this._snapshot.createEdge(this._globalEdgeIndex); 373 return this._edgeInstance; 374 }, 375 376 /** 377 * @return {string} 378 */ 379 toString: function() 380 { 381 return this._edge().toString(); 382 }, 383 384 /** 385 * @override 386 * @return {number} 387 */ 388 itemIndex: function() 389 { 390 return this._retainerIndex; 391 }, 392 393 /** 394 * @override 395 * @return {!WebInspector.HeapSnapshotCommon.Edge} 396 */ 397 serialize: function() 398 { 399 return new WebInspector.HeapSnapshotCommon.Edge(this.name(), this.node().serialize(), this.type(), this._globalEdgeIndex); 400 }, 401 402 /** 403 * @return {string} 404 */ 405 type: function() 406 { 407 return this._edge().type(); 408 } 409 } 410 411 /** 412 * @constructor 413 * @implements {WebInspector.HeapSnapshotItemIterator} 414 * @param {!WebInspector.HeapSnapshotNode} retainedNode 415 */ 416 WebInspector.HeapSnapshotRetainerEdgeIterator = function(retainedNode) 417 { 418 var snapshot = retainedNode._snapshot; 419 var retainedNodeOrdinal = retainedNode.ordinal(); 420 var retainerIndex = snapshot._firstRetainerIndex[retainedNodeOrdinal]; 421 this._retainersEnd = snapshot._firstRetainerIndex[retainedNodeOrdinal + 1]; 422 this.retainer = snapshot.createRetainingEdge(retainerIndex); 423 } 424 425 WebInspector.HeapSnapshotRetainerEdgeIterator.prototype = { 426 /** 427 * @return {boolean} 428 */ 429 hasNext: function() 430 { 431 return this.retainer.retainerIndex() < this._retainersEnd; 432 }, 433 434 /** 435 * @return {!WebInspector.HeapSnapshotRetainerEdge} 436 */ 437 item: function() 438 { 439 return this.retainer; 440 }, 441 442 next: function() 443 { 444 this.retainer.setRetainerIndex(this.retainer.retainerIndex() + 1); 445 } 446 }; 447 448 /** 449 * @constructor 450 * @implements {WebInspector.HeapSnapshotItem} 451 * @param {!WebInspector.HeapSnapshot} snapshot 452 * @param {number=} nodeIndex 453 */ 454 WebInspector.HeapSnapshotNode = function(snapshot, nodeIndex) 455 { 456 this._snapshot = snapshot; 457 this.nodeIndex = nodeIndex || 0; 458 } 459 460 WebInspector.HeapSnapshotNode.prototype = { 461 /** 462 * @return {number} 463 */ 464 distance: function() 465 { 466 return this._snapshot._nodeDistances[this.nodeIndex / this._snapshot._nodeFieldCount]; 467 }, 468 469 /** 470 * @return {string} 471 */ 472 className: function() 473 { 474 throw new Error("Not implemented"); 475 }, 476 477 /** 478 * @return {number} 479 */ 480 classIndex: function() 481 { 482 throw new Error("Not implemented"); 483 }, 484 485 /** 486 * @return {number} 487 */ 488 dominatorIndex: function() 489 { 490 var nodeFieldCount = this._snapshot._nodeFieldCount; 491 return this._snapshot._dominatorsTree[this.nodeIndex / this._snapshot._nodeFieldCount] * nodeFieldCount; 492 }, 493 494 /** 495 * @return {!WebInspector.HeapSnapshotEdgeIterator} 496 */ 497 edges: function() 498 { 499 return new WebInspector.HeapSnapshotEdgeIterator(this); 500 }, 501 502 /** 503 * @return {number} 504 */ 505 edgesCount: function() 506 { 507 return (this.edgeIndexesEnd() - this.edgeIndexesStart()) / this._snapshot._edgeFieldsCount; 508 }, 509 510 /** 511 * @return {number} 512 */ 513 id: function() 514 { 515 throw new Error("Not implemented"); 516 }, 517 518 /** 519 * @return {boolean} 520 */ 521 isRoot: function() 522 { 523 return this.nodeIndex === this._snapshot._rootNodeIndex; 524 }, 525 526 /** 527 * @return {string} 528 */ 529 name: function() 530 { 531 return this._snapshot.strings[this._name()]; 532 }, 533 534 /** 535 * @return {number} 536 */ 537 retainedSize: function() 538 { 539 return this._snapshot._retainedSizes[this.ordinal()]; 540 }, 541 542 /** 543 * @return {!WebInspector.HeapSnapshotRetainerEdgeIterator} 544 */ 545 retainers: function() 546 { 547 return new WebInspector.HeapSnapshotRetainerEdgeIterator(this); 548 }, 549 550 /** 551 * @return {number} 552 */ 553 retainersCount: function() 554 { 555 var snapshot = this._snapshot; 556 var ordinal = this.ordinal(); 557 return snapshot._firstRetainerIndex[ordinal + 1] - snapshot._firstRetainerIndex[ordinal]; 558 }, 559 560 /** 561 * @return {number} 562 */ 563 selfSize: function() 564 { 565 var snapshot = this._snapshot; 566 return snapshot.nodes[this.nodeIndex + snapshot._nodeSelfSizeOffset]; 567 }, 568 569 /** 570 * @return {string} 571 */ 572 type: function() 573 { 574 return this._snapshot._nodeTypes[this._type()]; 575 }, 576 577 /** 578 * @return {number} 579 */ 580 traceNodeId: function() 581 { 582 var snapshot = this._snapshot; 583 return snapshot.nodes[this.nodeIndex + snapshot._nodeTraceNodeIdOffset]; 584 }, 585 586 /** 587 * @override 588 * @return {number} 589 */ 590 itemIndex: function() 591 { 592 return this.nodeIndex; 593 }, 594 595 /** 596 * @override 597 * @return {!WebInspector.HeapSnapshotCommon.Node} 598 */ 599 serialize: function() 600 { 601 return new WebInspector.HeapSnapshotCommon.Node(this.id(), this.name(), this.distance(), this.nodeIndex, this.retainedSize(), this.selfSize(), this.type()); 602 }, 603 604 /** 605 * @return {number} 606 */ 607 _name: function() 608 { 609 var snapshot = this._snapshot; 610 return snapshot.nodes[this.nodeIndex + snapshot._nodeNameOffset]; 611 }, 612 613 /** 614 * @return {number} 615 */ 616 edgeIndexesStart: function() 617 { 618 return this._snapshot._firstEdgeIndexes[this.ordinal()]; 619 }, 620 621 /** 622 * @return {number} 623 */ 624 edgeIndexesEnd: function() 625 { 626 return this._snapshot._firstEdgeIndexes[this.ordinal() + 1]; 627 }, 628 629 /** 630 * @return {number} 631 */ 632 ordinal: function() 633 { 634 return this.nodeIndex / this._snapshot._nodeFieldCount; 635 }, 636 637 /** 638 * @return {number} 639 */ 640 _nextNodeIndex: function() 641 { 642 return this.nodeIndex + this._snapshot._nodeFieldCount; 643 }, 644 645 /** 646 * @return {number} 647 */ 648 _type: function() 649 { 650 var snapshot = this._snapshot; 651 return snapshot.nodes[this.nodeIndex + snapshot._nodeTypeOffset]; 652 } 653 }; 654 655 /** 656 * @constructor 657 * @implements {WebInspector.HeapSnapshotItemIterator} 658 * @param {!WebInspector.HeapSnapshotNode} node 659 */ 660 WebInspector.HeapSnapshotNodeIterator = function(node) 661 { 662 this.node = node; 663 this._nodesLength = node._snapshot.nodes.length; 664 } 665 666 WebInspector.HeapSnapshotNodeIterator.prototype = { 667 /** 668 * @return {boolean} 669 */ 670 hasNext: function() 671 { 672 return this.node.nodeIndex < this._nodesLength; 673 }, 674 675 /** 676 * @return {!WebInspector.HeapSnapshotNode} 677 */ 678 item: function() 679 { 680 return this.node; 681 }, 682 683 next: function() 684 { 685 this.node.nodeIndex = this.node._nextNodeIndex(); 686 } 687 } 688 689 690 /** 691 * @constructor 692 * @implements {WebInspector.HeapSnapshotItemIterator} 693 * @param {!WebInspector.HeapSnapshotItemIndexProvider} itemProvider 694 * @param {!Array.<number>|!Uint32Array} indexes 695 */ 696 WebInspector.HeapSnapshotIndexRangeIterator = function(itemProvider, indexes) 697 { 698 this._itemProvider = itemProvider; 699 this._indexes = indexes; 700 this._position = 0; 701 } 702 703 WebInspector.HeapSnapshotIndexRangeIterator.prototype = { 704 /** 705 * @return {boolean} 706 */ 707 hasNext: function() 708 { 709 return this._position < this._indexes.length 710 }, 711 712 /** 713 * @return {!WebInspector.HeapSnapshotItem} 714 */ 715 item: function() 716 { 717 var index = this._indexes[this._position]; 718 return this._itemProvider.itemForIndex(index); 719 }, 720 721 next: function() 722 { 723 ++this._position; 724 } 725 } 726 727 728 /** 729 * @constructor 730 * @implements {WebInspector.HeapSnapshotItemIterator} 731 * @param {!WebInspector.HeapSnapshotItemIterator} iterator 732 * @param {function(!WebInspector.HeapSnapshotItem):boolean=} filter 733 */ 734 WebInspector.HeapSnapshotFilteredIterator = function(iterator, filter) 735 { 736 this._iterator = iterator; 737 this._filter = filter; 738 this._skipFilteredItems(); 739 } 740 741 WebInspector.HeapSnapshotFilteredIterator.prototype = { 742 /** 743 * @return {boolean} 744 */ 745 hasNext: function() 746 { 747 return this._iterator.hasNext(); 748 }, 749 750 /** 751 * @return {!WebInspector.HeapSnapshotItem} 752 */ 753 item: function() 754 { 755 return this._iterator.item(); 756 }, 757 758 next: function() 759 { 760 this._iterator.next(); 761 this._skipFilteredItems(); 762 }, 763 764 _skipFilteredItems: function() 765 { 766 while (this._iterator.hasNext() && !this._filter(this._iterator.item())) { 767 this._iterator.next(); 768 } 769 } 770 } 771 772 773 /** 774 * @param {!WebInspector.HeapSnapshotWorkerDispatcher=} dispatcher 775 * @constructor 776 */ 777 WebInspector.HeapSnapshotProgress = function(dispatcher) 778 { 779 this._dispatcher = dispatcher; 780 } 781 782 WebInspector.HeapSnapshotProgress.prototype = { 783 /** 784 * @param {string} status 785 */ 786 updateStatus: function(status) 787 { 788 this._sendUpdateEvent(WebInspector.UIString(status)); 789 }, 790 791 /** 792 * @param {string} title 793 * @param {number} value 794 * @param {number} total 795 */ 796 updateProgress: function(title, value, total) 797 { 798 var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0); 799 this._sendUpdateEvent(WebInspector.UIString(title, percentValue)); 800 }, 801 802 /** 803 * @param {string} text 804 */ 805 _sendUpdateEvent: function(text) 806 { 807 // May be undefined in tests. 808 if (this._dispatcher) 809 this._dispatcher.sendEvent(WebInspector.HeapSnapshotProgressEvent.Update, text); 810 } 811 } 812 813 814 /** 815 * @param {!Object} profile 816 * @param {!WebInspector.HeapSnapshotProgress} progress 817 * @constructor 818 */ 819 WebInspector.HeapSnapshot = function(profile, progress) 820 { 821 /** @type {!Uint32Array} */ 822 this.nodes = profile.nodes; 823 /** @type {!Uint32Array} */ 824 this.containmentEdges = profile.edges; 825 /** @type {!HeapSnapshotMetainfo} */ 826 this._metaNode = profile.snapshot.meta; 827 /** @type {!Array.<string>} */ 828 this.strings = profile.strings; 829 this._progress = progress; 830 831 this._noDistance = -5; 832 this._rootNodeIndex = 0; 833 if (profile.snapshot.root_index) 834 this._rootNodeIndex = profile.snapshot.root_index; 835 836 this._snapshotDiffs = {}; 837 this._aggregatesForDiff = null; 838 this._aggregates = {}; 839 this._aggregatesSortedFlags = {}; 840 841 this._init(); 842 843 if (profile.snapshot.trace_function_count) { 844 this._progress.updateStatus("Building allocation statistics\u2026"); 845 var nodes = this.nodes; 846 var nodesLength = nodes.length; 847 var nodeFieldCount = this._nodeFieldCount; 848 var node = this.rootNode(); 849 var liveObjects = {}; 850 for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) { 851 node.nodeIndex = nodeIndex; 852 var traceNodeId = node.traceNodeId(); 853 var stats = liveObjects[traceNodeId]; 854 if (!stats) 855 liveObjects[traceNodeId] = stats = { count: 0, size: 0, ids: [] }; 856 stats.count++; 857 stats.size += node.selfSize(); 858 stats.ids.push(node.id()); 859 } 860 this._allocationProfile = new WebInspector.AllocationProfile(profile, liveObjects); 861 this._progress.updateStatus("Done"); 862 } 863 } 864 865 /** 866 * @constructor 867 */ 868 function HeapSnapshotMetainfo() 869 { 870 // New format. 871 this.node_fields = []; 872 this.node_types = []; 873 this.edge_fields = []; 874 this.edge_types = []; 875 this.trace_function_info_fields = []; 876 this.trace_node_fields = []; 877 this.type_strings = {}; 878 } 879 880 /** 881 * @constructor 882 */ 883 function HeapSnapshotHeader() 884 { 885 // New format. 886 this.title = ""; 887 this.meta = new HeapSnapshotMetainfo(); 888 this.node_count = 0; 889 this.edge_count = 0; 890 } 891 892 WebInspector.HeapSnapshot.prototype = { 893 _init: function() 894 { 895 var meta = this._metaNode; 896 897 this._nodeTypeOffset = meta.node_fields.indexOf("type"); 898 this._nodeNameOffset = meta.node_fields.indexOf("name"); 899 this._nodeIdOffset = meta.node_fields.indexOf("id"); 900 this._nodeSelfSizeOffset = meta.node_fields.indexOf("self_size"); 901 this._nodeEdgeCountOffset = meta.node_fields.indexOf("edge_count"); 902 this._nodeTraceNodeIdOffset = meta.node_fields.indexOf("trace_node_id"); 903 this._nodeFieldCount = meta.node_fields.length; 904 905 this._nodeTypes = meta.node_types[this._nodeTypeOffset]; 906 this._nodeArrayType = this._nodeTypes.indexOf("array"); 907 this._nodeHiddenType = this._nodeTypes.indexOf("hidden"); 908 this._nodeObjectType = this._nodeTypes.indexOf("object"); 909 this._nodeNativeType = this._nodeTypes.indexOf("native"); 910 this._nodeConsStringType = this._nodeTypes.indexOf("concatenated string"); 911 this._nodeSlicedStringType = this._nodeTypes.indexOf("sliced string"); 912 this._nodeCodeType = this._nodeTypes.indexOf("code"); 913 this._nodeSyntheticType = this._nodeTypes.indexOf("synthetic"); 914 915 this._edgeFieldsCount = meta.edge_fields.length; 916 this._edgeTypeOffset = meta.edge_fields.indexOf("type"); 917 this._edgeNameOffset = meta.edge_fields.indexOf("name_or_index"); 918 this._edgeToNodeOffset = meta.edge_fields.indexOf("to_node"); 919 920 this._edgeTypes = meta.edge_types[this._edgeTypeOffset]; 921 this._edgeTypes.push("invisible"); 922 this._edgeElementType = this._edgeTypes.indexOf("element"); 923 this._edgeHiddenType = this._edgeTypes.indexOf("hidden"); 924 this._edgeInternalType = this._edgeTypes.indexOf("internal"); 925 this._edgeShortcutType = this._edgeTypes.indexOf("shortcut"); 926 this._edgeWeakType = this._edgeTypes.indexOf("weak"); 927 this._edgeInvisibleType = this._edgeTypes.indexOf("invisible"); 928 929 this.nodeCount = this.nodes.length / this._nodeFieldCount; 930 this._edgeCount = this.containmentEdges.length / this._edgeFieldsCount; 931 932 this._retainedSizes = new Float64Array(this.nodeCount); 933 this._firstEdgeIndexes = new Uint32Array(this.nodeCount + 1); 934 this._retainingNodes = new Uint32Array(this._edgeCount); 935 this._retainingEdges = new Uint32Array(this._edgeCount); 936 this._firstRetainerIndex = new Uint32Array(this.nodeCount + 1); 937 this._nodeDistances = new Int32Array(this.nodeCount); 938 this._firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1); 939 this._dominatedNodes = new Uint32Array(this.nodeCount - 1); 940 941 this._progress.updateStatus("Building edge indexes\u2026"); 942 this._buildEdgeIndexes(); 943 this._progress.updateStatus("Building retainers\u2026"); 944 this._buildRetainers(); 945 this._progress.updateStatus("Calculating node flags\u2026"); 946 this._calculateFlags(); 947 this._progress.updateStatus("Calculating distances\u2026"); 948 this.calculateDistances(); 949 this._progress.updateStatus("Building postorder index\u2026"); 950 var result = this._buildPostOrderIndex(); 951 // Actually it is array that maps node ordinal number to dominator node ordinal number. 952 this._progress.updateStatus("Building dominator tree\u2026"); 953 this._dominatorsTree = this._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex); 954 this._progress.updateStatus("Calculating retained sizes\u2026"); 955 this._calculateRetainedSizes(result.postOrderIndex2NodeOrdinal); 956 this._progress.updateStatus("Buiding dominated nodes\u2026"); 957 this._buildDominatedNodes(); 958 this._progress.updateStatus("Calculating statistics\u2026"); 959 this._calculateStatistics(); 960 this._progress.updateStatus("Finished processing."); 961 }, 962 963 _buildEdgeIndexes: function() 964 { 965 var nodes = this.nodes; 966 var nodeCount = this.nodeCount; 967 var firstEdgeIndexes = this._firstEdgeIndexes; 968 var nodeFieldCount = this._nodeFieldCount; 969 var edgeFieldsCount = this._edgeFieldsCount; 970 var nodeEdgeCountOffset = this._nodeEdgeCountOffset; 971 firstEdgeIndexes[nodeCount] = this.containmentEdges.length; 972 for (var nodeOrdinal = 0, edgeIndex = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) { 973 firstEdgeIndexes[nodeOrdinal] = edgeIndex; 974 edgeIndex += nodes[nodeOrdinal * nodeFieldCount + nodeEdgeCountOffset] * edgeFieldsCount; 975 } 976 }, 977 978 _buildRetainers: function() 979 { 980 var retainingNodes = this._retainingNodes; 981 var retainingEdges = this._retainingEdges; 982 // Index of the first retainer in the _retainingNodes and _retainingEdges 983 // arrays. Addressed by retained node index. 984 var firstRetainerIndex = this._firstRetainerIndex; 985 986 var containmentEdges = this.containmentEdges; 987 var edgeFieldsCount = this._edgeFieldsCount; 988 var nodeFieldCount = this._nodeFieldCount; 989 var edgeToNodeOffset = this._edgeToNodeOffset; 990 var firstEdgeIndexes = this._firstEdgeIndexes; 991 var nodeCount = this.nodeCount; 992 993 for (var toNodeFieldIndex = edgeToNodeOffset, l = containmentEdges.length; toNodeFieldIndex < l; toNodeFieldIndex += edgeFieldsCount) { 994 var toNodeIndex = containmentEdges[toNodeFieldIndex]; 995 if (toNodeIndex % nodeFieldCount) 996 throw new Error("Invalid toNodeIndex " + toNodeIndex); 997 ++firstRetainerIndex[toNodeIndex / nodeFieldCount]; 998 } 999 for (var i = 0, firstUnusedRetainerSlot = 0; i < nodeCount; i++) { 1000 var retainersCount = firstRetainerIndex[i]; 1001 firstRetainerIndex[i] = firstUnusedRetainerSlot; 1002 retainingNodes[firstUnusedRetainerSlot] = retainersCount; 1003 firstUnusedRetainerSlot += retainersCount; 1004 } 1005 firstRetainerIndex[nodeCount] = retainingNodes.length; 1006 1007 var nextNodeFirstEdgeIndex = firstEdgeIndexes[0]; 1008 for (var srcNodeOrdinal = 0; srcNodeOrdinal < nodeCount; ++srcNodeOrdinal) { 1009 var firstEdgeIndex = nextNodeFirstEdgeIndex; 1010 nextNodeFirstEdgeIndex = firstEdgeIndexes[srcNodeOrdinal + 1]; 1011 var srcNodeIndex = srcNodeOrdinal * nodeFieldCount; 1012 for (var edgeIndex = firstEdgeIndex; edgeIndex < nextNodeFirstEdgeIndex; edgeIndex += edgeFieldsCount) { 1013 var toNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 1014 if (toNodeIndex % nodeFieldCount) 1015 throw new Error("Invalid toNodeIndex " + toNodeIndex); 1016 var firstRetainerSlotIndex = firstRetainerIndex[toNodeIndex / nodeFieldCount]; 1017 var nextUnusedRetainerSlotIndex = firstRetainerSlotIndex + (--retainingNodes[firstRetainerSlotIndex]); 1018 retainingNodes[nextUnusedRetainerSlotIndex] = srcNodeIndex; 1019 retainingEdges[nextUnusedRetainerSlotIndex] = edgeIndex; 1020 } 1021 } 1022 }, 1023 1024 /** 1025 * @param {number=} nodeIndex 1026 */ 1027 createNode: function(nodeIndex) 1028 { 1029 throw new Error("Not implemented"); 1030 }, 1031 1032 /** 1033 * @param {number} edgeIndex 1034 * @return {!WebInspector.JSHeapSnapshotEdge} 1035 */ 1036 createEdge: function(edgeIndex) 1037 { 1038 throw new Error("Not implemented"); 1039 }, 1040 1041 /** 1042 * @param {number} retainerIndex 1043 * @return {!WebInspector.JSHeapSnapshotRetainerEdge} 1044 */ 1045 createRetainingEdge: function(retainerIndex) 1046 { 1047 throw new Error("Not implemented"); 1048 }, 1049 1050 _allNodes: function() 1051 { 1052 return new WebInspector.HeapSnapshotNodeIterator(this.rootNode()); 1053 }, 1054 1055 /** 1056 * @return {!WebInspector.HeapSnapshotNode} 1057 */ 1058 rootNode: function() 1059 { 1060 return this.createNode(this._rootNodeIndex); 1061 }, 1062 1063 get rootNodeIndex() 1064 { 1065 return this._rootNodeIndex; 1066 }, 1067 1068 get totalSize() 1069 { 1070 return this.rootNode().retainedSize(); 1071 }, 1072 1073 _getDominatedIndex: function(nodeIndex) 1074 { 1075 if (nodeIndex % this._nodeFieldCount) 1076 throw new Error("Invalid nodeIndex: " + nodeIndex); 1077 return this._firstDominatedNodeIndex[nodeIndex / this._nodeFieldCount]; 1078 }, 1079 1080 /** 1081 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter 1082 * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} 1083 */ 1084 aggregatesWithFilter: function(nodeFilter) 1085 { 1086 var minNodeId = nodeFilter.minNodeId; 1087 var maxNodeId = nodeFilter.maxNodeId; 1088 var allocationNodeId = nodeFilter.allocationNodeId; 1089 var key; 1090 var filter; 1091 if (typeof allocationNodeId === "number") { 1092 filter = this._createAllocationStackFilter(allocationNodeId); 1093 } else if (typeof minNodeId === "number" && typeof maxNodeId === "number") { 1094 key = minNodeId + ".." + maxNodeId; 1095 filter = this._createNodeIdFilter(minNodeId, maxNodeId); 1096 } else { 1097 key = "allObjects"; 1098 } 1099 return this.aggregates(false, key, filter); 1100 }, 1101 1102 /** 1103 * @param {number} minNodeId 1104 * @param {number} maxNodeId 1105 * @return {function(!WebInspector.HeapSnapshotNode):boolean} 1106 */ 1107 _createNodeIdFilter: function(minNodeId, maxNodeId) 1108 { 1109 /** 1110 * @param {!WebInspector.HeapSnapshotNode} node 1111 * @return {boolean} 1112 */ 1113 function nodeIdFilter(node) 1114 { 1115 var id = node.id(); 1116 return id > minNodeId && id <= maxNodeId; 1117 } 1118 return nodeIdFilter; 1119 }, 1120 1121 /** 1122 * @param {number} bottomUpAllocationNodeId 1123 * @return {function(!WebInspector.HeapSnapshotNode):boolean|undefined} 1124 */ 1125 _createAllocationStackFilter: function(bottomUpAllocationNodeId) 1126 { 1127 var traceIds = this._allocationProfile.traceIds(bottomUpAllocationNodeId); 1128 if (!traceIds.length) 1129 return undefined; 1130 var set = {}; 1131 for (var i = 0; i < traceIds.length; i++) 1132 set[traceIds[i]] = true; 1133 /** 1134 * @param {!WebInspector.HeapSnapshotNode} node 1135 * @return {boolean} 1136 */ 1137 function traceIdFilter(node) 1138 { 1139 return !!set[node.traceNodeId()]; 1140 }; 1141 return traceIdFilter; 1142 }, 1143 1144 /** 1145 * @param {boolean} sortedIndexes 1146 * @param {string=} key 1147 * @param {function(!WebInspector.HeapSnapshotNode):boolean=} filter 1148 * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} 1149 */ 1150 aggregates: function(sortedIndexes, key, filter) 1151 { 1152 var aggregatesByClassName = key && this._aggregates[key]; 1153 if (!aggregatesByClassName) { 1154 var aggregates = this._buildAggregates(filter); 1155 this._calculateClassesRetainedSize(aggregates.aggregatesByClassIndex, filter); 1156 aggregatesByClassName = aggregates.aggregatesByClassName; 1157 if (key) 1158 this._aggregates[key] = aggregatesByClassName; 1159 } 1160 1161 if (sortedIndexes && (!key || !this._aggregatesSortedFlags[key])) { 1162 this._sortAggregateIndexes(aggregatesByClassName); 1163 if (key) 1164 this._aggregatesSortedFlags[key] = sortedIndexes; 1165 } 1166 return aggregatesByClassName; 1167 }, 1168 1169 /** 1170 * @return {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} 1171 */ 1172 allocationTracesTops: function() 1173 { 1174 return this._allocationProfile.serializeTraceTops(); 1175 }, 1176 1177 /** 1178 * @param {number} nodeId 1179 * @return {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers} 1180 */ 1181 allocationNodeCallers: function(nodeId) 1182 { 1183 return this._allocationProfile.serializeCallers(nodeId); 1184 }, 1185 1186 /** 1187 * @param {number} nodeIndex 1188 * @return {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} 1189 */ 1190 allocationStack: function(nodeIndex) 1191 { 1192 var node = this.createNode(nodeIndex); 1193 var allocationNodeId = node.traceNodeId(); 1194 if (!allocationNodeId) 1195 return null; 1196 return this._allocationProfile.serializeAllocationStack(allocationNodeId); 1197 }, 1198 1199 /** 1200 * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>} 1201 */ 1202 aggregatesForDiff: function() 1203 { 1204 if (this._aggregatesForDiff) 1205 return this._aggregatesForDiff; 1206 1207 var aggregatesByClassName = this.aggregates(true, "allObjects"); 1208 this._aggregatesForDiff = {}; 1209 1210 var node = this.createNode(); 1211 for (var className in aggregatesByClassName) { 1212 var aggregate = aggregatesByClassName[className]; 1213 var indexes = aggregate.idxs; 1214 var ids = new Array(indexes.length); 1215 var selfSizes = new Array(indexes.length); 1216 for (var i = 0; i < indexes.length; i++) { 1217 node.nodeIndex = indexes[i]; 1218 ids[i] = node.id(); 1219 selfSizes[i] = node.selfSize(); 1220 } 1221 1222 this._aggregatesForDiff[className] = { 1223 indexes: indexes, 1224 ids: ids, 1225 selfSizes: selfSizes 1226 }; 1227 } 1228 return this._aggregatesForDiff; 1229 }, 1230 1231 /** 1232 * @param {!WebInspector.HeapSnapshotNode} node 1233 * @return {!boolean} 1234 */ 1235 _isUserRoot: function(node) 1236 { 1237 return true; 1238 }, 1239 1240 /** 1241 * @param {function(!WebInspector.HeapSnapshotNode)} action 1242 * @param {boolean=} userRootsOnly 1243 */ 1244 forEachRoot: function(action, userRootsOnly) 1245 { 1246 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) { 1247 var node = iter.edge.node(); 1248 if (!userRootsOnly || this._isUserRoot(node)) 1249 action(node); 1250 } 1251 }, 1252 1253 /** 1254 * @param {function(!WebInspector.HeapSnapshotNode,!WebInspector.HeapSnapshotEdge):boolean=} filter 1255 */ 1256 calculateDistances: function(filter) 1257 { 1258 var nodeFieldCount = this._nodeFieldCount; 1259 var nodeCount = this.nodeCount; 1260 var distances = this._nodeDistances; 1261 var noDistance = this._noDistance; 1262 for (var i = 0; i < nodeCount; ++i) 1263 distances[i] = noDistance; 1264 1265 var nodesToVisit = new Uint32Array(this.nodeCount); 1266 var nodesToVisitLength = 0; 1267 1268 /** 1269 * @param {number} distance 1270 * @param {!WebInspector.HeapSnapshotNode} node 1271 */ 1272 function enqueueNode(distance, node) 1273 { 1274 var ordinal = node.ordinal(); 1275 if (distances[ordinal] !== noDistance) 1276 return; 1277 distances[ordinal] = distance; 1278 nodesToVisit[nodesToVisitLength++] = node.nodeIndex; 1279 } 1280 1281 this.forEachRoot(enqueueNode.bind(null, 1), true); 1282 this._bfs(nodesToVisit, nodesToVisitLength, distances, filter); 1283 1284 // bfs for the rest of objects 1285 nodesToVisitLength = 0; 1286 this.forEachRoot(enqueueNode.bind(null, WebInspector.HeapSnapshotCommon.baseSystemDistance), false); 1287 this._bfs(nodesToVisit, nodesToVisitLength, distances, filter); 1288 }, 1289 1290 /** 1291 * @param {!Uint32Array} nodesToVisit 1292 * @param {!number} nodesToVisitLength 1293 * @param {!Int32Array} distances 1294 * @param {function(!WebInspector.HeapSnapshotNode,!WebInspector.HeapSnapshotEdge):boolean=} filter 1295 */ 1296 _bfs: function(nodesToVisit, nodesToVisitLength, distances, filter) 1297 { 1298 // Preload fields into local variables for better performance. 1299 var edgeFieldsCount = this._edgeFieldsCount; 1300 var nodeFieldCount = this._nodeFieldCount; 1301 var containmentEdges = this.containmentEdges; 1302 var firstEdgeIndexes = this._firstEdgeIndexes; 1303 var edgeToNodeOffset = this._edgeToNodeOffset; 1304 var edgeTypeOffset = this._edgeTypeOffset; 1305 var nodeCount = this.nodeCount; 1306 var containmentEdgesLength = containmentEdges.length; 1307 var edgeWeakType = this._edgeWeakType; 1308 var noDistance = this._noDistance; 1309 1310 var index = 0; 1311 var edge = this.createEdge(0); 1312 var node = this.createNode(0); 1313 while (index < nodesToVisitLength) { 1314 var nodeIndex = nodesToVisit[index++]; // shift generates too much garbage. 1315 var nodeOrdinal = nodeIndex / nodeFieldCount; 1316 var distance = distances[nodeOrdinal] + 1; 1317 var firstEdgeIndex = firstEdgeIndexes[nodeOrdinal]; 1318 var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1]; 1319 node.nodeIndex = nodeIndex; 1320 for (var edgeIndex = firstEdgeIndex; edgeIndex < edgesEnd; edgeIndex += edgeFieldsCount) { 1321 var edgeType = containmentEdges[edgeIndex + edgeTypeOffset]; 1322 if (edgeType === edgeWeakType) 1323 continue; 1324 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 1325 var childNodeOrdinal = childNodeIndex / nodeFieldCount; 1326 if (distances[childNodeOrdinal] !== noDistance) 1327 continue; 1328 edge.edgeIndex = edgeIndex; 1329 if (filter && !filter(node, edge)) 1330 continue; 1331 distances[childNodeOrdinal] = distance; 1332 nodesToVisit[nodesToVisitLength++] = childNodeIndex; 1333 } 1334 } 1335 if (nodesToVisitLength > nodeCount) 1336 throw new Error("BFS failed. Nodes to visit (" + nodesToVisitLength + ") is more than nodes count (" + nodeCount + ")"); 1337 }, 1338 1339 _buildAggregates: function(filter) 1340 { 1341 var aggregates = {}; 1342 var aggregatesByClassName = {}; 1343 var classIndexes = []; 1344 var nodes = this.nodes; 1345 var mapAndFlag = this.userObjectsMapAndFlag(); 1346 var flags = mapAndFlag ? mapAndFlag.map : null; 1347 var flag = mapAndFlag ? mapAndFlag.flag : 0; 1348 var nodesLength = nodes.length; 1349 var nodeNativeType = this._nodeNativeType; 1350 var nodeFieldCount = this._nodeFieldCount; 1351 var selfSizeOffset = this._nodeSelfSizeOffset; 1352 var nodeTypeOffset = this._nodeTypeOffset; 1353 var node = this.rootNode(); 1354 var nodeDistances = this._nodeDistances; 1355 1356 for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) { 1357 var nodeOrdinal = nodeIndex / nodeFieldCount; 1358 if (flags && !(flags[nodeOrdinal] & flag)) 1359 continue; 1360 node.nodeIndex = nodeIndex; 1361 if (filter && !filter(node)) 1362 continue; 1363 var selfSize = nodes[nodeIndex + selfSizeOffset]; 1364 if (!selfSize && nodes[nodeIndex + nodeTypeOffset] !== nodeNativeType) 1365 continue; 1366 var classIndex = node.classIndex(); 1367 if (!(classIndex in aggregates)) { 1368 var nodeType = node.type(); 1369 var nameMatters = nodeType === "object" || nodeType === "native"; 1370 var value = { 1371 count: 1, 1372 distance: nodeDistances[nodeOrdinal], 1373 self: selfSize, 1374 maxRet: 0, 1375 type: nodeType, 1376 name: nameMatters ? node.name() : null, 1377 idxs: [nodeIndex] 1378 }; 1379 aggregates[classIndex] = value; 1380 classIndexes.push(classIndex); 1381 aggregatesByClassName[node.className()] = value; 1382 } else { 1383 var clss = aggregates[classIndex]; 1384 clss.distance = Math.min(clss.distance, nodeDistances[nodeOrdinal]); 1385 ++clss.count; 1386 clss.self += selfSize; 1387 clss.idxs.push(nodeIndex); 1388 } 1389 } 1390 1391 // Shave off provisionally allocated space. 1392 for (var i = 0, l = classIndexes.length; i < l; ++i) { 1393 var classIndex = classIndexes[i]; 1394 aggregates[classIndex].idxs = aggregates[classIndex].idxs.slice(); 1395 } 1396 return {aggregatesByClassName: aggregatesByClassName, aggregatesByClassIndex: aggregates}; 1397 }, 1398 1399 _calculateClassesRetainedSize: function(aggregates, filter) 1400 { 1401 var rootNodeIndex = this._rootNodeIndex; 1402 var node = this.createNode(rootNodeIndex); 1403 var list = [rootNodeIndex]; 1404 var sizes = [-1]; 1405 var classes = []; 1406 var seenClassNameIndexes = {}; 1407 var nodeFieldCount = this._nodeFieldCount; 1408 var nodeTypeOffset = this._nodeTypeOffset; 1409 var nodeNativeType = this._nodeNativeType; 1410 var dominatedNodes = this._dominatedNodes; 1411 var nodes = this.nodes; 1412 var mapAndFlag = this.userObjectsMapAndFlag(); 1413 var flags = mapAndFlag ? mapAndFlag.map : null; 1414 var flag = mapAndFlag ? mapAndFlag.flag : 0; 1415 var firstDominatedNodeIndex = this._firstDominatedNodeIndex; 1416 1417 while (list.length) { 1418 var nodeIndex = list.pop(); 1419 node.nodeIndex = nodeIndex; 1420 var classIndex = node.classIndex(); 1421 var seen = !!seenClassNameIndexes[classIndex]; 1422 var nodeOrdinal = nodeIndex / nodeFieldCount; 1423 var dominatedIndexFrom = firstDominatedNodeIndex[nodeOrdinal]; 1424 var dominatedIndexTo = firstDominatedNodeIndex[nodeOrdinal + 1]; 1425 1426 if (!seen && 1427 (!flags || (flags[nodeOrdinal] & flag)) && 1428 (!filter || filter(node)) && 1429 (node.selfSize() || nodes[nodeIndex + nodeTypeOffset] === nodeNativeType) 1430 ) { 1431 aggregates[classIndex].maxRet += node.retainedSize(); 1432 if (dominatedIndexFrom !== dominatedIndexTo) { 1433 seenClassNameIndexes[classIndex] = true; 1434 sizes.push(list.length); 1435 classes.push(classIndex); 1436 } 1437 } 1438 for (var i = dominatedIndexFrom; i < dominatedIndexTo; i++) 1439 list.push(dominatedNodes[i]); 1440 1441 var l = list.length; 1442 while (sizes[sizes.length - 1] === l) { 1443 sizes.pop(); 1444 classIndex = classes.pop(); 1445 seenClassNameIndexes[classIndex] = false; 1446 } 1447 } 1448 }, 1449 1450 _sortAggregateIndexes: function(aggregates) 1451 { 1452 var nodeA = this.createNode(); 1453 var nodeB = this.createNode(); 1454 for (var clss in aggregates) 1455 aggregates[clss].idxs.sort( 1456 function(idxA, idxB) { 1457 nodeA.nodeIndex = idxA; 1458 nodeB.nodeIndex = idxB; 1459 return nodeA.id() < nodeB.id() ? -1 : 1; 1460 }); 1461 }, 1462 1463 _buildPostOrderIndex: function() 1464 { 1465 var nodeFieldCount = this._nodeFieldCount; 1466 var nodes = this.nodes; 1467 var nodeCount = this.nodeCount; 1468 var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount; 1469 1470 var edgeFieldsCount = this._edgeFieldsCount; 1471 var edgeTypeOffset = this._edgeTypeOffset; 1472 var edgeToNodeOffset = this._edgeToNodeOffset; 1473 var edgeShortcutType = this._edgeShortcutType; 1474 var firstEdgeIndexes = this._firstEdgeIndexes; 1475 var containmentEdges = this.containmentEdges; 1476 1477 var mapAndFlag = this.userObjectsMapAndFlag(); 1478 var flags = mapAndFlag ? mapAndFlag.map : null; 1479 var flag = mapAndFlag ? mapAndFlag.flag : 0; 1480 1481 var stackNodes = new Uint32Array(nodeCount); 1482 var stackCurrentEdge = new Uint32Array(nodeCount); 1483 var postOrderIndex2NodeOrdinal = new Uint32Array(nodeCount); 1484 var nodeOrdinal2PostOrderIndex = new Uint32Array(nodeCount); 1485 var visited = new Uint8Array(nodeCount); 1486 var postOrderIndex = 0; 1487 1488 var stackTop = 0; 1489 stackNodes[0] = rootNodeOrdinal; 1490 stackCurrentEdge[0] = firstEdgeIndexes[rootNodeOrdinal]; 1491 visited[rootNodeOrdinal] = 1; 1492 1493 while (stackTop >= 0) { 1494 var nodeOrdinal = stackNodes[stackTop]; 1495 var edgeIndex = stackCurrentEdge[stackTop]; 1496 var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1]; 1497 1498 if (edgeIndex < edgesEnd) { 1499 stackCurrentEdge[stackTop] += edgeFieldsCount; 1500 if (nodeOrdinal !== rootNodeOrdinal && containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType) 1501 continue; 1502 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 1503 var childNodeOrdinal = childNodeIndex / nodeFieldCount; 1504 if (visited[childNodeOrdinal]) 1505 continue; 1506 var nodeFlag = !flags || (flags[nodeOrdinal] & flag); 1507 var childNodeFlag = !flags || (flags[childNodeOrdinal] & flag); 1508 // We are skipping the edges from non-page-owned nodes to page-owned nodes. 1509 // Otherwise the dominators for the objects that also were retained by debugger would be affected. 1510 if (nodeOrdinal !== rootNodeOrdinal && childNodeFlag && !nodeFlag) 1511 continue; 1512 ++stackTop; 1513 stackNodes[stackTop] = childNodeOrdinal; 1514 stackCurrentEdge[stackTop] = firstEdgeIndexes[childNodeOrdinal]; 1515 visited[childNodeOrdinal] = 1; 1516 } else { 1517 // Done with all the node children 1518 nodeOrdinal2PostOrderIndex[nodeOrdinal] = postOrderIndex; 1519 postOrderIndex2NodeOrdinal[postOrderIndex++] = nodeOrdinal; 1520 --stackTop; 1521 } 1522 } 1523 1524 if (postOrderIndex !== nodeCount) { 1525 console.log("Error: Corrupted snapshot. " + (nodeCount - postOrderIndex) + " nodes are unreachable from the root:"); 1526 var dumpNode = this.rootNode(); 1527 for (var i = 0; i < nodeCount; ++i) { 1528 if (!visited[i]) { 1529 // Fix it by giving the node a postorder index anyway. 1530 nodeOrdinal2PostOrderIndex[i] = postOrderIndex; 1531 postOrderIndex2NodeOrdinal[postOrderIndex++] = i; 1532 dumpNode.nodeIndex = i * nodeFieldCount; 1533 console.log(JSON.stringify(dumpNode.serialize())); 1534 for (var retainers = dumpNode.retainers(); retainers.hasNext(); retainers = retainers.item().node().retainers()) 1535 console.log(" edgeName: " + retainers.item().name() + " nodeClassName: " + retainers.item().node().className()); 1536 } 1537 } 1538 } 1539 1540 return {postOrderIndex2NodeOrdinal: postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex: nodeOrdinal2PostOrderIndex}; 1541 }, 1542 1543 // The algorithm is based on the article: 1544 // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm" 1545 // Softw. Pract. Exper. 4 (2001), pp. 1-10. 1546 /** 1547 * @param {!Array.<number>} postOrderIndex2NodeOrdinal 1548 * @param {!Array.<number>} nodeOrdinal2PostOrderIndex 1549 */ 1550 _buildDominatorTree: function(postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex) 1551 { 1552 var nodeFieldCount = this._nodeFieldCount; 1553 var nodes = this.nodes; 1554 var firstRetainerIndex = this._firstRetainerIndex; 1555 var retainingNodes = this._retainingNodes; 1556 var retainingEdges = this._retainingEdges; 1557 var edgeFieldsCount = this._edgeFieldsCount; 1558 var edgeTypeOffset = this._edgeTypeOffset; 1559 var edgeToNodeOffset = this._edgeToNodeOffset; 1560 var edgeShortcutType = this._edgeShortcutType; 1561 var firstEdgeIndexes = this._firstEdgeIndexes; 1562 var containmentEdges = this.containmentEdges; 1563 var containmentEdgesLength = this.containmentEdges.length; 1564 var rootNodeIndex = this._rootNodeIndex; 1565 1566 var mapAndFlag = this.userObjectsMapAndFlag(); 1567 var flags = mapAndFlag ? mapAndFlag.map : null; 1568 var flag = mapAndFlag ? mapAndFlag.flag : 0; 1569 1570 var nodesCount = postOrderIndex2NodeOrdinal.length; 1571 var rootPostOrderedIndex = nodesCount - 1; 1572 var noEntry = nodesCount; 1573 var dominators = new Uint32Array(nodesCount); 1574 for (var i = 0; i < rootPostOrderedIndex; ++i) 1575 dominators[i] = noEntry; 1576 dominators[rootPostOrderedIndex] = rootPostOrderedIndex; 1577 1578 // The affected array is used to mark entries which dominators 1579 // have to be racalculated because of changes in their retainers. 1580 var affected = new Uint8Array(nodesCount); 1581 var nodeOrdinal; 1582 1583 { // Mark the root direct children as affected. 1584 nodeOrdinal = this._rootNodeIndex / nodeFieldCount; 1585 var beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset; 1586 var endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1]; 1587 for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex; 1588 toNodeFieldIndex < endEdgeToNodeFieldIndex; 1589 toNodeFieldIndex += edgeFieldsCount) { 1590 var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount; 1591 affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1; 1592 } 1593 } 1594 1595 var changed = true; 1596 while (changed) { 1597 changed = false; 1598 for (var postOrderIndex = rootPostOrderedIndex - 1; postOrderIndex >= 0; --postOrderIndex) { 1599 if (affected[postOrderIndex] === 0) 1600 continue; 1601 affected[postOrderIndex] = 0; 1602 // If dominator of the entry has already been set to root, 1603 // then it can't propagate any further. 1604 if (dominators[postOrderIndex] === rootPostOrderedIndex) 1605 continue; 1606 nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; 1607 var nodeFlag = !flags || (flags[nodeOrdinal] & flag); 1608 var newDominatorIndex = noEntry; 1609 var beginRetainerIndex = firstRetainerIndex[nodeOrdinal]; 1610 var endRetainerIndex = firstRetainerIndex[nodeOrdinal + 1]; 1611 for (var retainerIndex = beginRetainerIndex; retainerIndex < endRetainerIndex; ++retainerIndex) { 1612 var retainerEdgeIndex = retainingEdges[retainerIndex]; 1613 var retainerEdgeType = containmentEdges[retainerEdgeIndex + edgeTypeOffset]; 1614 var retainerNodeIndex = retainingNodes[retainerIndex]; 1615 if (retainerNodeIndex !== rootNodeIndex && retainerEdgeType === edgeShortcutType) 1616 continue; 1617 var retainerNodeOrdinal = retainerNodeIndex / nodeFieldCount; 1618 var retainerNodeFlag = !flags || (flags[retainerNodeOrdinal] & flag); 1619 // We are skipping the edges from non-page-owned nodes to page-owned nodes. 1620 // Otherwise the dominators for the objects that also were retained by debugger would be affected. 1621 if (retainerNodeIndex !== rootNodeIndex && nodeFlag && !retainerNodeFlag) 1622 continue; 1623 var retanerPostOrderIndex = nodeOrdinal2PostOrderIndex[retainerNodeOrdinal]; 1624 if (dominators[retanerPostOrderIndex] !== noEntry) { 1625 if (newDominatorIndex === noEntry) 1626 newDominatorIndex = retanerPostOrderIndex; 1627 else { 1628 while (retanerPostOrderIndex !== newDominatorIndex) { 1629 while (retanerPostOrderIndex < newDominatorIndex) 1630 retanerPostOrderIndex = dominators[retanerPostOrderIndex]; 1631 while (newDominatorIndex < retanerPostOrderIndex) 1632 newDominatorIndex = dominators[newDominatorIndex]; 1633 } 1634 } 1635 // If idom has already reached the root, it doesn't make sense 1636 // to check other retainers. 1637 if (newDominatorIndex === rootPostOrderedIndex) 1638 break; 1639 } 1640 } 1641 if (newDominatorIndex !== noEntry && dominators[postOrderIndex] !== newDominatorIndex) { 1642 dominators[postOrderIndex] = newDominatorIndex; 1643 changed = true; 1644 nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; 1645 beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset; 1646 endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1]; 1647 for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex; 1648 toNodeFieldIndex < endEdgeToNodeFieldIndex; 1649 toNodeFieldIndex += edgeFieldsCount) { 1650 var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount; 1651 affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1; 1652 } 1653 } 1654 } 1655 } 1656 1657 var dominatorsTree = new Uint32Array(nodesCount); 1658 for (var postOrderIndex = 0, l = dominators.length; postOrderIndex < l; ++postOrderIndex) { 1659 nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; 1660 dominatorsTree[nodeOrdinal] = postOrderIndex2NodeOrdinal[dominators[postOrderIndex]]; 1661 } 1662 return dominatorsTree; 1663 }, 1664 1665 _calculateRetainedSizes: function(postOrderIndex2NodeOrdinal) 1666 { 1667 var nodeCount = this.nodeCount; 1668 var nodes = this.nodes; 1669 var nodeSelfSizeOffset = this._nodeSelfSizeOffset; 1670 var nodeFieldCount = this._nodeFieldCount; 1671 var dominatorsTree = this._dominatorsTree; 1672 var retainedSizes = this._retainedSizes; 1673 1674 for (var nodeOrdinal = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) 1675 retainedSizes[nodeOrdinal] = nodes[nodeOrdinal * nodeFieldCount + nodeSelfSizeOffset]; 1676 1677 // Propagate retained sizes for each node excluding root. 1678 for (var postOrderIndex = 0; postOrderIndex < nodeCount - 1; ++postOrderIndex) { 1679 var nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; 1680 var dominatorOrdinal = dominatorsTree[nodeOrdinal]; 1681 retainedSizes[dominatorOrdinal] += retainedSizes[nodeOrdinal]; 1682 } 1683 }, 1684 1685 _buildDominatedNodes: function() 1686 { 1687 // Builds up two arrays: 1688 // - "dominatedNodes" is a continuous array, where each node owns an 1689 // interval (can be empty) with corresponding dominated nodes. 1690 // - "indexArray" is an array of indexes in the "dominatedNodes" 1691 // with the same positions as in the _nodeIndex. 1692 var indexArray = this._firstDominatedNodeIndex; 1693 // All nodes except the root have dominators. 1694 var dominatedNodes = this._dominatedNodes; 1695 1696 // Count the number of dominated nodes for each node. Skip the root (node at 1697 // index 0) as it is the only node that dominates itself. 1698 var nodeFieldCount = this._nodeFieldCount; 1699 var dominatorsTree = this._dominatorsTree; 1700 1701 var fromNodeOrdinal = 0; 1702 var toNodeOrdinal = this.nodeCount; 1703 var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount; 1704 if (rootNodeOrdinal === fromNodeOrdinal) 1705 fromNodeOrdinal = 1; 1706 else if (rootNodeOrdinal === toNodeOrdinal - 1) 1707 toNodeOrdinal = toNodeOrdinal - 1; 1708 else 1709 throw new Error("Root node is expected to be either first or last"); 1710 for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal) 1711 ++indexArray[dominatorsTree[nodeOrdinal]]; 1712 // Put in the first slot of each dominatedNodes slice the count of entries 1713 // that will be filled. 1714 var firstDominatedNodeIndex = 0; 1715 for (var i = 0, l = this.nodeCount; i < l; ++i) { 1716 var dominatedCount = dominatedNodes[firstDominatedNodeIndex] = indexArray[i]; 1717 indexArray[i] = firstDominatedNodeIndex; 1718 firstDominatedNodeIndex += dominatedCount; 1719 } 1720 indexArray[this.nodeCount] = dominatedNodes.length; 1721 // Fill up the dominatedNodes array with indexes of dominated nodes. Skip the root (node at 1722 // index 0) as it is the only node that dominates itself. 1723 for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal) { 1724 var dominatorOrdinal = dominatorsTree[nodeOrdinal]; 1725 var dominatedRefIndex = indexArray[dominatorOrdinal]; 1726 dominatedRefIndex += (--dominatedNodes[dominatedRefIndex]); 1727 dominatedNodes[dominatedRefIndex] = nodeOrdinal * nodeFieldCount; 1728 } 1729 }, 1730 1731 _calculateFlags: function() 1732 { 1733 throw new Error("Not implemented"); 1734 }, 1735 1736 _calculateStatistics: function() 1737 { 1738 throw new Error("Not implemented"); 1739 }, 1740 1741 userObjectsMapAndFlag: function() 1742 { 1743 throw new Error("Not implemented"); 1744 }, 1745 1746 /** 1747 * @param {string} baseSnapshotId 1748 * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>} baseSnapshotAggregates 1749 * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Diff>} 1750 */ 1751 calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates) 1752 { 1753 var snapshotDiff = this._snapshotDiffs[baseSnapshotId]; 1754 if (snapshotDiff) 1755 return snapshotDiff; 1756 snapshotDiff = {}; 1757 1758 var aggregates = this.aggregates(true, "allObjects"); 1759 for (var className in baseSnapshotAggregates) { 1760 var baseAggregate = baseSnapshotAggregates[className]; 1761 var diff = this._calculateDiffForClass(baseAggregate, aggregates[className]); 1762 if (diff) 1763 snapshotDiff[className] = diff; 1764 } 1765 var emptyBaseAggregate = new WebInspector.HeapSnapshotCommon.AggregateForDiff(); 1766 for (var className in aggregates) { 1767 if (className in baseSnapshotAggregates) 1768 continue; 1769 snapshotDiff[className] = this._calculateDiffForClass(emptyBaseAggregate, aggregates[className]); 1770 } 1771 1772 this._snapshotDiffs[baseSnapshotId] = snapshotDiff; 1773 return snapshotDiff; 1774 }, 1775 1776 /** 1777 * @param {!WebInspector.HeapSnapshotCommon.AggregateForDiff} baseAggregate 1778 * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate 1779 * @return {?WebInspector.HeapSnapshotCommon.Diff} 1780 */ 1781 _calculateDiffForClass: function(baseAggregate, aggregate) 1782 { 1783 var baseIds = baseAggregate.ids; 1784 var baseIndexes = baseAggregate.indexes; 1785 var baseSelfSizes = baseAggregate.selfSizes; 1786 1787 var indexes = aggregate ? aggregate.idxs : []; 1788 1789 var i = 0, l = baseIds.length; 1790 var j = 0, m = indexes.length; 1791 var diff = new WebInspector.HeapSnapshotCommon.Diff(); 1792 1793 var nodeB = this.createNode(indexes[j]); 1794 while (i < l && j < m) { 1795 var nodeAId = baseIds[i]; 1796 if (nodeAId < nodeB.id()) { 1797 diff.deletedIndexes.push(baseIndexes[i]); 1798 diff.removedCount++; 1799 diff.removedSize += baseSelfSizes[i]; 1800 ++i; 1801 } else if (nodeAId > nodeB.id()) { // Native nodes(e.g. dom groups) may have ids less than max JS object id in the base snapshot 1802 diff.addedIndexes.push(indexes[j]); 1803 diff.addedCount++; 1804 diff.addedSize += nodeB.selfSize(); 1805 nodeB.nodeIndex = indexes[++j]; 1806 } else { // nodeAId === nodeB.id() 1807 ++i; 1808 nodeB.nodeIndex = indexes[++j]; 1809 } 1810 } 1811 while (i < l) { 1812 diff.deletedIndexes.push(baseIndexes[i]); 1813 diff.removedCount++; 1814 diff.removedSize += baseSelfSizes[i]; 1815 ++i; 1816 } 1817 while (j < m) { 1818 diff.addedIndexes.push(indexes[j]); 1819 diff.addedCount++; 1820 diff.addedSize += nodeB.selfSize(); 1821 nodeB.nodeIndex = indexes[++j]; 1822 } 1823 diff.countDelta = diff.addedCount - diff.removedCount; 1824 diff.sizeDelta = diff.addedSize - diff.removedSize; 1825 if (!diff.addedCount && !diff.removedCount) 1826 return null; 1827 return diff; 1828 }, 1829 1830 _nodeForSnapshotObjectId: function(snapshotObjectId) 1831 { 1832 for (var it = this._allNodes(); it.hasNext(); it.next()) { 1833 if (it.node.id() === snapshotObjectId) 1834 return it.node; 1835 } 1836 return null; 1837 }, 1838 1839 /** 1840 * @param {string} snapshotObjectId 1841 * @return {?string} 1842 */ 1843 nodeClassName: function(snapshotObjectId) 1844 { 1845 var node = this._nodeForSnapshotObjectId(snapshotObjectId); 1846 if (node) 1847 return node.className(); 1848 return null; 1849 }, 1850 1851 /** 1852 * @param {string} name 1853 * @return {!Array.<number>} 1854 */ 1855 idsOfObjectsWithName: function(name) 1856 { 1857 var ids = []; 1858 for (var it = this._allNodes(); it.hasNext(); it.next()) { 1859 if (it.item().name() === name) 1860 ids.push(it.item().id()); 1861 } 1862 return ids; 1863 }, 1864 1865 /** 1866 * @param {number} nodeIndex 1867 * @return {!WebInspector.HeapSnapshotEdgesProvider} 1868 */ 1869 createEdgesProvider: function(nodeIndex) 1870 { 1871 var node = this.createNode(nodeIndex); 1872 var filter = this.containmentEdgesFilter(); 1873 var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this); 1874 return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider); 1875 }, 1876 1877 /** 1878 * @param {number} nodeIndex 1879 * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter 1880 * @return {!WebInspector.HeapSnapshotEdgesProvider} 1881 */ 1882 createEdgesProviderForTest: function(nodeIndex, filter) 1883 { 1884 var node = this.createNode(nodeIndex); 1885 var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this); 1886 return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider); 1887 }, 1888 1889 /** 1890 * @return {?function(!WebInspector.HeapSnapshotEdge):boolean} 1891 */ 1892 retainingEdgesFilter: function() 1893 { 1894 return null; 1895 }, 1896 1897 /** 1898 * @return {?function(!WebInspector.HeapSnapshotEdge):boolean} 1899 */ 1900 containmentEdgesFilter: function() 1901 { 1902 return null; 1903 }, 1904 1905 /** 1906 * @param {number} nodeIndex 1907 * @return {!WebInspector.HeapSnapshotEdgesProvider} 1908 */ 1909 createRetainingEdgesProvider: function(nodeIndex) 1910 { 1911 var node = this.createNode(nodeIndex); 1912 var filter = this.retainingEdgesFilter(); 1913 var indexProvider = new WebInspector.HeapSnapshotRetainerEdgeIndexProvider(this); 1914 return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.retainers(), indexProvider); 1915 }, 1916 1917 /** 1918 * @param {string} baseSnapshotId 1919 * @param {string} className 1920 * @return {!WebInspector.HeapSnapshotNodesProvider} 1921 */ 1922 createAddedNodesProvider: function(baseSnapshotId, className) 1923 { 1924 var snapshotDiff = this._snapshotDiffs[baseSnapshotId]; 1925 var diffForClass = snapshotDiff[className]; 1926 return new WebInspector.HeapSnapshotNodesProvider(this, null, diffForClass.addedIndexes); 1927 }, 1928 1929 /** 1930 * @param {!Array.<number>} nodeIndexes 1931 * @return {!WebInspector.HeapSnapshotNodesProvider} 1932 */ 1933 createDeletedNodesProvider: function(nodeIndexes) 1934 { 1935 return new WebInspector.HeapSnapshotNodesProvider(this, null, nodeIndexes); 1936 }, 1937 1938 /** 1939 * @return {?function(!WebInspector.HeapSnapshotNode):boolean} 1940 */ 1941 classNodesFilter: function() 1942 { 1943 return null; 1944 }, 1945 1946 /** 1947 * @param {string} className 1948 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter 1949 * @return {!WebInspector.HeapSnapshotNodesProvider} 1950 */ 1951 createNodesProviderForClass: function(className, nodeFilter) 1952 { 1953 return new WebInspector.HeapSnapshotNodesProvider(this, this.classNodesFilter(), this.aggregatesWithFilter(nodeFilter)[className].idxs); 1954 }, 1955 1956 /** 1957 * @return {number} 1958 */ 1959 _maxJsNodeId: function() 1960 { 1961 var nodeFieldCount = this._nodeFieldCount; 1962 var nodes = this.nodes; 1963 var nodesLength = nodes.length; 1964 var id = 0; 1965 for (var nodeIndex = this._nodeIdOffset; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) { 1966 var nextId = nodes[nodeIndex]; 1967 // JS objects have odd ids, skip native objects. 1968 if (nextId % 2 === 0) 1969 continue; 1970 if (id < nextId) 1971 id = nextId; 1972 } 1973 return id; 1974 }, 1975 1976 /** 1977 * @return {!WebInspector.HeapSnapshotCommon.StaticData} 1978 */ 1979 updateStaticData: function() 1980 { 1981 return new WebInspector.HeapSnapshotCommon.StaticData(this.nodeCount, this._rootNodeIndex, this.totalSize, this._maxJsNodeId()); 1982 } 1983 }; 1984 1985 /** 1986 * @constructor 1987 * @param {!WebInspector.HeapSnapshotItemIterator} iterator 1988 * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider 1989 */ 1990 WebInspector.HeapSnapshotItemProvider = function(iterator, indexProvider) 1991 { 1992 this._iterator = iterator; 1993 this._indexProvider = indexProvider; 1994 this._isEmpty = !iterator.hasNext(); 1995 /** @type {?Array.<number>} */ 1996 this._iterationOrder = null; 1997 this._currentComparator = null; 1998 this._sortedPrefixLength = 0; 1999 this._sortedSuffixLength = 0; 2000 } 2001 2002 WebInspector.HeapSnapshotItemProvider.prototype = { 2003 _createIterationOrder: function() 2004 { 2005 if (this._iterationOrder) 2006 return; 2007 this._iterationOrder = []; 2008 for (var iterator = this._iterator; iterator.hasNext(); iterator.next()) 2009 this._iterationOrder.push(iterator.item().itemIndex()); 2010 }, 2011 2012 /** 2013 * @return {boolean} 2014 */ 2015 isEmpty: function() 2016 { 2017 return this._isEmpty; 2018 }, 2019 2020 /** 2021 * @param {number} begin 2022 * @param {number} end 2023 * @return {!WebInspector.HeapSnapshotCommon.ItemsRange} 2024 */ 2025 serializeItemsRange: function(begin, end) 2026 { 2027 this._createIterationOrder(); 2028 if (begin > end) 2029 throw new Error("Start position > end position: " + begin + " > " + end); 2030 if (end > this._iterationOrder.length) 2031 end = this._iterationOrder.length; 2032 if (this._sortedPrefixLength < end && begin < this._iterationOrder.length - this._sortedSuffixLength) { 2033 this.sort(this._currentComparator, this._sortedPrefixLength, this._iterationOrder.length - 1 - this._sortedSuffixLength, begin, end - 1); 2034 if (begin <= this._sortedPrefixLength) 2035 this._sortedPrefixLength = end; 2036 if (end >= this._iterationOrder.length - this._sortedSuffixLength) 2037 this._sortedSuffixLength = this._iterationOrder.length - begin; 2038 } 2039 var position = begin; 2040 var count = end - begin; 2041 var result = new Array(count); 2042 var iterator = this._iterator; 2043 for (var i = 0 ; i < count; ++i) { 2044 var itemIndex = this._iterationOrder[position++]; 2045 var item = this._indexProvider.itemForIndex(itemIndex); 2046 result[i] = item.serialize(); 2047 } 2048 return new WebInspector.HeapSnapshotCommon.ItemsRange(begin, end, this._iterationOrder.length, result); 2049 }, 2050 2051 sortAndRewind: function(comparator) 2052 { 2053 this._currentComparator = comparator; 2054 this._sortedPrefixLength = 0; 2055 this._sortedSuffixLength = 0; 2056 } 2057 } 2058 2059 /** 2060 * @constructor 2061 * @extends {WebInspector.HeapSnapshotItemProvider} 2062 * @param {!WebInspector.HeapSnapshot} snapshot 2063 * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter 2064 * @param {!WebInspector.HeapSnapshotEdgeIterator} edgesIter 2065 * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider 2066 */ 2067 WebInspector.HeapSnapshotEdgesProvider = function(snapshot, filter, edgesIter, indexProvider) 2068 { 2069 this.snapshot = snapshot; 2070 var iter = filter ? new WebInspector.HeapSnapshotFilteredIterator(edgesIter, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter)) : edgesIter; 2071 WebInspector.HeapSnapshotItemProvider.call(this, iter, indexProvider); 2072 } 2073 2074 WebInspector.HeapSnapshotEdgesProvider.prototype = { 2075 /** 2076 * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator 2077 * @param {number} leftBound 2078 * @param {number} rightBound 2079 * @param {number} windowLeft 2080 * @param {number} windowRight 2081 */ 2082 sort: function(comparator, leftBound, rightBound, windowLeft, windowRight) 2083 { 2084 var fieldName1 = comparator.fieldName1; 2085 var fieldName2 = comparator.fieldName2; 2086 var ascending1 = comparator.ascending1; 2087 var ascending2 = comparator.ascending2; 2088 2089 var edgeA = this._iterator.item().clone(); 2090 var edgeB = edgeA.clone(); 2091 var nodeA = this.snapshot.createNode(); 2092 var nodeB = this.snapshot.createNode(); 2093 2094 function compareEdgeFieldName(ascending, indexA, indexB) 2095 { 2096 edgeA.edgeIndex = indexA; 2097 edgeB.edgeIndex = indexB; 2098 if (edgeB.name() === "__proto__") return -1; 2099 if (edgeA.name() === "__proto__") return 1; 2100 var result = 2101 edgeA.hasStringName() === edgeB.hasStringName() ? 2102 (edgeA.name() < edgeB.name() ? -1 : (edgeA.name() > edgeB.name() ? 1 : 0)) : 2103 (edgeA.hasStringName() ? -1 : 1); 2104 return ascending ? result : -result; 2105 } 2106 2107 function compareNodeField(fieldName, ascending, indexA, indexB) 2108 { 2109 edgeA.edgeIndex = indexA; 2110 nodeA.nodeIndex = edgeA.nodeIndex(); 2111 var valueA = nodeA[fieldName](); 2112 2113 edgeB.edgeIndex = indexB; 2114 nodeB.nodeIndex = edgeB.nodeIndex(); 2115 var valueB = nodeB[fieldName](); 2116 2117 var result = valueA < valueB ? -1 : (valueA > valueB ? 1 : 0); 2118 return ascending ? result : -result; 2119 } 2120 2121 function compareEdgeAndNode(indexA, indexB) { 2122 var result = compareEdgeFieldName(ascending1, indexA, indexB); 2123 if (result === 0) 2124 result = compareNodeField(fieldName2, ascending2, indexA, indexB); 2125 if (result === 0) 2126 return indexA - indexB; 2127 return result; 2128 } 2129 2130 function compareNodeAndEdge(indexA, indexB) { 2131 var result = compareNodeField(fieldName1, ascending1, indexA, indexB); 2132 if (result === 0) 2133 result = compareEdgeFieldName(ascending2, indexA, indexB); 2134 if (result === 0) 2135 return indexA - indexB; 2136 return result; 2137 } 2138 2139 function compareNodeAndNode(indexA, indexB) { 2140 var result = compareNodeField(fieldName1, ascending1, indexA, indexB); 2141 if (result === 0) 2142 result = compareNodeField(fieldName2, ascending2, indexA, indexB); 2143 if (result === 0) 2144 return indexA - indexB; 2145 return result; 2146 } 2147 2148 if (fieldName1 === "!edgeName") 2149 this._iterationOrder.sortRange(compareEdgeAndNode, leftBound, rightBound, windowLeft, windowRight); 2150 else if (fieldName2 === "!edgeName") 2151 this._iterationOrder.sortRange(compareNodeAndEdge, leftBound, rightBound, windowLeft, windowRight); 2152 else 2153 this._iterationOrder.sortRange(compareNodeAndNode, leftBound, rightBound, windowLeft, windowRight); 2154 }, 2155 2156 __proto__: WebInspector.HeapSnapshotItemProvider.prototype 2157 } 2158 2159 2160 /** 2161 * @constructor 2162 * @extends {WebInspector.HeapSnapshotItemProvider} 2163 * @param {!WebInspector.HeapSnapshot} snapshot 2164 * @param {?function(!WebInspector.HeapSnapshotNode):boolean} filter 2165 * @param {(!Array.<number>|!Uint32Array)} nodeIndexes 2166 */ 2167 WebInspector.HeapSnapshotNodesProvider = function(snapshot, filter, nodeIndexes) 2168 { 2169 this.snapshot = snapshot; 2170 var indexProvider = new WebInspector.HeapSnapshotNodeIndexProvider(snapshot); 2171 var it = new WebInspector.HeapSnapshotIndexRangeIterator(indexProvider, nodeIndexes); 2172 2173 if (filter) 2174 it = new WebInspector.HeapSnapshotFilteredIterator(it, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter)); 2175 WebInspector.HeapSnapshotItemProvider.call(this, it, indexProvider); 2176 } 2177 2178 WebInspector.HeapSnapshotNodesProvider.prototype = { 2179 /** 2180 * @param {string} snapshotObjectId 2181 * @return {number} 2182 */ 2183 nodePosition: function(snapshotObjectId) 2184 { 2185 this._createIterationOrder(); 2186 var node = this.snapshot.createNode(); 2187 for (var i = 0; i < this._iterationOrder.length; i++) { 2188 node.nodeIndex = this._iterationOrder[i]; 2189 if (node.id() === snapshotObjectId) 2190 break; 2191 } 2192 if (i === this._iterationOrder.length) 2193 return -1; 2194 var targetNodeIndex = this._iterationOrder[i]; 2195 var smallerCount = 0; 2196 var compare = this._buildCompareFunction(this._currentComparator); 2197 for (var i = 0; i < this._iterationOrder.length; i++) { 2198 if (compare(this._iterationOrder[i], targetNodeIndex) < 0) 2199 ++smallerCount; 2200 } 2201 return smallerCount; 2202 }, 2203 2204 /** 2205 * @return {function(number,number):number} 2206 */ 2207 _buildCompareFunction: function(comparator) 2208 { 2209 var nodeA = this.snapshot.createNode(); 2210 var nodeB = this.snapshot.createNode(); 2211 var fieldAccessor1 = nodeA[comparator.fieldName1]; 2212 var fieldAccessor2 = nodeA[comparator.fieldName2]; 2213 var ascending1 = comparator.ascending1 ? 1 : -1; 2214 var ascending2 = comparator.ascending2 ? 1 : -1; 2215 2216 /** 2217 * @param {function():*} fieldAccessor 2218 * @param {number} ascending 2219 * @return {number} 2220 */ 2221 function sortByNodeField(fieldAccessor, ascending) 2222 { 2223 var valueA = fieldAccessor.call(nodeA); 2224 var valueB = fieldAccessor.call(nodeB); 2225 return valueA < valueB ? -ascending : (valueA > valueB ? ascending : 0); 2226 } 2227 2228 /** 2229 * @param {number} indexA 2230 * @param {number} indexB 2231 * @return {number} 2232 */ 2233 function sortByComparator(indexA, indexB) 2234 { 2235 nodeA.nodeIndex = indexA; 2236 nodeB.nodeIndex = indexB; 2237 var result = sortByNodeField(fieldAccessor1, ascending1); 2238 if (result === 0) 2239 result = sortByNodeField(fieldAccessor2, ascending2); 2240 return result || indexA - indexB; 2241 } 2242 2243 return sortByComparator; 2244 }, 2245 2246 /** 2247 * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator 2248 * @param {number} leftBound 2249 * @param {number} rightBound 2250 * @param {number} windowLeft 2251 * @param {number} windowRight 2252 */ 2253 sort: function(comparator, leftBound, rightBound, windowLeft, windowRight) 2254 { 2255 this._iterationOrder.sortRange(this._buildCompareFunction(comparator), leftBound, rightBound, windowLeft, windowRight); 2256 }, 2257 2258 __proto__: WebInspector.HeapSnapshotItemProvider.prototype 2259 } 2260 2261