• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * @constructor
7 * @extends {WebInspector.Object}
8 */
9WebInspector.TimelinePowerOverviewDataProvider = function()
10{
11    this._records = [];
12    this._energies = [];
13    this._times = [];
14    WebInspector.powerProfiler.addEventListener(WebInspector.PowerProfiler.EventTypes.PowerEventRecorded, this._onRecordAdded, this);
15}
16
17WebInspector.TimelinePowerOverviewDataProvider.prototype = {
18    dispose: function()
19    {
20        WebInspector.powerProfiler.removeEventListener(WebInspector.PowerProfiler.EventTypes.PowerEventRecorded, this._onRecordAdded, this);
21    },
22
23    /**
24     * @return {!Array.<!PowerAgent.PowerEvent>}
25     */
26    records : function()
27    {
28        // The last record is not used, as its "value" is not set.
29        return this._records.slice(0, this._records.length - 1);
30    },
31
32    /**
33     * @param {number} minTime
34     * @param {number} maxTime
35     * @return {number} energy in joules.
36     */
37    _calculateEnergy : function(minTime, maxTime)
38    {
39        var times = this._times;
40        var energies = this._energies;
41        var last = times.length - 1;
42
43        if (last < 1 || minTime >= times[last] || maxTime <= times[0])
44            return 0;
45
46        // Maximum index of element whose time <= minTime.
47        var start = Number.constrain(times.upperBound(minTime) - 1, 0, last);
48
49        // Minimum index of element whose time >= maxTime.
50        var end = Number.constrain(times.lowerBound(maxTime), 0, last);
51
52        var startTime = minTime < times[0] ? times[0] : minTime;
53        var endTime = maxTime > times[last] ? times[last] : maxTime;
54
55        if (start + 1 === end)
56           return (endTime - startTime) / (times[end] - times[start]) * (energies[end] - energies[start]) / 1000;
57
58        var totalEnergy = 0;
59        totalEnergy += energies[end - 1] - energies[start + 1];
60        totalEnergy += (times[start + 1] - startTime) / (times[start + 1] - times[start]) * (energies[start + 1] - energies[start]);
61        totalEnergy += (endTime - times[end - 1]) / (times[end] - times[end - 1]) * (energies[end] - energies[end - 1]);
62        return totalEnergy / 1000;
63    },
64
65    _onRecordAdded: function(event)
66    {
67        // "value" of original PowerEvent means the average power between previous sampling to current one.
68        // Here, it is converted to average power between current sampling to next one.
69        var record = event.data;
70        var curTime = record.timestamp;
71        var length = this._records.length;
72        var accumulatedEnergy = 0;
73        if (length) {
74            this._records[length - 1].value = record.value;
75
76            var prevTime = this._records[length - 1].timestamp;
77            accumulatedEnergy = this._energies[length - 1];
78            accumulatedEnergy += (curTime - prevTime) * record.value;
79        }
80        this._energies.push(accumulatedEnergy);
81        this._records.push(record);
82        this._times.push(curTime);
83    },
84
85    __proto__: WebInspector.Object.prototype
86}
87
88/**
89 * @constructor
90 * @extends {WebInspector.TimelineOverviewBase}
91 * @param {!WebInspector.TimelineModel} model
92 */
93WebInspector.TimelinePowerOverview = function(model)
94{
95    WebInspector.TimelineOverviewBase.call(this, model);
96    this.element.id = "timeline-overview-power";
97    this._dataProvider = new WebInspector.TimelinePowerOverviewDataProvider();
98
99    this._maxPowerLabel = this.element.createChild("div", "max memory-graph-label");
100    this._minPowerLabel = this.element.createChild("div", "min memory-graph-label");
101}
102
103WebInspector.TimelinePowerOverview.prototype = {
104    dispose: function()
105    {
106        this._dataProvider.dispose();
107    },
108
109    timelineStarted: function()
110    {
111        if (Capabilities.canProfilePower)
112            WebInspector.powerProfiler.startProfile();
113    },
114
115    timelineStopped: function()
116    {
117        if (Capabilities.canProfilePower)
118            WebInspector.powerProfiler.stopProfile();
119    },
120
121    _resetPowerLabels: function()
122    {
123        this._maxPowerLabel.textContent = "";
124        this._minPowerLabel.textContent = "";
125    },
126
127    update: function()
128    {
129        this.resetCanvas();
130
131        var records = this._dataProvider.records();
132        if (!records.length) {
133            this._resetPowerLabels();
134            return;
135        }
136
137        const lowerOffset = 3;
138        var maxPower = 0;
139        var minPower = 100000000000;
140        var minTime = this._model.minimumRecordTime();
141        var maxTime = this._model.maximumRecordTime();
142        for (var i = 0; i < records.length; i++) {
143            var record = records[i];
144            if (record.timestamp < minTime || record.timestamp > maxTime)
145                continue;
146            maxPower = Math.max(maxPower, record.value);
147            minPower = Math.min(minPower, record.value);
148        }
149        minPower = Math.min(minPower, maxPower);
150
151
152        var width = this._canvas.width;
153        var height = this._canvas.height - lowerOffset;
154        var xFactor = width / (maxTime - minTime);
155        var yFactor = height / Math.max(maxPower - minPower, 1);
156
157        var histogram = new Array(width);
158        for (var i = 0; i < records.length - 1; i++) {
159            var record = records[i];
160            if (record.timestamp < minTime || record.timestamp > maxTime)
161                continue;
162            var x = Math.round((record.timestamp - minTime) * xFactor);
163            var y = Math.round((record.value- minPower ) * yFactor);
164            histogram[x] = Math.max(histogram[x] || 0, y);
165        }
166
167        var y = 0;
168        var isFirstPoint = true;
169        var ctx = this._context;
170        ctx.save();
171        ctx.translate(0.5, 0.5);
172        ctx.beginPath();
173        ctx.moveTo(-1, this._canvas.height);
174        for (var x = 0; x < histogram.length; x++) {
175            if (typeof histogram[x] === "undefined")
176                continue;
177            if (isFirstPoint) {
178                isFirstPoint = false;
179                y = histogram[x];
180                ctx.lineTo(-1, height - y);
181            }
182            ctx.lineTo(x, height - y);
183            y = histogram[x];
184            ctx.lineTo(x, height - y);
185        }
186
187        ctx.lineTo(width, height - y);
188        ctx.lineTo(width, this._canvas.height);
189        ctx.lineTo(-1, this._canvas.height);
190        ctx.closePath();
191
192        ctx.fillStyle = "rgba(255,192,0, 0.8);";
193        ctx.fill();
194
195        ctx.lineWidth = 0.5;
196        ctx.strokeStyle = "rgba(20,0,0,0.8)";
197        ctx.stroke();
198        ctx.restore();
199
200        this._maxPowerLabel.textContent = WebInspector.UIString("%.2f\u2009watts", maxPower);
201        this._minPowerLabel.textContent = WebInspector.UIString("%.2f\u2009watts", minPower);
202    },
203
204    /**
205     * @param {number} minTime
206     * @param {number} maxTime
207     * @return {number} energy in joules.
208     */
209    calculateEnergy: function(minTime, maxTime)
210    {
211        return this._dataProvider._calculateEnergy(minTime, maxTime);
212    },
213
214    __proto__: WebInspector.TimelineOverviewBase.prototype
215}
216
217
218