Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / TimelineFrameModel.js
1 /*
2  * Copyright (C) 2013 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.Object}
34  * @param {!WebInspector.TimelineModel} model
35  */
36 WebInspector.TimelineFrameModel = function(model)
37 {
38     this._model = model;
39     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onRecordAdded, this);
40
41     this.reset();
42     var records = model.records;
43     for (var i = 0; i < records.length; ++i)
44         this._addRecord(records[i]);
45 }
46
47 WebInspector.TimelineFrameModel.Events = {
48     FrameAdded: "FrameAdded"
49 }
50
51 WebInspector.TimelineFrameModel.prototype = {
52     /**
53      * @return {!Array.<!WebInspector.TimelineFrame>}
54      */
55     frames: function()
56     {
57         return this._frames;
58     },
59
60     reset: function()
61     {
62         this._frames = [];
63         this._lastFrame = null;
64         this._hasThreadedCompositing = false;
65         this._mainFrameCommitted = false;
66         this._mainFrameRequested = false;
67         this._aggregatedMainThreadWork = null;
68         this._mergingBuffer = new WebInspector.TimelineMergingRecordBuffer();
69     },
70
71     _onRecordAdded: function(event)
72     {
73         var record = /** @type {!TimelineAgent.TimelineEvent} */(event.data);
74         this._addRecord(record);
75     },
76
77     /**
78      * @param {!TimelineAgent.TimelineEvent} record
79      */
80     _addRecord: function(record)
81     {
82         var recordTypes = WebInspector.TimelineModel.RecordType;
83         var programRecord = record.type === recordTypes.Program ? record : null;
84
85         // Start collecting main frame
86         if (programRecord) {
87             if (!this._aggregatedMainThreadWork && this._findRecordRecursively([recordTypes.ScheduleStyleRecalculation, recordTypes.InvalidateLayout, recordTypes.BeginFrame], programRecord))
88                 this._aggregatedMainThreadWork = {};
89         }
90
91         var records = this._mergingBuffer.process(record.thread, programRecord ? record.children || [] : [record]);
92         for (var i = 0; i < records.length; ++i) {
93             if (records[i].thread)
94                 this._addBackgroundRecord(records[i]);
95             else
96                 this._addMainThreadRecord(programRecord, records[i]);
97         }
98     },
99
100     /**
101      * @param {!TimelineAgent.TimelineEvent} record
102      */
103     _addBackgroundRecord: function(record)
104     {
105         var recordTypes = WebInspector.TimelineModel.RecordType;
106         if (!this._lastFrame) {
107             if (record.type === recordTypes.BeginFrame || record.type === recordTypes.DrawFrame)
108                 this._startBackgroundFrame(record);
109             return;
110         }
111
112         if (record.type === recordTypes.DrawFrame) {
113             // - if it wasn't drawn, it didn't happen!
114             // - only show frames that either did not wait for the main thread frame or had one committed.
115             if (this._mainFrameCommitted || !this._mainFrameRequested)
116                 this._startBackgroundFrame(record);
117             this._mainFrameCommitted = false;
118         } else if (record.type === recordTypes.RequestMainThreadFrame) {
119             this._mainFrameRequested = true;
120         } else if (record.type === recordTypes.ActivateLayerTree) {
121             this._mainFrameRequested = false;
122             this._mainFrameCommitted = true;
123             this._lastFrame._addTimeForCategories(this._aggregatedMainThreadWorkToAttachToBackgroundFrame);
124             this._aggregatedMainThreadWorkToAttachToBackgroundFrame = {};
125         }
126         this._lastFrame._addTimeFromRecord(record);
127     },
128
129     /**
130      * @param {?TimelineAgent.TimelineEvent} programRecord
131      * @param {!TimelineAgent.TimelineEvent} record
132      */
133     _addMainThreadRecord: function(programRecord, record)
134     {
135         var recordTypes = WebInspector.TimelineModel.RecordType;
136         if (!this._hasThreadedCompositing) {
137             if (record.type === recordTypes.BeginFrame)
138                 this._startMainThreadFrame(record);
139
140             if (!this._lastFrame)
141                 return;
142
143             this._lastFrame._addTimeFromRecord(record);
144
145             // Account for "other" time at the same time as the first child.
146             if (programRecord.children[0] === record) {
147                 this._deriveOtherTime(programRecord, this._lastFrame.timeByCategory);
148                 this._lastFrame._updateCpuTime();
149             }
150             return;
151         }
152
153         if (!this._aggregatedMainThreadWork)
154             return;
155
156         WebInspector.TimelineModel.aggregateTimeForRecord(this._aggregatedMainThreadWork, record);
157         if (programRecord.children[0] === record)
158             this._deriveOtherTime(programRecord, this._aggregatedMainThreadWork);
159
160         if (record.type === recordTypes.CompositeLayers) {
161             this._aggregatedMainThreadWorkToAttachToBackgroundFrame = this._aggregatedMainThreadWork;
162             this._aggregatedMainThreadWork = null;
163         }
164     },
165
166     /**
167      * @param {!TimelineAgent.TimelineEvent} programRecord
168      * @param {!Object} timeByCategory
169      */
170     _deriveOtherTime: function(programRecord, timeByCategory)
171     {
172         var accounted = 0;
173         for (var i = 0; i < programRecord.children.length; ++i)
174             accounted += WebInspector.TimelineModel.durationInSeconds(programRecord.children[i]);
175         var otherTime = WebInspector.TimelineModel.durationInSeconds(programRecord) - accounted;
176         timeByCategory["other"] = (timeByCategory["other"] || 0) + otherTime;
177     },
178
179     /**
180      * @param {!TimelineAgent.TimelineEvent} record
181      */
182     _startBackgroundFrame: function(record)
183     {
184         if (!this._hasThreadedCompositing) {
185             this._lastFrame = null;
186             this._hasThreadedCompositing = true;
187         }
188         if (this._lastFrame)
189             this._flushFrame(this._lastFrame, record);
190
191         this._lastFrame = new WebInspector.TimelineFrame(this, record);
192     },
193
194     /**
195      * @param {!TimelineAgent.TimelineEvent} record
196      */
197     _startMainThreadFrame: function(record)
198     {
199         if (this._lastFrame)
200             this._flushFrame(this._lastFrame, record);
201         this._lastFrame = new WebInspector.TimelineFrame(this, record);
202     },
203
204     /**
205      * @param {!WebInspector.TimelineFrame} frame
206      * @param {!Object} record
207      */
208     _flushFrame: function(frame, record)
209     {
210         frame._setEndTime(WebInspector.TimelineModel.startTimeInSeconds(record));
211         this._frames.push(frame);
212         this.dispatchEventToListeners(WebInspector.TimelineFrameModel.Events.FrameAdded, frame);
213     },
214
215     /**
216      * @param {!Array.<string>} types
217      * @param {!TimelineAgent.TimelineEvent} record
218      * @return {?TimelineAgent.TimelineEvent} record
219      */
220     _findRecordRecursively: function(types, record)
221     {
222         if (types.indexOf(record.type) >= 0)
223             return record;
224         if (!record.children)
225             return null;
226         for (var i = 0; i < record.children.length; ++i) {
227             var result = this._findRecordRecursively(types, record.children[i]);
228             if (result)
229                 return result;
230         }
231         return null;
232     },
233
234     dispose: function()
235     {
236         this._model.removeEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onRecordAdded, this);
237     },
238
239     __proto__: WebInspector.Object.prototype
240 }
241
242 /**
243  * @constructor
244  * @param {!Array.<!WebInspector.TimelineFrame>} frames
245  */
246 WebInspector.FrameStatistics = function(frames)
247 {
248     this.frameCount = frames.length;
249     this.minDuration = Infinity;
250     this.maxDuration = 0;
251     this.timeByCategory = {};
252     this.startOffset = frames[0].startTimeOffset;
253     var lastFrame = frames[this.frameCount - 1];
254     this.endOffset = lastFrame.startTimeOffset + lastFrame.duration;
255
256     var totalDuration = 0;
257     var sumOfSquares = 0;
258     for (var i = 0; i < this.frameCount; ++i) {
259         var duration = frames[i].duration;
260         totalDuration += duration;
261         sumOfSquares += duration * duration;
262         this.minDuration = Math.min(this.minDuration, duration);
263         this.maxDuration = Math.max(this.maxDuration, duration);
264         WebInspector.TimelineModel.aggregateTimeByCategory(this.timeByCategory, frames[i].timeByCategory);
265     }
266     this.average = totalDuration / this.frameCount;
267     var variance = sumOfSquares / this.frameCount - this.average * this.average;
268     this.stddev = Math.sqrt(variance);
269 }
270
271 /**
272  * @constructor
273  * @param {!WebInspector.TimelineFrameModel} model
274  * @param {!Object} record
275  */
276 WebInspector.TimelineFrame = function(model, record)
277 {
278     this.startTime = WebInspector.TimelineModel.startTimeInSeconds(record);
279     this.startTimeOffset = model._model.recordOffsetInSeconds(record);
280     this.endTime = this.startTime;
281     this.duration = 0;
282     this.timeByCategory = {};
283     this.cpuTime = 0;
284 }
285
286 WebInspector.TimelineFrame.prototype = {
287     /**
288      * @param {number} endTime
289      */
290     _setEndTime: function(endTime)
291     {
292         this.endTime = endTime;
293         this.duration = this.endTime - this.startTime;
294     },
295
296     /**
297      * @param {!TimelineAgent.TimelineEvent} record
298      */
299     _addTimeFromRecord: function(record)
300     {
301         if (!record.endTime)
302             return;
303         WebInspector.TimelineModel.aggregateTimeForRecord(this.timeByCategory, record);
304         this._updateCpuTime();
305     },
306
307     /**
308      * @param {!Object} timeByCategory
309      */
310     _addTimeForCategories: function(timeByCategory)
311     {
312         WebInspector.TimelineModel.aggregateTimeByCategory(this.timeByCategory, timeByCategory);
313         this._updateCpuTime();
314     },
315
316     _updateCpuTime: function()
317     {
318         this.cpuTime = 0;
319         for (var key in this.timeByCategory)
320             this.cpuTime += this.timeByCategory[key];
321     }
322 }