Home | History | Annotate | Download | only in front_end
      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  * @param {!WebInspector.TimelineModel} model
     34  * @param {!WebInspector.TimelineFrameOverview} frameOverview
     35  * @param {!WebInspector.TimelinePresentationModel} presentationModel
     36  */
     37 WebInspector.TimelineFrameController = function(model, frameOverview, presentationModel)
     38 {
     39     this._lastMainThreadFrame = null;
     40     this._lastBackgroundFrame = null;
     41     this._model = model;
     42     this._frameOverview = frameOverview;
     43     this._presentationModel = presentationModel;
     44     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onRecordAdded, this);
     45     this._model.addEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._onRecordsCleared, this);
     46 
     47     this._frameOverview.reset();
     48     var records = model.records;
     49     for (var i = 0; i < records.length; ++i)
     50         this._addRecord(records[i]);
     51     this._frameOverview.update();
     52 }
     53 
     54 WebInspector.TimelineFrameController.prototype = {
     55     _onRecordAdded: function(event)
     56     {
     57         this._addRecord(event.data);
     58     },
     59 
     60     _onRecordsCleared: function()
     61     {
     62         this._lastMainThreadFrame = null;
     63         this._lastBackgroundFrame = null;
     64     },
     65 
     66     _addRecord: function(record)
     67     {
     68         var records;
     69         var programRecord;
     70         if (record.type === WebInspector.TimelineModel.RecordType.Program) {
     71             programRecord = record;
     72             if (this._lastMainThreadFrame)
     73                 this._lastMainThreadFrame.timeByCategory["other"] += WebInspector.TimelineModel.durationInSeconds(programRecord);
     74             records = record["children"] || [];
     75         } else
     76             records = [record];
     77         records.forEach(this._innerAddRecord.bind(this, programRecord));
     78     },
     79 
     80     /**
     81      * @param {!Object} programRecord
     82      * @param {!Object} record
     83      */
     84     _innerAddRecord: function(programRecord, record)
     85     {
     86         var isFrameRecord = record.type === WebInspector.TimelineModel.RecordType.BeginFrame;
     87         var programTimeCarryover = isFrameRecord && programRecord ? WebInspector.TimelineModel.endTimeInSeconds(programRecord) - WebInspector.TimelineModel.startTimeInSeconds(record) : 0;
     88         var lastFrame = record.thread ? this._lastBackgroundFrame : this._lastMainThreadFrame;
     89         if (isFrameRecord && lastFrame) {
     90             this._flushFrame(lastFrame, record, programTimeCarryover);
     91             lastFrame = this._createFrame(record, programTimeCarryover);
     92         } else if (record.type === WebInspector.TimelineModel.RecordType.ActivateLayerTree) {
     93             if (lastFrame)
     94                 lastFrame.mainThreadFrameId = record.data.id;
     95         } else {
     96             if (!lastFrame)
     97                 lastFrame = this._createFrame(record, programTimeCarryover);
     98             if (!record.thread) {
     99                 WebInspector.TimelineModel.aggregateTimeForRecord(lastFrame.timeByCategory, record);
    100                 var duration = WebInspector.TimelineModel.durationInSeconds(record);
    101                 lastFrame.cpuTime += duration;
    102                 lastFrame.timeByCategory["other"] -= duration;
    103             }
    104         }
    105         if (record.thread)
    106             this._lastBackgroundFrame = lastFrame;
    107         else
    108             this._lastMainThreadFrame = lastFrame;
    109     },
    110 
    111     /**
    112      * @param {!WebInspector.TimelineFrame} frame
    113      * @param {!Object} record
    114      * @param {number} programTimeCarryover
    115      */
    116     _flushFrame: function(frame, record, programTimeCarryover)
    117     {
    118         frame.endTime = WebInspector.TimelineModel.startTimeInSeconds(record);
    119         frame.duration = frame.endTime - frame.startTime;
    120         frame.timeByCategory["other"] -= programTimeCarryover;
    121         // Alternatively, we could compute CPU time as sum of all Program events.
    122         // This way it's a bit more flexible, as it works in case there's no program events.
    123         frame.cpuTime += frame.timeByCategory["other"];
    124         this._frameOverview.addFrame(frame);
    125         this._presentationModel.addFrame(frame);
    126     },
    127 
    128     /**
    129      * @param {!Object} record
    130      * @param {number} programTimeCarryover
    131      */
    132     _createFrame: function(record, programTimeCarryover)
    133     {
    134         var frame = new WebInspector.TimelineFrame();
    135         frame.startTime = WebInspector.TimelineModel.startTimeInSeconds(record);
    136         frame.startTimeOffset = this._model.recordOffsetInSeconds(record);
    137         frame.timeByCategory["other"] = programTimeCarryover;
    138         frame.isBackground = !!record.thread;
    139         frame.id = record.data && record.data["id"];
    140         return frame;
    141     },
    142 
    143     dispose: function()
    144     {
    145         this._model.removeEventListener(WebInspector.TimelineModel.Events.RecordAdded, this._onRecordAdded, this);
    146         this._model.removeEventListener(WebInspector.TimelineModel.Events.RecordsCleared, this._onRecordsCleared, this);
    147     }
    148 }
    149 
    150 /**
    151  * @constructor
    152  * @param {!Array.<!WebInspector.TimelineFrame>} frames
    153  */
    154 WebInspector.FrameStatistics = function(frames)
    155 {
    156     this.frameCount = frames.length;
    157     this.minDuration = Infinity;
    158     this.maxDuration = 0;
    159     this.timeByCategory = {};
    160     this.startOffset = frames[0].startTimeOffset;
    161     var lastFrame = frames[this.frameCount - 1];
    162     this.endOffset = lastFrame.startTimeOffset + lastFrame.duration;
    163 
    164     var totalDuration = 0;
    165     var sumOfSquares = 0;
    166     for (var i = 0; i < this.frameCount; ++i) {
    167         var duration = frames[i].duration;
    168         totalDuration += duration;
    169         sumOfSquares += duration * duration;
    170         this.minDuration = Math.min(this.minDuration, duration);
    171         this.maxDuration = Math.max(this.maxDuration, duration);
    172         WebInspector.TimelineModel.aggregateTimeByCategory(this.timeByCategory, frames[i].timeByCategory);
    173     }
    174     this.average = totalDuration / this.frameCount;
    175     var variance = sumOfSquares / this.frameCount - this.average * this.average;
    176     this.stddev = Math.sqrt(variance);
    177 }
    178 
    179 /**
    180  * @constructor
    181  */
    182 WebInspector.TimelineFrame = function()
    183 {
    184     this.timeByCategory = {};
    185     this.cpuTime = 0;
    186     /** @type {string} */
    187     this.mainThreadFrameId;
    188 }
    189