Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / timeline / 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  */
34 WebInspector.TimelineFrameModelBase = function()
35 {
36     this.reset();
37 }
38
39 WebInspector.TimelineFrameModelBase.prototype = {
40     /**
41      * @param {boolean} value
42      */
43     setMergeRecords: function(value)
44     {
45     },
46
47     /**
48      * @return {!Array.<!WebInspector.TimelineFrame>}
49      */
50     frames: function()
51     {
52         return this._frames;
53     },
54
55     /**
56      * @param {number} startTime
57      * @param {number} endTime
58      * @return {!Array.<!WebInspector.TimelineFrame>}
59      */
60     filteredFrames: function(startTime, endTime)
61     {
62         /**
63          * @param {number} value
64          * @param {!WebInspector.TimelineFrame} object
65          * @return {number}
66          */
67         function compareStartTime(value, object)
68         {
69             return value - object.startTime;
70         }
71         /**
72          * @param {number} value
73          * @param {!WebInspector.TimelineFrame} object
74          * @return {number}
75          */
76         function compareEndTime(value, object)
77         {
78             return value - object.endTime;
79         }
80         var frames = this._frames;
81         var firstFrame = insertionIndexForObjectInListSortedByFunction(startTime, frames, compareEndTime);
82         var lastFrame = insertionIndexForObjectInListSortedByFunction(endTime, frames, compareStartTime);
83         return frames.slice(firstFrame, lastFrame);
84     },
85
86     reset: function()
87     {
88         this._minimumRecordTime = Infinity;
89         this._frames = [];
90         this._lastFrame = null;
91         this._lastLayerTree = null;
92         this._hasThreadedCompositing = false;
93         this._mainFrameCommitted = false;
94         this._mainFrameRequested = false;
95         this._framePendingCommit = null;
96     },
97
98     /**
99      * @param {number} startTime
100      */
101     handleBeginFrame: function(startTime)
102     {
103         if (!this._lastFrame)
104             this._startBackgroundFrame(startTime);
105     },
106
107     /**
108      * @param {number} startTime
109      */
110     handleDrawFrame: function(startTime)
111     {
112         if (!this._lastFrame) {
113             this._startBackgroundFrame(startTime);
114             return;
115         }
116
117         // - if it wasn't drawn, it didn't happen!
118         // - only show frames that either did not wait for the main thread frame or had one committed.
119         if (this._mainFrameCommitted || !this._mainFrameRequested)
120             this._startBackgroundFrame(startTime);
121         this._mainFrameCommitted = false;
122     },
123
124     handleActivateLayerTree: function()
125     {
126         if (!this._lastFrame)
127             return;
128         this._mainFrameRequested = false;
129         this._mainFrameCommitted = true;
130         if (this._framePendingActivation) {
131             this._lastFrame._addTimeForCategories(this._framePendingActivation.timeByCategory);
132             this._lastFrame.paints = this._framePendingActivation.paints;
133             this._framePendingActivation = null;
134         }
135     },
136
137     handleRequestMainThreadFrame: function()
138     {
139         if (!this._lastFrame)
140             return;
141         this._mainFrameRequested = true;
142     },
143
144     handleCompositeLayers: function()
145     {
146         if (!this._hasThreadedCompositing || !this._framePendingCommit)
147             return;
148         this._framePendingActivation = this._framePendingCommit;
149         this._framePendingCommit = null;
150     },
151
152     /**
153      * @param {!WebInspector.DeferredLayerTree} layerTree
154      */
155     handleLayerTreeSnapshot: function(layerTree)
156     {
157         this._lastLayerTree = layerTree;
158     },
159
160     /**
161      * @param {number} startTime
162      */
163     _startBackgroundFrame: function(startTime)
164     {
165         if (!this._hasThreadedCompositing) {
166             this._lastFrame = null;
167             this._hasThreadedCompositing = true;
168         }
169         if (this._lastFrame)
170             this._flushFrame(this._lastFrame, startTime);
171
172         this._lastFrame = new WebInspector.TimelineFrame(startTime, startTime - this._minimumRecordTime);
173     },
174
175     /**
176      * @param {number} startTime
177      */
178     _startMainThreadFrame: function(startTime)
179     {
180         if (this._lastFrame)
181             this._flushFrame(this._lastFrame, startTime);
182         this._lastFrame = new WebInspector.TimelineFrame(startTime, startTime - this._minimumRecordTime);
183     },
184
185     /**
186      * @param {!WebInspector.TimelineFrame} frame
187      * @param {number} endTime
188      */
189     _flushFrame: function(frame, endTime)
190     {
191         frame._setLayerTree(this._lastLayerTree);
192         frame._setEndTime(endTime);
193         this._frames.push(frame);
194     },
195
196     /**
197      * @param {!Array.<string>} types
198      * @param {!WebInspector.TimelineModel.Record} record
199      * @return {?WebInspector.TimelineModel.Record} record
200      */
201     _findRecordRecursively: function(types, record)
202     {
203         if (types.indexOf(record.type()) >= 0)
204             return record;
205         if (!record.children())
206             return null;
207         for (var i = 0; i < record.children().length; ++i) {
208             var result = this._findRecordRecursively(types, record.children()[i]);
209             if (result)
210                 return result;
211         }
212         return null;
213     }
214 }
215
216 /**
217  * @constructor
218  * @extends {WebInspector.TimelineFrameModelBase}
219  */
220 WebInspector.TimelineFrameModel = function()
221 {
222     WebInspector.TimelineFrameModelBase.call(this);
223 }
224
225 WebInspector.TimelineFrameModel._mainFrameMarkers = [
226     WebInspector.TimelineModel.RecordType.ScheduleStyleRecalculation,
227     WebInspector.TimelineModel.RecordType.InvalidateLayout,
228     WebInspector.TimelineModel.RecordType.BeginFrame,
229     WebInspector.TimelineModel.RecordType.ScrollLayer
230 ];
231
232 WebInspector.TimelineFrameModel.prototype = {
233     reset: function()
234     {
235         this._mergeRecords = true;
236         this._mergingBuffer = new WebInspector.TimelineMergingRecordBuffer();
237         WebInspector.TimelineFrameModelBase.prototype.reset.call(this);
238     },
239
240     /**
241      * @param {boolean} value
242      */
243     setMergeRecords: function(value)
244     {
245         this._mergeRecords = value;
246     },
247
248     /**
249      * @param {!Array.<!WebInspector.TimelineModel.Record>} records
250      */
251     addRecords: function(records)
252     {
253         if (!records.length)
254             return;
255         if (records[0].startTime() < this._minimumRecordTime)
256             this._minimumRecordTime = records[0].startTime();
257         for (var i = 0; i < records.length; ++i)
258             this.addRecord(records[i]);
259     },
260
261     /**
262      * @param {!WebInspector.TimelineModel.Record} record
263      */
264     addRecord: function(record)
265     {
266         var recordTypes = WebInspector.TimelineModel.RecordType;
267         var programRecord = record.type() === recordTypes.Program ? record : null;
268
269         // Start collecting main frame
270         if (programRecord) {
271             if (!this._framePendingCommit && this._findRecordRecursively(WebInspector.TimelineFrameModel._mainFrameMarkers, programRecord))
272                 this._framePendingCommit = new WebInspector.PendingFrame();
273         }
274         /** type {Array.<!WebInspector.TimelineModel.Record>} */
275         var records = [];
276         if (!this._mergeRecords)
277             records = [record];
278         else
279             records = this._mergingBuffer.process(record.thread(), /** type {Array.<!WebInspector.TimelineModel.Record>} */(programRecord ? record.children() || [] : [record]));
280         for (var i = 0; i < records.length; ++i) {
281             if (records[i].thread() === WebInspector.TimelineModel.MainThreadName)
282                 this._addMainThreadRecord(programRecord, records[i]);
283             else
284                 this._addBackgroundRecord(records[i]);
285         }
286     },
287
288     /**
289      * @param {!WebInspector.TimelineModel.Record} record
290      */
291     _addBackgroundRecord: function(record)
292     {
293         var recordTypes = WebInspector.TimelineModel.RecordType;
294         if (record.type() === recordTypes.BeginFrame)
295             this.handleBeginFrame(record.startTime());
296         else if (record.type() === recordTypes.DrawFrame)
297             this.handleDrawFrame(record.startTime());
298         else if (record.type() === recordTypes.RequestMainThreadFrame)
299             this.handleRequestMainThreadFrame();
300         else if (record.type() === recordTypes.ActivateLayerTree)
301             this.handleActivateLayerTree();
302
303         if (this._lastFrame)
304             this._lastFrame._addTimeFromRecord(record);
305     },
306
307     /**
308      * @param {?WebInspector.TimelineModel.Record} programRecord
309      * @param {!WebInspector.TimelineModel.Record} record
310      */
311     _addMainThreadRecord: function(programRecord, record)
312     {
313         var recordTypes = WebInspector.TimelineModel.RecordType;
314         if (record.type() === recordTypes.UpdateLayerTree && record.data()["layerTree"])
315             this.handleLayerTreeSnapshot(new WebInspector.DeferredAgentLayerTree(record.target(), record.data()["layerTree"]));
316         if (!this._hasThreadedCompositing) {
317             if (record.type() === recordTypes.BeginFrame)
318                 this._startMainThreadFrame(record.startTime());
319
320             if (!this._lastFrame)
321                 return;
322
323             this._lastFrame._addTimeFromRecord(record);
324
325             // Account for "other" time at the same time as the first child.
326             if (programRecord.children()[0] === record)
327                 this._lastFrame._addTimeForCategory("other", this._deriveOtherTime(programRecord));
328             return;
329         }
330
331         if (!this._framePendingCommit)
332             return;
333
334         WebInspector.TimelineUIUtilsImpl.aggregateTimeForRecord(this._framePendingCommit.timeByCategory, record);
335         if (programRecord.children()[0] === record)
336             this._framePendingCommit.timeByCategory["other"] = (this._framePendingCommit.timeByCategory["other"] || 0) + this._deriveOtherTime(programRecord);
337
338         if (record.type() === recordTypes.CompositeLayers)
339             this.handleCompositeLayers();
340     },
341
342     /**
343      * @param {!WebInspector.TimelineModel.Record} programRecord
344      * @return {number}
345      */
346     _deriveOtherTime: function(programRecord)
347     {
348         var accounted = 0;
349         for (var i = 0; i < programRecord.children().length; ++i)
350             accounted += programRecord.children()[i].endTime() - programRecord.children()[i].startTime();
351         return programRecord.endTime() - programRecord.startTime() - accounted;
352     },
353
354     __proto__: WebInspector.TimelineFrameModelBase.prototype,
355 };
356
357 /**
358  * @constructor
359  * @extends {WebInspector.TimelineFrameModelBase}
360  */
361 WebInspector.TracingTimelineFrameModel = function()
362 {
363     WebInspector.TimelineFrameModelBase.call(this);
364 }
365
366 WebInspector.TracingTimelineFrameModel._mainFrameMarkers = [
367     WebInspector.TracingTimelineModel.RecordType.ScheduleStyleRecalculation,
368     WebInspector.TracingTimelineModel.RecordType.InvalidateLayout,
369     WebInspector.TracingTimelineModel.RecordType.BeginMainThreadFrame,
370     WebInspector.TracingTimelineModel.RecordType.ScrollLayer
371 ];
372
373 WebInspector.TracingTimelineFrameModel.prototype = {
374     /**
375      * @param {!Array.<!WebInspector.TracingModel.Event>} events
376      * @param {string} sessionId
377      */
378     addTraceEvents: function(events, sessionId)
379     {
380         this._sessionId = sessionId;
381         if (!events.length)
382             return;
383         if (events[0].startTime < this._minimumRecordTime)
384             this._minimumRecordTime = events[0].startTime;
385         for (var i = 0; i < events.length; ++i)
386             this._addTraceEvent(events[i]);
387     },
388
389     /**
390      * @param {!WebInspector.TracingModel.Event} event
391      */
392     _addTraceEvent: function(event)
393     {
394         var eventNames = WebInspector.TracingTimelineModel.RecordType;
395
396         if (event.name === eventNames.SetLayerTreeId) {
397             if (this._sessionId === event.args["sessionId"])
398                 this._layerTreeId = event.args["layerTreeId"];
399             return;
400         }
401         if (event.name === eventNames.TracingStartedInPage) {
402             this._mainThread = event.thread;
403             return;
404         }
405         if (event.thread === this._mainThread)
406             this._addMainThreadTraceEvent(event);
407         else
408             this._addBackgroundTraceEvent(event);
409     },
410
411     /**
412      * @param {!WebInspector.TracingModel.Event} event
413      */
414     _addBackgroundTraceEvent: function(event)
415     {
416         var eventNames = WebInspector.TracingTimelineModel.RecordType;
417         if (event.phase === WebInspector.TracingModel.Phase.SnapshotObject && event.name === eventNames.LayerTreeHostImplSnapshot && parseInt(event.id, 0) === this._layerTreeId) {
418             var snapshot = /** @type {!WebInspector.TracingModel.ObjectSnapshot} */ (event);
419             this.handleLayerTreeSnapshot(new WebInspector.DeferredTracingLayerTree(snapshot));
420             return;
421         }
422         if (this._lastFrame && event.selfTime)
423             this._lastFrame._addTimeForCategory(WebInspector.TracingTimelineUIUtils.eventStyle(event).category.name, event.selfTime);
424
425         if (event.args["layerTreeId"] !== this._layerTreeId)
426             return;
427
428         var timestamp = event.startTime;
429         if (event.name === eventNames.BeginFrame)
430             this.handleBeginFrame(timestamp);
431         else if (event.name === eventNames.DrawFrame)
432             this.handleDrawFrame(timestamp);
433         else if (event.name === eventNames.ActivateLayerTree)
434             this.handleActivateLayerTree();
435         else if (event.name === eventNames.RequestMainThreadFrame)
436             this.handleRequestMainThreadFrame();
437     },
438
439     /**
440      * @param {!WebInspector.TracingModel.Event} event
441      */
442     _addMainThreadTraceEvent: function(event)
443     {
444         var eventNames = WebInspector.TracingTimelineModel.RecordType;
445         var timestamp = event.startTime;
446         var selfTime = event.selfTime || 0;
447
448         if (!this._hasThreadedCompositing) {
449             if (event.name === eventNames.BeginMainThreadFrame)
450                 this._startMainThreadFrame(timestamp);
451             if (!this._lastFrame)
452                 return;
453             if (!selfTime)
454                 return;
455
456             var categoryName = WebInspector.TracingTimelineUIUtils.eventStyle(event).category.name;
457             this._lastFrame._addTimeForCategory(categoryName, selfTime);
458             return;
459         }
460
461         if (!this._framePendingCommit && WebInspector.TracingTimelineFrameModel._mainFrameMarkers.indexOf(event.name) >= 0)
462             this._framePendingCommit = new WebInspector.PendingFrame();
463         if (!this._framePendingCommit)
464             return;
465         if (event.name === eventNames.Paint && event.args["data"]["layerId"] && event.picture)
466             this._framePendingCommit.paints.push(new WebInspector.LayerPaintEvent(event));
467
468         if (selfTime) {
469             var categoryName = WebInspector.TracingTimelineUIUtils.eventStyle(event).category.name;
470             this._framePendingCommit.timeByCategory[categoryName] = (this._framePendingCommit.timeByCategory[categoryName] || 0) + selfTime;
471         }
472         if (event.name === eventNames.CompositeLayers && event.args["layerTreeId"] === this._layerTreeId)
473             this.handleCompositeLayers();
474     },
475
476     __proto__: WebInspector.TimelineFrameModelBase.prototype
477 }
478
479 /**
480  * @constructor
481  * @extends {WebInspector.DeferredLayerTree}
482  * @param {!WebInspector.TracingModel.ObjectSnapshot} snapshot
483  */
484 WebInspector.DeferredTracingLayerTree = function(snapshot)
485 {
486     WebInspector.DeferredLayerTree.call(this, snapshot.thread.target());
487     this._snapshot = snapshot;
488 }
489
490 WebInspector.DeferredTracingLayerTree.prototype = {
491     /**
492      * @param {function(!WebInspector.LayerTreeBase)} callback
493      */
494     resolve: function(callback)
495     {
496         this._snapshot.requestObject(onGotObject.bind(this));
497         /**
498          * @this {WebInspector.DeferredTracingLayerTree}
499          * @param {?Object} result
500          */
501         function onGotObject(result)
502         {
503             if (!result)
504                 return;
505             var viewport = result["device_viewport_size"];
506             var rootLayer = result["active_tree"]["root_layer"];
507             var layerTree = new WebInspector.TracingLayerTree(this._target);
508             layerTree.setViewportSize(viewport);
509             layerTree.setLayers(rootLayer, callback.bind(null, layerTree));
510         }
511     },
512
513     __proto__: WebInspector.DeferredLayerTree.prototype
514 };
515
516
517 /**
518  * @constructor
519  * @param {!Array.<!WebInspector.TimelineFrame>} frames
520  */
521 WebInspector.FrameStatistics = function(frames)
522 {
523     this.frameCount = frames.length;
524     this.minDuration = Infinity;
525     this.maxDuration = 0;
526     this.timeByCategory = {};
527     this.startOffset = frames[0].startTimeOffset;
528     var lastFrame = frames[this.frameCount - 1];
529     this.endOffset = lastFrame.startTimeOffset + lastFrame.duration;
530
531     var totalDuration = 0;
532     var sumOfSquares = 0;
533     for (var i = 0; i < this.frameCount; ++i) {
534         var duration = frames[i].duration;
535         totalDuration += duration;
536         sumOfSquares += duration * duration;
537         this.minDuration = Math.min(this.minDuration, duration);
538         this.maxDuration = Math.max(this.maxDuration, duration);
539         WebInspector.FrameStatistics._aggregateTimeByCategory(this.timeByCategory, frames[i].timeByCategory);
540     }
541     this.average = totalDuration / this.frameCount;
542     var variance = sumOfSquares / this.frameCount - this.average * this.average;
543     this.stddev = Math.sqrt(variance);
544 }
545
546 /**
547  * @param {!Object} total
548  * @param {!Object} addend
549  */
550 WebInspector.FrameStatistics._aggregateTimeByCategory = function(total, addend)
551 {
552     for (var category in addend)
553         total[category] = (total[category] || 0) + addend[category];
554 }
555
556 /**
557  * @constructor
558  * @param {number} startTime
559  * @param {number} startTimeOffset
560  */
561 WebInspector.TimelineFrame = function(startTime, startTimeOffset)
562 {
563     this.startTime = startTime;
564     this.startTimeOffset = startTimeOffset;
565     this.endTime = this.startTime;
566     this.duration = 0;
567     this.timeByCategory = {};
568     this.cpuTime = 0;
569     /** @type {?WebInspector.DeferredLayerTree} */
570     this.layerTree = null;
571     this.paintTiles = null;
572 }
573
574 WebInspector.TimelineFrame.prototype = {
575     /**
576      * @param {number} endTime
577      */
578     _setEndTime: function(endTime)
579     {
580         this.endTime = endTime;
581         this.duration = this.endTime - this.startTime;
582     },
583
584     /**
585      * @param {?WebInspector.DeferredLayerTree} layerTree
586      */
587     _setLayerTree: function(layerTree)
588     {
589         this.layerTree = layerTree;
590     },
591
592     /**
593      * @param {!WebInspector.TimelineModel.Record} record
594      */
595     _addTimeFromRecord: function(record)
596     {
597         if (!record.endTime())
598             return;
599         var timeByCategory = {};
600         WebInspector.TimelineUIUtilsImpl.aggregateTimeForRecord(timeByCategory, record);
601         this._addTimeForCategories(timeByCategory);
602     },
603
604     /**
605      * @param {!Object} timeByCategory
606      */
607     _addTimeForCategories: function(timeByCategory)
608     {
609         for (var category in timeByCategory)
610             this._addTimeForCategory(category, timeByCategory[category]);
611     },
612
613     /**
614      * @param {string} category
615      * @param {number} time
616      */
617     _addTimeForCategory: function(category, time)
618     {
619         this.timeByCategory[category] = (this.timeByCategory[category] || 0) + time;
620         this.cpuTime += time;
621     },
622 }
623
624 /**
625  * @constructor
626  * @param {!WebInspector.TracingModel.Event} event
627  */
628 WebInspector.LayerPaintEvent = function(event)
629 {
630     this._event = event;
631 }
632
633 WebInspector.LayerPaintEvent.prototype = {
634     /**
635      * @return {string}
636      */
637     layerId: function()
638     {
639         return this._event.args["data"]["layerId"];
640     },
641
642     /**
643      * @return {!WebInspector.TracingModel.Event}
644      */
645     event: function()
646     {
647         return this._event;
648     },
649
650     /**
651      * @param {function(?Array.<number>, ?WebInspector.PaintProfilerSnapshot)} callback
652      */
653     loadPicture: function(callback)
654     {
655         var target = this._event.thread.target();
656         this._event.picture.requestObject(onGotObject);
657         /**
658          * @param {?Object} result
659          */
660         function onGotObject(result)
661         {
662             if (!result || !result["skp64"] || !target) {
663                 callback(null, null);
664                 return;
665             }
666             var rect = result["params"] && result["params"]["layer_rect"];
667             WebInspector.PaintProfilerSnapshot.load(target, result["skp64"], callback.bind(null, rect));
668         }
669     }
670 };
671
672 /**
673  * @constructor
674  */
675 WebInspector.PendingFrame = function()
676 {
677     /** @type {!Object.<string, number>} */
678     this.timeByCategory = {};
679     /** @type {!Array.<!WebInspector.LayerPaintEvent>} */
680     this.paints = [];
681 }