OLD | NEW |
(Empty) | |
| 1 /** |
| 2 * Copyright (C) 2014 Google Inc. All rights reserved. |
| 3 * |
| 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are |
| 6 * met: |
| 7 * |
| 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above |
| 11 * copyright notice, this list of conditions and the following disclaimer |
| 12 * in the documentation and/or other materials provided with the |
| 13 * distribution. |
| 14 * * Neither the name of Google Inc. nor the names of its |
| 15 * contributors may be used to endorse or promote products derived from |
| 16 * this software without specific prior written permission. |
| 17 * |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ |
| 30 |
| 31 /** |
| 32 * @constructor |
| 33 * @extends {WebInspector.VBox} |
| 34 * @param {!WebInspector.FlameChartDataProvider} dataProvider |
| 35 */ |
| 36 WebInspector.CPUProfileFlameChart = function(dataProvider) |
| 37 { |
| 38 WebInspector.VBox.call(this); |
| 39 this.registerRequiredCSS("flameChart.css"); |
| 40 this.element.id = "cpu-flame-chart"; |
| 41 |
| 42 this._overviewPane = new WebInspector.CPUProfileFlameChart.OverviewPane(data
Provider); |
| 43 this._overviewPane.show(this.element); |
| 44 |
| 45 this._mainPane = new WebInspector.FlameChart(dataProvider, this._overviewPan
e, true, false); |
| 46 this._mainPane.show(this.element); |
| 47 this._mainPane.addEventListener(WebInspector.FlameChart.Events.EntrySelected
, this._onEntrySelected, this); |
| 48 this._overviewPane._overviewGrid.addEventListener(WebInspector.OverviewGrid.
Events.WindowChanged, this._onWindowChanged, this); |
| 49 } |
| 50 |
| 51 WebInspector.CPUProfileFlameChart.prototype = { |
| 52 /** |
| 53 * @param {!WebInspector.Event} event |
| 54 */ |
| 55 _onWindowChanged: function(event) |
| 56 { |
| 57 this._mainPane.changeWindow(this._overviewPane._overviewGrid.windowLeft(
), this._overviewPane._overviewGrid.windowRight()); |
| 58 }, |
| 59 |
| 60 /** |
| 61 * @param {!number} timeLeft |
| 62 * @param {!number} timeRight |
| 63 */ |
| 64 selectRange: function(timeLeft, timeRight) |
| 65 { |
| 66 this._overviewPane._selectRange(timeLeft, timeRight); |
| 67 }, |
| 68 |
| 69 /** |
| 70 * @param {!WebInspector.Event} event |
| 71 */ |
| 72 _onEntrySelected: function(event) |
| 73 { |
| 74 this.dispatchEventToListeners(WebInspector.FlameChart.Events.EntrySelect
ed, event.data); |
| 75 }, |
| 76 |
| 77 update: function() |
| 78 { |
| 79 this._overviewPane.update(); |
| 80 this._mainPane.update(); |
| 81 }, |
| 82 |
| 83 __proto__: WebInspector.VBox.prototype |
| 84 }; |
| 85 |
| 86 /** |
| 87 * @constructor |
| 88 * @implements {WebInspector.TimelineGrid.Calculator} |
| 89 */ |
| 90 WebInspector.CPUProfileFlameChart.OverviewCalculator = function() |
| 91 { |
| 92 } |
| 93 |
| 94 WebInspector.CPUProfileFlameChart.OverviewCalculator.prototype = { |
| 95 /** |
| 96 * @return {number} |
| 97 */ |
| 98 paddingLeft: function() |
| 99 { |
| 100 return 0; |
| 101 }, |
| 102 |
| 103 /** |
| 104 * @param {!WebInspector.CPUProfileFlameChart.OverviewPane} overviewPane |
| 105 */ |
| 106 _updateBoundaries: function(overviewPane) |
| 107 { |
| 108 this._minimumBoundaries = 0; |
| 109 var totalTime = overviewPane._dataProvider.totalTime(); |
| 110 this._maximumBoundaries = totalTime; |
| 111 this._xScaleFactor = overviewPane._overviewCanvas.width / totalTime; |
| 112 }, |
| 113 |
| 114 /** |
| 115 * @param {number} time |
| 116 * @return {number} |
| 117 */ |
| 118 computePosition: function(time) |
| 119 { |
| 120 return (time - this._minimumBoundaries) * this._xScaleFactor; |
| 121 }, |
| 122 |
| 123 /** |
| 124 * @param {number} value |
| 125 * @param {number=} precision |
| 126 * @return {string} |
| 127 */ |
| 128 formatTime: function(value, precision) |
| 129 { |
| 130 return Number.secondsToString((value + this._minimumBoundaries) / 1000); |
| 131 }, |
| 132 |
| 133 /** |
| 134 * @return {number} |
| 135 */ |
| 136 maximumBoundary: function() |
| 137 { |
| 138 return this._maximumBoundaries; |
| 139 }, |
| 140 |
| 141 /** |
| 142 * @return {number} |
| 143 */ |
| 144 minimumBoundary: function() |
| 145 { |
| 146 return this._minimumBoundaries; |
| 147 }, |
| 148 |
| 149 /** |
| 150 * @return {number} |
| 151 */ |
| 152 zeroTime: function() |
| 153 { |
| 154 return this._minimumBoundaries; |
| 155 }, |
| 156 |
| 157 /** |
| 158 * @return {number} |
| 159 */ |
| 160 boundarySpan: function() |
| 161 { |
| 162 return this._maximumBoundaries - this._minimumBoundaries; |
| 163 } |
| 164 } |
| 165 |
| 166 /** |
| 167 * @constructor |
| 168 */ |
| 169 WebInspector.CPUProfileFlameChart.ColorGenerator = function() |
| 170 { |
| 171 this._colors = {}; |
| 172 this._currentColorIndex = 0; |
| 173 } |
| 174 |
| 175 WebInspector.CPUProfileFlameChart.ColorGenerator.prototype = { |
| 176 /** |
| 177 * @param {string} id |
| 178 * @param {string|!CanvasGradient} color |
| 179 */ |
| 180 setColorForID: function(id, color) |
| 181 { |
| 182 this._colors[id] = color; |
| 183 }, |
| 184 |
| 185 /** |
| 186 * @param {!string} id |
| 187 * @param {number=} sat |
| 188 * @return {!string} |
| 189 */ |
| 190 colorForID: function(id, sat) |
| 191 { |
| 192 if (typeof sat !== "number") |
| 193 sat = 100; |
| 194 var color = this._colors[id]; |
| 195 if (!color) { |
| 196 color = this._createColor(this._currentColorIndex++, sat); |
| 197 this._colors[id] = color; |
| 198 } |
| 199 return color; |
| 200 }, |
| 201 |
| 202 /** |
| 203 * @param {!number} index |
| 204 * @param {!number} sat |
| 205 */ |
| 206 _createColor: function(index, sat) |
| 207 { |
| 208 var hue = (index * 7 + 12 * (index % 2)) % 360; |
| 209 return "hsla(" + hue + ", " + sat + "%, 66%, 0.7)"; |
| 210 } |
| 211 } |
| 212 |
| 213 /** |
| 214 * @constructor |
| 215 * @extends {WebInspector.VBox} |
| 216 * @implements {WebInspector.FlameChartDelegate} |
| 217 * @param {!WebInspector.FlameChartDataProvider} dataProvider |
| 218 */ |
| 219 WebInspector.CPUProfileFlameChart.OverviewPane = function(dataProvider) |
| 220 { |
| 221 WebInspector.VBox.call(this); |
| 222 this.element.classList.add("flame-chart-overview-pane"); |
| 223 this._overviewContainer = this.element.createChild("div", "overview-containe
r"); |
| 224 this._overviewGrid = new WebInspector.OverviewGrid("flame-chart"); |
| 225 this._overviewGrid.element.classList.add("fill"); |
| 226 this._overviewCanvas = this._overviewContainer.createChild("canvas", "flame-
chart-overview-canvas"); |
| 227 this._overviewContainer.appendChild(this._overviewGrid.element); |
| 228 this._overviewCalculator = new WebInspector.CPUProfileFlameChart.OverviewCal
culator(); |
| 229 this._dataProvider = dataProvider; |
| 230 } |
| 231 |
| 232 WebInspector.CPUProfileFlameChart.OverviewPane.prototype = { |
| 233 /** |
| 234 * @param {number} windowStartTime |
| 235 * @param {number} windowEndTime |
| 236 */ |
| 237 requestWindowTimes: function(windowStartTime, windowEndTime) |
| 238 { |
| 239 this._overviewGrid.setWindow(windowStartTime / this._dataProvider.totalT
ime(), windowEndTime / this._dataProvider.totalTime()); |
| 240 }, |
| 241 |
| 242 /** |
| 243 * @param {!number} timeLeft |
| 244 * @param {!number} timeRight |
| 245 */ |
| 246 _selectRange: function(timeLeft, timeRight) |
| 247 { |
| 248 this._overviewGrid.setWindow(timeLeft / this._dataProvider.totalTime(),
timeRight / this._dataProvider.totalTime()); |
| 249 }, |
| 250 |
| 251 /** |
| 252 * @return {?WebInspector.FlameChart.TimelineData} |
| 253 */ |
| 254 _timelineData: function() |
| 255 { |
| 256 return this._dataProvider.timelineData(); |
| 257 }, |
| 258 |
| 259 onResize: function() |
| 260 { |
| 261 this._scheduleUpdate(); |
| 262 }, |
| 263 |
| 264 _scheduleUpdate: function() |
| 265 { |
| 266 if (this._updateTimerId) |
| 267 return; |
| 268 this._updateTimerId = requestAnimationFrame(this.update.bind(this)); |
| 269 }, |
| 270 |
| 271 update: function() |
| 272 { |
| 273 this._updateTimerId = 0; |
| 274 var timelineData = this._timelineData(); |
| 275 if (!timelineData) |
| 276 return; |
| 277 this._resetCanvas(this._overviewContainer.clientWidth, this._overviewCon
tainer.clientHeight - WebInspector.FlameChart.DividersBarHeight); |
| 278 this._overviewCalculator._updateBoundaries(this); |
| 279 this._overviewGrid.updateDividers(this._overviewCalculator); |
| 280 WebInspector.CPUProfileFlameChart.OverviewPane.drawOverviewCanvas( |
| 281 this._dataProvider, |
| 282 timelineData, |
| 283 this._overviewCanvas.getContext("2d"), |
| 284 this._overviewContainer.clientWidth, |
| 285 this._overviewContainer.clientHeight - WebInspector.FlameChart.Divid
ersBarHeight |
| 286 ); |
| 287 }, |
| 288 |
| 289 /** |
| 290 * @param {!number} width |
| 291 * @param {!number} height |
| 292 */ |
| 293 _resetCanvas: function(width, height) |
| 294 { |
| 295 var ratio = window.devicePixelRatio; |
| 296 this._overviewCanvas.width = width * ratio; |
| 297 this._overviewCanvas.height = height * ratio; |
| 298 }, |
| 299 |
| 300 __proto__: WebInspector.VBox.prototype |
| 301 } |
| 302 |
| 303 /** |
| 304 * @param {!WebInspector.FlameChartDataProvider} dataProvider |
| 305 * @param {!WebInspector.FlameChart.TimelineData} timelineData |
| 306 * @param {!number} width |
| 307 */ |
| 308 WebInspector.CPUProfileFlameChart.OverviewPane.calculateDrawData = function(data
Provider, timelineData, width) |
| 309 { |
| 310 var entryOffsets = timelineData.entryOffsets; |
| 311 var entryTotalTimes = timelineData.entryTotalTimes; |
| 312 var entryLevels = timelineData.entryLevels; |
| 313 var length = entryOffsets.length; |
| 314 |
| 315 var drawData = new Uint8Array(width); |
| 316 var scaleFactor = width / dataProvider.totalTime(); |
| 317 |
| 318 for (var entryIndex = 0; entryIndex < length; ++entryIndex) { |
| 319 var start = Math.floor(entryOffsets[entryIndex] * scaleFactor); |
| 320 var finish = Math.floor((entryOffsets[entryIndex] + entryTotalTimes[entr
yIndex]) * scaleFactor); |
| 321 for (var x = start; x <= finish; ++x) |
| 322 drawData[x] = Math.max(drawData[x], entryLevels[entryIndex] + 1); |
| 323 } |
| 324 return drawData; |
| 325 } |
| 326 |
| 327 /** |
| 328 * @param {!WebInspector.FlameChartDataProvider} dataProvider |
| 329 * @param {!WebInspector.FlameChart.TimelineData} timelineData |
| 330 * @param {!Object} context |
| 331 * @param {!number} width |
| 332 * @param {!number} height |
| 333 */ |
| 334 WebInspector.CPUProfileFlameChart.OverviewPane.drawOverviewCanvas = function(dat
aProvider, timelineData, context, width, height) |
| 335 { |
| 336 var drawData = WebInspector.CPUProfileFlameChart.OverviewPane.calculateDrawD
ata(dataProvider, timelineData, width); |
| 337 if (!drawData) |
| 338 return; |
| 339 |
| 340 var ratio = window.devicePixelRatio; |
| 341 var canvasWidth = width * ratio; |
| 342 var canvasHeight = height * ratio; |
| 343 |
| 344 var yScaleFactor = canvasHeight / (dataProvider.maxStackDepth() * 1.1); |
| 345 context.lineWidth = 1; |
| 346 context.translate(0.5, 0.5); |
| 347 context.strokeStyle = "rgba(20,0,0,0.4)"; |
| 348 context.fillStyle = "rgba(214,225,254,0.8)"; |
| 349 context.moveTo(-1, canvasHeight - 1); |
| 350 if (drawData) |
| 351 context.lineTo(-1, Math.round(height - drawData[0] * yScaleFactor - 1)); |
| 352 var value; |
| 353 for (var x = 0; x < width; ++x) { |
| 354 value = Math.round(canvasHeight - drawData[x] * yScaleFactor - 1); |
| 355 context.lineTo(x * ratio, value); |
| 356 } |
| 357 context.lineTo(canvasWidth + 1, value); |
| 358 context.lineTo(canvasWidth + 1, canvasHeight - 1); |
| 359 context.fill(); |
| 360 context.stroke(); |
| 361 context.closePath(); |
| 362 } |
OLD | NEW |