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 * @constructor 33 * @implements {WebInspector.ProfileType.DataDisplayDelegate} 34 * @extends {WebInspector.VBox} 35 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 36 * @param {!WebInspector.HeapProfileHeader} profile 37 */ 38 WebInspector.HeapSnapshotView = function(dataDisplayDelegate, profile) 39 { 40 WebInspector.VBox.call(this); 41 42 this.element.classList.add("heap-snapshot-view"); 43 44 profile.profileType().addEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this); 45 profile.profileType().addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this); 46 47 if (profile.profileType().id === WebInspector.TrackingHeapSnapshotProfileType.TypeId) { 48 this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile); 49 this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this)); 50 } 51 52 this._parentDataDisplayDelegate = dataDisplayDelegate; 53 54 this._splitView = new WebInspector.SplitView(false, true, "heapSnapshotSplitViewState", 200, 200); 55 this._splitView.show(this.element); 56 57 this._containmentView = new WebInspector.VBox(); 58 this._containmentView.setMinimumSize(50, 25); 59 this._containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid(this); 60 this._containmentDataGrid.show(this._containmentView.element); 61 this._containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this); 62 63 this._statisticsView = new WebInspector.HeapSnapshotStatisticsView(); 64 65 this._constructorsView = new WebInspector.VBox(); 66 this._constructorsView.setMinimumSize(50, 25); 67 68 this._constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid(this); 69 this._constructorsDataGrid.show(this._constructorsView.element); 70 this._constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this); 71 72 this._diffView = new WebInspector.VBox(); 73 this._diffView.setMinimumSize(50, 25); 74 75 this._diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid(this); 76 this._diffDataGrid.show(this._diffView.element); 77 this._diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this); 78 79 if (profile._hasAllocationStacks) { 80 this._allocationView = new WebInspector.VBox(); 81 this._allocationView.setMinimumSize(50, 25); 82 this._allocationDataGrid = new WebInspector.AllocationDataGrid(profile.target() , this); 83 this._allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this); 84 this._allocationDataGrid.show(this._allocationView.element); 85 86 this._allocationStackView = new WebInspector.HeapAllocationStackView(profile.target()); 87 this._allocationStackView.setMinimumSize(50, 25); 88 89 this._tabbedPane = new WebInspector.TabbedPane(); 90 this._tabbedPane.closeableTabs = false; 91 this._tabbedPane.headerElement().classList.add("heap-object-details-header"); 92 } 93 94 this._retainmentView = new WebInspector.VBox(); 95 this._retainmentView.setMinimumSize(50, 21); 96 this._retainmentView.element.classList.add("retaining-paths-view"); 97 98 var splitViewResizer; 99 if (this._allocationStackView) { 100 this._tabbedPane = new WebInspector.TabbedPane(); 101 this._tabbedPane.closeableTabs = false; 102 this._tabbedPane.headerElement().classList.add("heap-object-details-header"); 103 104 this._tabbedPane.appendTab("retainers", WebInspector.UIString("Retainers"), this._retainmentView); 105 this._tabbedPane.appendTab("allocation-stack", WebInspector.UIString("Allocation stack"), this._allocationStackView); 106 107 splitViewResizer = this._tabbedPane.headerElement(); 108 this._objectDetailsView = this._tabbedPane; 109 } else { 110 var retainmentViewHeader = document.createElementWithClass("div", "heap-snapshot-view-resizer"); 111 var retainingPathsTitleDiv = retainmentViewHeader.createChild("div", "title"); 112 var retainingPathsTitle = retainingPathsTitleDiv.createChild("span"); 113 retainingPathsTitle.textContent = WebInspector.UIString("Retainers"); 114 this._retainmentView.element.appendChild(retainmentViewHeader); 115 116 splitViewResizer = retainmentViewHeader; 117 this._objectDetailsView = this._retainmentView; 118 } 119 this._splitView.hideDefaultResizer(); 120 this._splitView.installResizer(splitViewResizer); 121 122 this._retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid(this); 123 this._retainmentDataGrid.show(this._retainmentView.element); 124 this._retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this); 125 this._retainmentDataGrid.reset(); 126 127 this._perspectives = []; 128 this._perspectives.push(new WebInspector.HeapSnapshotView.SummaryPerspective()); 129 if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType) 130 this._perspectives.push(new WebInspector.HeapSnapshotView.ComparisonPerspective()); 131 this._perspectives.push(new WebInspector.HeapSnapshotView.ContainmentPerspective()); 132 if (this._allocationView) 133 this._perspectives.push(new WebInspector.HeapSnapshotView.AllocationPerspective()); 134 this._perspectives.push(new WebInspector.HeapSnapshotView.StatisticsPerspective()); 135 136 this._perspectiveSelect = new WebInspector.StatusBarComboBox(this._onSelectedPerspectiveChanged.bind(this)); 137 for (var i = 0; i < this._perspectives.length; ++i) 138 this._perspectiveSelect.createOption(this._perspectives[i].title()); 139 140 this._profile = profile; 141 142 this._baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this)); 143 this._baseSelect.visible = false; 144 this._updateBaseOptions(); 145 146 this._filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this)); 147 this._filterSelect.visible = false; 148 this._updateFilterOptions(); 149 150 this._classNameFilter = new WebInspector.StatusBarInput("Class filter"); 151 this._classNameFilter.visible = false; 152 this._constructorsDataGrid.setNameFilter(this._classNameFilter); 153 this._diffDataGrid.setNameFilter(this._classNameFilter); 154 155 this._selectedSizeText = new WebInspector.StatusBarText(""); 156 157 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true); 158 159 this._currentPerspectiveIndex = 0; 160 this._currentPerspective = this._perspectives[0]; 161 this._currentPerspective.activate(this); 162 this._dataGrid = this._currentPerspective.masterGrid(this); 163 164 this._refreshView(); 165 } 166 167 /** 168 * @constructor 169 * @param {string} title 170 */ 171 WebInspector.HeapSnapshotView.Perspective = function(title) 172 { 173 this._title = title; 174 } 175 176 WebInspector.HeapSnapshotView.Perspective.prototype = { 177 /** 178 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 179 */ 180 activate: function(heapSnapshotView) { }, 181 182 /** 183 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 184 */ 185 deactivate: function(heapSnapshotView) 186 { 187 heapSnapshotView._baseSelect.visible = false; 188 heapSnapshotView._filterSelect.visible = false; 189 heapSnapshotView._classNameFilter.visible = false; 190 if (heapSnapshotView._trackingOverviewGrid) 191 heapSnapshotView._trackingOverviewGrid.detach(); 192 if (heapSnapshotView._allocationView) 193 heapSnapshotView._allocationView.detach(); 194 if (heapSnapshotView._statisticsView) 195 heapSnapshotView._statisticsView.detach(); 196 197 heapSnapshotView._splitView.detach(); 198 heapSnapshotView._splitView.detachChildViews(); 199 }, 200 201 /** 202 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 203 * @return {?WebInspector.DataGrid} 204 */ 205 masterGrid: function(heapSnapshotView) 206 { 207 return null; 208 }, 209 210 /** 211 * @return {string} 212 */ 213 title: function() 214 { 215 return this._title; 216 }, 217 218 /** 219 * @return {boolean} 220 */ 221 supportsSearch: function() 222 { 223 return false; 224 } 225 } 226 227 /** 228 * @constructor 229 * @extends {WebInspector.HeapSnapshotView.Perspective} 230 */ 231 WebInspector.HeapSnapshotView.SummaryPerspective = function() 232 { 233 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Summary")); 234 } 235 236 WebInspector.HeapSnapshotView.SummaryPerspective.prototype = { 237 /** 238 * @override 239 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 240 */ 241 activate: function(heapSnapshotView) 242 { 243 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement()); 244 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement()); 245 heapSnapshotView._splitView.show(heapSnapshotView.element); 246 heapSnapshotView._filterSelect.visible = true; 247 heapSnapshotView._classNameFilter.visible = true; 248 if (heapSnapshotView._trackingOverviewGrid) { 249 heapSnapshotView._trackingOverviewGrid.show(heapSnapshotView.element, heapSnapshotView._splitView.element); 250 heapSnapshotView._trackingOverviewGrid.update(); 251 heapSnapshotView._trackingOverviewGrid._updateGrid(); 252 } 253 }, 254 255 /** 256 * @override 257 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 258 * @return {?WebInspector.DataGrid} 259 */ 260 masterGrid: function(heapSnapshotView) 261 { 262 return heapSnapshotView._constructorsDataGrid; 263 }, 264 265 /** 266 * @override 267 * @return {boolean} 268 */ 269 supportsSearch: function() 270 { 271 return true; 272 }, 273 274 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype 275 } 276 277 /** 278 * @constructor 279 * @extends {WebInspector.HeapSnapshotView.Perspective} 280 */ 281 WebInspector.HeapSnapshotView.ComparisonPerspective = function() 282 { 283 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Comparison")); 284 } 285 286 WebInspector.HeapSnapshotView.ComparisonPerspective.prototype = { 287 /** 288 * @override 289 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 290 */ 291 activate: function(heapSnapshotView) 292 { 293 heapSnapshotView._diffView.show(heapSnapshotView._splitView.mainElement()); 294 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement()); 295 heapSnapshotView._splitView.show(heapSnapshotView.element); 296 heapSnapshotView._baseSelect.visible = true; 297 heapSnapshotView._classNameFilter.visible = true; 298 }, 299 300 /** 301 * @override 302 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 303 * @return {?WebInspector.DataGrid} 304 */ 305 masterGrid: function(heapSnapshotView) 306 { 307 return heapSnapshotView._diffDataGrid; 308 }, 309 310 /** 311 * @override 312 * @return {boolean} 313 */ 314 supportsSearch: function() 315 { 316 return true; 317 }, 318 319 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype 320 } 321 322 /** 323 * @constructor 324 * @extends {WebInspector.HeapSnapshotView.Perspective} 325 */ 326 WebInspector.HeapSnapshotView.ContainmentPerspective = function() 327 { 328 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Containment")); 329 } 330 331 WebInspector.HeapSnapshotView.ContainmentPerspective.prototype = { 332 /** 333 * @override 334 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 335 */ 336 activate: function(heapSnapshotView) 337 { 338 heapSnapshotView._containmentView.show(heapSnapshotView._splitView.mainElement()); 339 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement()); 340 heapSnapshotView._splitView.show(heapSnapshotView.element); 341 }, 342 343 /** 344 * @override 345 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 346 * @return {?WebInspector.DataGrid} 347 */ 348 masterGrid: function(heapSnapshotView) 349 { 350 return heapSnapshotView._containmentDataGrid; 351 }, 352 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype 353 } 354 355 /** 356 * @constructor 357 * @extends {WebInspector.HeapSnapshotView.Perspective} 358 */ 359 WebInspector.HeapSnapshotView.AllocationPerspective = function() 360 { 361 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Allocation")); 362 this._allocationSplitView = new WebInspector.SplitView(false, true, "heapSnapshotAllocationSplitViewState", 200, 200); 363 364 var resizer = document.createElementWithClass("div", "heap-snapshot-view-resizer"); 365 var title = resizer.createChild("div", "title").createChild("span"); 366 title.textContent = WebInspector.UIString("Live objects"); 367 this._allocationSplitView.hideDefaultResizer(); 368 this._allocationSplitView.installResizer(resizer); 369 370 this._allocationSplitView.sidebarElement().appendChild(resizer); 371 } 372 373 WebInspector.HeapSnapshotView.AllocationPerspective.prototype = { 374 /** 375 * @override 376 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 377 */ 378 activate: function(heapSnapshotView) 379 { 380 heapSnapshotView._allocationView.show(this._allocationSplitView.mainElement()); 381 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement()); 382 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement()); 383 heapSnapshotView._splitView.show(this._allocationSplitView.sidebarElement()); 384 this._allocationSplitView.show(heapSnapshotView.element); 385 386 heapSnapshotView._constructorsDataGrid.clear(); 387 var selectedNode = heapSnapshotView._allocationDataGrid.selectedNode; 388 if (selectedNode) 389 heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId()); 390 }, 391 392 /** 393 * @override 394 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 395 */ 396 deactivate: function(heapSnapshotView) 397 { 398 this._allocationSplitView.detach(); 399 WebInspector.HeapSnapshotView.Perspective.prototype.deactivate.call(this, heapSnapshotView); 400 }, 401 402 /** 403 * @override 404 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 405 * @return {?WebInspector.DataGrid} 406 */ 407 masterGrid: function(heapSnapshotView) 408 { 409 return heapSnapshotView._allocationDataGrid; 410 }, 411 412 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype 413 } 414 415 /** 416 * @constructor 417 * @extends {WebInspector.HeapSnapshotView.Perspective} 418 */ 419 WebInspector.HeapSnapshotView.StatisticsPerspective = function() 420 { 421 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Statistics")); 422 } 423 424 WebInspector.HeapSnapshotView.StatisticsPerspective.prototype = { 425 /** 426 * @override 427 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 428 */ 429 activate: function(heapSnapshotView) 430 { 431 heapSnapshotView._statisticsView.show(heapSnapshotView.element); 432 }, 433 434 /** 435 * @override 436 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView 437 * @return {?WebInspector.DataGrid} 438 */ 439 masterGrid: function(heapSnapshotView) 440 { 441 return null; 442 }, 443 444 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype 445 } 446 447 448 WebInspector.HeapSnapshotView.prototype = { 449 /** 450 * @override 451 * @param {?WebInspector.ProfileHeader} profile 452 * @return {?WebInspector.View} 453 */ 454 showProfile: function(profile) 455 { 456 return this._parentDataDisplayDelegate.showProfile(profile); 457 }, 458 459 /** 460 * @override 461 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId 462 * @param {string} perspectiveName 463 */ 464 showObject: function(snapshotObjectId, perspectiveName) 465 { 466 if (snapshotObjectId <= this._profile.maxJSObjectId) 467 this.highlightLiveObject(perspectiveName, snapshotObjectId); 468 else 469 this._parentDataDisplayDelegate.showObject(snapshotObjectId, perspectiveName); 470 }, 471 472 _refreshView: function() 473 { 474 this._profile.load(profileCallback.bind(this)); 475 476 /** 477 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy 478 * @this {WebInspector.HeapSnapshotView} 479 */ 480 function profileCallback(heapSnapshotProxy) 481 { 482 heapSnapshotProxy.getStatistics(this._gotStatistics.bind(this)); 483 var list = this._profiles(); 484 var profileIndex = list.indexOf(this._profile); 485 this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1)); 486 this._dataGrid.setDataSource(heapSnapshotProxy); 487 if (this._trackingOverviewGrid) 488 this._trackingOverviewGrid._updateGrid(); 489 } 490 }, 491 492 /** 493 * @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics 494 */ 495 _gotStatistics: function(statistics) 496 { 497 this._statisticsView.setTotal(statistics.total); 498 this._statisticsView.addRecord(statistics.code, WebInspector.UIString("Code"), "#f77"); 499 this._statisticsView.addRecord(statistics.strings, WebInspector.UIString("Strings"), "#5e5"); 500 this._statisticsView.addRecord(statistics.jsArrays, WebInspector.UIString("JS Arrays"), "#7af"); 501 this._statisticsView.addRecord(statistics.native, WebInspector.UIString("Typed Arrays"), "#fc5"); 502 this._statisticsView.addRecord(statistics.total, WebInspector.UIString("Total")); 503 }, 504 505 _onIdsRangeChanged: function(event) 506 { 507 var minId = event.data.minId; 508 var maxId = event.data.maxId; 509 this._selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size))); 510 if (this._constructorsDataGrid.snapshot) 511 this._constructorsDataGrid.setSelectionRange(minId, maxId); 512 }, 513 514 get statusBarItems() 515 { 516 var result = [this._perspectiveSelect.element, this._classNameFilter.element]; 517 if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType) 518 result.push(this._baseSelect.element, this._filterSelect.element); 519 result.push(this._selectedSizeText.element); 520 return result; 521 }, 522 523 wasShown: function() 524 { 525 // FIXME: load base and current snapshots in parallel 526 this._profile.load(profileCallback.bind(this)); 527 528 /** 529 * @this {WebInspector.HeapSnapshotView} 530 */ 531 function profileCallback() { 532 this._profile._wasShown(); 533 if (this._baseProfile) 534 this._baseProfile.load(function() { }); 535 } 536 }, 537 538 willHide: function() 539 { 540 this._currentSearchResultIndex = -1; 541 this._popoverHelper.hidePopover(); 542 if (this.helpPopover && this.helpPopover.isShowing()) 543 this.helpPopover.hide(); 544 }, 545 546 searchCanceled: function() 547 { 548 if (this._searchResults) { 549 for (var i = 0; i < this._searchResults.length; ++i) { 550 var node = this._searchResults[i].node; 551 delete node._searchMatched; 552 node.refresh(); 553 } 554 } 555 556 delete this._searchFinishedCallback; 557 this._currentSearchResultIndex = -1; 558 this._searchResults = []; 559 }, 560 561 /** 562 * @param {string} query 563 * @param {function(!WebInspector.View, number)} finishedCallback 564 */ 565 performSearch: function(query, finishedCallback) 566 { 567 // Call searchCanceled since it will reset everything we need before doing a new search. 568 this.searchCanceled(); 569 570 query = query.trim(); 571 572 if (!query) 573 return; 574 if (!this._currentPerspective.supportsSearch()) 575 return; 576 577 /** 578 * @param {boolean} found 579 * @this {WebInspector.HeapSnapshotView} 580 */ 581 function didHighlight(found) 582 { 583 finishedCallback(this, found ? 1 : 0); 584 } 585 586 if (query.charAt(0) === "@") { 587 var snapshotNodeId = parseInt(query.substring(1), 10); 588 if (!isNaN(snapshotNodeId)) 589 this._dataGrid.highlightObjectByHeapSnapshotId(String(snapshotNodeId), didHighlight.bind(this)); 590 else 591 finishedCallback(this, 0); 592 return; 593 } 594 595 this._searchFinishedCallback = finishedCallback; 596 var nameRegExp = createPlainTextSearchRegex(query, "i"); 597 598 function matchesByName(gridNode) { 599 return ("_name" in gridNode) && nameRegExp.test(gridNode._name); 600 } 601 602 function matchesQuery(gridNode) 603 { 604 delete gridNode._searchMatched; 605 if (matchesByName(gridNode)) { 606 gridNode._searchMatched = true; 607 gridNode.refresh(); 608 return true; 609 } 610 return false; 611 } 612 613 var current = this._dataGrid.rootNode().children[0]; 614 var depth = 0; 615 var info = {}; 616 617 // Restrict to type nodes and instances. 618 const maxDepth = 1; 619 620 while (current) { 621 if (matchesQuery(current)) 622 this._searchResults.push({ node: current }); 623 current = current.traverseNextNode(false, null, (depth >= maxDepth), info); 624 depth += info.depthChange; 625 } 626 627 finishedCallback(this, this._searchResults.length); 628 }, 629 630 jumpToFirstSearchResult: function() 631 { 632 if (!this._searchResults || !this._searchResults.length) 633 return; 634 this._currentSearchResultIndex = 0; 635 this._jumpToSearchResult(this._currentSearchResultIndex); 636 }, 637 638 jumpToLastSearchResult: function() 639 { 640 if (!this._searchResults || !this._searchResults.length) 641 return; 642 this._currentSearchResultIndex = (this._searchResults.length - 1); 643 this._jumpToSearchResult(this._currentSearchResultIndex); 644 }, 645 646 jumpToNextSearchResult: function() 647 { 648 if (!this._searchResults || !this._searchResults.length) 649 return; 650 if (++this._currentSearchResultIndex >= this._searchResults.length) 651 this._currentSearchResultIndex = 0; 652 this._jumpToSearchResult(this._currentSearchResultIndex); 653 }, 654 655 jumpToPreviousSearchResult: function() 656 { 657 if (!this._searchResults || !this._searchResults.length) 658 return; 659 if (--this._currentSearchResultIndex < 0) 660 this._currentSearchResultIndex = (this._searchResults.length - 1); 661 this._jumpToSearchResult(this._currentSearchResultIndex); 662 }, 663 664 /** 665 * @return {boolean} 666 */ 667 showingFirstSearchResult: function() 668 { 669 return (this._currentSearchResultIndex === 0); 670 }, 671 672 /** 673 * @return {boolean} 674 */ 675 showingLastSearchResult: function() 676 { 677 return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1)); 678 }, 679 680 /** 681 * @return {number} 682 */ 683 currentSearchResultIndex: function() { 684 return this._currentSearchResultIndex; 685 }, 686 687 _jumpToSearchResult: function(index) 688 { 689 var searchResult = this._searchResults[index]; 690 if (!searchResult) 691 return; 692 693 var node = searchResult.node; 694 node.revealAndSelect(); 695 }, 696 697 refreshVisibleData: function() 698 { 699 if (!this._dataGrid) 700 return; 701 var child = this._dataGrid.rootNode().children[0]; 702 while (child) { 703 child.refresh(); 704 child = child.traverseNextNode(false, null, true); 705 } 706 }, 707 708 _changeBase: function() 709 { 710 if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()]) 711 return; 712 713 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()]; 714 var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid); 715 // Change set base data source only if main data source is already set. 716 if (dataGrid.snapshot) 717 this._baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid)); 718 719 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults) 720 return; 721 722 // The current search needs to be performed again. First negate out previous match 723 // count by calling the search finished callback with a negative number of matches. 724 // Then perform the search again with the same query and callback. 725 this._searchFinishedCallback(this, -this._searchResults.length); 726 this.performSearch(this.currentQuery, this._searchFinishedCallback); 727 }, 728 729 _changeFilter: function() 730 { 731 var profileIndex = this._filterSelect.selectedIndex() - 1; 732 this._dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex); 733 734 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, { 735 action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged, 736 label: this._filterSelect.selectedOption().label 737 }); 738 739 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults) 740 return; 741 742 // The current search needs to be performed again. First negate out previous match 743 // count by calling the search finished callback with a negative number of matches. 744 // Then perform the search again with the same query and callback. 745 this._searchFinishedCallback(this, -this._searchResults.length); 746 this.performSearch(this.currentQuery, this._searchFinishedCallback); 747 }, 748 749 /** 750 * @return {!Array.<!WebInspector.ProfileHeader>} 751 */ 752 _profiles: function() 753 { 754 return this._profile.profileType().getProfiles(); 755 }, 756 757 /** 758 * @param {!WebInspector.ContextMenu} contextMenu 759 * @param {!Event} event 760 */ 761 populateContextMenu: function(contextMenu, event) 762 { 763 if (this._dataGrid) 764 this._dataGrid.populateContextMenu(contextMenu, event); 765 }, 766 767 _selectionChanged: function(event) 768 { 769 var selectedNode = event.target.selectedNode; 770 this._setSelectedNodeForDetailsView(selectedNode); 771 this._inspectedObjectChanged(event); 772 }, 773 774 _onSelectAllocationNode: function(event) 775 { 776 var selectedNode = event.target.selectedNode; 777 this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId()); 778 this._setSelectedNodeForDetailsView(null); 779 }, 780 781 _inspectedObjectChanged: function(event) 782 { 783 var selectedNode = event.target.selectedNode; 784 var target = this._profile.target(); 785 if (target && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode) 786 target.consoleAgent().addInspectedHeapObject(selectedNode.snapshotNodeId); 787 }, 788 789 /** 790 * @param {?WebInspector.HeapSnapshotGridNode} nodeItem 791 */ 792 _setSelectedNodeForDetailsView: function(nodeItem) 793 { 794 var dataSource = nodeItem && nodeItem.retainersDataSource(); 795 if (dataSource) { 796 this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex); 797 if (this._allocationStackView) 798 this._allocationStackView.setAllocatedObject(dataSource.snapshot, dataSource.snapshotNodeIndex) 799 } else { 800 if (this._allocationStackView) 801 this._allocationStackView.clear(); 802 this._retainmentDataGrid.reset(); 803 } 804 }, 805 806 /** 807 * @param {string} perspectiveTitle 808 * @param {function()} callback 809 */ 810 _changePerspectiveAndWait: function(perspectiveTitle, callback) 811 { 812 var perspectiveIndex = null; 813 for (var i = 0; i < this._perspectives.length; ++i) { 814 if (this._perspectives[i].title() === perspectiveTitle) { 815 perspectiveIndex = i; 816 break; 817 } 818 } 819 if (this._currentPerspectiveIndex === perspectiveIndex || perspectiveIndex === null) { 820 setTimeout(callback, 0); 821 return; 822 } 823 824 /** 825 * @this {WebInspector.HeapSnapshotView} 826 */ 827 function dataGridContentShown(event) 828 { 829 var dataGrid = event.data; 830 dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this); 831 if (dataGrid === this._dataGrid) 832 callback(); 833 } 834 this._perspectives[perspectiveIndex].masterGrid(this).addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this); 835 836 this._perspectiveSelect.setSelectedIndex(perspectiveIndex); 837 this._changePerspective(perspectiveIndex); 838 }, 839 840 _updateDataSourceAndView: function() 841 { 842 var dataGrid = this._dataGrid; 843 if (!dataGrid || dataGrid.snapshot) 844 return; 845 846 this._profile.load(didLoadSnapshot.bind(this)); 847 848 /** 849 * @this {WebInspector.HeapSnapshotView} 850 */ 851 function didLoadSnapshot(snapshotProxy) 852 { 853 if (this._dataGrid !== dataGrid) 854 return; 855 if (dataGrid.snapshot !== snapshotProxy) 856 dataGrid.setDataSource(snapshotProxy); 857 if (dataGrid === this._diffDataGrid) { 858 if (!this._baseProfile) 859 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()]; 860 this._baseProfile.load(didLoadBaseSnaphot.bind(this)); 861 } 862 } 863 864 /** 865 * @this {WebInspector.HeapSnapshotView} 866 */ 867 function didLoadBaseSnaphot(baseSnapshotProxy) 868 { 869 if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy) 870 this._diffDataGrid.setBaseDataSource(baseSnapshotProxy); 871 } 872 }, 873 874 _onSelectedPerspectiveChanged: function(event) 875 { 876 this._changePerspective(event.target.selectedIndex); 877 // FIXME: This is needed by CodeSchool extension. 878 this._onSelectedViewChanged(event); 879 }, 880 881 _onSelectedViewChanged: function(event) 882 { 883 }, 884 885 /** 886 * @param {number} selectedIndex 887 */ 888 _changePerspective: function(selectedIndex) 889 { 890 if (selectedIndex === this._currentPerspectiveIndex) 891 return; 892 893 this._currentPerspectiveIndex = selectedIndex; 894 895 this._currentPerspective.deactivate(this); 896 var perspective = this._perspectives[selectedIndex]; 897 this._currentPerspective = perspective; 898 this._dataGrid = perspective.masterGrid(this); 899 perspective.activate(this); 900 901 this.refreshVisibleData(); 902 if (this._dataGrid) 903 this._dataGrid.updateWidths(); 904 905 this._updateDataSourceAndView(); 906 907 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults) 908 return; 909 910 // The current search needs to be performed again. First negate out previous match 911 // count by calling the search finished callback with a negative number of matches. 912 // Then perform the search again the with same query and callback. 913 this._searchFinishedCallback(this, -this._searchResults.length); 914 this.performSearch(this.currentQuery, this._searchFinishedCallback); 915 }, 916 917 /** 918 * @param {string} perspectiveName 919 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId 920 */ 921 highlightLiveObject: function(perspectiveName, snapshotObjectId) 922 { 923 this._changePerspectiveAndWait(perspectiveName, didChangePerspective.bind(this)); 924 925 /** 926 * @this {WebInspector.HeapSnapshotView} 927 */ 928 function didChangePerspective() 929 { 930 this._dataGrid.highlightObjectByHeapSnapshotId(snapshotObjectId, didHighlightObject); 931 } 932 933 function didHighlightObject(found) 934 { 935 if (!found) 936 WebInspector.console.error("Cannot find corresponding heap snapshot node"); 937 } 938 }, 939 940 _getHoverAnchor: function(target) 941 { 942 var span = target.enclosingNodeOrSelfWithNodeName("span"); 943 if (!span) 944 return; 945 var row = target.enclosingNodeOrSelfWithNodeName("tr"); 946 if (!row) 947 return; 948 span.node = row._dataGridNode; 949 return span; 950 }, 951 952 _resolveObjectForPopover: function(element, showCallback, objectGroupName) 953 { 954 if (!this._profile.target()) 955 return; 956 element.node.queryObjectContent(this._profile.target(), showCallback, objectGroupName); 957 }, 958 959 _updateBaseOptions: function() 960 { 961 var list = this._profiles(); 962 // We're assuming that snapshots can only be added. 963 if (this._baseSelect.size() === list.length) 964 return; 965 966 for (var i = this._baseSelect.size(), n = list.length; i < n; ++i) { 967 var title = list[i].title; 968 this._baseSelect.createOption(title); 969 } 970 }, 971 972 _updateFilterOptions: function() 973 { 974 var list = this._profiles(); 975 // We're assuming that snapshots can only be added. 976 if (this._filterSelect.size() - 1 === list.length) 977 return; 978 979 if (!this._filterSelect.size()) 980 this._filterSelect.createOption(WebInspector.UIString("All objects")); 981 982 for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) { 983 var title = list[i].title; 984 if (!i) 985 title = WebInspector.UIString("Objects allocated before %s", title); 986 else 987 title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title); 988 this._filterSelect.createOption(title); 989 } 990 }, 991 992 _updateControls: function() 993 { 994 this._updateBaseOptions(); 995 this._updateFilterOptions(); 996 }, 997 998 /** 999 * @param {!WebInspector.Event} event 1000 */ 1001 _onReceiveSnapshot: function(event) 1002 { 1003 this._updateControls(); 1004 }, 1005 1006 /** 1007 * @param {!WebInspector.Event} event 1008 */ 1009 _onProfileHeaderRemoved: function(event) 1010 { 1011 var profile = event.data; 1012 if (this._profile === profile) { 1013 this.detach(); 1014 this._profile.profileType().removeEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this); 1015 this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this); 1016 this.dispose(); 1017 } else { 1018 this._updateControls(); 1019 } 1020 }, 1021 1022 dispose: function() 1023 { 1024 if (this._allocationStackView) { 1025 this._allocationStackView.clear(); 1026 this._allocationDataGrid.dispose(); 1027 } 1028 }, 1029 1030 __proto__: WebInspector.VBox.prototype 1031 } 1032 1033 /** 1034 * @constructor 1035 * @extends {WebInspector.ProfileType} 1036 * @implements {WebInspector.TargetManager.Observer} 1037 * @param {string=} id 1038 * @param {string=} title 1039 */ 1040 WebInspector.HeapSnapshotProfileType = function(id, title) 1041 { 1042 WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot")); 1043 WebInspector.targetManager.observeTargets(this); 1044 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ResetProfiles, this._resetProfiles, this); 1045 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.AddHeapSnapshotChunk, this._addHeapSnapshotChunk, this); 1046 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ReportHeapSnapshotProgress, this._reportHeapSnapshotProgress, this); 1047 } 1048 1049 WebInspector.HeapSnapshotProfileType.TypeId = "HEAP"; 1050 WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived"; 1051 1052 WebInspector.HeapSnapshotProfileType.prototype = { 1053 /** 1054 * @param {!WebInspector.Target} target 1055 */ 1056 targetAdded: function(target) 1057 { 1058 target.heapProfilerModel.enable(); 1059 }, 1060 1061 /** 1062 * @param {!WebInspector.Target} target 1063 */ 1064 targetRemoved: function(target) 1065 { 1066 }, 1067 1068 /** 1069 * @override 1070 * @return {string} 1071 */ 1072 fileExtension: function() 1073 { 1074 return ".heapsnapshot"; 1075 }, 1076 1077 get buttonTooltip() 1078 { 1079 return WebInspector.UIString("Take heap snapshot."); 1080 }, 1081 1082 /** 1083 * @override 1084 * @return {boolean} 1085 */ 1086 isInstantProfile: function() 1087 { 1088 return true; 1089 }, 1090 1091 /** 1092 * @override 1093 * @return {boolean} 1094 */ 1095 buttonClicked: function() 1096 { 1097 this._takeHeapSnapshot(function() {}); 1098 WebInspector.userMetrics.ProfilesHeapProfileTaken.record(); 1099 return false; 1100 }, 1101 1102 get treeItemTitle() 1103 { 1104 return WebInspector.UIString("HEAP SNAPSHOTS"); 1105 }, 1106 1107 get description() 1108 { 1109 return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes."); 1110 }, 1111 1112 /** 1113 * @override 1114 * @param {string} title 1115 * @return {!WebInspector.ProfileHeader} 1116 */ 1117 createProfileLoadedFromFile: function(title) 1118 { 1119 return new WebInspector.HeapProfileHeader(null, this, title); 1120 }, 1121 1122 _takeHeapSnapshot: function(callback) 1123 { 1124 if (this.profileBeingRecorded()) 1125 return; 1126 var target = /** @type {!WebInspector.Target} */ (WebInspector.context.flavor(WebInspector.Target)); 1127 var profile = new WebInspector.HeapProfileHeader(target, this); 1128 this.setProfileBeingRecorded(profile); 1129 this.addProfile(profile); 1130 profile.updateStatus(WebInspector.UIString("Snapshotting\u2026")); 1131 1132 /** 1133 * @param {?string} error 1134 * @this {WebInspector.HeapSnapshotProfileType} 1135 */ 1136 function didTakeHeapSnapshot(error) 1137 { 1138 var profile = this._profileBeingRecorded; 1139 profile.title = WebInspector.UIString("Snapshot %d", profile.uid); 1140 profile._finishLoad(); 1141 this.setProfileBeingRecorded(null); 1142 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile); 1143 callback(); 1144 } 1145 target.heapProfilerAgent().takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this)); 1146 }, 1147 1148 /** 1149 * @param {!WebInspector.Event} event 1150 */ 1151 _addHeapSnapshotChunk: function(event) 1152 { 1153 if (!this.profileBeingRecorded()) 1154 return; 1155 var chunk = /** @type {string} */(event.data); 1156 this.profileBeingRecorded().transferChunk(chunk); 1157 }, 1158 1159 /** 1160 * @param {!WebInspector.Event} event 1161 */ 1162 _reportHeapSnapshotProgress: function(event) 1163 { 1164 var profile = this.profileBeingRecorded(); 1165 if (!profile) 1166 return; 1167 var data = /** @type {{done: number, total: number, finished: boolean}} */ (event.data); 1168 profile.updateStatus(WebInspector.UIString("%.0f%", (data.done / data.total) * 100), true); 1169 if (data.finished) 1170 profile._prepareToLoad(); 1171 }, 1172 1173 _resetProfiles: function() 1174 { 1175 this._reset(); 1176 }, 1177 1178 _snapshotReceived: function(profile) 1179 { 1180 if (this._profileBeingRecorded === profile) 1181 this.setProfileBeingRecorded(null); 1182 this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile); 1183 }, 1184 1185 __proto__: WebInspector.ProfileType.prototype 1186 } 1187 1188 1189 /** 1190 * @constructor 1191 * @extends {WebInspector.HeapSnapshotProfileType} 1192 */ 1193 WebInspector.TrackingHeapSnapshotProfileType = function() 1194 { 1195 WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations")); 1196 } 1197 1198 WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD"; 1199 1200 WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate"; 1201 WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted"; 1202 WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped"; 1203 1204 WebInspector.TrackingHeapSnapshotProfileType.prototype = { 1205 1206 /** 1207 * @param {!WebInspector.Target} target 1208 */ 1209 targetAdded: function(target) 1210 { 1211 WebInspector.HeapSnapshotProfileType.prototype.targetAdded.call(this, target); 1212 target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this); 1213 target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this); 1214 }, 1215 1216 /** 1217 * @param {!WebInspector.Target} target 1218 */ 1219 targetRemoved: function(target) 1220 { 1221 WebInspector.HeapSnapshotProfileType.prototype.targetRemoved.call(this, target); 1222 target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this); 1223 target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this); 1224 }, 1225 1226 /** 1227 * @param {!WebInspector.Event} event 1228 */ 1229 _heapStatsUpdate: function(event) 1230 { 1231 if (!this._profileSamples) 1232 return; 1233 var samples = /** @type {!Array.<number>} */ (event.data); 1234 var index; 1235 for (var i = 0; i < samples.length; i += 3) { 1236 index = samples[i]; 1237 var count = samples[i+1]; 1238 var size = samples[i+2]; 1239 this._profileSamples.sizes[index] = size; 1240 if (!this._profileSamples.max[index]) 1241 this._profileSamples.max[index] = size; 1242 } 1243 }, 1244 1245 /** 1246 * @param {!WebInspector.Event} event 1247 */ 1248 _lastSeenObjectId: function(event) 1249 { 1250 var profileSamples = this._profileSamples; 1251 if (!profileSamples) 1252 return; 1253 var data = /** @type {{lastSeenObjectId: number, timestamp: number}} */ (event.data); 1254 var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1); 1255 profileSamples.ids[currentIndex] = data.lastSeenObjectId; 1256 if (!profileSamples.max[currentIndex]) { 1257 profileSamples.max[currentIndex] = 0; 1258 profileSamples.sizes[currentIndex] = 0; 1259 } 1260 profileSamples.timestamps[currentIndex] = data.timestamp; 1261 if (profileSamples.totalTime < data.timestamp - profileSamples.timestamps[0]) 1262 profileSamples.totalTime *= 2; 1263 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples); 1264 this._profileBeingRecorded.updateStatus(null, true); 1265 }, 1266 1267 /** 1268 * @override 1269 * @return {boolean} 1270 */ 1271 hasTemporaryView: function() 1272 { 1273 return true; 1274 }, 1275 1276 get buttonTooltip() 1277 { 1278 return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile."); 1279 }, 1280 1281 /** 1282 * @override 1283 * @return {boolean} 1284 */ 1285 isInstantProfile: function() 1286 { 1287 return false; 1288 }, 1289 1290 /** 1291 * @override 1292 * @return {boolean} 1293 */ 1294 buttonClicked: function() 1295 { 1296 return this._toggleRecording(); 1297 }, 1298 1299 _startRecordingProfile: function() 1300 { 1301 if (this.profileBeingRecorded()) 1302 return; 1303 var recordAllocationStacks = WebInspector.settings.recordAllocationStacks.get(); 1304 this._addNewProfile(recordAllocationStacks); 1305 this.profileBeingRecorded().target().heapProfilerAgent().startTrackingHeapObjects(recordAllocationStacks); 1306 }, 1307 1308 /** 1309 * @param {boolean} withAllocationStacks 1310 */ 1311 _addNewProfile: function(withAllocationStacks) 1312 { 1313 var target = WebInspector.context.flavor(WebInspector.Target); 1314 this.setProfileBeingRecorded(new WebInspector.HeapProfileHeader(target, this, undefined, withAllocationStacks)); 1315 this._lastSeenIndex = -1; 1316 this._profileSamples = { 1317 'sizes': [], 1318 'ids': [], 1319 'timestamps': [], 1320 'max': [], 1321 'totalTime': 30000 1322 }; 1323 this._profileBeingRecorded._profileSamples = this._profileSamples; 1324 this._recording = true; 1325 this.addProfile(this._profileBeingRecorded); 1326 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026")); 1327 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted); 1328 }, 1329 1330 _stopRecordingProfile: function() 1331 { 1332 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026")); 1333 /** 1334 * @param {?string} error 1335 * @this {WebInspector.HeapSnapshotProfileType} 1336 */ 1337 function didTakeHeapSnapshot(error) 1338 { 1339 var profile = this.profileBeingRecorded(); 1340 if (!profile) 1341 return; 1342 profile._finishLoad(); 1343 this._profileSamples = null; 1344 this.setProfileBeingRecorded(null); 1345 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile); 1346 } 1347 1348 this._profileBeingRecorded.target().heapProfilerAgent().stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this)); 1349 this._recording = false; 1350 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped); 1351 }, 1352 1353 _toggleRecording: function() 1354 { 1355 if (this._recording) 1356 this._stopRecordingProfile(); 1357 else 1358 this._startRecordingProfile(); 1359 return this._recording; 1360 }, 1361 1362 get treeItemTitle() 1363 { 1364 return WebInspector.UIString("HEAP TIMELINES"); 1365 }, 1366 1367 get description() 1368 { 1369 return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks."); 1370 }, 1371 1372 /** 1373 * @override 1374 */ 1375 resetProfiles: function() 1376 { 1377 var wasRecording = this._recording; 1378 var recordingAllocationStacks = wasRecording && this.profileBeingRecorded()._hasAllocationStacks; 1379 // Clear current profile to avoid stopping backend. 1380 this.setProfileBeingRecorded(null); 1381 WebInspector.HeapSnapshotProfileType.prototype.resetProfiles.call(this); 1382 this._profileSamples = null; 1383 this._lastSeenIndex = -1; 1384 if (wasRecording) 1385 this._addNewProfile(recordingAllocationStacks); 1386 }, 1387 1388 /** 1389 * @override 1390 */ 1391 profileBeingRecordedRemoved: function() 1392 { 1393 this._stopRecordingProfile(); 1394 this._profileSamples = null; 1395 }, 1396 1397 __proto__: WebInspector.HeapSnapshotProfileType.prototype 1398 } 1399 1400 /** 1401 * @constructor 1402 * @extends {WebInspector.ProfileHeader} 1403 * @param {?WebInspector.Target} target 1404 * @param {!WebInspector.HeapSnapshotProfileType} type 1405 * @param {string=} title 1406 * @param {boolean=} hasAllocationStacks 1407 */ 1408 WebInspector.HeapProfileHeader = function(target, type, title, hasAllocationStacks) 1409 { 1410 WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UIString("Snapshot %d", type.nextProfileUid())); 1411 this._hasAllocationStacks = !!hasAllocationStacks; 1412 this.maxJSObjectId = -1; 1413 /** 1414 * @type {?WebInspector.HeapSnapshotWorkerProxy} 1415 */ 1416 this._workerProxy = null; 1417 /** 1418 * @type {?WebInspector.OutputStream} 1419 */ 1420 this._receiver = null; 1421 /** 1422 * @type {?WebInspector.HeapSnapshotProxy} 1423 */ 1424 this._snapshotProxy = null; 1425 /** 1426 * @type {?Array.<function(!WebInspector.HeapSnapshotProxy)>} 1427 */ 1428 this._loadCallbacks = []; 1429 this._totalNumberOfChunks = 0; 1430 this._bufferedWriter = null; 1431 } 1432 1433 WebInspector.HeapProfileHeader.prototype = { 1434 /** 1435 * @override 1436 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 1437 * @return {!WebInspector.ProfileSidebarTreeElement} 1438 */ 1439 createSidebarTreeElement: function(dataDisplayDelegate) 1440 { 1441 return new WebInspector.ProfileSidebarTreeElement(dataDisplayDelegate, this, "heap-snapshot-sidebar-tree-item"); 1442 }, 1443 1444 /** 1445 * @override 1446 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate 1447 * @return {!WebInspector.HeapSnapshotView} 1448 */ 1449 createView: function(dataDisplayDelegate) 1450 { 1451 return new WebInspector.HeapSnapshotView(dataDisplayDelegate, this); 1452 }, 1453 1454 /** 1455 * @override 1456 * @param {function(!WebInspector.HeapSnapshotProxy):void} callback 1457 */ 1458 load: function(callback) 1459 { 1460 if (this.uid === -1) 1461 return; 1462 if (this._snapshotProxy) { 1463 callback(this._snapshotProxy); 1464 return; 1465 } 1466 this._loadCallbacks.push(callback); 1467 }, 1468 1469 _prepareToLoad: function() 1470 { 1471 console.assert(!this._receiver, "Already loading"); 1472 this._setupWorker(); 1473 this.updateStatus(WebInspector.UIString("Loading\u2026"), true); 1474 }, 1475 1476 _finishLoad: function() 1477 { 1478 if (!this._wasDisposed) 1479 this._receiver.close(); 1480 if (this._bufferedWriter) { 1481 this._bufferedWriter.finishWriting(this._didWriteToTempFile.bind(this)); 1482 this._bufferedWriter = null; 1483 } 1484 }, 1485 1486 _didWriteToTempFile: function(tempFile) 1487 { 1488 if (this._wasDisposed) { 1489 if (tempFile) 1490 tempFile.remove(); 1491 return; 1492 } 1493 this._tempFile = tempFile; 1494 if (!tempFile) 1495 this._failedToCreateTempFile = true; 1496 if (this._onTempFileReady) { 1497 this._onTempFileReady(); 1498 this._onTempFileReady = null; 1499 } 1500 }, 1501 1502 _setupWorker: function() 1503 { 1504 /** 1505 * @this {WebInspector.HeapProfileHeader} 1506 */ 1507 function setProfileWait(event) 1508 { 1509 this.updateStatus(null, event.data); 1510 } 1511 console.assert(!this._workerProxy, "HeapSnapshotWorkerProxy already exists"); 1512 this._workerProxy = new WebInspector.HeapSnapshotWorkerProxy(this._handleWorkerEvent.bind(this)); 1513 this._workerProxy.addEventListener("wait", setProfileWait, this); 1514 this._receiver = this._workerProxy.createLoader(this.uid, this._snapshotReceived.bind(this)); 1515 }, 1516 1517 /** 1518 * @param {string} eventName 1519 * @param {*} data 1520 */ 1521 _handleWorkerEvent: function(eventName, data) 1522 { 1523 if (WebInspector.HeapSnapshotProgressEvent.Update !== eventName) 1524 return; 1525 var subtitle = /** @type {string} */ (data); 1526 this.updateStatus(subtitle); 1527 }, 1528 1529 /** 1530 * @override 1531 */ 1532 dispose: function() 1533 { 1534 if (this._workerProxy) 1535 this._workerProxy.dispose(); 1536 this.removeTempFile(); 1537 this._wasDisposed = true; 1538 }, 1539 1540 _didCompleteSnapshotTransfer: function() 1541 { 1542 if (!this._snapshotProxy) 1543 return; 1544 this.updateStatus(Number.bytesToString(this._snapshotProxy.totalSize), false); 1545 }, 1546 1547 /** 1548 * @param {string} chunk 1549 */ 1550 transferChunk: function(chunk) 1551 { 1552 if (!this._bufferedWriter) 1553 this._bufferedWriter = new WebInspector.DeferredTempFile("heap-profiler", String(this.uid)); 1554 this._bufferedWriter.write([chunk]); 1555 1556 ++this._totalNumberOfChunks; 1557 this._receiver.write(chunk, function() {}); 1558 }, 1559 1560 _snapshotReceived: function(snapshotProxy) 1561 { 1562 if (this._wasDisposed) 1563 return; 1564 this._receiver = null; 1565 this._snapshotProxy = snapshotProxy; 1566 this.maxJSObjectId = snapshotProxy.maxJSObjectId(); 1567 this._didCompleteSnapshotTransfer(); 1568 this._workerProxy.startCheckingForLongRunningCalls(); 1569 this.notifySnapshotReceived(); 1570 }, 1571 1572 notifySnapshotReceived: function() 1573 { 1574 for (var i = 0; i < this._loadCallbacks.length; i++) 1575 this._loadCallbacks[i](/** @type {!WebInspector.HeapSnapshotProxy} */ (this._snapshotProxy)); 1576 this._loadCallbacks = null; 1577 this._profileType._snapshotReceived(this); 1578 if (this.canSaveToFile()) 1579 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived); 1580 }, 1581 1582 // Hook point for tests. 1583 _wasShown: function() 1584 { 1585 }, 1586 1587 /** 1588 * @override 1589 * @return {boolean} 1590 */ 1591 canSaveToFile: function() 1592 { 1593 return !this.fromFile() && !!this._snapshotProxy; 1594 }, 1595 1596 /** 1597 * @override 1598 */ 1599 saveToFile: function() 1600 { 1601 var fileOutputStream = new WebInspector.FileOutputStream(); 1602 1603 /** 1604 * @param {boolean} accepted 1605 * @this {WebInspector.HeapProfileHeader} 1606 */ 1607 function onOpen(accepted) 1608 { 1609 if (!accepted) 1610 return; 1611 if (this._failedToCreateTempFile) { 1612 WebInspector.console.error("Failed to open temp file with heap snapshot"); 1613 fileOutputStream.close(); 1614 } else if (this._tempFile) { 1615 var delegate = new WebInspector.SaveSnapshotOutputStreamDelegate(this); 1616 this._tempFile.writeToOutputSteam(fileOutputStream, delegate); 1617 } else { 1618 this._onTempFileReady = onOpen.bind(this, accepted); 1619 this._updateSaveProgress(0, 1); 1620 } 1621 } 1622 this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension(); 1623 fileOutputStream.open(this._fileName, onOpen.bind(this)); 1624 }, 1625 1626 _updateSaveProgress: function(value, total) 1627 { 1628 var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0); 1629 this.updateStatus(WebInspector.UIString("Saving\u2026 %d\%", percentValue)); 1630 }, 1631 1632 /** 1633 * @override 1634 * @param {!File} file 1635 */ 1636 loadFromFile: function(file) 1637 { 1638 this.updateStatus(WebInspector.UIString("Loading\u2026"), true); 1639 this._setupWorker(); 1640 var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this); 1641 var fileReader = this._createFileReader(file, delegate); 1642 fileReader.start(this._receiver); 1643 }, 1644 1645 _createFileReader: function(file, delegate) 1646 { 1647 return new WebInspector.ChunkedFileReader(file, 10000000, delegate); 1648 }, 1649 1650 __proto__: WebInspector.ProfileHeader.prototype 1651 } 1652 1653 /** 1654 * @constructor 1655 * @implements {WebInspector.OutputStreamDelegate} 1656 */ 1657 WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader) 1658 { 1659 this._snapshotHeader = snapshotHeader; 1660 } 1661 1662 WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = { 1663 onTransferStarted: function() 1664 { 1665 }, 1666 1667 /** 1668 * @param {!WebInspector.ChunkedReader} reader 1669 */ 1670 onChunkTransferred: function(reader) 1671 { 1672 }, 1673 1674 onTransferFinished: function() 1675 { 1676 }, 1677 1678 /** 1679 * @param {!WebInspector.ChunkedReader} reader 1680 * @param {!Event} e 1681 */ 1682 onError: function (reader, e) 1683 { 1684 var subtitle; 1685 switch(e.target.error.code) { 1686 case e.target.error.NOT_FOUND_ERR: 1687 subtitle = WebInspector.UIString("'%s' not found.", reader.fileName()); 1688 break; 1689 case e.target.error.NOT_READABLE_ERR: 1690 subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName()); 1691 break; 1692 case e.target.error.ABORT_ERR: 1693 return; 1694 default: 1695 subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code); 1696 } 1697 this._snapshotHeader.updateStatus(subtitle); 1698 } 1699 } 1700 1701 /** 1702 * @constructor 1703 * @implements {WebInspector.OutputStreamDelegate} 1704 * @param {!WebInspector.HeapProfileHeader} profileHeader 1705 */ 1706 WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader) 1707 { 1708 this._profileHeader = profileHeader; 1709 } 1710 1711 WebInspector.SaveSnapshotOutputStreamDelegate.prototype = { 1712 onTransferStarted: function() 1713 { 1714 this._profileHeader._updateSaveProgress(0, 1); 1715 }, 1716 1717 onTransferFinished: function() 1718 { 1719 this._profileHeader._didCompleteSnapshotTransfer(); 1720 }, 1721 1722 /** 1723 * @param {!WebInspector.ChunkedReader} reader 1724 */ 1725 onChunkTransferred: function(reader) 1726 { 1727 this._profileHeader._updateSaveProgress(reader.loadedSize(), reader.fileSize()); 1728 }, 1729 1730 /** 1731 * @param {!WebInspector.ChunkedReader} reader 1732 * @param {!Event} event 1733 */ 1734 onError: function(reader, event) 1735 { 1736 WebInspector.console.error("Failed to read heap snapshot from temp file: " + /** @type {!ErrorEvent} */ (event).message); 1737 this.onTransferFinished(); 1738 } 1739 } 1740 1741 /** 1742 * @constructor 1743 * @extends {WebInspector.VBox} 1744 * @param {!WebInspector.HeapProfileHeader} heapProfileHeader 1745 */ 1746 WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader) 1747 { 1748 WebInspector.VBox.call(this); 1749 this.registerRequiredCSS("flameChart.css"); 1750 this.element.id = "heap-recording-view"; 1751 this.element.classList.add("heap-tracking-overview"); 1752 1753 this._overviewContainer = this.element.createChild("div", "overview-container"); 1754 this._overviewGrid = new WebInspector.OverviewGrid("heap-recording"); 1755 this._overviewGrid.element.classList.add("fill"); 1756 1757 this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas"); 1758 this._overviewContainer.appendChild(this._overviewGrid.element); 1759 this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator(); 1760 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this); 1761 1762 this._profileSamples = heapProfileHeader._profileSamples; 1763 if (heapProfileHeader.profileType().profileBeingRecorded() === heapProfileHeader) { 1764 this._profileType = heapProfileHeader.profileType(); 1765 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this); 1766 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this); 1767 } 1768 var timestamps = this._profileSamples.timestamps; 1769 var totalTime = this._profileSamples.totalTime; 1770 this._windowLeft = 0.0; 1771 this._windowRight = totalTime && timestamps.length ? (timestamps[timestamps.length - 1] - timestamps[0]) / totalTime : 1.0; 1772 this._overviewGrid.setWindow(this._windowLeft, this._windowRight); 1773 this._yScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale(); 1774 this._xScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale(); 1775 } 1776 1777 WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged"; 1778 1779 WebInspector.HeapTrackingOverviewGrid.prototype = { 1780 _onStopTracking: function(event) 1781 { 1782 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this); 1783 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this); 1784 }, 1785 1786 _onHeapStatsUpdate: function(event) 1787 { 1788 this._profileSamples = event.data; 1789 this._scheduleUpdate(); 1790 }, 1791 1792 /** 1793 * @param {number} width 1794 * @param {number} height 1795 */ 1796 _drawOverviewCanvas: function(width, height) 1797 { 1798 if (!this._profileSamples) 1799 return; 1800 var profileSamples = this._profileSamples; 1801 var sizes = profileSamples.sizes; 1802 var topSizes = profileSamples.max; 1803 var timestamps = profileSamples.timestamps; 1804 var startTime = timestamps[0]; 1805 var endTime = timestamps[timestamps.length - 1]; 1806 1807 var scaleFactor = this._xScale.nextScale(width / profileSamples.totalTime); 1808 var maxSize = 0; 1809 /** 1810 * @param {!Array.<number>} sizes 1811 * @param {function(number, number):void} callback 1812 */ 1813 function aggregateAndCall(sizes, callback) 1814 { 1815 var size = 0; 1816 var currentX = 0; 1817 for (var i = 1; i < timestamps.length; ++i) { 1818 var x = Math.floor((timestamps[i] - startTime) * scaleFactor); 1819 if (x !== currentX) { 1820 if (size) 1821 callback(currentX, size); 1822 size = 0; 1823 currentX = x; 1824 } 1825 size += sizes[i]; 1826 } 1827 callback(currentX, size); 1828 } 1829 1830 /** 1831 * @param {number} x 1832 * @param {number} size 1833 */ 1834 function maxSizeCallback(x, size) 1835 { 1836 maxSize = Math.max(maxSize, size); 1837 } 1838 1839 aggregateAndCall(sizes, maxSizeCallback); 1840 1841 var yScaleFactor = this._yScale.nextScale(maxSize ? height / (maxSize * 1.1) : 0.0); 1842 1843 this._overviewCanvas.width = width * window.devicePixelRatio; 1844 this._overviewCanvas.height = height * window.devicePixelRatio; 1845 this._overviewCanvas.style.width = width + "px"; 1846 this._overviewCanvas.style.height = height + "px"; 1847 1848 var context = this._overviewCanvas.getContext("2d"); 1849 context.scale(window.devicePixelRatio, window.devicePixelRatio); 1850 1851 context.beginPath(); 1852 context.lineWidth = 2; 1853 context.strokeStyle = "rgba(192, 192, 192, 0.6)"; 1854 var currentX = (endTime - startTime) * scaleFactor; 1855 context.moveTo(currentX, height - 1); 1856 context.lineTo(currentX, 0); 1857 context.stroke(); 1858 context.closePath(); 1859 1860 var gridY; 1861 var gridValue; 1862 var gridLabelHeight = 14; 1863 if (yScaleFactor) { 1864 const maxGridValue = (height - gridLabelHeight) / yScaleFactor; 1865 // The round value calculation is a bit tricky, because 1866 // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer, 1867 // e.g. a round value 10KB is 10240 bytes. 1868 gridValue = Math.pow(1024, Math.floor(Math.log(maxGridValue) / Math.log(1024))); 1869 gridValue *= Math.pow(10, Math.floor(Math.log(maxGridValue / gridValue) / Math.LN10)); 1870 if (gridValue * 5 <= maxGridValue) 1871 gridValue *= 5; 1872 gridY = Math.round(height - gridValue * yScaleFactor - 0.5) + 0.5; 1873 context.beginPath(); 1874 context.lineWidth = 1; 1875 context.strokeStyle = "rgba(0, 0, 0, 0.2)"; 1876 context.moveTo(0, gridY); 1877 context.lineTo(width, gridY); 1878 context.stroke(); 1879 context.closePath(); 1880 } 1881 1882 /** 1883 * @param {number} x 1884 * @param {number} size 1885 */ 1886 function drawBarCallback(x, size) 1887 { 1888 context.moveTo(x, height - 1); 1889 context.lineTo(x, Math.round(height - size * yScaleFactor - 1)); 1890 } 1891 1892 context.beginPath(); 1893 context.lineWidth = 2; 1894 context.strokeStyle = "rgba(192, 192, 192, 0.6)"; 1895 aggregateAndCall(topSizes, drawBarCallback); 1896 context.stroke(); 1897 context.closePath(); 1898 1899 context.beginPath(); 1900 context.lineWidth = 2; 1901 context.strokeStyle = "rgba(0, 0, 192, 0.8)"; 1902 aggregateAndCall(sizes, drawBarCallback); 1903 context.stroke(); 1904 context.closePath(); 1905 1906 if (gridValue) { 1907 var label = Number.bytesToString(gridValue); 1908 var labelPadding = 4; 1909 var labelX = 0; 1910 var labelY = gridY - 0.5; 1911 var labelWidth = 2 * labelPadding + context.measureText(label).width; 1912 context.beginPath(); 1913 context.textBaseline = "bottom"; 1914 context.font = "10px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family"); 1915 context.fillStyle = "rgba(255, 255, 255, 0.75)"; 1916 context.fillRect(labelX, labelY - gridLabelHeight, labelWidth, gridLabelHeight); 1917 context.fillStyle = "rgb(64, 64, 64)"; 1918 context.fillText(label, labelX + labelPadding, labelY); 1919 context.fill(); 1920 context.closePath(); 1921 } 1922 }, 1923 1924 onResize: function() 1925 { 1926 this._updateOverviewCanvas = true; 1927 this._scheduleUpdate(); 1928 }, 1929 1930 _onWindowChanged: function() 1931 { 1932 if (!this._updateGridTimerId) 1933 this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10); 1934 }, 1935 1936 _scheduleUpdate: function() 1937 { 1938 if (this._updateTimerId) 1939 return; 1940 this._updateTimerId = setTimeout(this.update.bind(this), 10); 1941 }, 1942 1943 _updateBoundaries: function() 1944 { 1945 this._windowLeft = this._overviewGrid.windowLeft(); 1946 this._windowRight = this._overviewGrid.windowRight(); 1947 this._windowWidth = this._windowRight - this._windowLeft; 1948 }, 1949 1950 update: function() 1951 { 1952 this._updateTimerId = null; 1953 if (!this.isShowing()) 1954 return; 1955 this._updateBoundaries(); 1956 this._overviewCalculator._updateBoundaries(this); 1957 this._overviewGrid.updateDividers(this._overviewCalculator); 1958 this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20); 1959 }, 1960 1961 _updateGrid: function() 1962 { 1963 this._updateGridTimerId = 0; 1964 this._updateBoundaries(); 1965 var ids = this._profileSamples.ids; 1966 var timestamps = this._profileSamples.timestamps; 1967 var sizes = this._profileSamples.sizes; 1968 var startTime = timestamps[0]; 1969 var totalTime = this._profileSamples.totalTime; 1970 var timeLeft = startTime + totalTime * this._windowLeft; 1971 var timeRight = startTime + totalTime * this._windowRight; 1972 var minId = 0; 1973 var maxId = ids[ids.length - 1] + 1; 1974 var size = 0; 1975 for (var i = 0; i < timestamps.length; ++i) { 1976 if (!timestamps[i]) 1977 continue; 1978 if (timestamps[i] > timeRight) 1979 break; 1980 maxId = ids[i]; 1981 if (timestamps[i] < timeLeft) { 1982 minId = ids[i]; 1983 continue; 1984 } 1985 size += sizes[i]; 1986 } 1987 1988 this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size}); 1989 }, 1990 1991 __proto__: WebInspector.VBox.prototype 1992 } 1993 1994 1995 /** 1996 * @constructor 1997 */ 1998 WebInspector.HeapTrackingOverviewGrid.SmoothScale = function() 1999 { 2000 this._lastUpdate = 0; 2001 this._currentScale = 0.0; 2002 } 2003 2004 WebInspector.HeapTrackingOverviewGrid.SmoothScale.prototype = { 2005 /** 2006 * @param {number} target 2007 * @return {number} 2008 */ 2009 nextScale: function(target) { 2010 target = target || this._currentScale; 2011 if (this._currentScale) { 2012 var now = Date.now(); 2013 var timeDeltaMs = now - this._lastUpdate; 2014 this._lastUpdate = now; 2015 var maxChangePerSec = 20; 2016 var maxChangePerDelta = Math.pow(maxChangePerSec, timeDeltaMs / 1000); 2017 var scaleChange = target / this._currentScale; 2018 this._currentScale *= Number.constrain(scaleChange, 1 / maxChangePerDelta, maxChangePerDelta); 2019 } else 2020 this._currentScale = target; 2021 return this._currentScale; 2022 } 2023 } 2024 2025 2026 /** 2027 * @constructor 2028 * @implements {WebInspector.TimelineGrid.Calculator} 2029 */ 2030 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function() 2031 { 2032 } 2033 2034 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = { 2035 /** 2036 * @return {number} 2037 */ 2038 paddingLeft: function() 2039 { 2040 return 0; 2041 }, 2042 2043 /** 2044 * @param {!WebInspector.HeapTrackingOverviewGrid} chart 2045 */ 2046 _updateBoundaries: function(chart) 2047 { 2048 this._minimumBoundaries = 0; 2049 this._maximumBoundaries = chart._profileSamples.totalTime; 2050 this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries; 2051 }, 2052 2053 /** 2054 * @param {number} time 2055 * @return {number} 2056 */ 2057 computePosition: function(time) 2058 { 2059 return (time - this._minimumBoundaries) * this._xScaleFactor; 2060 }, 2061 2062 /** 2063 * @param {number} value 2064 * @param {number=} precision 2065 * @return {string} 2066 */ 2067 formatTime: function(value, precision) 2068 { 2069 return Number.secondsToString(value / 1000, !!precision); 2070 }, 2071 2072 /** 2073 * @return {number} 2074 */ 2075 maximumBoundary: function() 2076 { 2077 return this._maximumBoundaries; 2078 }, 2079 2080 /** 2081 * @return {number} 2082 */ 2083 minimumBoundary: function() 2084 { 2085 return this._minimumBoundaries; 2086 }, 2087 2088 /** 2089 * @return {number} 2090 */ 2091 zeroTime: function() 2092 { 2093 return this._minimumBoundaries; 2094 }, 2095 2096 /** 2097 * @return {number} 2098 */ 2099 boundarySpan: function() 2100 { 2101 return this._maximumBoundaries - this._minimumBoundaries; 2102 } 2103 } 2104 2105 2106 /** 2107 * @constructor 2108 * @extends {WebInspector.VBox} 2109 */ 2110 WebInspector.HeapSnapshotStatisticsView = function() 2111 { 2112 WebInspector.VBox.call(this); 2113 this.setMinimumSize(50, 25); 2114 this._pieChart = new WebInspector.PieChart(150, WebInspector.HeapSnapshotStatisticsView._valueFormatter); 2115 this.element.appendChild(this._pieChart.element); 2116 this._labels = this.element.createChild("div", "heap-snapshot-stats-legend"); 2117 } 2118 2119 /** 2120 * @param {number} value 2121 * @return {string} 2122 */ 2123 WebInspector.HeapSnapshotStatisticsView._valueFormatter = function(value) 2124 { 2125 return WebInspector.UIString("%s KB", Number.withThousandsSeparator(Math.round(value / 1024))); 2126 } 2127 2128 WebInspector.HeapSnapshotStatisticsView.prototype = { 2129 /** 2130 * @param {number} value 2131 */ 2132 setTotal: function(value) 2133 { 2134 this._pieChart.setTotal(value); 2135 }, 2136 2137 /** 2138 * @param {number} value 2139 * @param {string} name 2140 * @param {string=} color 2141 */ 2142 addRecord: function(value, name, color) 2143 { 2144 if (color) 2145 this._pieChart.addSlice(value, color); 2146 2147 var node = this._labels.createChild("div"); 2148 var swatchDiv = node.createChild("div", "heap-snapshot-stats-swatch"); 2149 var nameDiv = node.createChild("div", "heap-snapshot-stats-name"); 2150 var sizeDiv = node.createChild("div", "heap-snapshot-stats-size"); 2151 if (color) 2152 swatchDiv.style.backgroundColor = color; 2153 else 2154 swatchDiv.classList.add("heap-snapshot-stats-empty-swatch"); 2155 nameDiv.textContent = name; 2156 sizeDiv.textContent = WebInspector.HeapSnapshotStatisticsView._valueFormatter(value); 2157 }, 2158 2159 __proto__: WebInspector.VBox.prototype 2160 } 2161 2162 /** 2163 * @constructor 2164 * @extends {WebInspector.View} 2165 * @param {?WebInspector.Target} target 2166 */ 2167 WebInspector.HeapAllocationStackView = function(target) 2168 { 2169 WebInspector.View.call(this); 2170 this._target = target;; 2171 this._linkifier = new WebInspector.Linkifier(); 2172 } 2173 2174 WebInspector.HeapAllocationStackView.prototype = { 2175 /** 2176 * @param {!WebInspector.HeapSnapshotProxy} snapshot 2177 * @param {number} snapshotNodeIndex 2178 */ 2179 setAllocatedObject: function(snapshot, snapshotNodeIndex) 2180 { 2181 this.clear(); 2182 snapshot.allocationStack(snapshotNodeIndex, this._didReceiveAllocationStack.bind(this)); 2183 }, 2184 2185 clear: function() 2186 { 2187 this.element.removeChildren(); 2188 this._linkifier.reset(); 2189 }, 2190 2191 /** 2192 * @param {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} frames 2193 */ 2194 _didReceiveAllocationStack: function(frames) 2195 { 2196 if (!frames) { 2197 var stackDiv = this.element.createChild("div", "no-heap-allocation-stack"); 2198 stackDiv.createTextChild(WebInspector.UIString("Stack was not recorded for this object because it had been allocated before this profile recording started.")); 2199 return; 2200 } 2201 2202 var stackDiv = this.element.createChild("div", "heap-allocation-stack"); 2203 for (var i = 0; i < frames.length; i++) { 2204 var frame = frames[i]; 2205 var frameDiv = stackDiv.createChild("div", "stack-frame"); 2206 var name = frameDiv.createChild("div"); 2207 name.textContent = frame.functionName; 2208 if (frame.scriptId) { 2209 var urlElement = this._linkifier.linkifyScriptLocation(this._target, String(frame.scriptId), frame.scriptName, frame.line - 1, frame.column - 1); 2210 frameDiv.appendChild(urlElement); 2211 } 2212 } 2213 }, 2214 2215 __proto__: WebInspector.View.prototype 2216 } 2217