| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| 11 * copyright notice, this list of conditions and the following disclaimer | 11 * copyright notice, this list of conditions and the following disclaimer |
| 12 * in the documentation and/or other materials provided with the | 12 * in the documentation and/or other materials provided with the |
| 13 * distribution. | 13 * distribution. |
| 14 * * Neither the name of Google Inc. nor the names of its | 14 * * Neither the name of Google Inc. nor the names of its |
| 15 * contributors may be used to endorse or promote products derived from | 15 * contributors may be used to endorse or promote products derived from |
| 16 * this software without specific prior written permission. | 16 * this software without specific prior written permission. |
| 17 * | 17 * |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | |
| 31 /** | 30 /** |
| 32 * @constructor | 31 * @unrestricted |
| 33 * @param {function(!WebInspector.TracingModel.Event):string} categoryMapper | |
| 34 */ | 32 */ |
| 35 WebInspector.TimelineFrameModel = function(categoryMapper) | 33 WebInspector.TimelineFrameModel = class { |
| 36 { | 34 /** |
| 35 * @param {function(!WebInspector.TracingModel.Event):string} categoryMapper |
| 36 */ |
| 37 constructor(categoryMapper) { |
| 37 this._categoryMapper = categoryMapper; | 38 this._categoryMapper = categoryMapper; |
| 38 this.reset(); | 39 this.reset(); |
| 40 } |
| 41 |
| 42 /** |
| 43 * @return {!Array.<!WebInspector.TimelineFrame>} |
| 44 */ |
| 45 frames() { |
| 46 return this._frames; |
| 47 } |
| 48 |
| 49 /** |
| 50 * @param {number} startTime |
| 51 * @param {number} endTime |
| 52 * @return {!Array.<!WebInspector.TimelineFrame>} |
| 53 */ |
| 54 filteredFrames(startTime, endTime) { |
| 55 /** |
| 56 * @param {number} value |
| 57 * @param {!WebInspector.TimelineFrame} object |
| 58 * @return {number} |
| 59 */ |
| 60 function compareStartTime(value, object) { |
| 61 return value - object.startTime; |
| 62 } |
| 63 /** |
| 64 * @param {number} value |
| 65 * @param {!WebInspector.TimelineFrame} object |
| 66 * @return {number} |
| 67 */ |
| 68 function compareEndTime(value, object) { |
| 69 return value - object.endTime; |
| 70 } |
| 71 var frames = this._frames; |
| 72 var firstFrame = frames.lowerBound(startTime, compareEndTime); |
| 73 var lastFrame = frames.lowerBound(endTime, compareStartTime); |
| 74 return frames.slice(firstFrame, lastFrame); |
| 75 } |
| 76 |
| 77 /** |
| 78 * @param {!WebInspector.TracingModel.Event} rasterTask |
| 79 * @return {boolean} |
| 80 */ |
| 81 hasRasterTile(rasterTask) { |
| 82 var data = rasterTask.args['tileData']; |
| 83 if (!data) |
| 84 return false; |
| 85 var frameId = data['sourceFrameNumber']; |
| 86 var frame = frameId && this._frameById[frameId]; |
| 87 if (!frame || !frame.layerTree) |
| 88 return false; |
| 89 return true; |
| 90 } |
| 91 |
| 92 /** |
| 93 * @param {!WebInspector.TracingModel.Event} rasterTask |
| 94 * @return Promise<?{rect: !DOMAgent.Rect, snapshot: !WebInspector.PaintProfil
erSnapshot}>} |
| 95 */ |
| 96 rasterTilePromise(rasterTask) { |
| 97 if (!this._target) |
| 98 return Promise.resolve(null); |
| 99 var data = rasterTask.args['tileData']; |
| 100 var frameId = data['sourceFrameNumber']; |
| 101 var tileId = data['tileId'] && data['tileId']['id_ref']; |
| 102 var frame = frameId && this._frameById[frameId]; |
| 103 if (!frame || !frame.layerTree || !tileId) |
| 104 return Promise.resolve(null); |
| 105 |
| 106 return frame.layerTree.layerTreePromise().then(layerTree => layerTree && lay
erTree.pictureForRasterTile(tileId)); |
| 107 } |
| 108 |
| 109 reset() { |
| 110 this._minimumRecordTime = Infinity; |
| 111 this._frames = []; |
| 112 this._frameById = {}; |
| 113 this._lastFrame = null; |
| 114 this._lastLayerTree = null; |
| 115 this._mainFrameCommitted = false; |
| 116 this._mainFrameRequested = false; |
| 117 this._framePendingCommit = null; |
| 118 this._lastBeginFrame = null; |
| 119 this._lastNeedsBeginFrame = null; |
| 120 this._framePendingActivation = null; |
| 121 this._lastTaskBeginTime = null; |
| 122 this._target = null; |
| 123 this._sessionId = null; |
| 124 this._currentTaskTimeByCategory = {}; |
| 125 } |
| 126 |
| 127 /** |
| 128 * @param {number} startTime |
| 129 */ |
| 130 handleBeginFrame(startTime) { |
| 131 if (!this._lastFrame) |
| 132 this._startFrame(startTime); |
| 133 this._lastBeginFrame = startTime; |
| 134 } |
| 135 |
| 136 /** |
| 137 * @param {number} startTime |
| 138 */ |
| 139 handleDrawFrame(startTime) { |
| 140 if (!this._lastFrame) { |
| 141 this._startFrame(startTime); |
| 142 return; |
| 143 } |
| 144 |
| 145 // - if it wasn't drawn, it didn't happen! |
| 146 // - only show frames that either did not wait for the main thread frame or
had one committed. |
| 147 if (this._mainFrameCommitted || !this._mainFrameRequested) { |
| 148 if (this._lastNeedsBeginFrame) { |
| 149 var idleTimeEnd = this._framePendingActivation ? this._framePendingActiv
ation.triggerTime : |
| 150 (this._lastBeginFrame |
| this._lastNeedsBeginFrame); |
| 151 if (idleTimeEnd > this._lastFrame.startTime) { |
| 152 this._lastFrame.idle = true; |
| 153 this._startFrame(idleTimeEnd); |
| 154 if (this._framePendingActivation) |
| 155 this._commitPendingFrame(); |
| 156 this._lastBeginFrame = null; |
| 157 } |
| 158 this._lastNeedsBeginFrame = null; |
| 159 } |
| 160 this._startFrame(startTime); |
| 161 } |
| 162 this._mainFrameCommitted = false; |
| 163 } |
| 164 |
| 165 handleActivateLayerTree() { |
| 166 if (!this._lastFrame) |
| 167 return; |
| 168 if (this._framePendingActivation && !this._lastNeedsBeginFrame) |
| 169 this._commitPendingFrame(); |
| 170 } |
| 171 |
| 172 handleRequestMainThreadFrame() { |
| 173 if (!this._lastFrame) |
| 174 return; |
| 175 this._mainFrameRequested = true; |
| 176 } |
| 177 |
| 178 handleCompositeLayers() { |
| 179 if (!this._framePendingCommit) |
| 180 return; |
| 181 this._framePendingActivation = this._framePendingCommit; |
| 182 this._framePendingCommit = null; |
| 183 this._mainFrameRequested = false; |
| 184 this._mainFrameCommitted = true; |
| 185 } |
| 186 |
| 187 /** |
| 188 * @param {!WebInspector.TracingFrameLayerTree} layerTree |
| 189 */ |
| 190 handleLayerTreeSnapshot(layerTree) { |
| 191 this._lastLayerTree = layerTree; |
| 192 } |
| 193 |
| 194 /** |
| 195 * @param {number} startTime |
| 196 * @param {boolean} needsBeginFrame |
| 197 */ |
| 198 handleNeedFrameChanged(startTime, needsBeginFrame) { |
| 199 if (needsBeginFrame) |
| 200 this._lastNeedsBeginFrame = startTime; |
| 201 } |
| 202 |
| 203 /** |
| 204 * @param {number} startTime |
| 205 */ |
| 206 _startFrame(startTime) { |
| 207 if (this._lastFrame) |
| 208 this._flushFrame(this._lastFrame, startTime); |
| 209 this._lastFrame = new WebInspector.TimelineFrame(startTime, startTime - this
._minimumRecordTime); |
| 210 } |
| 211 |
| 212 /** |
| 213 * @param {!WebInspector.TimelineFrame} frame |
| 214 * @param {number} endTime |
| 215 */ |
| 216 _flushFrame(frame, endTime) { |
| 217 frame._setLayerTree(this._lastLayerTree); |
| 218 frame._setEndTime(endTime); |
| 219 if (this._lastLayerTree) |
| 220 this._lastLayerTree._setPaints(frame._paints); |
| 221 if (this._frames.length && (frame.startTime !== this._frames.peekLast().endT
ime || frame.startTime > frame.endTime)) |
| 222 console.assert( |
| 223 false, `Inconsistent frame time for frame ${this._frames.length} (${fr
ame.startTime} - ${frame.endTime})`); |
| 224 this._frames.push(frame); |
| 225 if (typeof frame._mainFrameId === 'number') |
| 226 this._frameById[frame._mainFrameId] = frame; |
| 227 } |
| 228 |
| 229 _commitPendingFrame() { |
| 230 this._lastFrame._addTimeForCategories(this._framePendingActivation.timeByCat
egory); |
| 231 this._lastFrame._paints = this._framePendingActivation.paints; |
| 232 this._lastFrame._mainFrameId = this._framePendingActivation.mainFrameId; |
| 233 this._framePendingActivation = null; |
| 234 } |
| 235 |
| 236 /** |
| 237 * @param {!Array.<string>} types |
| 238 * @param {!WebInspector.TimelineModel.Record} record |
| 239 * @return {?WebInspector.TimelineModel.Record} record |
| 240 */ |
| 241 _findRecordRecursively(types, record) { |
| 242 if (types.indexOf(record.type()) >= 0) |
| 243 return record; |
| 244 if (!record.children()) |
| 245 return null; |
| 246 for (var i = 0; i < record.children().length; ++i) { |
| 247 var result = this._findRecordRecursively(types, record.children()[i]); |
| 248 if (result) |
| 249 return result; |
| 250 } |
| 251 return null; |
| 252 } |
| 253 |
| 254 /** |
| 255 * @param {?WebInspector.Target} target |
| 256 * @param {!Array.<!WebInspector.TracingModel.Event>} events |
| 257 * @param {string} sessionId |
| 258 */ |
| 259 addTraceEvents(target, events, sessionId) { |
| 260 this._target = target; |
| 261 this._sessionId = sessionId; |
| 262 if (!events.length) |
| 263 return; |
| 264 if (events[0].startTime < this._minimumRecordTime) |
| 265 this._minimumRecordTime = events[0].startTime; |
| 266 for (var i = 0; i < events.length; ++i) |
| 267 this._addTraceEvent(events[i]); |
| 268 } |
| 269 |
| 270 /** |
| 271 * @param {!WebInspector.TracingModel.Event} event |
| 272 */ |
| 273 _addTraceEvent(event) { |
| 274 var eventNames = WebInspector.TimelineModel.RecordType; |
| 275 |
| 276 if (event.name === eventNames.SetLayerTreeId) { |
| 277 var sessionId = event.args['sessionId'] || event.args['data']['sessionId']
; |
| 278 if (this._sessionId === sessionId) |
| 279 this._layerTreeId = event.args['layerTreeId'] || event.args['data']['lay
erTreeId']; |
| 280 } else if (event.name === eventNames.TracingStartedInPage) { |
| 281 this._mainThread = event.thread; |
| 282 } else if ( |
| 283 event.phase === WebInspector.TracingModel.Phase.SnapshotObject && |
| 284 event.name === eventNames.LayerTreeHostImplSnapshot && parseInt(event.id
, 0) === this._layerTreeId) { |
| 285 var snapshot = /** @type {!WebInspector.TracingModel.ObjectSnapshot} */ (e
vent); |
| 286 this.handleLayerTreeSnapshot(new WebInspector.TracingFrameLayerTree(this._
target, snapshot)); |
| 287 } else { |
| 288 this._processCompositorEvents(event); |
| 289 if (event.thread === this._mainThread) |
| 290 this._addMainThreadTraceEvent(event); |
| 291 else if (this._lastFrame && event.selfTime && !WebInspector.TracingModel.i
sTopLevelEvent(event)) |
| 292 this._lastFrame._addTimeForCategory(this._categoryMapper(event), event.s
elfTime); |
| 293 } |
| 294 } |
| 295 |
| 296 /** |
| 297 * @param {!WebInspector.TracingModel.Event} event |
| 298 */ |
| 299 _processCompositorEvents(event) { |
| 300 var eventNames = WebInspector.TimelineModel.RecordType; |
| 301 |
| 302 if (event.args['layerTreeId'] !== this._layerTreeId) |
| 303 return; |
| 304 |
| 305 var timestamp = event.startTime; |
| 306 if (event.name === eventNames.BeginFrame) |
| 307 this.handleBeginFrame(timestamp); |
| 308 else if (event.name === eventNames.DrawFrame) |
| 309 this.handleDrawFrame(timestamp); |
| 310 else if (event.name === eventNames.ActivateLayerTree) |
| 311 this.handleActivateLayerTree(); |
| 312 else if (event.name === eventNames.RequestMainThreadFrame) |
| 313 this.handleRequestMainThreadFrame(); |
| 314 else if (event.name === eventNames.NeedsBeginFrameChanged) |
| 315 this.handleNeedFrameChanged(timestamp, event.args['data'] && event.args['d
ata']['needsBeginFrame']); |
| 316 } |
| 317 |
| 318 /** |
| 319 * @param {!WebInspector.TracingModel.Event} event |
| 320 */ |
| 321 _addMainThreadTraceEvent(event) { |
| 322 var eventNames = WebInspector.TimelineModel.RecordType; |
| 323 var timestamp = event.startTime; |
| 324 var selfTime = event.selfTime || 0; |
| 325 |
| 326 if (WebInspector.TracingModel.isTopLevelEvent(event)) { |
| 327 this._currentTaskTimeByCategory = {}; |
| 328 this._lastTaskBeginTime = event.startTime; |
| 329 } |
| 330 if (!this._framePendingCommit && WebInspector.TimelineFrameModel._mainFrameM
arkers.indexOf(event.name) >= 0) |
| 331 this._framePendingCommit = |
| 332 new WebInspector.PendingFrame(this._lastTaskBeginTime || event.startTi
me, this._currentTaskTimeByCategory); |
| 333 if (!this._framePendingCommit) { |
| 334 this._addTimeForCategory(this._currentTaskTimeByCategory, event); |
| 335 return; |
| 336 } |
| 337 this._addTimeForCategory(this._framePendingCommit.timeByCategory, event); |
| 338 |
| 339 if (event.name === eventNames.BeginMainThreadFrame && event.args['data'] &&
event.args['data']['frameId']) |
| 340 this._framePendingCommit.mainFrameId = event.args['data']['frameId']; |
| 341 if (event.name === eventNames.Paint && event.args['data']['layerId'] && even
t.picture && this._target) |
| 342 this._framePendingCommit.paints.push(new WebInspector.LayerPaintEvent(even
t, this._target)); |
| 343 if (event.name === eventNames.CompositeLayers && event.args['layerTreeId'] =
== this._layerTreeId) |
| 344 this.handleCompositeLayers(); |
| 345 } |
| 346 |
| 347 /** |
| 348 * @param {!Object.<string, number>} timeByCategory |
| 349 * @param {!WebInspector.TracingModel.Event} event |
| 350 */ |
| 351 _addTimeForCategory(timeByCategory, event) { |
| 352 if (!event.selfTime) |
| 353 return; |
| 354 var categoryName = this._categoryMapper(event); |
| 355 timeByCategory[categoryName] = (timeByCategory[categoryName] || 0) + event.s
elfTime; |
| 356 } |
| 39 }; | 357 }; |
| 40 | 358 |
| 41 WebInspector.TimelineFrameModel._mainFrameMarkers = [ | 359 WebInspector.TimelineFrameModel._mainFrameMarkers = [ |
| 42 WebInspector.TimelineModel.RecordType.ScheduleStyleRecalculation, | 360 WebInspector.TimelineModel.RecordType.ScheduleStyleRecalculation, |
| 43 WebInspector.TimelineModel.RecordType.InvalidateLayout, | 361 WebInspector.TimelineModel.RecordType.InvalidateLayout, WebInspector.TimelineM
odel.RecordType.BeginMainThreadFrame, |
| 44 WebInspector.TimelineModel.RecordType.BeginMainThreadFrame, | 362 WebInspector.TimelineModel.RecordType.ScrollLayer |
| 45 WebInspector.TimelineModel.RecordType.ScrollLayer | |
| 46 ]; | 363 ]; |
| 47 | 364 |
| 48 WebInspector.TimelineFrameModel.prototype = { | |
| 49 /** | |
| 50 * @return {!Array.<!WebInspector.TimelineFrame>} | |
| 51 */ | |
| 52 frames: function() | |
| 53 { | |
| 54 return this._frames; | |
| 55 }, | |
| 56 | |
| 57 /** | |
| 58 * @param {number} startTime | |
| 59 * @param {number} endTime | |
| 60 * @return {!Array.<!WebInspector.TimelineFrame>} | |
| 61 */ | |
| 62 filteredFrames: function(startTime, endTime) | |
| 63 { | |
| 64 /** | |
| 65 * @param {number} value | |
| 66 * @param {!WebInspector.TimelineFrame} object | |
| 67 * @return {number} | |
| 68 */ | |
| 69 function compareStartTime(value, object) | |
| 70 { | |
| 71 return value - object.startTime; | |
| 72 } | |
| 73 /** | |
| 74 * @param {number} value | |
| 75 * @param {!WebInspector.TimelineFrame} object | |
| 76 * @return {number} | |
| 77 */ | |
| 78 function compareEndTime(value, object) | |
| 79 { | |
| 80 return value - object.endTime; | |
| 81 } | |
| 82 var frames = this._frames; | |
| 83 var firstFrame = frames.lowerBound(startTime, compareEndTime); | |
| 84 var lastFrame = frames.lowerBound(endTime, compareStartTime); | |
| 85 return frames.slice(firstFrame, lastFrame); | |
| 86 }, | |
| 87 | |
| 88 /** | |
| 89 * @param {!WebInspector.TracingModel.Event} rasterTask | |
| 90 * @return {boolean} | |
| 91 */ | |
| 92 hasRasterTile: function(rasterTask) | |
| 93 { | |
| 94 var data = rasterTask.args["tileData"]; | |
| 95 if (!data) | |
| 96 return false; | |
| 97 var frameId = data["sourceFrameNumber"]; | |
| 98 var frame = frameId && this._frameById[frameId]; | |
| 99 if (!frame || !frame.layerTree) | |
| 100 return false; | |
| 101 return true; | |
| 102 }, | |
| 103 | |
| 104 /** | |
| 105 * @param {!WebInspector.TracingModel.Event} rasterTask | |
| 106 * @return Promise<?{rect: !DOMAgent.Rect, snapshot: !WebInspector.PaintProf
ilerSnapshot}>} | |
| 107 */ | |
| 108 rasterTilePromise: function(rasterTask) | |
| 109 { | |
| 110 if (!this._target) | |
| 111 return Promise.resolve(null); | |
| 112 var data = rasterTask.args["tileData"]; | |
| 113 var frameId = data["sourceFrameNumber"]; | |
| 114 var tileId = data["tileId"] && data["tileId"]["id_ref"]; | |
| 115 var frame = frameId && this._frameById[frameId]; | |
| 116 if (!frame || !frame.layerTree || !tileId) | |
| 117 return Promise.resolve(null); | |
| 118 | |
| 119 return frame.layerTree.layerTreePromise().then(layerTree => layerTree &&
layerTree.pictureForRasterTile(tileId)); | |
| 120 }, | |
| 121 | |
| 122 reset: function() | |
| 123 { | |
| 124 this._minimumRecordTime = Infinity; | |
| 125 this._frames = []; | |
| 126 this._frameById = {}; | |
| 127 this._lastFrame = null; | |
| 128 this._lastLayerTree = null; | |
| 129 this._mainFrameCommitted = false; | |
| 130 this._mainFrameRequested = false; | |
| 131 this._framePendingCommit = null; | |
| 132 this._lastBeginFrame = null; | |
| 133 this._lastNeedsBeginFrame = null; | |
| 134 this._framePendingActivation = null; | |
| 135 this._lastTaskBeginTime = null; | |
| 136 this._target = null; | |
| 137 this._sessionId = null; | |
| 138 this._currentTaskTimeByCategory = {}; | |
| 139 }, | |
| 140 | |
| 141 /** | |
| 142 * @param {number} startTime | |
| 143 */ | |
| 144 handleBeginFrame: function(startTime) | |
| 145 { | |
| 146 if (!this._lastFrame) | |
| 147 this._startFrame(startTime); | |
| 148 this._lastBeginFrame = startTime; | |
| 149 }, | |
| 150 | |
| 151 /** | |
| 152 * @param {number} startTime | |
| 153 */ | |
| 154 handleDrawFrame: function(startTime) | |
| 155 { | |
| 156 if (!this._lastFrame) { | |
| 157 this._startFrame(startTime); | |
| 158 return; | |
| 159 } | |
| 160 | |
| 161 // - if it wasn't drawn, it didn't happen! | |
| 162 // - only show frames that either did not wait for the main thread frame
or had one committed. | |
| 163 if (this._mainFrameCommitted || !this._mainFrameRequested) { | |
| 164 if (this._lastNeedsBeginFrame) { | |
| 165 var idleTimeEnd = this._framePendingActivation ? this._framePend
ingActivation.triggerTime : (this._lastBeginFrame || this._lastNeedsBeginFrame); | |
| 166 if (idleTimeEnd > this._lastFrame.startTime) { | |
| 167 this._lastFrame.idle = true; | |
| 168 this._startFrame(idleTimeEnd); | |
| 169 if (this._framePendingActivation) | |
| 170 this._commitPendingFrame(); | |
| 171 this._lastBeginFrame = null; | |
| 172 } | |
| 173 this._lastNeedsBeginFrame = null; | |
| 174 } | |
| 175 this._startFrame(startTime); | |
| 176 } | |
| 177 this._mainFrameCommitted = false; | |
| 178 }, | |
| 179 | |
| 180 handleActivateLayerTree: function() | |
| 181 { | |
| 182 if (!this._lastFrame) | |
| 183 return; | |
| 184 if (this._framePendingActivation && !this._lastNeedsBeginFrame) | |
| 185 this._commitPendingFrame(); | |
| 186 }, | |
| 187 | |
| 188 handleRequestMainThreadFrame: function() | |
| 189 { | |
| 190 if (!this._lastFrame) | |
| 191 return; | |
| 192 this._mainFrameRequested = true; | |
| 193 }, | |
| 194 | |
| 195 handleCompositeLayers: function() | |
| 196 { | |
| 197 if (!this._framePendingCommit) | |
| 198 return; | |
| 199 this._framePendingActivation = this._framePendingCommit; | |
| 200 this._framePendingCommit = null; | |
| 201 this._mainFrameRequested = false; | |
| 202 this._mainFrameCommitted = true; | |
| 203 }, | |
| 204 | |
| 205 /** | |
| 206 * @param {!WebInspector.TracingFrameLayerTree} layerTree | |
| 207 */ | |
| 208 handleLayerTreeSnapshot: function(layerTree) | |
| 209 { | |
| 210 this._lastLayerTree = layerTree; | |
| 211 }, | |
| 212 | |
| 213 /** | |
| 214 * @param {number} startTime | |
| 215 * @param {boolean} needsBeginFrame | |
| 216 */ | |
| 217 handleNeedFrameChanged: function(startTime, needsBeginFrame) | |
| 218 { | |
| 219 if (needsBeginFrame) | |
| 220 this._lastNeedsBeginFrame = startTime; | |
| 221 }, | |
| 222 | |
| 223 /** | |
| 224 * @param {number} startTime | |
| 225 */ | |
| 226 _startFrame: function(startTime) | |
| 227 { | |
| 228 if (this._lastFrame) | |
| 229 this._flushFrame(this._lastFrame, startTime); | |
| 230 this._lastFrame = new WebInspector.TimelineFrame(startTime, startTime -
this._minimumRecordTime); | |
| 231 }, | |
| 232 | |
| 233 /** | |
| 234 * @param {!WebInspector.TimelineFrame} frame | |
| 235 * @param {number} endTime | |
| 236 */ | |
| 237 _flushFrame: function(frame, endTime) | |
| 238 { | |
| 239 frame._setLayerTree(this._lastLayerTree); | |
| 240 frame._setEndTime(endTime); | |
| 241 if (this._lastLayerTree) | |
| 242 this._lastLayerTree._setPaints(frame._paints); | |
| 243 if (this._frames.length && (frame.startTime !== this._frames.peekLast().
endTime || frame.startTime > frame.endTime)) | |
| 244 console.assert(false, `Inconsistent frame time for frame ${this._fra
mes.length} (${frame.startTime} - ${frame.endTime})`); | |
| 245 this._frames.push(frame); | |
| 246 if (typeof frame._mainFrameId === "number") | |
| 247 this._frameById[frame._mainFrameId] = frame; | |
| 248 }, | |
| 249 | |
| 250 _commitPendingFrame: function() | |
| 251 { | |
| 252 this._lastFrame._addTimeForCategories(this._framePendingActivation.timeB
yCategory); | |
| 253 this._lastFrame._paints = this._framePendingActivation.paints; | |
| 254 this._lastFrame._mainFrameId = this._framePendingActivation.mainFrameId; | |
| 255 this._framePendingActivation = null; | |
| 256 }, | |
| 257 | |
| 258 /** | |
| 259 * @param {!Array.<string>} types | |
| 260 * @param {!WebInspector.TimelineModel.Record} record | |
| 261 * @return {?WebInspector.TimelineModel.Record} record | |
| 262 */ | |
| 263 _findRecordRecursively: function(types, record) | |
| 264 { | |
| 265 if (types.indexOf(record.type()) >= 0) | |
| 266 return record; | |
| 267 if (!record.children()) | |
| 268 return null; | |
| 269 for (var i = 0; i < record.children().length; ++i) { | |
| 270 var result = this._findRecordRecursively(types, record.children()[i]
); | |
| 271 if (result) | |
| 272 return result; | |
| 273 } | |
| 274 return null; | |
| 275 }, | |
| 276 | |
| 277 /** | |
| 278 * @param {?WebInspector.Target} target | |
| 279 * @param {!Array.<!WebInspector.TracingModel.Event>} events | |
| 280 * @param {string} sessionId | |
| 281 */ | |
| 282 addTraceEvents: function(target, events, sessionId) | |
| 283 { | |
| 284 this._target = target; | |
| 285 this._sessionId = sessionId; | |
| 286 if (!events.length) | |
| 287 return; | |
| 288 if (events[0].startTime < this._minimumRecordTime) | |
| 289 this._minimumRecordTime = events[0].startTime; | |
| 290 for (var i = 0; i < events.length; ++i) | |
| 291 this._addTraceEvent(events[i]); | |
| 292 }, | |
| 293 | |
| 294 /** | |
| 295 * @param {!WebInspector.TracingModel.Event} event | |
| 296 */ | |
| 297 _addTraceEvent: function(event) | |
| 298 { | |
| 299 var eventNames = WebInspector.TimelineModel.RecordType; | |
| 300 | |
| 301 if (event.name === eventNames.SetLayerTreeId) { | |
| 302 var sessionId = event.args["sessionId"] || event.args["data"]["sessi
onId"]; | |
| 303 if (this._sessionId === sessionId) | |
| 304 this._layerTreeId = event.args["layerTreeId"] || event.args["dat
a"]["layerTreeId"]; | |
| 305 } else if (event.name === eventNames.TracingStartedInPage) { | |
| 306 this._mainThread = event.thread; | |
| 307 } else if (event.phase === WebInspector.TracingModel.Phase.SnapshotObjec
t && event.name === eventNames.LayerTreeHostImplSnapshot && parseInt(event.id, 0
) === this._layerTreeId) { | |
| 308 var snapshot = /** @type {!WebInspector.TracingModel.ObjectSnapshot}
*/ (event); | |
| 309 this.handleLayerTreeSnapshot(new WebInspector.TracingFrameLayerTree(
this._target, snapshot)); | |
| 310 } else { | |
| 311 this._processCompositorEvents(event); | |
| 312 if (event.thread === this._mainThread) | |
| 313 this._addMainThreadTraceEvent(event); | |
| 314 else if (this._lastFrame && event.selfTime && !WebInspector.TracingM
odel.isTopLevelEvent(event)) | |
| 315 this._lastFrame._addTimeForCategory(this._categoryMapper(event),
event.selfTime); | |
| 316 } | |
| 317 }, | |
| 318 | |
| 319 /** | |
| 320 * @param {!WebInspector.TracingModel.Event} event | |
| 321 */ | |
| 322 _processCompositorEvents: function(event) | |
| 323 { | |
| 324 var eventNames = WebInspector.TimelineModel.RecordType; | |
| 325 | |
| 326 if (event.args["layerTreeId"] !== this._layerTreeId) | |
| 327 return; | |
| 328 | |
| 329 var timestamp = event.startTime; | |
| 330 if (event.name === eventNames.BeginFrame) | |
| 331 this.handleBeginFrame(timestamp); | |
| 332 else if (event.name === eventNames.DrawFrame) | |
| 333 this.handleDrawFrame(timestamp); | |
| 334 else if (event.name === eventNames.ActivateLayerTree) | |
| 335 this.handleActivateLayerTree(); | |
| 336 else if (event.name === eventNames.RequestMainThreadFrame) | |
| 337 this.handleRequestMainThreadFrame(); | |
| 338 else if (event.name === eventNames.NeedsBeginFrameChanged) | |
| 339 this.handleNeedFrameChanged(timestamp, event.args["data"] && event.a
rgs["data"]["needsBeginFrame"]); | |
| 340 }, | |
| 341 | |
| 342 /** | |
| 343 * @param {!WebInspector.TracingModel.Event} event | |
| 344 */ | |
| 345 _addMainThreadTraceEvent: function(event) | |
| 346 { | |
| 347 var eventNames = WebInspector.TimelineModel.RecordType; | |
| 348 var timestamp = event.startTime; | |
| 349 var selfTime = event.selfTime || 0; | |
| 350 | |
| 351 if (WebInspector.TracingModel.isTopLevelEvent(event)) { | |
| 352 this._currentTaskTimeByCategory = {}; | |
| 353 this._lastTaskBeginTime = event.startTime; | |
| 354 } | |
| 355 if (!this._framePendingCommit && WebInspector.TimelineFrameModel._mainFr
ameMarkers.indexOf(event.name) >= 0) | |
| 356 this._framePendingCommit = new WebInspector.PendingFrame(this._lastT
askBeginTime || event.startTime, this._currentTaskTimeByCategory); | |
| 357 if (!this._framePendingCommit) { | |
| 358 this._addTimeForCategory(this._currentTaskTimeByCategory, event); | |
| 359 return; | |
| 360 } | |
| 361 this._addTimeForCategory(this._framePendingCommit.timeByCategory, event)
; | |
| 362 | |
| 363 if (event.name === eventNames.BeginMainThreadFrame && event.args["data"]
&& event.args["data"]["frameId"]) | |
| 364 this._framePendingCommit.mainFrameId = event.args["data"]["frameId"]
; | |
| 365 if (event.name === eventNames.Paint && event.args["data"]["layerId"] &&
event.picture && this._target) | |
| 366 this._framePendingCommit.paints.push(new WebInspector.LayerPaintEven
t(event, this._target)); | |
| 367 if (event.name === eventNames.CompositeLayers && event.args["layerTreeId
"] === this._layerTreeId) | |
| 368 this.handleCompositeLayers(); | |
| 369 }, | |
| 370 | |
| 371 /** | |
| 372 * @param {!Object.<string, number>} timeByCategory | |
| 373 * @param {!WebInspector.TracingModel.Event} event | |
| 374 */ | |
| 375 _addTimeForCategory: function(timeByCategory, event) | |
| 376 { | |
| 377 if (!event.selfTime) | |
| 378 return; | |
| 379 var categoryName = this._categoryMapper(event); | |
| 380 timeByCategory[categoryName] = (timeByCategory[categoryName] || 0) + eve
nt.selfTime; | |
| 381 }, | |
| 382 }; | |
| 383 | |
| 384 /** | 365 /** |
| 385 * @constructor | 366 * @unrestricted |
| 386 * @param {!WebInspector.Target} target | |
| 387 * @param {!WebInspector.TracingModel.ObjectSnapshot} snapshot | |
| 388 */ | 367 */ |
| 389 WebInspector.TracingFrameLayerTree = function(target, snapshot) | 368 WebInspector.TracingFrameLayerTree = class { |
| 390 { | 369 /** |
| 370 * @param {!WebInspector.Target} target |
| 371 * @param {!WebInspector.TracingModel.ObjectSnapshot} snapshot |
| 372 */ |
| 373 constructor(target, snapshot) { |
| 391 this._target = target; | 374 this._target = target; |
| 392 this._snapshot = snapshot; | 375 this._snapshot = snapshot; |
| 393 /** @type {!Array<!WebInspector.LayerPaintEvent>|undefined} */ | 376 /** @type {!Array<!WebInspector.LayerPaintEvent>|undefined} */ |
| 394 this._paints; | 377 this._paints; |
| 378 } |
| 379 |
| 380 /** |
| 381 * @return {!Promise<?WebInspector.TracingLayerTree>} |
| 382 */ |
| 383 layerTreePromise() { |
| 384 return this._snapshot.objectPromise().then(result => { |
| 385 if (!result) |
| 386 return null; |
| 387 var viewport = result['device_viewport_size']; |
| 388 var tiles = result['active_tiles']; |
| 389 var rootLayer = result['active_tree']['root_layer']; |
| 390 var layers = result['active_tree']['layers']; |
| 391 var layerTree = new WebInspector.TracingLayerTree(this._target); |
| 392 layerTree.setViewportSize(viewport); |
| 393 layerTree.setTiles(tiles); |
| 394 return new Promise( |
| 395 resolve => layerTree.setLayers(rootLayer, layers, this._paints || [],
() => resolve(layerTree))); |
| 396 }); |
| 397 } |
| 398 |
| 399 /** |
| 400 * @return {!Array<!WebInspector.LayerPaintEvent>} |
| 401 */ |
| 402 paints() { |
| 403 return this._paints || []; |
| 404 } |
| 405 |
| 406 /** |
| 407 * @param {!Array<!WebInspector.LayerPaintEvent>} paints |
| 408 */ |
| 409 _setPaints(paints) { |
| 410 this._paints = paints; |
| 411 } |
| 395 }; | 412 }; |
| 396 | 413 |
| 397 WebInspector.TracingFrameLayerTree.prototype = { | |
| 398 /** | |
| 399 * @return {!Promise<?WebInspector.TracingLayerTree>} | |
| 400 */ | |
| 401 layerTreePromise: function() | |
| 402 { | |
| 403 return this._snapshot.objectPromise().then(result => { | |
| 404 if (!result) | |
| 405 return null; | |
| 406 var viewport = result["device_viewport_size"]; | |
| 407 var tiles = result["active_tiles"]; | |
| 408 var rootLayer = result["active_tree"]["root_layer"]; | |
| 409 var layers = result["active_tree"]["layers"]; | |
| 410 var layerTree = new WebInspector.TracingLayerTree(this._target); | |
| 411 layerTree.setViewportSize(viewport); | |
| 412 layerTree.setTiles(tiles); | |
| 413 return new Promise(resolve => layerTree.setLayers(rootLayer, layers,
this._paints || [], () => resolve(layerTree))); | |
| 414 }); | |
| 415 }, | |
| 416 | |
| 417 /** | |
| 418 * @return {!Array<!WebInspector.LayerPaintEvent>} | |
| 419 */ | |
| 420 paints: function() | |
| 421 { | |
| 422 return this._paints || []; | |
| 423 }, | |
| 424 | |
| 425 /** | |
| 426 * @param {!Array<!WebInspector.LayerPaintEvent>} paints | |
| 427 */ | |
| 428 _setPaints: function(paints) | |
| 429 { | |
| 430 this._paints = paints; | |
| 431 } | |
| 432 }; | |
| 433 | |
| 434 | |
| 435 /** | 414 /** |
| 436 * @constructor | 415 * @unrestricted |
| 437 * @param {number} startTime | |
| 438 * @param {number} startTimeOffset | |
| 439 */ | 416 */ |
| 440 WebInspector.TimelineFrame = function(startTime, startTimeOffset) | 417 WebInspector.TimelineFrame = class { |
| 441 { | 418 /** |
| 419 * @param {number} startTime |
| 420 * @param {number} startTimeOffset |
| 421 */ |
| 422 constructor(startTime, startTimeOffset) { |
| 442 this.startTime = startTime; | 423 this.startTime = startTime; |
| 443 this.startTimeOffset = startTimeOffset; | 424 this.startTimeOffset = startTimeOffset; |
| 444 this.endTime = this.startTime; | 425 this.endTime = this.startTime; |
| 445 this.duration = 0; | 426 this.duration = 0; |
| 446 this.timeByCategory = {}; | 427 this.timeByCategory = {}; |
| 447 this.cpuTime = 0; | 428 this.cpuTime = 0; |
| 448 this.idle = false; | 429 this.idle = false; |
| 449 /** @type {?WebInspector.TracingFrameLayerTree} */ | 430 /** @type {?WebInspector.TracingFrameLayerTree} */ |
| 450 this.layerTree = null; | 431 this.layerTree = null; |
| 451 /** @type {!Array.<!WebInspector.LayerPaintEvent>} */ | 432 /** @type {!Array.<!WebInspector.LayerPaintEvent>} */ |
| 452 this._paints = []; | 433 this._paints = []; |
| 453 /** @type {number|undefined} */ | 434 /** @type {number|undefined} */ |
| 454 this._mainFrameId = undefined; | 435 this._mainFrameId = undefined; |
| 455 }; | 436 } |
| 456 | 437 |
| 457 WebInspector.TimelineFrame.prototype = { | 438 /** |
| 458 /** | 439 * @return {boolean} |
| 459 * @return {boolean} | 440 */ |
| 460 */ | 441 hasWarnings() { |
| 461 hasWarnings: function() | 442 var /** @const */ longFrameDurationThresholdMs = 22; |
| 462 { | 443 return !this.idle && this.duration > longFrameDurationThresholdMs; |
| 463 var /** @const */ longFrameDurationThresholdMs = 22; | 444 } |
| 464 return !this.idle && this.duration > longFrameDurationThresholdMs; | |
| 465 }, | |
| 466 | 445 |
| 467 /** | 446 /** |
| 468 * @param {number} endTime | 447 * @param {number} endTime |
| 469 */ | 448 */ |
| 470 _setEndTime: function(endTime) | 449 _setEndTime(endTime) { |
| 471 { | 450 this.endTime = endTime; |
| 472 this.endTime = endTime; | 451 this.duration = this.endTime - this.startTime; |
| 473 this.duration = this.endTime - this.startTime; | 452 } |
| 474 }, | |
| 475 | 453 |
| 476 /** | 454 /** |
| 477 * @param {?WebInspector.TracingFrameLayerTree} layerTree | 455 * @param {?WebInspector.TracingFrameLayerTree} layerTree |
| 478 */ | 456 */ |
| 479 _setLayerTree: function(layerTree) | 457 _setLayerTree(layerTree) { |
| 480 { | 458 this.layerTree = layerTree; |
| 481 this.layerTree = layerTree; | 459 } |
| 482 }, | |
| 483 | 460 |
| 484 /** | 461 /** |
| 485 * @param {!Object} timeByCategory | 462 * @param {!Object} timeByCategory |
| 486 */ | 463 */ |
| 487 _addTimeForCategories: function(timeByCategory) | 464 _addTimeForCategories(timeByCategory) { |
| 488 { | 465 for (var category in timeByCategory) |
| 489 for (var category in timeByCategory) | 466 this._addTimeForCategory(category, timeByCategory[category]); |
| 490 this._addTimeForCategory(category, timeByCategory[category]); | 467 } |
| 491 }, | |
| 492 | 468 |
| 493 /** | 469 /** |
| 494 * @param {string} category | 470 * @param {string} category |
| 495 * @param {number} time | 471 * @param {number} time |
| 496 */ | 472 */ |
| 497 _addTimeForCategory: function(category, time) | 473 _addTimeForCategory(category, time) { |
| 498 { | 474 this.timeByCategory[category] = (this.timeByCategory[category] || 0) + time; |
| 499 this.timeByCategory[category] = (this.timeByCategory[category] || 0) + t
ime; | 475 this.cpuTime += time; |
| 500 this.cpuTime += time; | 476 } |
| 501 }, | |
| 502 }; | 477 }; |
| 503 | 478 |
| 504 /** | 479 /** |
| 505 * @constructor | 480 * @unrestricted |
| 506 * @param {!WebInspector.TracingModel.Event} event | |
| 507 * @param {?WebInspector.Target} target | |
| 508 */ | 481 */ |
| 509 WebInspector.LayerPaintEvent = function(event, target) | 482 WebInspector.LayerPaintEvent = class { |
| 510 { | 483 /** |
| 484 * @param {!WebInspector.TracingModel.Event} event |
| 485 * @param {?WebInspector.Target} target |
| 486 */ |
| 487 constructor(event, target) { |
| 511 this._event = event; | 488 this._event = event; |
| 512 this._target = target; | 489 this._target = target; |
| 513 }; | 490 } |
| 514 | 491 |
| 515 WebInspector.LayerPaintEvent.prototype = { | 492 /** |
| 516 /** | 493 * @return {string} |
| 517 * @return {string} | 494 */ |
| 518 */ | 495 layerId() { |
| 519 layerId: function() | 496 return this._event.args['data']['layerId']; |
| 520 { | 497 } |
| 521 return this._event.args["data"]["layerId"]; | |
| 522 }, | |
| 523 | 498 |
| 524 /** | 499 /** |
| 525 * @return {!WebInspector.TracingModel.Event} | 500 * @return {!WebInspector.TracingModel.Event} |
| 526 */ | 501 */ |
| 527 event: function() | 502 event() { |
| 528 { | 503 return this._event; |
| 529 return this._event; | 504 } |
| 530 }, | |
| 531 | 505 |
| 532 /** | 506 /** |
| 533 * @return {!Promise<?{rect: !Array<number>, serializedPicture: string}>} | 507 * @return {!Promise<?{rect: !Array<number>, serializedPicture: string}>} |
| 534 */ | 508 */ |
| 535 picturePromise: function() | 509 picturePromise() { |
| 536 { | 510 return this._event.picture.objectPromise().then(result => { |
| 537 return this._event.picture.objectPromise().then(result => { | 511 if (!result) |
| 538 if (!result) | 512 return null; |
| 539 return null; | 513 var rect = result['params'] && result['params']['layer_rect']; |
| 540 var rect = result["params"] && result["params"]["layer_rect"]; | 514 var picture = result['skp64']; |
| 541 var picture = result["skp64"]; | 515 return rect && picture ? {rect: rect, serializedPicture: picture} : null; |
| 542 return rect && picture ? {rect: rect, serializedPicture: picture} :
null; | 516 }); |
| 543 }); | 517 } |
| 544 }, | |
| 545 | 518 |
| 546 /** | 519 /** |
| 547 * @return !Promise<?{rect: !Array<number>, snapshot: !WebInspector.PaintPro
filerSnapshot}>} | 520 * @return !Promise<?{rect: !Array<number>, snapshot: !WebInspector.PaintProfi
lerSnapshot}>} |
| 548 */ | 521 */ |
| 549 snapshotPromise: function() | 522 snapshotPromise() { |
| 550 { | 523 return this.picturePromise().then(picture => { |
| 551 return this.picturePromise().then(picture => { | 524 if (!picture || !this._target) |
| 552 if (!picture || !this._target) | 525 return null; |
| 553 return null; | 526 return WebInspector.PaintProfilerSnapshot.load(this._target, picture.seria
lizedPicture) |
| 554 return WebInspector.PaintProfilerSnapshot.load(this._target, picture
.serializedPicture).then(snapshot => snapshot ? {rect: picture.rect, snapshot: s
napshot} : null); | 527 .then(snapshot => snapshot ? {rect: picture.rect, snapshot: snapshot}
: null); |
| 555 }); | 528 }); |
| 556 } | 529 } |
| 557 }; | 530 }; |
| 558 | 531 |
| 559 /** | 532 /** |
| 560 * @constructor | 533 * @unrestricted |
| 561 * @param {number} triggerTime | |
| 562 * @param {!Object.<string, number>} timeByCategory | |
| 563 */ | 534 */ |
| 564 WebInspector.PendingFrame = function(triggerTime, timeByCategory) | 535 WebInspector.PendingFrame = class { |
| 565 { | 536 /** |
| 537 * @param {number} triggerTime |
| 538 * @param {!Object.<string, number>} timeByCategory |
| 539 */ |
| 540 constructor(triggerTime, timeByCategory) { |
| 566 /** @type {!Object.<string, number>} */ | 541 /** @type {!Object.<string, number>} */ |
| 567 this.timeByCategory = timeByCategory; | 542 this.timeByCategory = timeByCategory; |
| 568 /** @type {!Array.<!WebInspector.LayerPaintEvent>} */ | 543 /** @type {!Array.<!WebInspector.LayerPaintEvent>} */ |
| 569 this.paints = []; | 544 this.paints = []; |
| 570 /** @type {number|undefined} */ | 545 /** @type {number|undefined} */ |
| 571 this.mainFrameId = undefined; | 546 this.mainFrameId = undefined; |
| 572 this.triggerTime = triggerTime; | 547 this.triggerTime = triggerTime; |
| 548 } |
| 573 }; | 549 }; |
| OLD | NEW |