1/* 2 * Copyright (C) 2008 Apple 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 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26/** 27 * @constructor 28 * @extends {WebInspector.View} 29 * @param {WebInspector.CPUProfileHeader} profile 30 */ 31WebInspector.CPUProfileView = function(profile) 32{ 33 WebInspector.View.call(this); 34 35 this.element.addStyleClass("profile-view"); 36 37 this.showSelfTimeAsPercent = WebInspector.settings.createSetting("cpuProfilerShowSelfTimeAsPercent", true); 38 this.showTotalTimeAsPercent = WebInspector.settings.createSetting("cpuProfilerShowTotalTimeAsPercent", true); 39 this.showAverageTimeAsPercent = WebInspector.settings.createSetting("cpuProfilerShowAverageTimeAsPercent", true); 40 this._viewType = WebInspector.settings.createSetting("cpuProfilerView", WebInspector.CPUProfileView._TypeHeavy); 41 42 var columns = []; 43 columns.push({id: "self", title: WebInspector.UIString("Self"), width: "72px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}); 44 columns.push({id: "total", title: WebInspector.UIString("Total"), width: "72px", sortable: true}); 45 if (!Capabilities.samplingCPUProfiler) { 46 columns.push({id: "average", title: WebInspector.UIString("Average"), width: "72px", sortable: true}); 47 columns.push({id: "calls", title: WebInspector.UIString("Calls"), width: "54px", sortable: true}); 48 } 49 columns.push({id: "function", title: WebInspector.UIString("Function"), disclosure: true, sortable: true}); 50 51 this.dataGrid = new WebInspector.DataGrid(columns); 52 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortProfile, this); 53 this.dataGrid.element.addEventListener("mousedown", this._mouseDownInDataGrid.bind(this), true); 54 55 if (WebInspector.experimentsSettings.cpuFlameChart.isEnabled()) { 56 this._splitView = new WebInspector.SplitView(false, "flameChartSplitLocation"); 57 this._splitView.show(this.element); 58 59 this.flameChart = new WebInspector.FlameChart(this); 60 this.flameChart.addEventListener(WebInspector.FlameChart.Events.SelectedNode, this._revealProfilerNode.bind(this)); 61 this.flameChart.show(this._splitView.firstElement()); 62 63 this.dataGrid.show(this._splitView.secondElement()); 64 } else 65 this.dataGrid.show(this.element); 66 67 this.viewSelectComboBox = new WebInspector.StatusBarComboBox(this._changeView.bind(this)); 68 69 var heavyViewOption = this.viewSelectComboBox.createOption(WebInspector.UIString("Heavy (Bottom Up)"), "", WebInspector.CPUProfileView._TypeHeavy); 70 var treeViewOption = this.viewSelectComboBox.createOption(WebInspector.UIString("Tree (Top Down)"), "", WebInspector.CPUProfileView._TypeTree); 71 this.viewSelectComboBox.select(this._viewType.get() === WebInspector.CPUProfileView._TypeHeavy ? heavyViewOption : treeViewOption); 72 73 this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item"); 74 this.percentButton.addEventListener("click", this._percentClicked, this); 75 76 this.focusButton = new WebInspector.StatusBarButton(WebInspector.UIString("Focus selected function."), "focus-profile-node-status-bar-item"); 77 this.focusButton.setEnabled(false); 78 this.focusButton.addEventListener("click", this._focusClicked, this); 79 80 this.excludeButton = new WebInspector.StatusBarButton(WebInspector.UIString("Exclude selected function."), "exclude-profile-node-status-bar-item"); 81 this.excludeButton.setEnabled(false); 82 this.excludeButton.addEventListener("click", this._excludeClicked, this); 83 84 this.resetButton = new WebInspector.StatusBarButton(WebInspector.UIString("Restore all functions."), "reset-profile-status-bar-item"); 85 this.resetButton.visible = false; 86 this.resetButton.addEventListener("click", this._resetClicked, this); 87 88 this.profileHead = /** @type {?ProfilerAgent.CPUProfileNode} */ (null); 89 this.profile = profile; 90 91 this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultFormatter(30)); 92 93 ProfilerAgent.getCPUProfile(this.profile.uid, this._getCPUProfileCallback.bind(this)); 94} 95 96WebInspector.CPUProfileView._TypeTree = "Tree"; 97WebInspector.CPUProfileView._TypeHeavy = "Heavy"; 98 99WebInspector.CPUProfileView.prototype = { 100 _revealProfilerNode: function(event) 101 { 102 var current = this.profileDataGridTree.children[0]; 103 104 while (current && current.profileNode !== event.data) 105 current = current.traverseNextNode(false, null, false); 106 107 if (current) 108 current.revealAndSelect(); 109 }, 110 111 /** 112 * @param {?Protocol.Error} error 113 * @param {ProfilerAgent.CPUProfile} profile 114 */ 115 _getCPUProfileCallback: function(error, profile) 116 { 117 if (error) 118 return; 119 120 if (!profile.head) { 121 // Profiling was tentatively terminated with the "Clear all profiles." button. 122 return; 123 } 124 this.profileHead = profile.head; 125 this.samples = profile.samples; 126 127 if (profile.idleTime) 128 this._injectIdleTimeNode(profile); 129 130 this._assignParentsInProfile(); 131 if (this.samples) 132 this._buildIdToNodeMap(); 133 this._changeView(); 134 this._updatePercentButton(); 135 if (this.flameChart) 136 this.flameChart.update(); 137 }, 138 139 statusBarItems: function() 140 { 141 return [this.viewSelectComboBox.element, this.percentButton.element, this.focusButton.element, this.excludeButton.element, this.resetButton.element]; 142 }, 143 144 /** 145 * @return {!WebInspector.ProfileDataGridTree} 146 */ 147 _getBottomUpProfileDataGridTree: function() 148 { 149 if (!this._bottomUpProfileDataGridTree) 150 this._bottomUpProfileDataGridTree = new WebInspector.BottomUpProfileDataGridTree(this, this.profileHead); 151 return this._bottomUpProfileDataGridTree; 152 }, 153 154 /** 155 * @return {!WebInspector.ProfileDataGridTree} 156 */ 157 _getTopDownProfileDataGridTree: function() 158 { 159 if (!this._topDownProfileDataGridTree) 160 this._topDownProfileDataGridTree = new WebInspector.TopDownProfileDataGridTree(this, this.profileHead); 161 return this._topDownProfileDataGridTree; 162 }, 163 164 willHide: function() 165 { 166 this._currentSearchResultIndex = -1; 167 }, 168 169 refresh: function() 170 { 171 var selectedProfileNode = this.dataGrid.selectedNode ? this.dataGrid.selectedNode.profileNode : null; 172 173 this.dataGrid.rootNode().removeChildren(); 174 175 var children = this.profileDataGridTree.children; 176 var count = children.length; 177 178 for (var index = 0; index < count; ++index) 179 this.dataGrid.rootNode().appendChild(children[index]); 180 181 if (selectedProfileNode) 182 selectedProfileNode.selected = true; 183 }, 184 185 refreshVisibleData: function() 186 { 187 var child = this.dataGrid.rootNode().children[0]; 188 while (child) { 189 child.refresh(); 190 child = child.traverseNextNode(false, null, true); 191 } 192 }, 193 194 refreshShowAsPercents: function() 195 { 196 this._updatePercentButton(); 197 this.refreshVisibleData(); 198 }, 199 200 searchCanceled: function() 201 { 202 if (this._searchResults) { 203 for (var i = 0; i < this._searchResults.length; ++i) { 204 var profileNode = this._searchResults[i].profileNode; 205 206 delete profileNode._searchMatchedSelfColumn; 207 delete profileNode._searchMatchedTotalColumn; 208 delete profileNode._searchMatchedAverageColumn; 209 delete profileNode._searchMatchedCallsColumn; 210 delete profileNode._searchMatchedFunctionColumn; 211 212 profileNode.refresh(); 213 } 214 } 215 216 delete this._searchFinishedCallback; 217 this._currentSearchResultIndex = -1; 218 this._searchResults = []; 219 }, 220 221 performSearch: function(query, finishedCallback) 222 { 223 // Call searchCanceled since it will reset everything we need before doing a new search. 224 this.searchCanceled(); 225 226 query = query.trim(); 227 228 if (!query.length) 229 return; 230 231 this._searchFinishedCallback = finishedCallback; 232 233 var greaterThan = (query.startsWith(">")); 234 var lessThan = (query.startsWith("<")); 235 var equalTo = (query.startsWith("=") || ((greaterThan || lessThan) && query.indexOf("=") === 1)); 236 var percentUnits = (query.lastIndexOf("%") === (query.length - 1)); 237 var millisecondsUnits = (query.length > 2 && query.lastIndexOf("ms") === (query.length - 2)); 238 var secondsUnits = (!millisecondsUnits && query.lastIndexOf("s") === (query.length - 1)); 239 240 var queryNumber = parseFloat(query); 241 if (greaterThan || lessThan || equalTo) { 242 if (equalTo && (greaterThan || lessThan)) 243 queryNumber = parseFloat(query.substring(2)); 244 else 245 queryNumber = parseFloat(query.substring(1)); 246 } 247 248 var queryNumberMilliseconds = (secondsUnits ? (queryNumber * 1000) : queryNumber); 249 250 // Make equalTo implicitly true if it wasn't specified there is no other operator. 251 if (!isNaN(queryNumber) && !(greaterThan || lessThan)) 252 equalTo = true; 253 254 var matcher = new RegExp(query.escapeForRegExp(), "i"); 255 256 function matchesQuery(/*ProfileDataGridNode*/ profileDataGridNode) 257 { 258 delete profileDataGridNode._searchMatchedSelfColumn; 259 delete profileDataGridNode._searchMatchedTotalColumn; 260 delete profileDataGridNode._searchMatchedAverageColumn; 261 delete profileDataGridNode._searchMatchedCallsColumn; 262 delete profileDataGridNode._searchMatchedFunctionColumn; 263 264 if (percentUnits) { 265 if (lessThan) { 266 if (profileDataGridNode.selfPercent < queryNumber) 267 profileDataGridNode._searchMatchedSelfColumn = true; 268 if (profileDataGridNode.totalPercent < queryNumber) 269 profileDataGridNode._searchMatchedTotalColumn = true; 270 if (profileDataGridNode.averagePercent < queryNumberMilliseconds) 271 profileDataGridNode._searchMatchedAverageColumn = true; 272 } else if (greaterThan) { 273 if (profileDataGridNode.selfPercent > queryNumber) 274 profileDataGridNode._searchMatchedSelfColumn = true; 275 if (profileDataGridNode.totalPercent > queryNumber) 276 profileDataGridNode._searchMatchedTotalColumn = true; 277 if (profileDataGridNode.averagePercent < queryNumberMilliseconds) 278 profileDataGridNode._searchMatchedAverageColumn = true; 279 } 280 281 if (equalTo) { 282 if (profileDataGridNode.selfPercent == queryNumber) 283 profileDataGridNode._searchMatchedSelfColumn = true; 284 if (profileDataGridNode.totalPercent == queryNumber) 285 profileDataGridNode._searchMatchedTotalColumn = true; 286 if (profileDataGridNode.averagePercent < queryNumberMilliseconds) 287 profileDataGridNode._searchMatchedAverageColumn = true; 288 } 289 } else if (millisecondsUnits || secondsUnits) { 290 if (lessThan) { 291 if (profileDataGridNode.selfTime < queryNumberMilliseconds) 292 profileDataGridNode._searchMatchedSelfColumn = true; 293 if (profileDataGridNode.totalTime < queryNumberMilliseconds) 294 profileDataGridNode._searchMatchedTotalColumn = true; 295 if (profileDataGridNode.averageTime < queryNumberMilliseconds) 296 profileDataGridNode._searchMatchedAverageColumn = true; 297 } else if (greaterThan) { 298 if (profileDataGridNode.selfTime > queryNumberMilliseconds) 299 profileDataGridNode._searchMatchedSelfColumn = true; 300 if (profileDataGridNode.totalTime > queryNumberMilliseconds) 301 profileDataGridNode._searchMatchedTotalColumn = true; 302 if (profileDataGridNode.averageTime > queryNumberMilliseconds) 303 profileDataGridNode._searchMatchedAverageColumn = true; 304 } 305 306 if (equalTo) { 307 if (profileDataGridNode.selfTime == queryNumberMilliseconds) 308 profileDataGridNode._searchMatchedSelfColumn = true; 309 if (profileDataGridNode.totalTime == queryNumberMilliseconds) 310 profileDataGridNode._searchMatchedTotalColumn = true; 311 if (profileDataGridNode.averageTime == queryNumberMilliseconds) 312 profileDataGridNode._searchMatchedAverageColumn = true; 313 } 314 } else { 315 if (equalTo && profileDataGridNode.numberOfCalls == queryNumber) 316 profileDataGridNode._searchMatchedCallsColumn = true; 317 if (greaterThan && profileDataGridNode.numberOfCalls > queryNumber) 318 profileDataGridNode._searchMatchedCallsColumn = true; 319 if (lessThan && profileDataGridNode.numberOfCalls < queryNumber) 320 profileDataGridNode._searchMatchedCallsColumn = true; 321 } 322 323 if (profileDataGridNode.functionName.match(matcher) || (profileDataGridNode.url && profileDataGridNode.url.match(matcher))) 324 profileDataGridNode._searchMatchedFunctionColumn = true; 325 326 if (profileDataGridNode._searchMatchedSelfColumn || 327 profileDataGridNode._searchMatchedTotalColumn || 328 profileDataGridNode._searchMatchedAverageColumn || 329 profileDataGridNode._searchMatchedCallsColumn || 330 profileDataGridNode._searchMatchedFunctionColumn) 331 { 332 profileDataGridNode.refresh(); 333 return true; 334 } 335 336 return false; 337 } 338 339 var current = this.profileDataGridTree.children[0]; 340 341 while (current) { 342 if (matchesQuery(current)) { 343 this._searchResults.push({ profileNode: current }); 344 } 345 346 current = current.traverseNextNode(false, null, false); 347 } 348 349 finishedCallback(this, this._searchResults.length); 350 }, 351 352 jumpToFirstSearchResult: function() 353 { 354 if (!this._searchResults || !this._searchResults.length) 355 return; 356 this._currentSearchResultIndex = 0; 357 this._jumpToSearchResult(this._currentSearchResultIndex); 358 }, 359 360 jumpToLastSearchResult: function() 361 { 362 if (!this._searchResults || !this._searchResults.length) 363 return; 364 this._currentSearchResultIndex = (this._searchResults.length - 1); 365 this._jumpToSearchResult(this._currentSearchResultIndex); 366 }, 367 368 jumpToNextSearchResult: function() 369 { 370 if (!this._searchResults || !this._searchResults.length) 371 return; 372 if (++this._currentSearchResultIndex >= this._searchResults.length) 373 this._currentSearchResultIndex = 0; 374 this._jumpToSearchResult(this._currentSearchResultIndex); 375 }, 376 377 jumpToPreviousSearchResult: function() 378 { 379 if (!this._searchResults || !this._searchResults.length) 380 return; 381 if (--this._currentSearchResultIndex < 0) 382 this._currentSearchResultIndex = (this._searchResults.length - 1); 383 this._jumpToSearchResult(this._currentSearchResultIndex); 384 }, 385 386 showingFirstSearchResult: function() 387 { 388 return (this._currentSearchResultIndex === 0); 389 }, 390 391 showingLastSearchResult: function() 392 { 393 return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1)); 394 }, 395 396 _jumpToSearchResult: function(index) 397 { 398 var searchResult = this._searchResults[index]; 399 if (!searchResult) 400 return; 401 402 var profileNode = searchResult.profileNode; 403 profileNode.revealAndSelect(); 404 }, 405 406 _changeView: function() 407 { 408 if (!this.profile) 409 return; 410 411 switch (this.viewSelectComboBox.selectedOption().value) { 412 case WebInspector.CPUProfileView._TypeTree: 413 this.profileDataGridTree = this._getTopDownProfileDataGridTree(); 414 this._sortProfile(); 415 this._viewType.set(WebInspector.CPUProfileView._TypeTree); 416 break; 417 case WebInspector.CPUProfileView._TypeHeavy: 418 this.profileDataGridTree = this._getBottomUpProfileDataGridTree(); 419 this._sortProfile(); 420 this._viewType.set(WebInspector.CPUProfileView._TypeHeavy); 421 } 422 423 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults) 424 return; 425 426 // The current search needs to be performed again. First negate out previous match 427 // count by calling the search finished callback with a negative number of matches. 428 // Then perform the search again the with same query and callback. 429 this._searchFinishedCallback(this, -this._searchResults.length); 430 this.performSearch(this.currentQuery, this._searchFinishedCallback); 431 }, 432 433 _percentClicked: function(event) 434 { 435 var currentState = this.showSelfTimeAsPercent.get() && this.showTotalTimeAsPercent.get() && this.showAverageTimeAsPercent.get(); 436 this.showSelfTimeAsPercent.set(!currentState); 437 this.showTotalTimeAsPercent.set(!currentState); 438 this.showAverageTimeAsPercent.set(!currentState); 439 this.refreshShowAsPercents(); 440 }, 441 442 _updatePercentButton: function() 443 { 444 if (this.showSelfTimeAsPercent.get() && this.showTotalTimeAsPercent.get() && this.showAverageTimeAsPercent.get()) { 445 this.percentButton.title = WebInspector.UIString("Show absolute total and self times."); 446 this.percentButton.toggled = true; 447 } else { 448 this.percentButton.title = WebInspector.UIString("Show total and self times as percentages."); 449 this.percentButton.toggled = false; 450 } 451 }, 452 453 _focusClicked: function(event) 454 { 455 if (!this.dataGrid.selectedNode) 456 return; 457 458 this.resetButton.visible = true; 459 this.profileDataGridTree.focus(this.dataGrid.selectedNode); 460 this.refresh(); 461 this.refreshVisibleData(); 462 }, 463 464 _excludeClicked: function(event) 465 { 466 var selectedNode = this.dataGrid.selectedNode 467 468 if (!selectedNode) 469 return; 470 471 selectedNode.deselect(); 472 473 this.resetButton.visible = true; 474 this.profileDataGridTree.exclude(selectedNode); 475 this.refresh(); 476 this.refreshVisibleData(); 477 }, 478 479 _resetClicked: function(event) 480 { 481 this.resetButton.visible = false; 482 this.profileDataGridTree.restore(); 483 this._linkifier.reset(); 484 this.refresh(); 485 this.refreshVisibleData(); 486 }, 487 488 _dataGridNodeSelected: function(node) 489 { 490 this.focusButton.setEnabled(true); 491 this.excludeButton.setEnabled(true); 492 }, 493 494 _dataGridNodeDeselected: function(node) 495 { 496 this.focusButton.setEnabled(false); 497 this.excludeButton.setEnabled(false); 498 }, 499 500 _sortProfile: function() 501 { 502 var sortAscending = this.dataGrid.isSortOrderAscending(); 503 var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier(); 504 var sortProperty = { 505 "average": "averageTime", 506 "self": "selfTime", 507 "total": "totalTime", 508 "calls": "numberOfCalls", 509 "function": "functionName" 510 }[sortColumnIdentifier]; 511 512 this.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyComparator(sortProperty, sortAscending)); 513 514 this.refresh(); 515 }, 516 517 _mouseDownInDataGrid: function(event) 518 { 519 if (event.detail < 2) 520 return; 521 522 var cell = event.target.enclosingNodeOrSelfWithNodeName("td"); 523 if (!cell || (!cell.hasStyleClass("total-column") && !cell.hasStyleClass("self-column") && !cell.hasStyleClass("average-column"))) 524 return; 525 526 if (cell.hasStyleClass("total-column")) 527 this.showTotalTimeAsPercent.set(!this.showTotalTimeAsPercent.get()); 528 else if (cell.hasStyleClass("self-column")) 529 this.showSelfTimeAsPercent.set(!this.showSelfTimeAsPercent.get()); 530 else if (cell.hasStyleClass("average-column")) 531 this.showAverageTimeAsPercent.set(!this.showAverageTimeAsPercent.get()); 532 533 this.refreshShowAsPercents(); 534 535 event.consume(true); 536 }, 537 538 _assignParentsInProfile: function() 539 { 540 var head = this.profileHead; 541 head.parent = null; 542 head.head = null; 543 var nodesToTraverse = [ { parent: head, children: head.children } ]; 544 while (nodesToTraverse.length > 0) { 545 var pair = nodesToTraverse.pop(); 546 var parent = pair.parent; 547 var children = pair.children; 548 var length = children.length; 549 for (var i = 0; i < length; ++i) { 550 children[i].head = head; 551 children[i].parent = parent; 552 if (children[i].children.length > 0) 553 nodesToTraverse.push({ parent: children[i], children: children[i].children }); 554 } 555 } 556 }, 557 558 _buildIdToNodeMap: function() 559 { 560 var idToNode = this._idToNode = {}; 561 var stack = [this.profileHead]; 562 while (stack.length) { 563 var node = stack.pop(); 564 idToNode[node.id] = node; 565 for (var i = 0; i < node.children.length; i++) 566 stack.push(node.children[i]); 567 } 568 }, 569 570 /** 571 * @param {ProfilerAgent.CPUProfile} profile 572 */ 573 _injectIdleTimeNode: function(profile) 574 { 575 var idleTime = profile.idleTime; 576 var nodes = profile.head.children; 577 578 var programNode = {selfTime: 0}; 579 for (var i = nodes.length - 1; i >= 0; --i) { 580 if (nodes[i].functionName === "(program)") { 581 programNode = nodes[i]; 582 break; 583 } 584 } 585 var programTime = programNode.selfTime; 586 if (idleTime > programTime) 587 idleTime = programTime; 588 programTime = programTime - idleTime; 589 programNode.selfTime = programTime; 590 programNode.totalTime = programTime; 591 var idleNode = { 592 functionName: "(idle)", 593 url: null, 594 lineNumber: 0, 595 totalTime: idleTime, 596 selfTime: idleTime, 597 numberOfCalls: 0, 598 visible: true, 599 callUID: 0, 600 children: [] 601 }; 602 nodes.push(idleNode); 603 }, 604 605 __proto__: WebInspector.View.prototype 606} 607 608/** 609 * @constructor 610 * @extends {WebInspector.ProfileType} 611 * @implements {ProfilerAgent.Dispatcher} 612 */ 613WebInspector.CPUProfileType = function() 614{ 615 WebInspector.ProfileType.call(this, WebInspector.CPUProfileType.TypeId, WebInspector.UIString("Collect JavaScript CPU Profile")); 616 InspectorBackend.registerProfilerDispatcher(this); 617 this._recording = false; 618 WebInspector.CPUProfileType.instance = this; 619} 620 621WebInspector.CPUProfileType.TypeId = "CPU"; 622 623WebInspector.CPUProfileType.prototype = { 624 get buttonTooltip() 625 { 626 return this._recording ? WebInspector.UIString("Stop CPU profiling.") : WebInspector.UIString("Start CPU profiling."); 627 }, 628 629 /** 630 * @override 631 * @return {boolean} 632 */ 633 buttonClicked: function() 634 { 635 if (this._recording) { 636 this.stopRecordingProfile(); 637 return false; 638 } else { 639 this.startRecordingProfile(); 640 return true; 641 } 642 }, 643 644 get treeItemTitle() 645 { 646 return WebInspector.UIString("CPU PROFILES"); 647 }, 648 649 get description() 650 { 651 return WebInspector.UIString("CPU profiles show where the execution time is spent in your page's JavaScript functions."); 652 }, 653 654 /** 655 * @param {ProfilerAgent.ProfileHeader} profileHeader 656 */ 657 addProfileHeader: function(profileHeader) 658 { 659 this.addProfile(this.createProfile(profileHeader)); 660 }, 661 662 isRecordingProfile: function() 663 { 664 return this._recording; 665 }, 666 667 startRecordingProfile: function() 668 { 669 this._recording = true; 670 WebInspector.userMetrics.ProfilesCPUProfileTaken.record(); 671 ProfilerAgent.start(); 672 }, 673 674 stopRecordingProfile: function() 675 { 676 this._recording = false; 677 ProfilerAgent.stop(); 678 }, 679 680 /** 681 * @param {boolean} isProfiling 682 */ 683 setRecordingProfile: function(isProfiling) 684 { 685 this._recording = isProfiling; 686 }, 687 688 /** 689 * @override 690 * @param {string=} title 691 * @return {!WebInspector.ProfileHeader} 692 */ 693 createTemporaryProfile: function(title) 694 { 695 title = title || WebInspector.UIString("Recording\u2026"); 696 return new WebInspector.CPUProfileHeader(this, title); 697 }, 698 699 /** 700 * @override 701 * @param {ProfilerAgent.ProfileHeader} profile 702 * @return {!WebInspector.ProfileHeader} 703 */ 704 createProfile: function(profile) 705 { 706 return new WebInspector.CPUProfileHeader(this, profile.title, profile.uid); 707 }, 708 709 /** 710 * @override 711 * @param {!WebInspector.ProfileHeader} profile 712 */ 713 removeProfile: function(profile) 714 { 715 WebInspector.ProfileType.prototype.removeProfile.call(this, profile); 716 if (!profile.isTemporary) 717 ProfilerAgent.removeProfile(this.id, profile.uid); 718 }, 719 720 /** 721 * @override 722 * @param {function(this:WebInspector.ProfileType, ?string, Array.<ProfilerAgent.ProfileHeader>)} populateCallback 723 */ 724 _requestProfilesFromBackend: function(populateCallback) 725 { 726 ProfilerAgent.getProfileHeaders(populateCallback); 727 }, 728 729 /** 730 * @override 731 */ 732 resetProfiles: function() 733 { 734 this._reset(); 735 }, 736 737 /** @deprecated To be removed from the protocol */ 738 addHeapSnapshotChunk: function(uid, chunk) 739 { 740 throw new Error("Never called"); 741 }, 742 743 /** @deprecated To be removed from the protocol */ 744 finishHeapSnapshot: function(uid) 745 { 746 throw new Error("Never called"); 747 }, 748 749 /** @deprecated To be removed from the protocol */ 750 reportHeapSnapshotProgress: function(done, total) 751 { 752 throw new Error("Never called"); 753 }, 754 755 __proto__: WebInspector.ProfileType.prototype 756} 757 758/** 759 * @constructor 760 * @extends {WebInspector.ProfileHeader} 761 * @param {!WebInspector.CPUProfileType} type 762 * @param {string} title 763 * @param {number=} uid 764 */ 765WebInspector.CPUProfileHeader = function(type, title, uid) 766{ 767 WebInspector.ProfileHeader.call(this, type, title, uid); 768} 769 770WebInspector.CPUProfileHeader.prototype = { 771 /** 772 * @override 773 */ 774 createSidebarTreeElement: function() 775 { 776 return new WebInspector.ProfileSidebarTreeElement(this, WebInspector.UIString("Profile %d"), "profile-sidebar-tree-item"); 777 }, 778 779 /** 780 * @override 781 * @param {WebInspector.ProfilesPanel} profilesPanel 782 */ 783 createView: function(profilesPanel) 784 { 785 return new WebInspector.CPUProfileView(this); 786 }, 787 788 __proto__: WebInspector.ProfileHeader.prototype 789} 790