From 0a16a7f08a38dbf4da5b21b3eeb772b7f87daf7f Mon Sep 17 00:00:00 2001 From: "loislo@chromium.org" Date: Mon, 4 Mar 2013 12:39:49 +0000 Subject: [PATCH] Web Inspector: implement Flame Chart for CPU profiler. https://bugs.webkit.org/show_bug.cgi?id=111162 Reviewed by Yury Semikhatsky. It is an initial implementation. The next step is to provide function names and other stats about the hovered item. * WebCore.gypi: * WebCore.vcproj/WebCore.vcproj: * WebCore.vcxproj/WebCore.vcxproj: * WebCore.vcxproj/WebCore.vcxproj.filters: * inspector/compile-front-end.py: * inspector/front-end/CPUProfileView.js: (WebInspector.CPUProfileView.prototype._getCPUProfileCallback): * inspector/front-end/FlameChart.js: Added. (WebInspector.FlameChart): (WebInspector.FlameChart.prototype._onMouseMove): (WebInspector.FlameChart.prototype.findNodeCallback): (WebInspector.FlameChart.prototype._coordinatesToNode): (WebInspector.FlameChart.prototype.onResize): (WebInspector.FlameChart.prototype._rootNodes): (WebInspector.FlameChart.prototype.draw): (WebInspector.FlameChart.prototype._drawNode): (WebInspector.FlameChart.prototype._forEachNode): (WebInspector.FlameChart.prototype._drawBar): (WebInspector.FlameChart.prototype.update): * inspector/front-end/Settings.js: (WebInspector.ExperimentsSettings): * inspector/front-end/WebKit.qrc: * inspector/front-end/flameChart.css: Added. (.flame-chart): git-svn-id: https://svn.webkit.org/repository/webkit/trunk@144618 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- Source/WebCore/ChangeLog | 35 ++++ Source/WebCore/WebCore.gypi | 2 + Source/WebCore/WebCore.vcproj/WebCore.vcproj | 4 + Source/WebCore/WebCore.vcxproj/WebCore.vcxproj | 5 +- .../WebCore.vcxproj/WebCore.vcxproj.filters | 7 +- Source/WebCore/inspector/compile-front-end.py | 1 + .../WebCore/inspector/front-end/CPUProfileView.js | 16 +- Source/WebCore/inspector/front-end/FlameChart.js | 194 +++++++++++++++++++++ Source/WebCore/inspector/front-end/Settings.js | 1 + Source/WebCore/inspector/front-end/WebKit.qrc | 2 + Source/WebCore/inspector/front-end/flameChart.css | 4 + 11 files changed, 266 insertions(+), 5 deletions(-) create mode 100644 Source/WebCore/inspector/front-end/FlameChart.js create mode 100644 Source/WebCore/inspector/front-end/flameChart.css diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index a6c6edb..a2a3442 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,38 @@ +2013-03-04 Ilya Tikhonovsky + + Web Inspector: implement Flame Chart for CPU profiler. + https://bugs.webkit.org/show_bug.cgi?id=111162 + + Reviewed by Yury Semikhatsky. + + It is an initial implementation. The next step is to provide + function names and other stats about the hovered item. + + * WebCore.gypi: + * WebCore.vcproj/WebCore.vcproj: + * WebCore.vcxproj/WebCore.vcxproj: + * WebCore.vcxproj/WebCore.vcxproj.filters: + * inspector/compile-front-end.py: + * inspector/front-end/CPUProfileView.js: + (WebInspector.CPUProfileView.prototype._getCPUProfileCallback): + * inspector/front-end/FlameChart.js: Added. + (WebInspector.FlameChart): + (WebInspector.FlameChart.prototype._onMouseMove): + (WebInspector.FlameChart.prototype.findNodeCallback): + (WebInspector.FlameChart.prototype._coordinatesToNode): + (WebInspector.FlameChart.prototype.onResize): + (WebInspector.FlameChart.prototype._rootNodes): + (WebInspector.FlameChart.prototype.draw): + (WebInspector.FlameChart.prototype._drawNode): + (WebInspector.FlameChart.prototype._forEachNode): + (WebInspector.FlameChart.prototype._drawBar): + (WebInspector.FlameChart.prototype.update): + * inspector/front-end/Settings.js: + (WebInspector.ExperimentsSettings): + * inspector/front-end/WebKit.qrc: + * inspector/front-end/flameChart.css: Added. + (.flame-chart): + 2013-03-04 Marja Hölttä [V8] Add a "context type" parameter to GetTemplate and ConfigureV8SomethingTemplate functions diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi index a71fe47..9139e7b 100644 --- a/Source/WebCore/WebCore.gypi +++ b/Source/WebCore/WebCore.gypi @@ -5448,6 +5448,7 @@ 'inspector/front-end/dataGrid.css', 'inspector/front-end/elementsPanel.css', 'inspector/front-end/filteredItemSelectionDialog.css', + 'inspector/front-end/flameChart.css', 'inspector/front-end/heapProfiler.css', 'inspector/front-end/helpScreen.css', 'inspector/front-end/indexedDBViews.css', @@ -5536,6 +5537,7 @@ 'inspector/front-end/BottomUpProfileDataGridTree.js', 'inspector/front-end/CPUProfileView.js', 'inspector/front-end/CSSSelectorProfileView.js', + 'inspector/front-end/FlameChart.js', 'inspector/front-end/HeapSnapshot.js', 'inspector/front-end/HeapSnapshotDataGrids.js', 'inspector/front-end/HeapSnapshotGridNodes.js', diff --git a/Source/WebCore/WebCore.vcproj/WebCore.vcproj b/Source/WebCore/WebCore.vcproj/WebCore.vcproj index ec42643..fa18bd6 100755 --- a/Source/WebCore/WebCore.vcproj/WebCore.vcproj +++ b/Source/WebCore/WebCore.vcproj/WebCore.vcproj @@ -77218,6 +77218,10 @@ > + + diff --git a/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj b/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj index c2bf7ff..44acb91 100644 --- a/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj +++ b/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj @@ -1,4 +1,4 @@ - + @@ -10950,6 +10950,7 @@ + @@ -11127,4 +11128,4 @@ - \ No newline at end of file + diff --git a/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters b/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters index 2b374ad..049b7bc 100644 --- a/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters +++ b/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -14635,6 +14635,9 @@ inspector\front-end + + inspector\front-end + inspector\front-end @@ -15270,4 +15273,4 @@ rendering - \ No newline at end of file + diff --git a/Source/WebCore/inspector/compile-front-end.py b/Source/WebCore/inspector/compile-front-end.py index e99e377..cdc70c9 100755 --- a/Source/WebCore/inspector/compile-front-end.py +++ b/Source/WebCore/inspector/compile-front-end.py @@ -337,6 +337,7 @@ modules = [ "BottomUpProfileDataGridTree.js", "CPUProfileView.js", "CSSSelectorProfileView.js", + "FlameChart.js", "HeapSnapshot.js", "HeapSnapshotDataGrids.js", "HeapSnapshotGridNodes.js", diff --git a/Source/WebCore/inspector/front-end/CPUProfileView.js b/Source/WebCore/inspector/front-end/CPUProfileView.js index c26796e..2788a3d 100644 --- a/Source/WebCore/inspector/front-end/CPUProfileView.js +++ b/Source/WebCore/inspector/front-end/CPUProfileView.js @@ -53,7 +53,17 @@ WebInspector.CPUProfileView = function(profile) this.dataGrid = new WebInspector.DataGrid(columns); this.dataGrid.addEventListener("sorting changed", this._sortProfile, this); this.dataGrid.element.addEventListener("mousedown", this._mouseDownInDataGrid.bind(this), true); - this.dataGrid.show(this.element); + + if (WebInspector.experimentsSettings.cpuFlameChart.isEnabled()) { + this._splitView = new WebInspector.SplitView(false, "flameChartSplitLocation"); + this._splitView.show(this.element); + + this.dataGrid.show(this._splitView.firstElement()); + + this.flameChart = new WebInspector.FlameChart(this); + this.flameChart.show(this._splitView.secondElement()); + } else + this.dataGrid.show(this.element); this.viewSelectComboBox = new WebInspector.StatusBarComboBox(this._changeView.bind(this)); @@ -109,6 +119,8 @@ WebInspector.CPUProfileView.prototype = { this._assignParentsInProfile(); this._changeView(); this._updatePercentButton(); + if (this.flameChart) + this.flameChart.update(); }, get statusBarItems() @@ -750,3 +762,5 @@ WebInspector.CPUProfileHeader.prototype = { __proto__: WebInspector.ProfileHeader.prototype } + +importScript("FlameChart.js"); diff --git a/Source/WebCore/inspector/front-end/FlameChart.js b/Source/WebCore/inspector/front-end/FlameChart.js new file mode 100644 index 0000000..a957d96 --- /dev/null +++ b/Source/WebCore/inspector/front-end/FlameChart.js @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @constructor + * @extends {WebInspector.View} + * @param {WebInspector.CPUProfileView} cpuProfileView + */ +WebInspector.FlameChart = function(cpuProfileView) +{ + WebInspector.View.call(this); + this.registerRequiredCSS("flameChart.css"); + + this.element.className = "flame-chart"; + this._canvas = this.element.createChild("canvas"); + this._cpuProfileView = cpuProfileView; + this._xScaleFactor = 0.0; + this._yScaleFactor = 10; + this._minWidth = 3; + this.element.onmousemove = this._onMouseMove.bind(this); +} + +WebInspector.FlameChart.prototype = { + _onMouseMove: function(e) + { + var node = this._coordinatesToNode(e.offsetX, e.offsetY); + if (node !== this._highlightedNode) { + this._highlightedNode = node; + this.update(); + } + }, + + /** + * @param {!number} x + * @param {!number} y + */ + _coordinatesToNode: function(x, y) + { + var cursorOffset = x / this._xScaleFactor; + var cursorLevel = (this._canvas.height - y) / this._yScaleFactor; + this._highlightedNode = null; + var cursorNode; + + function findNodeCallback(offset, level, node) + { + if (cursorLevel > level && cursorLevel < level + 1 && cursorOffset > offset && cursorOffset < offset + node.totalTime) + cursorNode = node; + } + this._forEachNode(findNodeCallback.bind(this)); + return cursorNode; + }, + + onResize: function() + { + this.draw(this.element.clientWidth, this.element.clientHeight); + }, + + /** + * @return {Array. } + */ + _rootNodes: function() + { + if (this._rootNodesArray) + return this._rootNodesArray.slice(); + + var profileHead = this._cpuProfileView.profileHead; + if (!profileHead) + return null; + var totalTime = 0; + var nodes = []; + for (var i = 0; i < profileHead.children.length; ++i) { + var node = profileHead.children[i]; + if (node.functionName === "(program)" || node.functionName === "(idle)") + continue; + totalTime += node.totalTime; + nodes.push(node); + } + + this._rootNodesArray = nodes; + this._totalTime = totalTime; + return nodes.slice(); + }, + + /** + * @param {!number} height + * @param {!number} width + */ + draw: function(width, height) + { + if (!this._rootNodes()) + return; + + var margin = 0; + this._canvas.height = height - margin; + this._canvas.width = width - margin; + + this._xScaleFactor = width / this._totalTime; + this._colorIndex = 0; + + this._context = this._canvas.getContext("2d"); + + this._forEachNode(this._drawNode.bind(this)); + }, + + _drawNode: function(offset, level, node) + { + ++this._colorIndex; + var hue = (this._colorIndex * 2 + 11 * (this._colorIndex % 2)) % 360; + var lightness = this._highlightedNode === node ? 33 : 67; + var color = "hsl(" + hue + ", 100%, " + lightness + "%)"; + this._drawBar(this._context, offset, level, node, color); + }, + + _forEachNode: function(callback) + { + var nodes = this._rootNodes(); + var levelOffsets = /** @type {Array.} */ [0]; + var levelExitIndexes = [0]; + + while (nodes.length) { + var level = levelOffsets.length - 1; + var node = nodes.pop(); + if (node.totalTime * this._xScaleFactor > this._minWidth) { + var offset = levelOffsets[level]; + callback(offset, level, node); + levelOffsets[level] += node.totalTime; + if (node.children.length) { + levelExitIndexes.push(nodes.length); + levelOffsets.push(offset + node.selfTime / 2); + nodes = nodes.concat(node.children); + } + } + while (nodes.length === levelExitIndexes[levelExitIndexes.length - 1]) { + levelOffsets.pop(); + levelExitIndexes.pop(); + } + } + }, + + /** + * @param {Object} context + * @param {number} offset + * @param {number} level + * @param {!ProfilerAgent.CPUProfileNode} node + * @param {!WebInspector.Color} hslColor + */ + _drawBar: function(context, offset, level, node, hslColor) + { + var width = node.totalTime * this._xScaleFactor; + var height = this._yScaleFactor; + var x = offset * this._xScaleFactor; + var y = this._canvas.height - level * this._yScaleFactor - height; + context.beginPath(); + context.rect(x, y, width - 1, height - 1); + context.fillStyle = hslColor; + context.fill(); + }, + + update: function() + { + this.draw(this.element.clientWidth, this.element.clientHeight); + }, + + __proto__: WebInspector.View.prototype +}; + +//@ sourceURL=http://localhost/inspector/front-end/FlameChart.js diff --git a/Source/WebCore/inspector/front-end/Settings.js b/Source/WebCore/inspector/front-end/Settings.js index c694b89..4edfa9d 100644 --- a/Source/WebCore/inspector/front-end/Settings.js +++ b/Source/WebCore/inspector/front-end/Settings.js @@ -217,6 +217,7 @@ WebInspector.ExperimentsSettings = function() this.showWhitespaceInEditor = this._createExperiment("showWhitespaceInEditor", "Show whitespace characters in editor"); this.textEditorSmartBraces = this._createExperiment("textEditorSmartBraces", "Enable smart braces in text editor"); this.separateProfilers = this._createExperiment("separateProfilers", "Separate profiler tools"); + this.cpuFlameChart = this._createExperiment("cpuFlameChart", "Show Flame Chart in CPU Profiler"); this._cleanUpSetting(); } diff --git a/Source/WebCore/inspector/front-end/WebKit.qrc b/Source/WebCore/inspector/front-end/WebKit.qrc index 35abe2b..0b6eca8 100644 --- a/Source/WebCore/inspector/front-end/WebKit.qrc +++ b/Source/WebCore/inspector/front-end/WebKit.qrc @@ -83,6 +83,7 @@ FileSystemProjectDelegate.js FileUtils.js FilteredItemSelectionDialog.js + FlameChart.js FontView.js GoToLineDialog.js HAREntry.js @@ -244,6 +245,7 @@ dialog.css elementsPanel.css filteredItemSelectionDialog.css + flameChart.css heapProfiler.css helpScreen.css indexedDBViews.css diff --git a/Source/WebCore/inspector/front-end/flameChart.css b/Source/WebCore/inspector/front-end/flameChart.css new file mode 100644 index 0000000..6776a64 --- /dev/null +++ b/Source/WebCore/inspector/front-end/flameChart.css @@ -0,0 +1,4 @@ +.flame-chart { + height: 100%; + overflow: hidden; +} -- 1.8.3.1