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 */ 34WebInspector.HeapSnapshotItem = function() { } 35 36WebInspector.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 */ 54WebInspector.HeapSnapshotEdge = function(snapshot, edgeIndex) 55{ 56 this._snapshot = snapshot; 57 this._edges = snapshot._containmentEdges; 58 this.edgeIndex = edgeIndex || 0; 59} 60 61WebInspector.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 */ 146WebInspector.HeapSnapshotItemIterator = function() { } 147 148WebInspector.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 */ 166WebInspector.HeapSnapshotItemIndexProvider = function() { } 167 168WebInspector.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 */ 181WebInspector.HeapSnapshotNodeIndexProvider = function(snapshot) 182{ 183 this._node = snapshot.createNode(); 184} 185 186WebInspector.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 */ 204WebInspector.HeapSnapshotEdgeIndexProvider = function(snapshot) 205{ 206 this._edge = snapshot.createEdge(0); 207} 208 209WebInspector.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 */ 227WebInspector.HeapSnapshotRetainerEdgeIndexProvider = function(snapshot) 228{ 229 this._retainerEdge = snapshot.createRetainingEdge(0); 230} 231 232WebInspector.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 */ 250WebInspector.HeapSnapshotEdgeIterator = function(node) 251{ 252 this._sourceNode = node; 253 this.edge = node._snapshot.createEdge(node._edgeIndexesStart()); 254} 255 256WebInspector.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 */ 285WebInspector.HeapSnapshotRetainerEdge = function(snapshot, retainerIndex) 286{ 287 this._snapshot = snapshot; 288 this.setRetainerIndex(retainerIndex); 289} 290 291WebInspector.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 */ 416WebInspector.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 425WebInspector.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 */ 454WebInspector.HeapSnapshotNode = function(snapshot, nodeIndex) 455{ 456 this._snapshot = snapshot; 457 this.nodeIndex = nodeIndex || 0; 458} 459 460WebInspector.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 */ 660WebInspector.HeapSnapshotNodeIterator = function(node) 661{ 662 this.node = node; 663 this._nodesLength = node._snapshot._nodes.length; 664} 665 666WebInspector.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 */ 696WebInspector.HeapSnapshotIndexRangeIterator = function(itemProvider, indexes) 697{ 698 this._itemProvider = itemProvider; 699 this._indexes = indexes; 700 this._position = 0; 701} 702 703WebInspector.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 */ 734WebInspector.HeapSnapshotFilteredIterator = function(iterator, filter) 735{ 736 this._iterator = iterator; 737 this._filter = filter; 738 this._skipFilteredItems(); 739} 740 741WebInspector.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 */ 777WebInspector.HeapSnapshotProgress = function(dispatcher) 778{ 779 this._dispatcher = dispatcher; 780} 781 782WebInspector.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 * @param {boolean} showHiddenData 818 * @constructor 819 */ 820WebInspector.HeapSnapshot = function(profile, progress, showHiddenData) 821{ 822 this._nodes = profile.nodes; 823 this._containmentEdges = profile.edges; 824 /** @type {!HeapSnapshotMetainfo} */ 825 this._metaNode = profile.snapshot.meta; 826 this._strings = profile.strings; 827 this._progress = progress; 828 829 this._noDistance = -5; 830 this._rootNodeIndex = 0; 831 if (profile.snapshot.root_index) 832 this._rootNodeIndex = profile.snapshot.root_index; 833 834 this._snapshotDiffs = {}; 835 this._aggregatesForDiff = null; 836 this._aggregates = {}; 837 this._aggregatesSortedFlags = {}; 838 this._showHiddenData = showHiddenData; 839 840 this._init(); 841 842 if (profile.snapshot.trace_function_count) { 843 this._progress.updateStatus("Buiding allocation statistics\u2026"); 844 var nodes = this._nodes; 845 var nodesLength = nodes.length; 846 var nodeFieldCount = this._nodeFieldCount; 847 var node = this.rootNode(); 848 var liveObjects = {}; 849 for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) { 850 node.nodeIndex = nodeIndex; 851 var traceNodeId = node.traceNodeId(); 852 var stats = liveObjects[traceNodeId]; 853 if (!stats) { 854 liveObjects[traceNodeId] = stats = { count: 0, size: 0, ids: []}; 855 } 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 */ 868function 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 */ 883function 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 892WebInspector.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._nodeHiddenType = this._nodeTypes.indexOf("hidden"); 907 this._nodeObjectType = this._nodeTypes.indexOf("object"); 908 this._nodeNativeType = this._nodeTypes.indexOf("native"); 909 this._nodeConsStringType = this._nodeTypes.indexOf("concatenated string"); 910 this._nodeSlicedStringType = this._nodeTypes.indexOf("sliced string"); 911 this._nodeCodeType = this._nodeTypes.indexOf("code"); 912 this._nodeSyntheticType = this._nodeTypes.indexOf("synthetic"); 913 914 this._edgeFieldsCount = meta.edge_fields.length; 915 this._edgeTypeOffset = meta.edge_fields.indexOf("type"); 916 this._edgeNameOffset = meta.edge_fields.indexOf("name_or_index"); 917 this._edgeToNodeOffset = meta.edge_fields.indexOf("to_node"); 918 919 this._edgeTypes = meta.edge_types[this._edgeTypeOffset]; 920 this._edgeTypes.push("invisible"); 921 this._edgeElementType = this._edgeTypes.indexOf("element"); 922 this._edgeHiddenType = this._edgeTypes.indexOf("hidden"); 923 this._edgeInternalType = this._edgeTypes.indexOf("internal"); 924 this._edgeShortcutType = this._edgeTypes.indexOf("shortcut"); 925 this._edgeWeakType = this._edgeTypes.indexOf("weak"); 926 this._edgeInvisibleType = this._edgeTypes.indexOf("invisible"); 927 928 this.nodeCount = this._nodes.length / this._nodeFieldCount; 929 this._edgeCount = this._containmentEdges.length / this._edgeFieldsCount; 930 931 this._progress.updateStatus("Building edge indexes\u2026"); 932 this._buildEdgeIndexes(); 933 this._progress.updateStatus("Building retainers\u2026"); 934 this._buildRetainers(); 935 this._progress.updateStatus("Calculating node flags\u2026"); 936 this._calculateFlags(); 937 this._progress.updateStatus("Calculating distances\u2026"); 938 this._calculateDistances(); 939 this._progress.updateStatus("Building postorder index\u2026"); 940 var result = this._buildPostOrderIndex(); 941 // Actually it is array that maps node ordinal number to dominator node ordinal number. 942 this._progress.updateStatus("Building dominator tree\u2026"); 943 this._dominatorsTree = this._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex); 944 this._progress.updateStatus("Calculating retained sizes\u2026"); 945 this._calculateRetainedSizes(result.postOrderIndex2NodeOrdinal); 946 this._progress.updateStatus("Buiding dominated nodes\u2026"); 947 this._buildDominatedNodes(); 948 this._progress.updateStatus("Calculating statistics\u2026"); 949 this._calculateStatistics(); 950 this._progress.updateStatus("Finished processing."); 951 }, 952 953 _buildEdgeIndexes: function() 954 { 955 var nodes = this._nodes; 956 var nodeCount = this.nodeCount; 957 var firstEdgeIndexes = this._firstEdgeIndexes = new Uint32Array(nodeCount + 1); 958 var nodeFieldCount = this._nodeFieldCount; 959 var edgeFieldsCount = this._edgeFieldsCount; 960 var nodeEdgeCountOffset = this._nodeEdgeCountOffset; 961 firstEdgeIndexes[nodeCount] = this._containmentEdges.length; 962 for (var nodeOrdinal = 0, edgeIndex = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) { 963 firstEdgeIndexes[nodeOrdinal] = edgeIndex; 964 edgeIndex += nodes[nodeOrdinal * nodeFieldCount + nodeEdgeCountOffset] * edgeFieldsCount; 965 } 966 }, 967 968 _buildRetainers: function() 969 { 970 var retainingNodes = this._retainingNodes = new Uint32Array(this._edgeCount); 971 var retainingEdges = this._retainingEdges = new Uint32Array(this._edgeCount); 972 // Index of the first retainer in the _retainingNodes and _retainingEdges 973 // arrays. Addressed by retained node index. 974 var firstRetainerIndex = this._firstRetainerIndex = new Uint32Array(this.nodeCount + 1); 975 976 var containmentEdges = this._containmentEdges; 977 var edgeFieldsCount = this._edgeFieldsCount; 978 var nodeFieldCount = this._nodeFieldCount; 979 var edgeToNodeOffset = this._edgeToNodeOffset; 980 var firstEdgeIndexes = this._firstEdgeIndexes; 981 var nodeCount = this.nodeCount; 982 983 for (var toNodeFieldIndex = edgeToNodeOffset, l = containmentEdges.length; toNodeFieldIndex < l; toNodeFieldIndex += edgeFieldsCount) { 984 var toNodeIndex = containmentEdges[toNodeFieldIndex]; 985 if (toNodeIndex % nodeFieldCount) 986 throw new Error("Invalid toNodeIndex " + toNodeIndex); 987 ++firstRetainerIndex[toNodeIndex / nodeFieldCount]; 988 } 989 for (var i = 0, firstUnusedRetainerSlot = 0; i < nodeCount; i++) { 990 var retainersCount = firstRetainerIndex[i]; 991 firstRetainerIndex[i] = firstUnusedRetainerSlot; 992 retainingNodes[firstUnusedRetainerSlot] = retainersCount; 993 firstUnusedRetainerSlot += retainersCount; 994 } 995 firstRetainerIndex[nodeCount] = retainingNodes.length; 996 997 var nextNodeFirstEdgeIndex = firstEdgeIndexes[0]; 998 for (var srcNodeOrdinal = 0; srcNodeOrdinal < nodeCount; ++srcNodeOrdinal) { 999 var firstEdgeIndex = nextNodeFirstEdgeIndex; 1000 nextNodeFirstEdgeIndex = firstEdgeIndexes[srcNodeOrdinal + 1]; 1001 var srcNodeIndex = srcNodeOrdinal * nodeFieldCount; 1002 for (var edgeIndex = firstEdgeIndex; edgeIndex < nextNodeFirstEdgeIndex; edgeIndex += edgeFieldsCount) { 1003 var toNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 1004 if (toNodeIndex % nodeFieldCount) 1005 throw new Error("Invalid toNodeIndex " + toNodeIndex); 1006 var firstRetainerSlotIndex = firstRetainerIndex[toNodeIndex / nodeFieldCount]; 1007 var nextUnusedRetainerSlotIndex = firstRetainerSlotIndex + (--retainingNodes[firstRetainerSlotIndex]); 1008 retainingNodes[nextUnusedRetainerSlotIndex] = srcNodeIndex; 1009 retainingEdges[nextUnusedRetainerSlotIndex] = edgeIndex; 1010 } 1011 } 1012 }, 1013 1014 /** 1015 * @param {number=} nodeIndex 1016 */ 1017 createNode: function(nodeIndex) 1018 { 1019 throw new Error("Not implemented"); 1020 }, 1021 1022 /** 1023 * @param {number} edgeIndex 1024 * @return {!WebInspector.JSHeapSnapshotEdge} 1025 */ 1026 createEdge: function(edgeIndex) 1027 { 1028 throw new Error("Not implemented"); 1029 }, 1030 1031 /** 1032 * @param {number} retainerIndex 1033 * @return {!WebInspector.JSHeapSnapshotRetainerEdge} 1034 */ 1035 createRetainingEdge: function(retainerIndex) 1036 { 1037 throw new Error("Not implemented"); 1038 }, 1039 1040 dispose: function() 1041 { 1042 delete this._nodes; 1043 delete this._strings; 1044 delete this._retainingEdges; 1045 delete this._retainingNodes; 1046 delete this._firstRetainerIndex; 1047 delete this._aggregates; 1048 delete this._aggregatesSortedFlags; 1049 delete this._dominatedNodes; 1050 delete this._firstDominatedNodeIndex; 1051 delete this._nodeDistances; 1052 delete this._dominatorsTree; 1053 }, 1054 1055 _allNodes: function() 1056 { 1057 return new WebInspector.HeapSnapshotNodeIterator(this.rootNode()); 1058 }, 1059 1060 /** 1061 * @return {!WebInspector.HeapSnapshotNode} 1062 */ 1063 rootNode: function() 1064 { 1065 return this.createNode(this._rootNodeIndex); 1066 }, 1067 1068 get rootNodeIndex() 1069 { 1070 return this._rootNodeIndex; 1071 }, 1072 1073 get totalSize() 1074 { 1075 return this.rootNode().retainedSize(); 1076 }, 1077 1078 _getDominatedIndex: function(nodeIndex) 1079 { 1080 if (nodeIndex % this._nodeFieldCount) 1081 throw new Error("Invalid nodeIndex: " + nodeIndex); 1082 return this._firstDominatedNodeIndex[nodeIndex / this._nodeFieldCount]; 1083 }, 1084 1085 /** 1086 * @param {!WebInspector.HeapSnapshotNode} node 1087 * @return {!Uint32Array} 1088 */ 1089 _dominatedNodesOfNode: function(node) 1090 { 1091 var dominatedIndexFrom = this._getDominatedIndex(node.nodeIndex); 1092 var dominatedIndexTo = this._getDominatedIndex(node._nextNodeIndex()); 1093 return this._dominatedNodes.subarray(dominatedIndexFrom, dominatedIndexTo); 1094 }, 1095 1096 /** 1097 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter 1098 * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} 1099 */ 1100 aggregatesWithFilter: function(nodeFilter) 1101 { 1102 var minNodeId = nodeFilter.minNodeId; 1103 var maxNodeId = nodeFilter.maxNodeId; 1104 var allocationNodeId = nodeFilter.allocationNodeId; 1105 var key; 1106 var filter; 1107 if (typeof allocationNodeId === "number") { 1108 filter = this._createAllocationStackFilter(allocationNodeId); 1109 } else if (typeof minNodeId === "number" && typeof maxNodeId === "number") { 1110 key = minNodeId + ".." + maxNodeId; 1111 filter = this._createNodeIdFilter(minNodeId, maxNodeId); 1112 } else { 1113 key = "allObjects"; 1114 } 1115 return this.aggregates(false, key, filter); 1116 }, 1117 1118 /** 1119 * @param {number} minNodeId 1120 * @param {number} maxNodeId 1121 * @return {function(!WebInspector.HeapSnapshotNode):boolean} 1122 */ 1123 _createNodeIdFilter: function(minNodeId, maxNodeId) 1124 { 1125 /** 1126 * @param {!WebInspector.HeapSnapshotNode} node 1127 * @return {boolean} 1128 */ 1129 function nodeIdFilter(node) 1130 { 1131 var id = node.id(); 1132 return id > minNodeId && id <= maxNodeId; 1133 } 1134 return nodeIdFilter; 1135 }, 1136 1137 /** 1138 * @param {number} bottomUpAllocationNodeId 1139 * @return {function(!WebInspector.HeapSnapshotNode):boolean|undefined} 1140 */ 1141 _createAllocationStackFilter: function(bottomUpAllocationNodeId) 1142 { 1143 var traceIds = this._allocationProfile.traceIds(bottomUpAllocationNodeId); 1144 if (!traceIds.length) 1145 return undefined; 1146 var set = {}; 1147 for (var i = 0; i < traceIds.length; i++) 1148 set[traceIds[i]] = true; 1149 /** 1150 * @param {!WebInspector.HeapSnapshotNode} node 1151 * @return {boolean} 1152 */ 1153 function traceIdFilter(node) 1154 { 1155 return !!set[node.traceNodeId()]; 1156 }; 1157 return traceIdFilter; 1158 }, 1159 1160 /** 1161 * @param {boolean} sortedIndexes 1162 * @param {string=} key 1163 * @param {function(!WebInspector.HeapSnapshotNode):boolean=} filter 1164 * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} 1165 */ 1166 aggregates: function(sortedIndexes, key, filter) 1167 { 1168 var aggregatesByClassName = key && this._aggregates[key]; 1169 if (!aggregatesByClassName) { 1170 var aggregates = this._buildAggregates(filter); 1171 this._calculateClassesRetainedSize(aggregates.aggregatesByClassIndex, filter); 1172 aggregatesByClassName = aggregates.aggregatesByClassName; 1173 if (key) 1174 this._aggregates[key] = aggregatesByClassName; 1175 } 1176 1177 if (sortedIndexes && (!key || !this._aggregatesSortedFlags[key])) { 1178 this._sortAggregateIndexes(aggregatesByClassName); 1179 if (key) 1180 this._aggregatesSortedFlags[key] = sortedIndexes; 1181 } 1182 return aggregatesByClassName; 1183 }, 1184 1185 /** 1186 * @return {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} 1187 */ 1188 allocationTracesTops: function() 1189 { 1190 return this._allocationProfile.serializeTraceTops(); 1191 }, 1192 1193 /** 1194 * @param {number} nodeId 1195 * @return {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers} 1196 */ 1197 allocationNodeCallers: function(nodeId) 1198 { 1199 return this._allocationProfile.serializeCallers(nodeId); 1200 }, 1201 1202 /** 1203 * @param {number} nodeIndex 1204 * @return {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} 1205 */ 1206 allocationStack: function(nodeIndex) 1207 { 1208 var node = this.createNode(nodeIndex); 1209 var allocationNodeId = node.traceNodeId(); 1210 if (!allocationNodeId) 1211 return null; 1212 return this._allocationProfile.serializeAllocationStack(allocationNodeId); 1213 }, 1214 1215 /** 1216 * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>} 1217 */ 1218 aggregatesForDiff: function() 1219 { 1220 if (this._aggregatesForDiff) 1221 return this._aggregatesForDiff; 1222 1223 var aggregatesByClassName = this.aggregates(true, "allObjects"); 1224 this._aggregatesForDiff = {}; 1225 1226 var node = this.createNode(); 1227 for (var className in aggregatesByClassName) { 1228 var aggregate = aggregatesByClassName[className]; 1229 var indexes = aggregate.idxs; 1230 var ids = new Array(indexes.length); 1231 var selfSizes = new Array(indexes.length); 1232 for (var i = 0; i < indexes.length; i++) { 1233 node.nodeIndex = indexes[i]; 1234 ids[i] = node.id(); 1235 selfSizes[i] = node.selfSize(); 1236 } 1237 1238 this._aggregatesForDiff[className] = { 1239 indexes: indexes, 1240 ids: ids, 1241 selfSizes: selfSizes 1242 }; 1243 } 1244 return this._aggregatesForDiff; 1245 }, 1246 1247 /** 1248 * @param {!WebInspector.HeapSnapshotNode} node 1249 * @return {!boolean} 1250 */ 1251 _isUserRoot: function(node) 1252 { 1253 return true; 1254 }, 1255 1256 /** 1257 * @param {function(!WebInspector.HeapSnapshotNode)} action 1258 * @param {boolean=} userRootsOnly 1259 */ 1260 forEachRoot: function(action, userRootsOnly) 1261 { 1262 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) { 1263 var node = iter.edge.node(); 1264 if (!userRootsOnly || this._isUserRoot(node)) 1265 action(node); 1266 } 1267 }, 1268 1269 _calculateDistances: function() 1270 { 1271 var nodeFieldCount = this._nodeFieldCount; 1272 var nodeCount = this.nodeCount; 1273 var distances = this._nodeDistances = new Int32Array(nodeCount); 1274 var noDistance = this._noDistance; 1275 for (var i = 0; i < nodeCount; ++i) 1276 distances[i] = noDistance; 1277 1278 var nodesToVisit = new Uint32Array(this.nodeCount); 1279 var nodesToVisitLength = 0; 1280 1281 /** 1282 * @param {number} distance 1283 * @param {!WebInspector.HeapSnapshotNode} node 1284 */ 1285 function enqueueNode(distance, node) 1286 { 1287 var ordinal = node._ordinal(); 1288 if (distances[ordinal] !== noDistance) 1289 return; 1290 distances[ordinal] = distance; 1291 nodesToVisit[nodesToVisitLength++] = node.nodeIndex; 1292 } 1293 1294 this.forEachRoot(enqueueNode.bind(null, 1), true); 1295 this._bfs(nodesToVisit, nodesToVisitLength, distances); 1296 1297 // bfs for the rest of objects 1298 nodesToVisitLength = 0; 1299 this.forEachRoot(enqueueNode.bind(null, WebInspector.HeapSnapshotCommon.baseSystemDistance), false); 1300 this._bfs(nodesToVisit, nodesToVisitLength, distances); 1301 }, 1302 1303 /** 1304 * @param {!Uint32Array} nodesToVisit 1305 * @param {!number} nodesToVisitLength 1306 * @param {!Int32Array} distances 1307 */ 1308 _bfs: function(nodesToVisit, nodesToVisitLength, distances) 1309 { 1310 // Preload fields into local variables for better performance. 1311 var edgeFieldsCount = this._edgeFieldsCount; 1312 var nodeFieldCount = this._nodeFieldCount; 1313 var containmentEdges = this._containmentEdges; 1314 var firstEdgeIndexes = this._firstEdgeIndexes; 1315 var edgeToNodeOffset = this._edgeToNodeOffset; 1316 var edgeTypeOffset = this._edgeTypeOffset; 1317 var nodeCount = this.nodeCount; 1318 var containmentEdgesLength = containmentEdges.length; 1319 var edgeWeakType = this._edgeWeakType; 1320 var noDistance = this._noDistance; 1321 1322 var index = 0; 1323 while (index < nodesToVisitLength) { 1324 var nodeIndex = nodesToVisit[index++]; // shift generates too much garbage. 1325 var nodeOrdinal = nodeIndex / nodeFieldCount; 1326 var distance = distances[nodeOrdinal] + 1; 1327 var firstEdgeIndex = firstEdgeIndexes[nodeOrdinal]; 1328 var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1]; 1329 for (var edgeIndex = firstEdgeIndex; edgeIndex < edgesEnd; edgeIndex += edgeFieldsCount) { 1330 var edgeType = containmentEdges[edgeIndex + edgeTypeOffset]; 1331 if (edgeType == edgeWeakType) 1332 continue; 1333 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 1334 var childNodeOrdinal = childNodeIndex / nodeFieldCount; 1335 if (distances[childNodeOrdinal] !== noDistance) 1336 continue; 1337 distances[childNodeOrdinal] = distance; 1338 nodesToVisit[nodesToVisitLength++] = childNodeIndex; 1339 } 1340 } 1341 if (nodesToVisitLength > nodeCount) 1342 throw new Error("BFS failed. Nodes to visit (" + nodesToVisitLength + ") is more than nodes count (" + nodeCount + ")"); 1343 }, 1344 1345 _buildAggregates: function(filter) 1346 { 1347 var aggregates = {}; 1348 var aggregatesByClassName = {}; 1349 var classIndexes = []; 1350 var nodes = this._nodes; 1351 var mapAndFlag = this.userObjectsMapAndFlag(); 1352 var flags = mapAndFlag ? mapAndFlag.map : null; 1353 var flag = mapAndFlag ? mapAndFlag.flag : 0; 1354 var nodesLength = nodes.length; 1355 var nodeNativeType = this._nodeNativeType; 1356 var nodeFieldCount = this._nodeFieldCount; 1357 var selfSizeOffset = this._nodeSelfSizeOffset; 1358 var nodeTypeOffset = this._nodeTypeOffset; 1359 var node = this.rootNode(); 1360 var nodeDistances = this._nodeDistances; 1361 1362 for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) { 1363 var nodeOrdinal = nodeIndex / nodeFieldCount; 1364 if (flags && !(flags[nodeOrdinal] & flag)) 1365 continue; 1366 node.nodeIndex = nodeIndex; 1367 if (filter && !filter(node)) 1368 continue; 1369 var selfSize = nodes[nodeIndex + selfSizeOffset]; 1370 if (!selfSize && nodes[nodeIndex + nodeTypeOffset] !== nodeNativeType) 1371 continue; 1372 var classIndex = node.classIndex(); 1373 if (!(classIndex in aggregates)) { 1374 var nodeType = node.type(); 1375 var nameMatters = nodeType === "object" || nodeType === "native"; 1376 var value = { 1377 count: 1, 1378 distance: nodeDistances[nodeOrdinal], 1379 self: selfSize, 1380 maxRet: 0, 1381 type: nodeType, 1382 name: nameMatters ? node.name() : null, 1383 idxs: [nodeIndex] 1384 }; 1385 aggregates[classIndex] = value; 1386 classIndexes.push(classIndex); 1387 aggregatesByClassName[node.className()] = value; 1388 } else { 1389 var clss = aggregates[classIndex]; 1390 clss.distance = Math.min(clss.distance, nodeDistances[nodeOrdinal]); 1391 ++clss.count; 1392 clss.self += selfSize; 1393 clss.idxs.push(nodeIndex); 1394 } 1395 } 1396 1397 // Shave off provisionally allocated space. 1398 for (var i = 0, l = classIndexes.length; i < l; ++i) { 1399 var classIndex = classIndexes[i]; 1400 aggregates[classIndex].idxs = aggregates[classIndex].idxs.slice(); 1401 } 1402 return {aggregatesByClassName: aggregatesByClassName, aggregatesByClassIndex: aggregates}; 1403 }, 1404 1405 _calculateClassesRetainedSize: function(aggregates, filter) 1406 { 1407 var rootNodeIndex = this._rootNodeIndex; 1408 var node = this.createNode(rootNodeIndex); 1409 var list = [rootNodeIndex]; 1410 var sizes = [-1]; 1411 var classes = []; 1412 var seenClassNameIndexes = {}; 1413 var nodeFieldCount = this._nodeFieldCount; 1414 var nodeTypeOffset = this._nodeTypeOffset; 1415 var nodeNativeType = this._nodeNativeType; 1416 var dominatedNodes = this._dominatedNodes; 1417 var nodes = this._nodes; 1418 var mapAndFlag = this.userObjectsMapAndFlag(); 1419 var flags = mapAndFlag ? mapAndFlag.map : null; 1420 var flag = mapAndFlag ? mapAndFlag.flag : 0; 1421 var firstDominatedNodeIndex = this._firstDominatedNodeIndex; 1422 1423 while (list.length) { 1424 var nodeIndex = list.pop(); 1425 node.nodeIndex = nodeIndex; 1426 var classIndex = node.classIndex(); 1427 var seen = !!seenClassNameIndexes[classIndex]; 1428 var nodeOrdinal = nodeIndex / nodeFieldCount; 1429 var dominatedIndexFrom = firstDominatedNodeIndex[nodeOrdinal]; 1430 var dominatedIndexTo = firstDominatedNodeIndex[nodeOrdinal + 1]; 1431 1432 if (!seen && 1433 (!flags || (flags[nodeOrdinal] & flag)) && 1434 (!filter || filter(node)) && 1435 (node.selfSize() || nodes[nodeIndex + nodeTypeOffset] === nodeNativeType) 1436 ) { 1437 aggregates[classIndex].maxRet += node.retainedSize(); 1438 if (dominatedIndexFrom !== dominatedIndexTo) { 1439 seenClassNameIndexes[classIndex] = true; 1440 sizes.push(list.length); 1441 classes.push(classIndex); 1442 } 1443 } 1444 for (var i = dominatedIndexFrom; i < dominatedIndexTo; i++) 1445 list.push(dominatedNodes[i]); 1446 1447 var l = list.length; 1448 while (sizes[sizes.length - 1] === l) { 1449 sizes.pop(); 1450 classIndex = classes.pop(); 1451 seenClassNameIndexes[classIndex] = false; 1452 } 1453 } 1454 }, 1455 1456 _sortAggregateIndexes: function(aggregates) 1457 { 1458 var nodeA = this.createNode(); 1459 var nodeB = this.createNode(); 1460 for (var clss in aggregates) 1461 aggregates[clss].idxs.sort( 1462 function(idxA, idxB) { 1463 nodeA.nodeIndex = idxA; 1464 nodeB.nodeIndex = idxB; 1465 return nodeA.id() < nodeB.id() ? -1 : 1; 1466 }); 1467 }, 1468 1469 _buildPostOrderIndex: function() 1470 { 1471 var nodeFieldCount = this._nodeFieldCount; 1472 var nodes = this._nodes; 1473 var nodeCount = this.nodeCount; 1474 var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount; 1475 1476 var edgeFieldsCount = this._edgeFieldsCount; 1477 var edgeTypeOffset = this._edgeTypeOffset; 1478 var edgeToNodeOffset = this._edgeToNodeOffset; 1479 var edgeShortcutType = this._edgeShortcutType; 1480 var firstEdgeIndexes = this._firstEdgeIndexes; 1481 var containmentEdges = this._containmentEdges; 1482 1483 var mapAndFlag = this.userObjectsMapAndFlag(); 1484 var flags = mapAndFlag ? mapAndFlag.map : null; 1485 var flag = mapAndFlag ? mapAndFlag.flag : 0; 1486 1487 var stackNodes = new Uint32Array(nodeCount); 1488 var stackCurrentEdge = new Uint32Array(nodeCount); 1489 var postOrderIndex2NodeOrdinal = new Uint32Array(nodeCount); 1490 var nodeOrdinal2PostOrderIndex = new Uint32Array(nodeCount); 1491 var visited = new Uint8Array(nodeCount); 1492 var postOrderIndex = 0; 1493 1494 var stackTop = 0; 1495 stackNodes[0] = rootNodeOrdinal; 1496 stackCurrentEdge[0] = firstEdgeIndexes[rootNodeOrdinal]; 1497 visited[rootNodeOrdinal] = 1; 1498 1499 while (stackTop >= 0) { 1500 var nodeOrdinal = stackNodes[stackTop]; 1501 var edgeIndex = stackCurrentEdge[stackTop]; 1502 var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1]; 1503 1504 if (edgeIndex < edgesEnd) { 1505 stackCurrentEdge[stackTop] += edgeFieldsCount; 1506 if (nodeOrdinal !== rootNodeOrdinal && containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType) 1507 continue; 1508 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 1509 var childNodeOrdinal = childNodeIndex / nodeFieldCount; 1510 if (visited[childNodeOrdinal]) 1511 continue; 1512 var nodeFlag = !flags || (flags[nodeOrdinal] & flag); 1513 var childNodeFlag = !flags || (flags[childNodeOrdinal] & flag); 1514 // We are skipping the edges from non-page-owned nodes to page-owned nodes. 1515 // Otherwise the dominators for the objects that also were retained by debugger would be affected. 1516 if (nodeOrdinal !== rootNodeOrdinal && childNodeFlag && !nodeFlag) 1517 continue; 1518 ++stackTop; 1519 stackNodes[stackTop] = childNodeOrdinal; 1520 stackCurrentEdge[stackTop] = firstEdgeIndexes[childNodeOrdinal]; 1521 visited[childNodeOrdinal] = 1; 1522 } else { 1523 // Done with all the node children 1524 nodeOrdinal2PostOrderIndex[nodeOrdinal] = postOrderIndex; 1525 postOrderIndex2NodeOrdinal[postOrderIndex++] = nodeOrdinal; 1526 --stackTop; 1527 } 1528 } 1529 1530 if (postOrderIndex !== nodeCount) { 1531 console.log("Error: Corrupted snapshot. " + (nodeCount - postOrderIndex) + " nodes are unreachable from the root:"); 1532 var dumpNode = this.rootNode(); 1533 for (var i = 0; i < nodeCount; ++i) { 1534 if (!visited[i]) { 1535 // Fix it by giving the node a postorder index anyway. 1536 nodeOrdinal2PostOrderIndex[i] = postOrderIndex; 1537 postOrderIndex2NodeOrdinal[postOrderIndex++] = i; 1538 dumpNode.nodeIndex = i * nodeFieldCount; 1539 console.log(JSON.stringify(dumpNode.serialize())); 1540 for (var retainers = dumpNode.retainers(); retainers.hasNext(); retainers = retainers.item().node().retainers()) 1541 console.log(" edgeName: " + retainers.item().name() + " nodeClassName: " + retainers.item().node().className()); 1542 } 1543 } 1544 } 1545 1546 return {postOrderIndex2NodeOrdinal: postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex: nodeOrdinal2PostOrderIndex}; 1547 }, 1548 1549 // The algorithm is based on the article: 1550 // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm" 1551 // Softw. Pract. Exper. 4 (2001), pp. 1-10. 1552 /** 1553 * @param {!Array.<number>} postOrderIndex2NodeOrdinal 1554 * @param {!Array.<number>} nodeOrdinal2PostOrderIndex 1555 */ 1556 _buildDominatorTree: function(postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex) 1557 { 1558 var nodeFieldCount = this._nodeFieldCount; 1559 var nodes = this._nodes; 1560 var firstRetainerIndex = this._firstRetainerIndex; 1561 var retainingNodes = this._retainingNodes; 1562 var retainingEdges = this._retainingEdges; 1563 var edgeFieldsCount = this._edgeFieldsCount; 1564 var edgeTypeOffset = this._edgeTypeOffset; 1565 var edgeToNodeOffset = this._edgeToNodeOffset; 1566 var edgeShortcutType = this._edgeShortcutType; 1567 var firstEdgeIndexes = this._firstEdgeIndexes; 1568 var containmentEdges = this._containmentEdges; 1569 var containmentEdgesLength = this._containmentEdges.length; 1570 var rootNodeIndex = this._rootNodeIndex; 1571 1572 var mapAndFlag = this.userObjectsMapAndFlag(); 1573 var flags = mapAndFlag ? mapAndFlag.map : null; 1574 var flag = mapAndFlag ? mapAndFlag.flag : 0; 1575 1576 var nodesCount = postOrderIndex2NodeOrdinal.length; 1577 var rootPostOrderedIndex = nodesCount - 1; 1578 var noEntry = nodesCount; 1579 var dominators = new Uint32Array(nodesCount); 1580 for (var i = 0; i < rootPostOrderedIndex; ++i) 1581 dominators[i] = noEntry; 1582 dominators[rootPostOrderedIndex] = rootPostOrderedIndex; 1583 1584 // The affected array is used to mark entries which dominators 1585 // have to be racalculated because of changes in their retainers. 1586 var affected = new Uint8Array(nodesCount); 1587 var nodeOrdinal; 1588 1589 { // Mark the root direct children as affected. 1590 nodeOrdinal = this._rootNodeIndex / nodeFieldCount; 1591 var beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset; 1592 var endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1]; 1593 for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex; 1594 toNodeFieldIndex < endEdgeToNodeFieldIndex; 1595 toNodeFieldIndex += edgeFieldsCount) { 1596 var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount; 1597 affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1; 1598 } 1599 } 1600 1601 var changed = true; 1602 while (changed) { 1603 changed = false; 1604 for (var postOrderIndex = rootPostOrderedIndex - 1; postOrderIndex >= 0; --postOrderIndex) { 1605 if (affected[postOrderIndex] === 0) 1606 continue; 1607 affected[postOrderIndex] = 0; 1608 // If dominator of the entry has already been set to root, 1609 // then it can't propagate any further. 1610 if (dominators[postOrderIndex] === rootPostOrderedIndex) 1611 continue; 1612 nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; 1613 var nodeFlag = !flags || (flags[nodeOrdinal] & flag); 1614 var newDominatorIndex = noEntry; 1615 var beginRetainerIndex = firstRetainerIndex[nodeOrdinal]; 1616 var endRetainerIndex = firstRetainerIndex[nodeOrdinal + 1]; 1617 for (var retainerIndex = beginRetainerIndex; retainerIndex < endRetainerIndex; ++retainerIndex) { 1618 var retainerEdgeIndex = retainingEdges[retainerIndex]; 1619 var retainerEdgeType = containmentEdges[retainerEdgeIndex + edgeTypeOffset]; 1620 var retainerNodeIndex = retainingNodes[retainerIndex]; 1621 if (retainerNodeIndex !== rootNodeIndex && retainerEdgeType === edgeShortcutType) 1622 continue; 1623 var retainerNodeOrdinal = retainerNodeIndex / nodeFieldCount; 1624 var retainerNodeFlag = !flags || (flags[retainerNodeOrdinal] & flag); 1625 // We are skipping the edges from non-page-owned nodes to page-owned nodes. 1626 // Otherwise the dominators for the objects that also were retained by debugger would be affected. 1627 if (retainerNodeIndex !== rootNodeIndex && nodeFlag && !retainerNodeFlag) 1628 continue; 1629 var retanerPostOrderIndex = nodeOrdinal2PostOrderIndex[retainerNodeOrdinal]; 1630 if (dominators[retanerPostOrderIndex] !== noEntry) { 1631 if (newDominatorIndex === noEntry) 1632 newDominatorIndex = retanerPostOrderIndex; 1633 else { 1634 while (retanerPostOrderIndex !== newDominatorIndex) { 1635 while (retanerPostOrderIndex < newDominatorIndex) 1636 retanerPostOrderIndex = dominators[retanerPostOrderIndex]; 1637 while (newDominatorIndex < retanerPostOrderIndex) 1638 newDominatorIndex = dominators[newDominatorIndex]; 1639 } 1640 } 1641 // If idom has already reached the root, it doesn't make sense 1642 // to check other retainers. 1643 if (newDominatorIndex === rootPostOrderedIndex) 1644 break; 1645 } 1646 } 1647 if (newDominatorIndex !== noEntry && dominators[postOrderIndex] !== newDominatorIndex) { 1648 dominators[postOrderIndex] = newDominatorIndex; 1649 changed = true; 1650 nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; 1651 beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset; 1652 endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1]; 1653 for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex; 1654 toNodeFieldIndex < endEdgeToNodeFieldIndex; 1655 toNodeFieldIndex += edgeFieldsCount) { 1656 var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount; 1657 affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1; 1658 } 1659 } 1660 } 1661 } 1662 1663 var dominatorsTree = new Uint32Array(nodesCount); 1664 for (var postOrderIndex = 0, l = dominators.length; postOrderIndex < l; ++postOrderIndex) { 1665 nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; 1666 dominatorsTree[nodeOrdinal] = postOrderIndex2NodeOrdinal[dominators[postOrderIndex]]; 1667 } 1668 return dominatorsTree; 1669 }, 1670 1671 _calculateRetainedSizes: function(postOrderIndex2NodeOrdinal) 1672 { 1673 var nodeCount = this.nodeCount; 1674 var nodes = this._nodes; 1675 var nodeSelfSizeOffset = this._nodeSelfSizeOffset; 1676 var nodeFieldCount = this._nodeFieldCount; 1677 var dominatorsTree = this._dominatorsTree; 1678 var retainedSizes = this._retainedSizes = new Float64Array(nodeCount); 1679 1680 for (var nodeOrdinal = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) 1681 retainedSizes[nodeOrdinal] = nodes[nodeOrdinal * nodeFieldCount + nodeSelfSizeOffset]; 1682 1683 // Propagate retained sizes for each node excluding root. 1684 for (var postOrderIndex = 0; postOrderIndex < nodeCount - 1; ++postOrderIndex) { 1685 var nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; 1686 var dominatorOrdinal = dominatorsTree[nodeOrdinal]; 1687 retainedSizes[dominatorOrdinal] += retainedSizes[nodeOrdinal]; 1688 } 1689 }, 1690 1691 _buildDominatedNodes: function() 1692 { 1693 // Builds up two arrays: 1694 // - "dominatedNodes" is a continuous array, where each node owns an 1695 // interval (can be empty) with corresponding dominated nodes. 1696 // - "indexArray" is an array of indexes in the "dominatedNodes" 1697 // with the same positions as in the _nodeIndex. 1698 var indexArray = this._firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1); 1699 // All nodes except the root have dominators. 1700 var dominatedNodes = this._dominatedNodes = new Uint32Array(this.nodeCount - 1); 1701 1702 // Count the number of dominated nodes for each node. Skip the root (node at 1703 // index 0) as it is the only node that dominates itself. 1704 var nodeFieldCount = this._nodeFieldCount; 1705 var dominatorsTree = this._dominatorsTree; 1706 1707 var fromNodeOrdinal = 0; 1708 var toNodeOrdinal = this.nodeCount; 1709 var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount; 1710 if (rootNodeOrdinal === fromNodeOrdinal) 1711 fromNodeOrdinal = 1; 1712 else if (rootNodeOrdinal === toNodeOrdinal - 1) 1713 toNodeOrdinal = toNodeOrdinal - 1; 1714 else 1715 throw new Error("Root node is expected to be either first or last"); 1716 for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal) 1717 ++indexArray[dominatorsTree[nodeOrdinal]]; 1718 // Put in the first slot of each dominatedNodes slice the count of entries 1719 // that will be filled. 1720 var firstDominatedNodeIndex = 0; 1721 for (var i = 0, l = this.nodeCount; i < l; ++i) { 1722 var dominatedCount = dominatedNodes[firstDominatedNodeIndex] = indexArray[i]; 1723 indexArray[i] = firstDominatedNodeIndex; 1724 firstDominatedNodeIndex += dominatedCount; 1725 } 1726 indexArray[this.nodeCount] = dominatedNodes.length; 1727 // Fill up the dominatedNodes array with indexes of dominated nodes. Skip the root (node at 1728 // index 0) as it is the only node that dominates itself. 1729 for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal) { 1730 var dominatorOrdinal = dominatorsTree[nodeOrdinal]; 1731 var dominatedRefIndex = indexArray[dominatorOrdinal]; 1732 dominatedRefIndex += (--dominatedNodes[dominatedRefIndex]); 1733 dominatedNodes[dominatedRefIndex] = nodeOrdinal * nodeFieldCount; 1734 } 1735 }, 1736 1737 _calculateFlags: function() 1738 { 1739 throw new Error("Not implemented"); 1740 }, 1741 1742 _calculateStatistics: function() 1743 { 1744 throw new Error("Not implemented"); 1745 }, 1746 1747 userObjectsMapAndFlag: function() 1748 { 1749 throw new Error("Not implemented"); 1750 }, 1751 1752 /** 1753 * @param {string} baseSnapshotId 1754 * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>} baseSnapshotAggregates 1755 * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Diff>} 1756 */ 1757 calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates) 1758 { 1759 var snapshotDiff = this._snapshotDiffs[baseSnapshotId]; 1760 if (snapshotDiff) 1761 return snapshotDiff; 1762 snapshotDiff = {}; 1763 1764 var aggregates = this.aggregates(true, "allObjects"); 1765 for (var className in baseSnapshotAggregates) { 1766 var baseAggregate = baseSnapshotAggregates[className]; 1767 var diff = this._calculateDiffForClass(baseAggregate, aggregates[className]); 1768 if (diff) 1769 snapshotDiff[className] = diff; 1770 } 1771 var emptyBaseAggregate = new WebInspector.HeapSnapshotCommon.AggregateForDiff(); 1772 for (var className in aggregates) { 1773 if (className in baseSnapshotAggregates) 1774 continue; 1775 snapshotDiff[className] = this._calculateDiffForClass(emptyBaseAggregate, aggregates[className]); 1776 } 1777 1778 this._snapshotDiffs[baseSnapshotId] = snapshotDiff; 1779 return snapshotDiff; 1780 }, 1781 1782 /** 1783 * @param {!WebInspector.HeapSnapshotCommon.AggregateForDiff} baseAggregate 1784 * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate 1785 * @return {?WebInspector.HeapSnapshotCommon.Diff} 1786 */ 1787 _calculateDiffForClass: function(baseAggregate, aggregate) 1788 { 1789 var baseIds = baseAggregate.ids; 1790 var baseIndexes = baseAggregate.indexes; 1791 var baseSelfSizes = baseAggregate.selfSizes; 1792 1793 var indexes = aggregate ? aggregate.idxs : []; 1794 1795 var i = 0, l = baseIds.length; 1796 var j = 0, m = indexes.length; 1797 var diff = new WebInspector.HeapSnapshotCommon.Diff(); 1798 1799 var nodeB = this.createNode(indexes[j]); 1800 while (i < l && j < m) { 1801 var nodeAId = baseIds[i]; 1802 if (nodeAId < nodeB.id()) { 1803 diff.deletedIndexes.push(baseIndexes[i]); 1804 diff.removedCount++; 1805 diff.removedSize += baseSelfSizes[i]; 1806 ++i; 1807 } else if (nodeAId > nodeB.id()) { // Native nodes(e.g. dom groups) may have ids less than max JS object id in the base snapshot 1808 diff.addedIndexes.push(indexes[j]); 1809 diff.addedCount++; 1810 diff.addedSize += nodeB.selfSize(); 1811 nodeB.nodeIndex = indexes[++j]; 1812 } else { // nodeAId === nodeB.id() 1813 ++i; 1814 nodeB.nodeIndex = indexes[++j]; 1815 } 1816 } 1817 while (i < l) { 1818 diff.deletedIndexes.push(baseIndexes[i]); 1819 diff.removedCount++; 1820 diff.removedSize += baseSelfSizes[i]; 1821 ++i; 1822 } 1823 while (j < m) { 1824 diff.addedIndexes.push(indexes[j]); 1825 diff.addedCount++; 1826 diff.addedSize += nodeB.selfSize(); 1827 nodeB.nodeIndex = indexes[++j]; 1828 } 1829 diff.countDelta = diff.addedCount - diff.removedCount; 1830 diff.sizeDelta = diff.addedSize - diff.removedSize; 1831 if (!diff.addedCount && !diff.removedCount) 1832 return null; 1833 return diff; 1834 }, 1835 1836 _nodeForSnapshotObjectId: function(snapshotObjectId) 1837 { 1838 for (var it = this._allNodes(); it.hasNext(); it.next()) { 1839 if (it.node.id() === snapshotObjectId) 1840 return it.node; 1841 } 1842 return null; 1843 }, 1844 1845 /** 1846 * @param {string} snapshotObjectId 1847 * @return {?string} 1848 */ 1849 nodeClassName: function(snapshotObjectId) 1850 { 1851 var node = this._nodeForSnapshotObjectId(snapshotObjectId); 1852 if (node) 1853 return node.className(); 1854 return null; 1855 }, 1856 1857 /** 1858 * @param {string} name 1859 * @return {!Array.<number>} 1860 */ 1861 idsOfObjectsWithName: function(name) 1862 { 1863 var ids = []; 1864 for (var it = this._allNodes(); it.hasNext(); it.next()) { 1865 if (it.item().name() === name) 1866 ids.push(it.item().id()); 1867 } 1868 return ids; 1869 }, 1870 1871 /** 1872 * @param {string} snapshotObjectId 1873 * @return {?Array.<string>} 1874 */ 1875 dominatorIdsForNode: function(snapshotObjectId) 1876 { 1877 var node = this._nodeForSnapshotObjectId(snapshotObjectId); 1878 if (!node) 1879 return null; 1880 var result = []; 1881 while (!node.isRoot()) { 1882 result.push(node.id()); 1883 node.nodeIndex = node.dominatorIndex(); 1884 } 1885 return result; 1886 }, 1887 1888 /** 1889 * @param {number} nodeIndex 1890 * @return {!WebInspector.HeapSnapshotEdgesProvider} 1891 */ 1892 createEdgesProvider: function(nodeIndex) 1893 { 1894 var node = this.createNode(nodeIndex); 1895 var filter = this.containmentEdgesFilter(); 1896 var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this); 1897 return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider); 1898 }, 1899 1900 /** 1901 * @param {number} nodeIndex 1902 * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter 1903 * @return {!WebInspector.HeapSnapshotEdgesProvider} 1904 */ 1905 createEdgesProviderForTest: function(nodeIndex, filter) 1906 { 1907 var node = this.createNode(nodeIndex); 1908 var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this); 1909 return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider); 1910 }, 1911 1912 /** 1913 * @return {?function(!WebInspector.HeapSnapshotEdge):boolean} 1914 */ 1915 retainingEdgesFilter: function() 1916 { 1917 return null; 1918 }, 1919 1920 /** 1921 * @return {?function(!WebInspector.HeapSnapshotEdge):boolean} 1922 */ 1923 containmentEdgesFilter: function() 1924 { 1925 return null; 1926 }, 1927 1928 /** 1929 * @param {number} nodeIndex 1930 * @return {!WebInspector.HeapSnapshotEdgesProvider} 1931 */ 1932 createRetainingEdgesProvider: function(nodeIndex) 1933 { 1934 var node = this.createNode(nodeIndex); 1935 var filter = this.retainingEdgesFilter(); 1936 var indexProvider = new WebInspector.HeapSnapshotRetainerEdgeIndexProvider(this); 1937 return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.retainers(), indexProvider); 1938 }, 1939 1940 /** 1941 * @param {string} baseSnapshotId 1942 * @param {string} className 1943 * @return {!WebInspector.HeapSnapshotNodesProvider} 1944 */ 1945 createAddedNodesProvider: function(baseSnapshotId, className) 1946 { 1947 var snapshotDiff = this._snapshotDiffs[baseSnapshotId]; 1948 var diffForClass = snapshotDiff[className]; 1949 return new WebInspector.HeapSnapshotNodesProvider(this, null, diffForClass.addedIndexes); 1950 }, 1951 1952 /** 1953 * @param {!Array.<number>} nodeIndexes 1954 * @return {!WebInspector.HeapSnapshotNodesProvider} 1955 */ 1956 createDeletedNodesProvider: function(nodeIndexes) 1957 { 1958 return new WebInspector.HeapSnapshotNodesProvider(this, null, nodeIndexes); 1959 }, 1960 1961 /** 1962 * @return {?function(!WebInspector.HeapSnapshotNode):boolean} 1963 */ 1964 classNodesFilter: function() 1965 { 1966 return null; 1967 }, 1968 1969 /** 1970 * @param {string} className 1971 * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter 1972 * @return {!WebInspector.HeapSnapshotNodesProvider} 1973 */ 1974 createNodesProviderForClass: function(className, nodeFilter) 1975 { 1976 return new WebInspector.HeapSnapshotNodesProvider(this, this.classNodesFilter(), this.aggregatesWithFilter(nodeFilter)[className].idxs); 1977 }, 1978 1979 /** 1980 * @param {number} nodeIndex 1981 * @return {!WebInspector.HeapSnapshotNodesProvider} 1982 */ 1983 createNodesProviderForDominator: function(nodeIndex) 1984 { 1985 var node = this.createNode(nodeIndex); 1986 return new WebInspector.HeapSnapshotNodesProvider(this, null, this._dominatedNodesOfNode(node)); 1987 }, 1988 1989 /** 1990 * @return {number} 1991 */ 1992 _maxJsNodeId: function() 1993 { 1994 var nodeFieldCount = this._nodeFieldCount; 1995 var nodes = this._nodes; 1996 var nodesLength = nodes.length; 1997 var id = 0; 1998 for (var nodeIndex = this._nodeIdOffset; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) { 1999 var nextId = nodes[nodeIndex]; 2000 // JS objects have odd ids, skip native objects. 2001 if (nextId % 2 === 0) 2002 continue; 2003 if (id < nextId) 2004 id = nextId; 2005 } 2006 return id; 2007 }, 2008 2009 /** 2010 * @return {!WebInspector.HeapSnapshotCommon.StaticData} 2011 */ 2012 updateStaticData: function() 2013 { 2014 return new WebInspector.HeapSnapshotCommon.StaticData(this.nodeCount, this._rootNodeIndex, this.totalSize, this._maxJsNodeId()); 2015 } 2016}; 2017 2018/** 2019 * @constructor 2020 * @param {!WebInspector.HeapSnapshotItemIterator} iterator 2021 * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider 2022 */ 2023WebInspector.HeapSnapshotItemProvider = function(iterator, indexProvider) 2024{ 2025 this._iterator = iterator; 2026 this._indexProvider = indexProvider; 2027 this._isEmpty = !iterator.hasNext(); 2028 /** @type {?Array.<number>} */ 2029 this._iterationOrder = null; 2030 this._currentComparator = null; 2031 this._sortedPrefixLength = 0; 2032 this._sortedSuffixLength = 0; 2033} 2034 2035WebInspector.HeapSnapshotItemProvider.prototype = { 2036 _createIterationOrder: function() 2037 { 2038 if (this._iterationOrder) 2039 return; 2040 this._iterationOrder = []; 2041 for (var iterator = this._iterator; iterator.hasNext(); iterator.next()) 2042 this._iterationOrder.push(iterator.item().itemIndex()); 2043 }, 2044 2045 /** 2046 * @return {boolean} 2047 */ 2048 isEmpty: function() 2049 { 2050 return this._isEmpty; 2051 }, 2052 2053 /** 2054 * @param {number} begin 2055 * @param {number} end 2056 * @return {!WebInspector.HeapSnapshotCommon.ItemsRange} 2057 */ 2058 serializeItemsRange: function(begin, end) 2059 { 2060 this._createIterationOrder(); 2061 if (begin > end) 2062 throw new Error("Start position > end position: " + begin + " > " + end); 2063 if (end > this._iterationOrder.length) 2064 end = this._iterationOrder.length; 2065 if (this._sortedPrefixLength < end && begin < this._iterationOrder.length - this._sortedSuffixLength) { 2066 this.sort(this._currentComparator, this._sortedPrefixLength, this._iterationOrder.length - 1 - this._sortedSuffixLength, begin, end - 1); 2067 if (begin <= this._sortedPrefixLength) 2068 this._sortedPrefixLength = end; 2069 if (end >= this._iterationOrder.length - this._sortedSuffixLength) 2070 this._sortedSuffixLength = this._iterationOrder.length - begin; 2071 } 2072 var position = begin; 2073 var count = end - begin; 2074 var result = new Array(count); 2075 var iterator = this._iterator; 2076 for (var i = 0 ; i < count; ++i) { 2077 var itemIndex = this._iterationOrder[position++]; 2078 var item = this._indexProvider.itemForIndex(itemIndex); 2079 result[i] = item.serialize(); 2080 } 2081 return new WebInspector.HeapSnapshotCommon.ItemsRange(begin, end, this._iterationOrder.length, result); 2082 }, 2083 2084 sortAndRewind: function(comparator) 2085 { 2086 this._currentComparator = comparator; 2087 this._sortedPrefixLength = 0; 2088 this._sortedSuffixLength = 0; 2089 } 2090} 2091 2092/** 2093 * @constructor 2094 * @extends {WebInspector.HeapSnapshotItemProvider} 2095 * @param {!WebInspector.HeapSnapshot} snapshot 2096 * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter 2097 * @param {!WebInspector.HeapSnapshotEdgeIterator} edgesIter 2098 * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider 2099 */ 2100WebInspector.HeapSnapshotEdgesProvider = function(snapshot, filter, edgesIter, indexProvider) 2101{ 2102 this.snapshot = snapshot; 2103 var iter = filter ? new WebInspector.HeapSnapshotFilteredIterator(edgesIter, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter)) : edgesIter; 2104 WebInspector.HeapSnapshotItemProvider.call(this, iter, indexProvider); 2105} 2106 2107WebInspector.HeapSnapshotEdgesProvider.prototype = { 2108 /** 2109 * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator 2110 * @param {number} leftBound 2111 * @param {number} rightBound 2112 * @param {number} windowLeft 2113 * @param {number} windowRight 2114 */ 2115 sort: function(comparator, leftBound, rightBound, windowLeft, windowRight) 2116 { 2117 var fieldName1 = comparator.fieldName1; 2118 var fieldName2 = comparator.fieldName2; 2119 var ascending1 = comparator.ascending1; 2120 var ascending2 = comparator.ascending2; 2121 2122 var edgeA = this._iterator.item().clone(); 2123 var edgeB = edgeA.clone(); 2124 var nodeA = this.snapshot.createNode(); 2125 var nodeB = this.snapshot.createNode(); 2126 2127 function compareEdgeFieldName(ascending, indexA, indexB) 2128 { 2129 edgeA.edgeIndex = indexA; 2130 edgeB.edgeIndex = indexB; 2131 if (edgeB.name() === "__proto__") return -1; 2132 if (edgeA.name() === "__proto__") return 1; 2133 var result = 2134 edgeA.hasStringName() === edgeB.hasStringName() ? 2135 (edgeA.name() < edgeB.name() ? -1 : (edgeA.name() > edgeB.name() ? 1 : 0)) : 2136 (edgeA.hasStringName() ? -1 : 1); 2137 return ascending ? result : -result; 2138 } 2139 2140 function compareNodeField(fieldName, ascending, indexA, indexB) 2141 { 2142 edgeA.edgeIndex = indexA; 2143 nodeA.nodeIndex = edgeA.nodeIndex(); 2144 var valueA = nodeA[fieldName](); 2145 2146 edgeB.edgeIndex = indexB; 2147 nodeB.nodeIndex = edgeB.nodeIndex(); 2148 var valueB = nodeB[fieldName](); 2149 2150 var result = valueA < valueB ? -1 : (valueA > valueB ? 1 : 0); 2151 return ascending ? result : -result; 2152 } 2153 2154 function compareEdgeAndNode(indexA, indexB) { 2155 var result = compareEdgeFieldName(ascending1, indexA, indexB); 2156 if (result === 0) 2157 result = compareNodeField(fieldName2, ascending2, indexA, indexB); 2158 if (result === 0) 2159 return indexA - indexB; 2160 return result; 2161 } 2162 2163 function compareNodeAndEdge(indexA, indexB) { 2164 var result = compareNodeField(fieldName1, ascending1, indexA, indexB); 2165 if (result === 0) 2166 result = compareEdgeFieldName(ascending2, indexA, indexB); 2167 if (result === 0) 2168 return indexA - indexB; 2169 return result; 2170 } 2171 2172 function compareNodeAndNode(indexA, indexB) { 2173 var result = compareNodeField(fieldName1, ascending1, indexA, indexB); 2174 if (result === 0) 2175 result = compareNodeField(fieldName2, ascending2, indexA, indexB); 2176 if (result === 0) 2177 return indexA - indexB; 2178 return result; 2179 } 2180 2181 if (fieldName1 === "!edgeName") 2182 this._iterationOrder.sortRange(compareEdgeAndNode, leftBound, rightBound, windowLeft, windowRight); 2183 else if (fieldName2 === "!edgeName") 2184 this._iterationOrder.sortRange(compareNodeAndEdge, leftBound, rightBound, windowLeft, windowRight); 2185 else 2186 this._iterationOrder.sortRange(compareNodeAndNode, leftBound, rightBound, windowLeft, windowRight); 2187 }, 2188 2189 __proto__: WebInspector.HeapSnapshotItemProvider.prototype 2190} 2191 2192 2193/** 2194 * @constructor 2195 * @extends {WebInspector.HeapSnapshotItemProvider} 2196 * @param {!WebInspector.HeapSnapshot} snapshot 2197 * @param {?function(!WebInspector.HeapSnapshotNode):boolean} filter 2198 * @param {(!Array.<number>|!Uint32Array)} nodeIndexes 2199 */ 2200WebInspector.HeapSnapshotNodesProvider = function(snapshot, filter, nodeIndexes) 2201{ 2202 this.snapshot = snapshot; 2203 var indexProvider = new WebInspector.HeapSnapshotNodeIndexProvider(snapshot); 2204 var it = new WebInspector.HeapSnapshotIndexRangeIterator(indexProvider, nodeIndexes); 2205 2206 if (filter) 2207 it = new WebInspector.HeapSnapshotFilteredIterator(it, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter)); 2208 WebInspector.HeapSnapshotItemProvider.call(this, it, indexProvider); 2209} 2210 2211WebInspector.HeapSnapshotNodesProvider.prototype = { 2212 /** 2213 * @param {string} snapshotObjectId 2214 * @return {number} 2215 */ 2216 nodePosition: function(snapshotObjectId) 2217 { 2218 this._createIterationOrder(); 2219 var node = this.snapshot.createNode(); 2220 for (var i = 0; i < this._iterationOrder.length; i++) { 2221 node.nodeIndex = this._iterationOrder[i]; 2222 if (node.id() === snapshotObjectId) 2223 break; 2224 } 2225 if (i === this._iterationOrder.length) 2226 return -1; 2227 var targetNodeIndex = this._iterationOrder[i]; 2228 var smallerCount = 0; 2229 var compare = this._buildCompareFunction(this._currentComparator); 2230 for (var i = 0; i < this._iterationOrder.length; i++) { 2231 if (compare(this._iterationOrder[i], targetNodeIndex) < 0) 2232 ++smallerCount; 2233 } 2234 return smallerCount; 2235 }, 2236 2237 /** 2238 * @return {function(number,number):number} 2239 */ 2240 _buildCompareFunction: function(comparator) 2241 { 2242 var nodeA = this.snapshot.createNode(); 2243 var nodeB = this.snapshot.createNode(); 2244 var fieldAccessor1 = nodeA[comparator.fieldName1]; 2245 var fieldAccessor2 = nodeA[comparator.fieldName2]; 2246 var ascending1 = comparator.ascending1 ? 1 : -1; 2247 var ascending2 = comparator.ascending2 ? 1 : -1; 2248 2249 /** 2250 * @param {function():*} fieldAccessor 2251 * @param {number} ascending 2252 * @return {number} 2253 */ 2254 function sortByNodeField(fieldAccessor, ascending) 2255 { 2256 var valueA = fieldAccessor.call(nodeA); 2257 var valueB = fieldAccessor.call(nodeB); 2258 return valueA < valueB ? -ascending : (valueA > valueB ? ascending : 0); 2259 } 2260 2261 /** 2262 * @param {number} indexA 2263 * @param {number} indexB 2264 * @return {number} 2265 */ 2266 function sortByComparator(indexA, indexB) 2267 { 2268 nodeA.nodeIndex = indexA; 2269 nodeB.nodeIndex = indexB; 2270 var result = sortByNodeField(fieldAccessor1, ascending1); 2271 if (result === 0) 2272 result = sortByNodeField(fieldAccessor2, ascending2); 2273 return result || indexA - indexB; 2274 } 2275 2276 return sortByComparator; 2277 }, 2278 2279 /** 2280 * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator 2281 * @param {number} leftBound 2282 * @param {number} rightBound 2283 * @param {number} windowLeft 2284 * @param {number} windowRight 2285 */ 2286 sort: function(comparator, leftBound, rightBound, windowLeft, windowRight) 2287 { 2288 this._iterationOrder.sortRange(this._buildCompareFunction(comparator), leftBound, rightBound, windowLeft, windowRight); 2289 }, 2290 2291 __proto__: WebInspector.HeapSnapshotItemProvider.prototype 2292} 2293 2294