| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.Object} |
| */ |
| WebInspector.TimelinePowerOverviewDataProvider = function() |
| { |
| this._records = []; |
| this._energies = []; |
| this._times = []; |
| WebInspector.powerProfiler.addEventListener(WebInspector.PowerProfiler.EventTypes.PowerEventRecorded, this._onRecordAdded, this); |
| } |
| |
| WebInspector.TimelinePowerOverviewDataProvider.prototype = { |
| dispose: function() |
| { |
| WebInspector.powerProfiler.removeEventListener(WebInspector.PowerProfiler.EventTypes.PowerEventRecorded, this._onRecordAdded, this); |
| }, |
| |
| /** |
| * @return {!Array.<!PowerAgent.PowerEvent>} |
| */ |
| records : function() |
| { |
| // The last record is not used, as its "value" is not set. |
| return this._records.slice(0, this._records.length - 1); |
| }, |
| |
| /** |
| * @param {number} minTime |
| * @param {number} maxTime |
| * @return {number} energy in joules. |
| */ |
| _calculateEnergy : function(minTime, maxTime) |
| { |
| var times = this._times; |
| var energies = this._energies; |
| var last = times.length - 1; |
| |
| if (last < 1 || minTime >= times[last] || maxTime <= times[0]) |
| return 0; |
| |
| // Maximum index of element whose time <= minTime. |
| var start = Number.constrain(times.upperBound(minTime) - 1, 0, last); |
| |
| // Minimum index of element whose time >= maxTime. |
| var end = Number.constrain(times.lowerBound(maxTime), 0, last); |
| |
| var startTime = minTime < times[0] ? times[0] : minTime; |
| var endTime = maxTime > times[last] ? times[last] : maxTime; |
| |
| if (start + 1 === end) |
| return (endTime - startTime) / (times[end] - times[start]) * (energies[end] - energies[start]) / 1000; |
| |
| var totalEnergy = 0; |
| totalEnergy += energies[end - 1] - energies[start + 1]; |
| totalEnergy += (times[start + 1] - startTime) / (times[start + 1] - times[start]) * (energies[start + 1] - energies[start]); |
| totalEnergy += (endTime - times[end - 1]) / (times[end] - times[end - 1]) * (energies[end] - energies[end - 1]); |
| return totalEnergy / 1000; |
| }, |
| |
| _onRecordAdded: function(event) |
| { |
| // "value" of original PowerEvent means the average power between previous sampling to current one. |
| // Here, it is converted to average power between current sampling to next one. |
| var record = event.data; |
| var curTime = record.timestamp; |
| var length = this._records.length; |
| var accumulatedEnergy = 0; |
| if (length) { |
| this._records[length - 1].value = record.value; |
| |
| var prevTime = this._records[length - 1].timestamp; |
| accumulatedEnergy = this._energies[length - 1]; |
| accumulatedEnergy += (curTime - prevTime) * record.value; |
| } |
| this._energies.push(accumulatedEnergy); |
| this._records.push(record); |
| this._times.push(curTime); |
| }, |
| |
| __proto__: WebInspector.Object.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.TimelineOverviewBase} |
| * @param {!WebInspector.TimelineModel} model |
| */ |
| WebInspector.TimelinePowerOverview = function(model) |
| { |
| WebInspector.TimelineOverviewBase.call(this, model); |
| this.element.id = "timeline-overview-power"; |
| this._dataProvider = new WebInspector.TimelinePowerOverviewDataProvider(); |
| |
| this._maxPowerLabel = this.element.createChild("div", "max memory-graph-label"); |
| this._minPowerLabel = this.element.createChild("div", "min memory-graph-label"); |
| } |
| |
| WebInspector.TimelinePowerOverview.prototype = { |
| dispose: function() |
| { |
| this._dataProvider.dispose(); |
| }, |
| |
| timelineStarted: function() |
| { |
| if (WebInspector.targetManager.mainTarget().hasCapability(WebInspector.Target.Capabilities.CanProfilePower)) |
| WebInspector.powerProfiler.startProfile(); |
| }, |
| |
| timelineStopped: function() |
| { |
| if (WebInspector.targetManager.mainTarget().hasCapability(WebInspector.Target.Capabilities.CanProfilePower)) |
| WebInspector.powerProfiler.stopProfile(); |
| }, |
| |
| _resetPowerLabels: function() |
| { |
| this._maxPowerLabel.textContent = ""; |
| this._minPowerLabel.textContent = ""; |
| }, |
| |
| update: function() |
| { |
| this.resetCanvas(); |
| |
| var records = this._dataProvider.records(); |
| if (!records.length) { |
| this._resetPowerLabels(); |
| return; |
| } |
| |
| const lowerOffset = 3; |
| var maxPower = 0; |
| var minPower = 100000000000; |
| var minTime = this._model.minimumRecordTime(); |
| var maxTime = this._model.maximumRecordTime(); |
| for (var i = 0; i < records.length; i++) { |
| var record = records[i]; |
| if (record.timestamp < minTime || record.timestamp > maxTime) |
| continue; |
| maxPower = Math.max(maxPower, record.value); |
| minPower = Math.min(minPower, record.value); |
| } |
| minPower = Math.min(minPower, maxPower); |
| |
| |
| var width = this._canvas.width; |
| var height = this._canvas.height - lowerOffset; |
| var xFactor = width / (maxTime - minTime); |
| var yFactor = height / Math.max(maxPower - minPower, 1); |
| |
| var histogram = new Array(width); |
| for (var i = 0; i < records.length - 1; i++) { |
| var record = records[i]; |
| if (record.timestamp < minTime || record.timestamp > maxTime) |
| continue; |
| var x = Math.round((record.timestamp - minTime) * xFactor); |
| var y = Math.round((record.value- minPower ) * yFactor); |
| histogram[x] = Math.max(histogram[x] || 0, y); |
| } |
| |
| var y = 0; |
| var isFirstPoint = true; |
| var ctx = this._context; |
| ctx.save(); |
| ctx.translate(0.5, 0.5); |
| ctx.beginPath(); |
| ctx.moveTo(-1, this._canvas.height); |
| for (var x = 0; x < histogram.length; x++) { |
| if (typeof histogram[x] === "undefined") |
| continue; |
| if (isFirstPoint) { |
| isFirstPoint = false; |
| y = histogram[x]; |
| ctx.lineTo(-1, height - y); |
| } |
| ctx.lineTo(x, height - y); |
| y = histogram[x]; |
| ctx.lineTo(x, height - y); |
| } |
| |
| ctx.lineTo(width, height - y); |
| ctx.lineTo(width, this._canvas.height); |
| ctx.lineTo(-1, this._canvas.height); |
| ctx.closePath(); |
| |
| ctx.fillStyle = "rgba(255,192,0, 0.8);"; |
| ctx.fill(); |
| |
| ctx.lineWidth = 0.5; |
| ctx.strokeStyle = "rgba(20,0,0,0.8)"; |
| ctx.stroke(); |
| ctx.restore(); |
| |
| this._maxPowerLabel.textContent = WebInspector.UIString("%.2f\u2009watts", maxPower); |
| this._minPowerLabel.textContent = WebInspector.UIString("%.2f\u2009watts", minPower); |
| }, |
| |
| /** |
| * @param {number} minTime |
| * @param {number} maxTime |
| * @return {number} energy in joules. |
| */ |
| calculateEnergy: function(minTime, maxTime) |
| { |
| return this._dataProvider._calculateEnergy(minTime, maxTime); |
| }, |
| |
| __proto__: WebInspector.TimelineOverviewBase.prototype |
| } |
| |
| |