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