Home | History | Annotate | Download | only in front-end
      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 // FIXME: Rename the file.
     27 
     28 WebInspector.CPUProfileView = function(profile)
     29 {
     30     WebInspector.View.call(this);
     31 
     32     this.element.addStyleClass("profile-view");
     33 
     34     this.showSelfTimeAsPercent = true;
     35     this.showTotalTimeAsPercent = true;
     36     this.showAverageTimeAsPercent = true;
     37 
     38     var columns = { "self": { title: WebInspector.UIString("Self"), width: "72px", sort: "descending", sortable: true },
     39                     "total": { title: WebInspector.UIString("Total"), width: "72px", sortable: true },
     40                     "average": { title: WebInspector.UIString("Average"), width: "72px", sortable: true },
     41                     "calls": { title: WebInspector.UIString("Calls"), width: "54px", sortable: true },
     42                     "function": { title: WebInspector.UIString("Function"), disclosure: true, sortable: true } };
     43 
     44     if (Preferences.samplingCPUProfiler) {
     45         delete columns.average;
     46         delete columns.calls;
     47     }
     48 
     49     this.dataGrid = new WebInspector.DataGrid(columns);
     50     this.dataGrid.addEventListener("sorting changed", this._sortData, this);
     51     this.dataGrid.element.addEventListener("mousedown", this._mouseDownInDataGrid.bind(this), true);
     52     this.element.appendChild(this.dataGrid.element);
     53 
     54     this.viewSelectElement = document.createElement("select");
     55     this.viewSelectElement.className = "status-bar-item";
     56     this.viewSelectElement.addEventListener("change", this._changeView.bind(this), false);
     57     this.view = "Heavy";
     58 
     59     var heavyViewOption = document.createElement("option");
     60     heavyViewOption.label = WebInspector.UIString("Heavy (Bottom Up)");
     61     var treeViewOption = document.createElement("option");
     62     treeViewOption.label = WebInspector.UIString("Tree (Top Down)");
     63     this.viewSelectElement.appendChild(heavyViewOption);
     64     this.viewSelectElement.appendChild(treeViewOption);
     65 
     66     this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item");
     67     this.percentButton.addEventListener("click", this._percentClicked.bind(this), false);
     68 
     69     this.focusButton = new WebInspector.StatusBarButton(WebInspector.UIString("Focus selected function."), "focus-profile-node-status-bar-item");
     70     this.focusButton.disabled = true;
     71     this.focusButton.addEventListener("click", this._focusClicked.bind(this), false);
     72 
     73     this.excludeButton = new WebInspector.StatusBarButton(WebInspector.UIString("Exclude selected function."), "exclude-profile-node-status-bar-item");
     74     this.excludeButton.disabled = true;
     75     this.excludeButton.addEventListener("click", this._excludeClicked.bind(this), false);
     76 
     77     this.resetButton = new WebInspector.StatusBarButton(WebInspector.UIString("Restore all functions."), "reset-profile-status-bar-item");
     78     this.resetButton.visible = false;
     79     this.resetButton.addEventListener("click", this._resetClicked.bind(this), false);
     80 
     81     this.profile = profile;
     82 
     83     var self = this;
     84     function profileCallback(profile)
     85     {
     86         self.profile = profile;
     87         self._assignParentsInProfile();
     88 
     89         self.profileDataGridTree = self.bottomUpProfileDataGridTree;
     90         self.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyComparator("selfTime", false));
     91 
     92         self.refresh();
     93 
     94         self._updatePercentButton();
     95     }
     96 
     97     var callId = WebInspector.Callback.wrap(profileCallback);
     98     InspectorBackend.getProfile(callId, this.profile.uid);
     99 }
    100 
    101 WebInspector.CPUProfileView.prototype = {
    102     get statusBarItems()
    103     {
    104         return [this.viewSelectElement, this.percentButton.element, this.focusButton.element, this.excludeButton.element, this.resetButton.element];
    105     },
    106 
    107     get profile()
    108     {
    109         return this._profile;
    110     },
    111 
    112     set profile(profile)
    113     {
    114         this._profile = profile;
    115     },
    116 
    117     get bottomUpProfileDataGridTree()
    118     {
    119         if (!this._bottomUpProfileDataGridTree)
    120             this._bottomUpProfileDataGridTree = new WebInspector.BottomUpProfileDataGridTree(this, this.profile.head);
    121         return this._bottomUpProfileDataGridTree;
    122     },
    123 
    124     get topDownProfileDataGridTree()
    125     {
    126         if (!this._topDownProfileDataGridTree)
    127             this._topDownProfileDataGridTree = new WebInspector.TopDownProfileDataGridTree(this, this.profile.head);
    128         return this._topDownProfileDataGridTree;
    129     },
    130 
    131     get currentTree()
    132     {
    133         return this._currentTree;
    134     },
    135 
    136     set currentTree(tree)
    137     {
    138         this._currentTree = tree;
    139         this.refresh();
    140     },
    141 
    142     get topDownTree()
    143     {
    144         if (!this._topDownTree) {
    145             this._topDownTree = WebInspector.TopDownTreeFactory.create(this.profile.head);
    146             this._sortProfile(this._topDownTree);
    147         }
    148 
    149         return this._topDownTree;
    150     },
    151 
    152     get bottomUpTree()
    153     {
    154         if (!this._bottomUpTree) {
    155             this._bottomUpTree = WebInspector.BottomUpTreeFactory.create(this.profile.head);
    156             this._sortProfile(this._bottomUpTree);
    157         }
    158 
    159         return this._bottomUpTree;
    160     },
    161 
    162     show: function(parentElement)
    163     {
    164         WebInspector.View.prototype.show.call(this, parentElement);
    165         this.dataGrid.updateWidths();
    166     },
    167 
    168     hide: function()
    169     {
    170         WebInspector.View.prototype.hide.call(this);
    171         this._currentSearchResultIndex = -1;
    172     },
    173 
    174     resize: function()
    175     {
    176         if (this.dataGrid)
    177             this.dataGrid.updateWidths();
    178     },
    179 
    180     refresh: function()
    181     {
    182         var selectedProfileNode = this.dataGrid.selectedNode ? this.dataGrid.selectedNode.profileNode : null;
    183 
    184         this.dataGrid.removeChildren();
    185 
    186         var children = this.profileDataGridTree.children;
    187         var count = children.length;
    188 
    189         for (var index = 0; index < count; ++index)
    190             this.dataGrid.appendChild(children[index]);
    191 
    192         if (selectedProfileNode)
    193             selectedProfileNode.selected = true;
    194     },
    195 
    196     refreshVisibleData: function()
    197     {
    198         var child = this.dataGrid.children[0];
    199         while (child) {
    200             child.refresh();
    201             child = child.traverseNextNode(false, null, true);
    202         }
    203     },
    204 
    205     refreshShowAsPercents: function()
    206     {
    207         this._updatePercentButton();
    208         this.refreshVisibleData();
    209     },
    210 
    211     searchCanceled: function()
    212     {
    213         if (this._searchResults) {
    214             for (var i = 0; i < this._searchResults.length; ++i) {
    215                 var profileNode = this._searchResults[i].profileNode;
    216 
    217                 delete profileNode._searchMatchedSelfColumn;
    218                 delete profileNode._searchMatchedTotalColumn;
    219                 delete profileNode._searchMatchedCallsColumn;
    220                 delete profileNode._searchMatchedFunctionColumn;
    221 
    222                 profileNode.refresh();
    223             }
    224         }
    225 
    226         delete this._searchFinishedCallback;
    227         this._currentSearchResultIndex = -1;
    228         this._searchResults = [];
    229     },
    230 
    231     performSearch: function(query, finishedCallback)
    232     {
    233         // Call searchCanceled since it will reset everything we need before doing a new search.
    234         this.searchCanceled();
    235 
    236         query = query.trim();
    237 
    238         if (!query.length)
    239             return;
    240 
    241         this._searchFinishedCallback = finishedCallback;
    242 
    243         var greaterThan = (query.indexOf(">") === 0);
    244         var lessThan = (query.indexOf("<") === 0);
    245         var equalTo = (query.indexOf("=") === 0 || ((greaterThan || lessThan) && query.indexOf("=") === 1));
    246         var percentUnits = (query.lastIndexOf("%") === (query.length - 1));
    247         var millisecondsUnits = (query.length > 2 && query.lastIndexOf("ms") === (query.length - 2));
    248         var secondsUnits = (!millisecondsUnits && query.lastIndexOf("s") === (query.length - 1));
    249 
    250         var queryNumber = parseFloat(query);
    251         if (greaterThan || lessThan || equalTo) {
    252             if (equalTo && (greaterThan || lessThan))
    253                 queryNumber = parseFloat(query.substring(2));
    254             else
    255                 queryNumber = parseFloat(query.substring(1));
    256         }
    257 
    258         var queryNumberMilliseconds = (secondsUnits ? (queryNumber * 1000) : queryNumber);
    259 
    260         // Make equalTo implicitly true if it wasn't specified there is no other operator.
    261         if (!isNaN(queryNumber) && !(greaterThan || lessThan))
    262             equalTo = true;
    263 
    264         function matchesQuery(/*ProfileDataGridNode*/ profileDataGridNode)
    265         {
    266             delete profileDataGridNode._searchMatchedSelfColumn;
    267             delete profileDataGridNode._searchMatchedTotalColumn;
    268             delete profileDataGridNode._searchMatchedAverageColumn;
    269             delete profileDataGridNode._searchMatchedCallsColumn;
    270             delete profileDataGridNode._searchMatchedFunctionColumn;
    271 
    272             if (percentUnits) {
    273                 if (lessThan) {
    274                     if (profileDataGridNode.selfPercent < queryNumber)
    275                         profileDataGridNode._searchMatchedSelfColumn = true;
    276                     if (profileDataGridNode.totalPercent < queryNumber)
    277                         profileDataGridNode._searchMatchedTotalColumn = true;
    278                     if (profileDataGridNode.averagePercent < queryNumberMilliseconds)
    279                         profileDataGridNode._searchMatchedAverageColumn = true;
    280                 } else if (greaterThan) {
    281                     if (profileDataGridNode.selfPercent > queryNumber)
    282                         profileDataGridNode._searchMatchedSelfColumn = true;
    283                     if (profileDataGridNode.totalPercent > queryNumber)
    284                         profileDataGridNode._searchMatchedTotalColumn = true;
    285                     if (profileDataGridNode.averagePercent < queryNumberMilliseconds)
    286                         profileDataGridNode._searchMatchedAverageColumn = true;
    287                 }
    288 
    289                 if (equalTo) {
    290                     if (profileDataGridNode.selfPercent == queryNumber)
    291                         profileDataGridNode._searchMatchedSelfColumn = true;
    292                     if (profileDataGridNode.totalPercent == queryNumber)
    293                         profileDataGridNode._searchMatchedTotalColumn = true;
    294                     if (profileDataGridNode.averagePercent < queryNumberMilliseconds)
    295                         profileDataGridNode._searchMatchedAverageColumn = true;
    296                 }
    297             } else if (millisecondsUnits || secondsUnits) {
    298                 if (lessThan) {
    299                     if (profileDataGridNode.selfTime < queryNumberMilliseconds)
    300                         profileDataGridNode._searchMatchedSelfColumn = true;
    301                     if (profileDataGridNode.totalTime < queryNumberMilliseconds)
    302                         profileDataGridNode._searchMatchedTotalColumn = true;
    303                     if (profileDataGridNode.averageTime < queryNumberMilliseconds)
    304                         profileDataGridNode._searchMatchedAverageColumn = true;
    305                 } else if (greaterThan) {
    306                     if (profileDataGridNode.selfTime > queryNumberMilliseconds)
    307                         profileDataGridNode._searchMatchedSelfColumn = true;
    308                     if (profileDataGridNode.totalTime > queryNumberMilliseconds)
    309                         profileDataGridNode._searchMatchedTotalColumn = true;
    310                     if (profileDataGridNode.averageTime > queryNumberMilliseconds)
    311                         profileDataGridNode._searchMatchedAverageColumn = true;
    312                 }
    313 
    314                 if (equalTo) {
    315                     if (profileDataGridNode.selfTime == queryNumberMilliseconds)
    316                         profileDataGridNode._searchMatchedSelfColumn = true;
    317                     if (profileDataGridNode.totalTime == queryNumberMilliseconds)
    318                         profileDataGridNode._searchMatchedTotalColumn = true;
    319                     if (profileDataGridNode.averageTime == queryNumberMilliseconds)
    320                         profileDataGridNode._searchMatchedAverageColumn = true;
    321                 }
    322             } else {
    323                 if (equalTo && profileDataGridNode.numberOfCalls == queryNumber)
    324                     profileDataGridNode._searchMatchedCallsColumn = true;
    325                 if (greaterThan && profileDataGridNode.numberOfCalls > queryNumber)
    326                     profileDataGridNode._searchMatchedCallsColumn = true;
    327                 if (lessThan && profileDataGridNode.numberOfCalls < queryNumber)
    328                     profileDataGridNode._searchMatchedCallsColumn = true;
    329             }
    330 
    331             if (profileDataGridNode.functionName.hasSubstring(query, true) || profileDataGridNode.url.hasSubstring(query, true))
    332                 profileDataGridNode._searchMatchedFunctionColumn = true;
    333 
    334             if (profileDataGridNode._searchMatchedSelfColumn ||
    335                 profileDataGridNode._searchMatchedTotalColumn ||
    336                 profileDataGridNode._searchMatchedAverageColumn ||
    337                 profileDataGridNode._searchMatchedCallsColumn ||
    338                 profileDataGridNode._searchMatchedFunctionColumn)
    339             {
    340                 profileDataGridNode.refresh();
    341                 return true;
    342             }
    343 
    344             return false;
    345         }
    346 
    347         var current = this.profileDataGridTree.children[0];
    348 
    349         while (current) {
    350             if (matchesQuery(current)) {
    351                 this._searchResults.push({ profileNode: current });
    352             }
    353 
    354             current = current.traverseNextNode(false, null, false);
    355         }
    356 
    357         finishedCallback(this, this._searchResults.length);
    358     },
    359 
    360     jumpToFirstSearchResult: function()
    361     {
    362         if (!this._searchResults || !this._searchResults.length)
    363             return;
    364         this._currentSearchResultIndex = 0;
    365         this._jumpToSearchResult(this._currentSearchResultIndex);
    366     },
    367 
    368     jumpToLastSearchResult: function()
    369     {
    370         if (!this._searchResults || !this._searchResults.length)
    371             return;
    372         this._currentSearchResultIndex = (this._searchResults.length - 1);
    373         this._jumpToSearchResult(this._currentSearchResultIndex);
    374     },
    375 
    376     jumpToNextSearchResult: function()
    377     {
    378         if (!this._searchResults || !this._searchResults.length)
    379             return;
    380         if (++this._currentSearchResultIndex >= this._searchResults.length)
    381             this._currentSearchResultIndex = 0;
    382         this._jumpToSearchResult(this._currentSearchResultIndex);
    383     },
    384 
    385     jumpToPreviousSearchResult: function()
    386     {
    387         if (!this._searchResults || !this._searchResults.length)
    388             return;
    389         if (--this._currentSearchResultIndex < 0)
    390             this._currentSearchResultIndex = (this._searchResults.length - 1);
    391         this._jumpToSearchResult(this._currentSearchResultIndex);
    392     },
    393 
    394     showingFirstSearchResult: function()
    395     {
    396         return (this._currentSearchResultIndex === 0);
    397     },
    398 
    399     showingLastSearchResult: function()
    400     {
    401         return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
    402     },
    403 
    404     _jumpToSearchResult: function(index)
    405     {
    406         var searchResult = this._searchResults[index];
    407         if (!searchResult)
    408             return;
    409 
    410         var profileNode = searchResult.profileNode;
    411         profileNode.reveal();
    412         profileNode.select();
    413     },
    414 
    415     _changeView: function(event)
    416     {
    417         if (!event || !this.profile)
    418             return;
    419 
    420         if (event.target.selectedIndex == 1 && this.view == "Heavy") {
    421             this.profileDataGridTree = this.topDownProfileDataGridTree;
    422             this._sortProfile();
    423             this.view = "Tree";
    424         } else if (event.target.selectedIndex == 0 && this.view == "Tree") {
    425             this.profileDataGridTree = this.bottomUpProfileDataGridTree;
    426             this._sortProfile();
    427             this.view = "Heavy";
    428         }
    429 
    430         if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
    431             return;
    432 
    433         // The current search needs to be performed again. First negate out previous match
    434         // count by calling the search finished callback with a negative number of matches.
    435         // Then perform the search again the with same query and callback.
    436         this._searchFinishedCallback(this, -this._searchResults.length);
    437         this.performSearch(this.currentQuery, this._searchFinishedCallback);
    438     },
    439 
    440     _percentClicked: function(event)
    441     {
    442         var currentState = this.showSelfTimeAsPercent && this.showTotalTimeAsPercent && this.showAverageTimeAsPercent;
    443         this.showSelfTimeAsPercent = !currentState;
    444         this.showTotalTimeAsPercent = !currentState;
    445         this.showAverageTimeAsPercent = !currentState;
    446         this.refreshShowAsPercents();
    447     },
    448 
    449     _updatePercentButton: function()
    450     {
    451         if (this.showSelfTimeAsPercent && this.showTotalTimeAsPercent && this.showAverageTimeAsPercent) {
    452             this.percentButton.title = WebInspector.UIString("Show absolute total and self times.");
    453             this.percentButton.toggled = true;
    454         } else {
    455             this.percentButton.title = WebInspector.UIString("Show total and self times as percentages.");
    456             this.percentButton.toggled = false;
    457         }
    458     },
    459 
    460     _focusClicked: function(event)
    461     {
    462         if (!this.dataGrid.selectedNode)
    463             return;
    464 
    465         this.resetButton.visible = true;
    466         this.profileDataGridTree.focus(this.dataGrid.selectedNode);
    467         this.refresh();
    468         this.refreshVisibleData();
    469     },
    470 
    471     _excludeClicked: function(event)
    472     {
    473         var selectedNode = this.dataGrid.selectedNode
    474 
    475         if (!selectedNode)
    476             return;
    477 
    478         selectedNode.deselect();
    479 
    480         this.resetButton.visible = true;
    481         this.profileDataGridTree.exclude(selectedNode);
    482         this.refresh();
    483         this.refreshVisibleData();
    484     },
    485 
    486     _resetClicked: function(event)
    487     {
    488         this.resetButton.visible = false;
    489         this.profileDataGridTree.restore();
    490         this.refresh();
    491         this.refreshVisibleData();
    492     },
    493 
    494     _dataGridNodeSelected: function(node)
    495     {
    496         this.focusButton.disabled = false;
    497         this.excludeButton.disabled = false;
    498     },
    499 
    500     _dataGridNodeDeselected: function(node)
    501     {
    502         this.focusButton.disabled = true;
    503         this.excludeButton.disabled = true;
    504     },
    505 
    506     _sortData: function(event)
    507     {
    508         this._sortProfile(this.profile);
    509     },
    510 
    511     _sortProfile: function()
    512     {
    513         var sortAscending = this.dataGrid.sortOrder === "ascending";
    514         var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier;
    515         var sortProperty = {
    516                 "average": "averageTime",
    517                 "self": "selfTime",
    518                 "total": "totalTime",
    519                 "calls": "numberOfCalls",
    520                 "function": "functionName"
    521             }[sortColumnIdentifier];
    522 
    523         this.profileDataGridTree.sort(WebInspector.ProfileDataGridTree.propertyComparator(sortProperty, sortAscending));
    524 
    525         this.refresh();
    526     },
    527 
    528     _mouseDownInDataGrid: function(event)
    529     {
    530         if (event.detail < 2)
    531             return;
    532 
    533         var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
    534         if (!cell || (!cell.hasStyleClass("total-column") && !cell.hasStyleClass("self-column") && !cell.hasStyleClass("average-column")))
    535             return;
    536 
    537         if (cell.hasStyleClass("total-column"))
    538             this.showTotalTimeAsPercent = !this.showTotalTimeAsPercent;
    539         else if (cell.hasStyleClass("self-column"))
    540             this.showSelfTimeAsPercent = !this.showSelfTimeAsPercent;
    541         else if (cell.hasStyleClass("average-column"))
    542             this.showAverageTimeAsPercent = !this.showAverageTimeAsPercent;
    543 
    544         this.refreshShowAsPercents();
    545 
    546         event.preventDefault();
    547         event.stopPropagation();
    548     },
    549 
    550     _assignParentsInProfile: function()
    551     {
    552         var head = this.profile.head;
    553         head.parent = null;
    554         head.head = null;
    555         var nodesToTraverse = [ { parent: head, children: head.children } ];
    556         while (nodesToTraverse.length > 0) {
    557             var pair = nodesToTraverse.shift();
    558             var parent = pair.parent;
    559             var children = pair.children;
    560             var length = children.length;
    561             for (var i = 0; i < length; ++i) {
    562                 children[i].head = head;
    563                 children[i].parent = parent;
    564                 if (children[i].children.length > 0)
    565                     nodesToTraverse.push({ parent: children[i], children: children[i].children });
    566             }
    567         }
    568     }
    569 }
    570 
    571 WebInspector.CPUProfileView.prototype.__proto__ = WebInspector.View.prototype;
    572 
    573 WebInspector.CPUProfileType = function()
    574 {
    575     WebInspector.ProfileType.call(this, WebInspector.CPUProfileType.TypeId, WebInspector.UIString("CPU PROFILES"));
    576     this._recording = false;
    577 }
    578 
    579 WebInspector.CPUProfileType.TypeId = "CPU";
    580 
    581 WebInspector.CPUProfileType.prototype = {
    582     get buttonTooltip()
    583     {
    584         return this._recording ? WebInspector.UIString("Stop profiling.") : WebInspector.UIString("Start profiling.");
    585     },
    586 
    587     get buttonStyle()
    588     {
    589         return this._recording ? "record-profile-status-bar-item status-bar-item toggled-on" : "record-profile-status-bar-item status-bar-item";
    590     },
    591 
    592     buttonClicked: function()
    593     {
    594         this._recording = !this._recording;
    595 
    596         if (this._recording)
    597             InspectorBackend.startProfiling();
    598         else
    599             InspectorBackend.stopProfiling();
    600     },
    601 
    602     get welcomeMessage()
    603     {
    604         return WebInspector.UIString("Start CPU profiling by pressing<br>the %s button on the status bar.");
    605     },
    606 
    607     setRecordingProfile: function(isProfiling)
    608     {
    609         this._recording = isProfiling;
    610     },
    611 
    612     createSidebarTreeElementForProfile: function(profile)
    613     {
    614         return new WebInspector.ProfileSidebarTreeElement(profile);
    615     },
    616 
    617     createView: function(profile)
    618     {
    619         return new WebInspector.CPUProfileView(profile);
    620     }
    621 }
    622 
    623 WebInspector.CPUProfileType.prototype.__proto__ = WebInspector.ProfileType.prototype;
    624