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