| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * @constructor | |
| 7 * @param {!WebInspector.TracingManager} tracingManager | |
| 8 * @param {!WebInspector.TracingModel} tracingModel | |
| 9 * @param {!WebInspector.TimelineModel.Filter} recordFilter | |
| 10 * @extends {WebInspector.TimelineModel} | |
| 11 */ | |
| 12 WebInspector.TracingTimelineModel = function(tracingManager, tracingModel, recor
dFilter) | |
| 13 { | |
| 14 WebInspector.TimelineModel.call(this); | |
| 15 | |
| 16 this._tracingManager = tracingManager; | |
| 17 this._tracingModel = tracingModel; | |
| 18 this._recordFilter = recordFilter; | |
| 19 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.Tra
cingStarted, this._onTracingStarted, this); | |
| 20 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.Eve
ntsCollected, this._onEventsCollected, this); | |
| 21 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.Tra
cingComplete, this._onTracingComplete, this); | |
| 22 this.reset(); | |
| 23 } | |
| 24 | |
| 25 WebInspector.TracingTimelineModel.RecordType = { | |
| 26 Program: "Program", | |
| 27 EventDispatch: "EventDispatch", | |
| 28 | |
| 29 GPUTask: "GPUTask", | |
| 30 | |
| 31 RequestMainThreadFrame: "RequestMainThreadFrame", | |
| 32 BeginFrame: "BeginFrame", | |
| 33 BeginMainThreadFrame: "BeginMainThreadFrame", | |
| 34 ActivateLayerTree: "ActivateLayerTree", | |
| 35 DrawFrame: "DrawFrame", | |
| 36 ScheduleStyleRecalculation: "ScheduleStyleRecalculation", | |
| 37 RecalculateStyles: "RecalculateStyles", | |
| 38 InvalidateLayout: "InvalidateLayout", | |
| 39 Layout: "Layout", | |
| 40 UpdateLayer: "UpdateLayer", | |
| 41 UpdateLayerTree: "UpdateLayerTree", | |
| 42 PaintSetup: "PaintSetup", | |
| 43 Paint: "Paint", | |
| 44 PaintImage: "PaintImage", | |
| 45 Rasterize: "Rasterize", | |
| 46 RasterTask: "RasterTask", | |
| 47 ScrollLayer: "ScrollLayer", | |
| 48 CompositeLayers: "CompositeLayers", | |
| 49 | |
| 50 StyleRecalcInvalidationTracking: "StyleRecalcInvalidationTracking", | |
| 51 LayoutInvalidationTracking: "LayoutInvalidationTracking", | |
| 52 LayerInvalidationTracking: "LayerInvalidationTracking", | |
| 53 PaintInvalidationTracking: "PaintInvalidationTracking", | |
| 54 | |
| 55 ParseHTML: "ParseHTML", | |
| 56 | |
| 57 TimerInstall: "TimerInstall", | |
| 58 TimerRemove: "TimerRemove", | |
| 59 TimerFire: "TimerFire", | |
| 60 | |
| 61 XHRReadyStateChange: "XHRReadyStateChange", | |
| 62 XHRLoad: "XHRLoad", | |
| 63 EvaluateScript: "EvaluateScript", | |
| 64 | |
| 65 MarkLoad: "MarkLoad", | |
| 66 MarkDOMContent: "MarkDOMContent", | |
| 67 MarkFirstPaint: "MarkFirstPaint", | |
| 68 | |
| 69 TimeStamp: "TimeStamp", | |
| 70 ConsoleTime: "ConsoleTime", | |
| 71 | |
| 72 ResourceSendRequest: "ResourceSendRequest", | |
| 73 ResourceReceiveResponse: "ResourceReceiveResponse", | |
| 74 ResourceReceivedData: "ResourceReceivedData", | |
| 75 ResourceFinish: "ResourceFinish", | |
| 76 | |
| 77 FunctionCall: "FunctionCall", | |
| 78 GCEvent: "GCEvent", | |
| 79 JSFrame: "JSFrame", | |
| 80 JSSample: "JSSample", | |
| 81 | |
| 82 UpdateCounters: "UpdateCounters", | |
| 83 | |
| 84 RequestAnimationFrame: "RequestAnimationFrame", | |
| 85 CancelAnimationFrame: "CancelAnimationFrame", | |
| 86 FireAnimationFrame: "FireAnimationFrame", | |
| 87 | |
| 88 WebSocketCreate : "WebSocketCreate", | |
| 89 WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest", | |
| 90 WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse", | |
| 91 WebSocketDestroy : "WebSocketDestroy", | |
| 92 | |
| 93 EmbedderCallback : "EmbedderCallback", | |
| 94 | |
| 95 CallStack: "CallStack", | |
| 96 SetLayerTreeId: "SetLayerTreeId", | |
| 97 TracingStartedInPage: "TracingStartedInPage", | |
| 98 TracingSessionIdForWorker: "TracingSessionIdForWorker", | |
| 99 | |
| 100 DecodeImage: "Decode Image", | |
| 101 ResizeImage: "Resize Image", | |
| 102 DrawLazyPixelRef: "Draw LazyPixelRef", | |
| 103 DecodeLazyPixelRef: "Decode LazyPixelRef", | |
| 104 | |
| 105 LazyPixelRef: "LazyPixelRef", | |
| 106 LayerTreeHostImplSnapshot: "cc::LayerTreeHostImpl", | |
| 107 PictureSnapshot: "cc::Picture", | |
| 108 | |
| 109 // CpuProfile is a virtual event created on frontend to support | |
| 110 // serialization of CPU Profiles within tracing timeline data. | |
| 111 CpuProfile: "CpuProfile" | |
| 112 }; | |
| 113 | |
| 114 /** | |
| 115 * @constructor | |
| 116 * @param {string} name | |
| 117 */ | |
| 118 WebInspector.TracingTimelineModel.VirtualThread = function(name) | |
| 119 { | |
| 120 this.name = name; | |
| 121 /** @type {!Array.<!WebInspector.TracingModel.Event>} */ | |
| 122 this.events = []; | |
| 123 /** @type {!Array.<!Array.<!WebInspector.TracingModel.Event>>} */ | |
| 124 this.asyncEvents = []; | |
| 125 } | |
| 126 | |
| 127 WebInspector.TracingTimelineModel.prototype = { | |
| 128 /** | |
| 129 * @param {boolean} captureCauses | |
| 130 * @param {boolean} enableJSSampling | |
| 131 * @param {boolean} captureMemory | |
| 132 * @param {boolean} capturePictures | |
| 133 */ | |
| 134 startRecording: function(captureCauses, enableJSSampling, captureMemory, cap
turePictures) | |
| 135 { | |
| 136 function disabledByDefault(category) | |
| 137 { | |
| 138 return "disabled-by-default-" + category; | |
| 139 } | |
| 140 var categoriesArray = [ | |
| 141 "-*", | |
| 142 disabledByDefault("devtools.timeline"), | |
| 143 disabledByDefault("devtools.timeline.frame"), | |
| 144 WebInspector.TracingModel.ConsoleEventCategory | |
| 145 ]; | |
| 146 if (captureCauses || enableJSSampling) | |
| 147 categoriesArray.push(disabledByDefault("devtools.timeline.stack")); | |
| 148 if (enableJSSampling) { | |
| 149 this._jsProfilerStarted = true; | |
| 150 this._currentTarget = WebInspector.context.flavor(WebInspector.Targe
t); | |
| 151 this._configureCpuProfilerSamplingInterval(); | |
| 152 this._currentTarget.profilerAgent().start(); | |
| 153 } | |
| 154 if (captureCauses && Runtime.experiments.isEnabled("timelineInvalidation
Tracking")) | |
| 155 categoriesArray.push(disabledByDefault("devtools.timeline.invalidati
onTracking")); | |
| 156 if (capturePictures) { | |
| 157 categoriesArray = categoriesArray.concat([ | |
| 158 disabledByDefault("devtools.timeline.layers"), | |
| 159 disabledByDefault("devtools.timeline.picture"), | |
| 160 disabledByDefault("blink.graphics_context_annotations")]); | |
| 161 } | |
| 162 var categories = categoriesArray.join(","); | |
| 163 this._startRecordingWithCategories(categories); | |
| 164 }, | |
| 165 | |
| 166 stopRecording: function() | |
| 167 { | |
| 168 if (this._jsProfilerStarted) { | |
| 169 this._stopCallbackBarrier = new CallbackBarrier(); | |
| 170 this._currentTarget.profilerAgent().stop(this._stopCallbackBarrier.c
reateCallback(this._didStopRecordingJSSamples.bind(this))); | |
| 171 this._jsProfilerStarted = false; | |
| 172 } | |
| 173 this._tracingManager.stop(); | |
| 174 }, | |
| 175 | |
| 176 /** | |
| 177 * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events | |
| 178 */ | |
| 179 setEventsForTest: function(events) | |
| 180 { | |
| 181 this._startCollectingTraceEvents(false); | |
| 182 this._tracingModel.addEvents(events); | |
| 183 this._onTracingComplete(); | |
| 184 }, | |
| 185 | |
| 186 _configureCpuProfilerSamplingInterval: function() | |
| 187 { | |
| 188 var intervalUs = WebInspector.settings.highResolutionCpuProfiling.get()
? 100 : 1000; | |
| 189 this._currentTarget.profilerAgent().setSamplingInterval(intervalUs, didC
hangeInterval); | |
| 190 | |
| 191 function didChangeInterval(error) | |
| 192 { | |
| 193 if (error) | |
| 194 WebInspector.console.error(error); | |
| 195 } | |
| 196 }, | |
| 197 | |
| 198 /** | |
| 199 * @param {string} categories | |
| 200 */ | |
| 201 _startRecordingWithCategories: function(categories) | |
| 202 { | |
| 203 this._tracingManager.start(categories, ""); | |
| 204 }, | |
| 205 | |
| 206 _onTracingStarted: function() | |
| 207 { | |
| 208 this._startCollectingTraceEvents(false); | |
| 209 }, | |
| 210 | |
| 211 /** | |
| 212 * @param {boolean} fromFile | |
| 213 */ | |
| 214 _startCollectingTraceEvents: function(fromFile) | |
| 215 { | |
| 216 this.reset(); | |
| 217 this._tracingModel.reset(); | |
| 218 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStarted, { fromFile: fromFile }); | |
| 219 }, | |
| 220 | |
| 221 /** | |
| 222 * @param {!WebInspector.Event} event | |
| 223 */ | |
| 224 _onEventsCollected: function(event) | |
| 225 { | |
| 226 var traceEvents = /** @type {!Array.<!WebInspector.TracingManager.EventP
ayload>} */ (event.data); | |
| 227 this._tracingModel.addEvents(traceEvents); | |
| 228 }, | |
| 229 | |
| 230 _onTracingComplete: function() | |
| 231 { | |
| 232 if (this._stopCallbackBarrier) | |
| 233 this._stopCallbackBarrier.callWhenDone(this._didStopRecordingTraceEv
ents.bind(this)); | |
| 234 else | |
| 235 this._didStopRecordingTraceEvents(); | |
| 236 }, | |
| 237 | |
| 238 /** | |
| 239 * @param {?Protocol.Error} error | |
| 240 * @param {?ProfilerAgent.CPUProfile} cpuProfile | |
| 241 */ | |
| 242 _didStopRecordingJSSamples: function(error, cpuProfile) | |
| 243 { | |
| 244 if (error) | |
| 245 WebInspector.console.error(error); | |
| 246 this._recordedCpuProfile = cpuProfile; | |
| 247 }, | |
| 248 | |
| 249 _didStopRecordingTraceEvents: function() | |
| 250 { | |
| 251 this._stopCallbackBarrier = null; | |
| 252 | |
| 253 if (this._recordedCpuProfile) { | |
| 254 this._injectCpuProfileEvent(this._recordedCpuProfile); | |
| 255 this._recordedCpuProfile = null; | |
| 256 } | |
| 257 this._tracingModel.tracingComplete(); | |
| 258 | |
| 259 var events = this._tracingModel.devtoolsPageMetadataEvents(); | |
| 260 var workerMetadataEvents = this._tracingModel.devtoolsWorkerMetadataEven
ts(); | |
| 261 | |
| 262 this._resetProcessingState(); | |
| 263 for (var i = 0, length = events.length; i < length; i++) { | |
| 264 var event = events[i]; | |
| 265 var process = event.thread.process(); | |
| 266 var startTime = event.startTime; | |
| 267 | |
| 268 var endTime = Infinity; | |
| 269 if (i + 1 < length) | |
| 270 endTime = events[i + 1].startTime; | |
| 271 | |
| 272 var threads = process.sortedThreads(); | |
| 273 for (var j = 0; j < threads.length; j++) { | |
| 274 var thread = threads[j]; | |
| 275 if (thread.name() === "WebCore: Worker" && !workerMetadataEvents
.some(function(e) { return e.args["data"]["workerThreadId"] === thread.id(); })) | |
| 276 continue; | |
| 277 this._processThreadEvents(startTime, endTime, event.thread, thre
ad); | |
| 278 } | |
| 279 } | |
| 280 this._resetProcessingState(); | |
| 281 | |
| 282 this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compare
StartTime); | |
| 283 | |
| 284 if (this._cpuProfile) { | |
| 285 this._processCpuProfile(this._cpuProfile); | |
| 286 this._cpuProfile = null; | |
| 287 } | |
| 288 this._buildTimelineRecords(); | |
| 289 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStopped); | |
| 290 }, | |
| 291 | |
| 292 /** | |
| 293 * @param {!ProfilerAgent.CPUProfile} cpuProfile | |
| 294 */ | |
| 295 _injectCpuProfileEvent: function(cpuProfile) | |
| 296 { | |
| 297 var metaEvent = this._tracingModel.devtoolsPageMetadataEvents().peekLast
(); | |
| 298 if (!metaEvent) | |
| 299 return; | |
| 300 var cpuProfileEvent = /** @type {!WebInspector.TracingManager.EventPaylo
ad} */ ({ | |
| 301 cat: WebInspector.TracingModel.DevToolsMetadataEventCategory, | |
| 302 ph: WebInspector.TracingModel.Phase.Instant, | |
| 303 ts: this._tracingModel.maximumRecordTime() * 1000, | |
| 304 pid: metaEvent.thread.process().id(), | |
| 305 tid: metaEvent.thread.id(), | |
| 306 name: WebInspector.TracingTimelineModel.RecordType.CpuProfile, | |
| 307 args: { data: { cpuProfile: cpuProfile } } | |
| 308 }); | |
| 309 this._tracingModel.addEvents([cpuProfileEvent]); | |
| 310 }, | |
| 311 | |
| 312 /** | |
| 313 * @param {!ProfilerAgent.CPUProfile} cpuProfile | |
| 314 */ | |
| 315 _processCpuProfile: function(cpuProfile) | |
| 316 { | |
| 317 var jsSamples = WebInspector.TimelineJSProfileProcessor.generateTracingE
ventsFromCpuProfile(this, cpuProfile); | |
| 318 this._inspectedTargetEvents = this._inspectedTargetEvents.mergeOrdered(j
sSamples, WebInspector.TracingModel.Event.orderedCompareStartTime); | |
| 319 this._setMainThreadEvents(this.mainThreadEvents().mergeOrdered(jsSamples
, WebInspector.TracingModel.Event.orderedCompareStartTime)); | |
| 320 var jsFrameEvents = WebInspector.TimelineJSProfileProcessor.generateJSFr
ameEvents(this.mainThreadEvents()); | |
| 321 this._setMainThreadEvents(jsFrameEvents.mergeOrdered(this.mainThreadEven
ts(), WebInspector.TracingModel.Event.orderedCompareStartTime)); | |
| 322 this._inspectedTargetEvents = jsFrameEvents.mergeOrdered(this._inspected
TargetEvents, WebInspector.TracingModel.Event.orderedCompareStartTime); | |
| 323 }, | |
| 324 | |
| 325 /** | |
| 326 * @return {number} | |
| 327 */ | |
| 328 minimumRecordTime: function() | |
| 329 { | |
| 330 return this._tracingModel.minimumRecordTime(); | |
| 331 }, | |
| 332 | |
| 333 /** | |
| 334 * @return {number} | |
| 335 */ | |
| 336 maximumRecordTime: function() | |
| 337 { | |
| 338 return this._tracingModel.maximumRecordTime(); | |
| 339 }, | |
| 340 | |
| 341 /** | |
| 342 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
| 343 */ | |
| 344 inspectedTargetEvents: function() | |
| 345 { | |
| 346 return this._inspectedTargetEvents; | |
| 347 }, | |
| 348 | |
| 349 /** | |
| 350 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
| 351 */ | |
| 352 mainThreadEvents: function() | |
| 353 { | |
| 354 return this._mainThreadEvents; | |
| 355 }, | |
| 356 | |
| 357 /** | |
| 358 * @param {!Array.<!WebInspector.TracingModel.Event>} events | |
| 359 */ | |
| 360 _setMainThreadEvents: function(events) | |
| 361 { | |
| 362 this._mainThreadEvents = events; | |
| 363 }, | |
| 364 | |
| 365 /** | |
| 366 * @return {!Array.<!Array.<!WebInspector.TracingModel.Event>>} | |
| 367 */ | |
| 368 mainThreadAsyncEvents: function() | |
| 369 { | |
| 370 return this._mainThreadAsyncEvents; | |
| 371 }, | |
| 372 | |
| 373 /** | |
| 374 * @return {!Array.<!WebInspector.TracingTimelineModel.VirtualThread>} | |
| 375 */ | |
| 376 virtualThreads: function() | |
| 377 { | |
| 378 return this._virtualThreads; | |
| 379 }, | |
| 380 | |
| 381 /** | |
| 382 * @param {!WebInspector.ChunkedFileReader} fileReader | |
| 383 * @param {!WebInspector.Progress} progress | |
| 384 * @return {!WebInspector.OutputStream} | |
| 385 */ | |
| 386 createLoader: function(fileReader, progress) | |
| 387 { | |
| 388 return new WebInspector.TracingModelLoader(this, fileReader, progress); | |
| 389 }, | |
| 390 | |
| 391 /** | |
| 392 * @param {!WebInspector.OutputStream} stream | |
| 393 */ | |
| 394 writeToStream: function(stream) | |
| 395 { | |
| 396 var saver = new WebInspector.TracingTimelineSaver(stream); | |
| 397 this._tracingModel.writeToStream(stream, saver); | |
| 398 }, | |
| 399 | |
| 400 reset: function() | |
| 401 { | |
| 402 this._virtualThreads = []; | |
| 403 this._mainThreadEvents = []; | |
| 404 this._mainThreadAsyncEvents = []; | |
| 405 this._inspectedTargetEvents = []; | |
| 406 WebInspector.TimelineModel.prototype.reset.call(this); | |
| 407 }, | |
| 408 | |
| 409 _buildTimelineRecords: function() | |
| 410 { | |
| 411 var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThrea
dEvents()); | |
| 412 | |
| 413 /** | |
| 414 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} a | |
| 415 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} b | |
| 416 * @return {number} | |
| 417 */ | |
| 418 function compareRecordStartTime(a, b) | |
| 419 { | |
| 420 // Never return 0 as otherwise equal records would be merged. | |
| 421 return (a.startTime() <= b.startTime()) ? -1 : +1; | |
| 422 } | |
| 423 | |
| 424 /** | |
| 425 * @param {!WebInspector.TracingTimelineModel.VirtualThread} virtualThre
ad | |
| 426 * @this {!WebInspector.TracingTimelineModel} | |
| 427 */ | |
| 428 function processVirtualThreadEvents(virtualThread) | |
| 429 { | |
| 430 var threadRecords = this._buildTimelineRecordsForThread(virtualThrea
d.events); | |
| 431 topLevelRecords = topLevelRecords.mergeOrdered(threadRecords, compar
eRecordStartTime); | |
| 432 } | |
| 433 this.virtualThreads().forEach(processVirtualThreadEvents.bind(this)); | |
| 434 | |
| 435 | |
| 436 for (var i = 0; i < topLevelRecords.length; i++) { | |
| 437 var record = topLevelRecords[i]; | |
| 438 if (record.type() === WebInspector.TracingTimelineModel.RecordType.P
rogram) | |
| 439 this._mainThreadTasks.push(record); | |
| 440 if (record.type() === WebInspector.TracingTimelineModel.RecordType.G
PUTask) | |
| 441 this._gpuThreadTasks.push(record); | |
| 442 } | |
| 443 this._records = topLevelRecords; | |
| 444 }, | |
| 445 | |
| 446 /** | |
| 447 * @param {!Array.<!WebInspector.TracingModel.Event>} threadEvents | |
| 448 * @return {!Array.<!WebInspector.TracingTimelineModel.TraceEventRecord>} | |
| 449 */ | |
| 450 _buildTimelineRecordsForThread: function(threadEvents) | |
| 451 { | |
| 452 var recordStack = []; | |
| 453 var topLevelRecords = []; | |
| 454 | |
| 455 for (var i = 0, size = threadEvents.length; i < size; ++i) { | |
| 456 var event = threadEvents[i]; | |
| 457 for (var top = recordStack.peekLast(); top && top._event.endTime <=
event.startTime; top = recordStack.peekLast()) { | |
| 458 recordStack.pop(); | |
| 459 if (!recordStack.length) | |
| 460 topLevelRecords.push(top); | |
| 461 } | |
| 462 if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd || even
t.phase === WebInspector.TracingModel.Phase.NestableAsyncEnd) | |
| 463 continue; | |
| 464 var parentRecord = recordStack.peekLast(); | |
| 465 // Maintain the back-end logic of old timeline, skip console.time()
/ console.timeEnd() that are not properly nested. | |
| 466 if (WebInspector.TracingModel.isAsyncBeginPhase(event.phase) && pare
ntRecord && event.endTime > parentRecord._event.endTime) | |
| 467 continue; | |
| 468 var record = new WebInspector.TracingTimelineModel.TraceEventRecord(
this, event); | |
| 469 if (WebInspector.TimelineUIUtils.isMarkerEvent(event)) | |
| 470 this._eventDividerRecords.push(record); | |
| 471 if (!this._recordFilter.accept(record)) | |
| 472 continue; | |
| 473 if (parentRecord) | |
| 474 parentRecord._addChild(record); | |
| 475 if (event.endTime) | |
| 476 recordStack.push(record); | |
| 477 } | |
| 478 | |
| 479 if (recordStack.length) | |
| 480 topLevelRecords.push(recordStack[0]); | |
| 481 | |
| 482 return topLevelRecords; | |
| 483 }, | |
| 484 | |
| 485 _resetProcessingState: function() | |
| 486 { | |
| 487 this._sendRequestEvents = {}; | |
| 488 this._timerEvents = {}; | |
| 489 this._requestAnimationFrameEvents = {}; | |
| 490 this._invalidationTracker = new WebInspector.InvalidationTracker(); | |
| 491 this._layoutInvalidate = {}; | |
| 492 this._lastScheduleStyleRecalculation = {}; | |
| 493 this._webSocketCreateEvents = {}; | |
| 494 this._paintImageEventByPixelRefId = {}; | |
| 495 this._lastPaintForLayer = {}; | |
| 496 this._lastRecalculateStylesEvent = null; | |
| 497 this._currentScriptEvent = null; | |
| 498 this._eventStack = []; | |
| 499 }, | |
| 500 | |
| 501 /** | |
| 502 * @param {number} startTime | |
| 503 * @param {?number} endTime | |
| 504 * @param {!WebInspector.TracingModel.Thread} mainThread | |
| 505 * @param {!WebInspector.TracingModel.Thread} thread | |
| 506 */ | |
| 507 _processThreadEvents: function(startTime, endTime, mainThread, thread) | |
| 508 { | |
| 509 var events = thread.events(); | |
| 510 var length = events.length; | |
| 511 var i = events.lowerBound(startTime, function (time, event) { return tim
e - event.startTime }); | |
| 512 | |
| 513 var threadEvents; | |
| 514 if (thread === mainThread) { | |
| 515 threadEvents = this._mainThreadEvents; | |
| 516 this._mainThreadAsyncEvents = this._mainThreadAsyncEvents.concat(thr
ead.asyncEvents()); | |
| 517 } else { | |
| 518 var virtualThread = new WebInspector.TracingTimelineModel.VirtualThr
ead(thread.name()); | |
| 519 threadEvents = virtualThread.events; | |
| 520 virtualThread.asyncEvents = virtualThread.asyncEvents.concat(thread.
asyncEvents()); | |
| 521 this._virtualThreads.push(virtualThread); | |
| 522 } | |
| 523 | |
| 524 this._eventStack = []; | |
| 525 for (; i < length; i++) { | |
| 526 var event = events[i]; | |
| 527 if (endTime && event.startTime >= endTime) | |
| 528 break; | |
| 529 this._processEvent(event); | |
| 530 threadEvents.push(event); | |
| 531 this._inspectedTargetEvents.push(event); | |
| 532 } | |
| 533 }, | |
| 534 | |
| 535 /** | |
| 536 * @param {!WebInspector.TracingModel.Event} event | |
| 537 */ | |
| 538 _processEvent: function(event) | |
| 539 { | |
| 540 var recordTypes = WebInspector.TracingTimelineModel.RecordType; | |
| 541 | |
| 542 var eventStack = this._eventStack; | |
| 543 while (eventStack.length && eventStack.peekLast().endTime < event.startT
ime) | |
| 544 eventStack.pop(); | |
| 545 var duration = event.duration; | |
| 546 if (duration) { | |
| 547 if (eventStack.length) { | |
| 548 var parent = eventStack.peekLast(); | |
| 549 parent.selfTime -= duration; | |
| 550 } | |
| 551 event.selfTime = duration; | |
| 552 eventStack.push(event); | |
| 553 } | |
| 554 | |
| 555 if (this._currentScriptEvent && event.startTime > this._currentScriptEve
nt.endTime) | |
| 556 this._currentScriptEvent = null; | |
| 557 | |
| 558 switch (event.name) { | |
| 559 case recordTypes.CallStack: | |
| 560 var lastMainThreadEvent = this.mainThreadEvents().peekLast(); | |
| 561 if (lastMainThreadEvent && event.args["stack"] && event.args["stack"
].length) | |
| 562 lastMainThreadEvent.stackTrace = event.args["stack"]; | |
| 563 break; | |
| 564 | |
| 565 case recordTypes.CpuProfile: | |
| 566 this._cpuProfile = event.args["data"]["cpuProfile"]; | |
| 567 break; | |
| 568 | |
| 569 case recordTypes.ResourceSendRequest: | |
| 570 this._sendRequestEvents[event.args["data"]["requestId"]] = event; | |
| 571 event.imageURL = event.args["data"]["url"]; | |
| 572 break; | |
| 573 | |
| 574 case recordTypes.ResourceReceiveResponse: | |
| 575 case recordTypes.ResourceReceivedData: | |
| 576 case recordTypes.ResourceFinish: | |
| 577 event.initiator = this._sendRequestEvents[event.args["data"]["reques
tId"]]; | |
| 578 if (event.initiator) | |
| 579 event.imageURL = event.initiator.imageURL; | |
| 580 break; | |
| 581 | |
| 582 case recordTypes.TimerInstall: | |
| 583 this._timerEvents[event.args["data"]["timerId"]] = event; | |
| 584 break; | |
| 585 | |
| 586 case recordTypes.TimerFire: | |
| 587 event.initiator = this._timerEvents[event.args["data"]["timerId"]]; | |
| 588 break; | |
| 589 | |
| 590 case recordTypes.RequestAnimationFrame: | |
| 591 this._requestAnimationFrameEvents[event.args["data"]["id"]] = event; | |
| 592 break; | |
| 593 | |
| 594 case recordTypes.FireAnimationFrame: | |
| 595 event.initiator = this._requestAnimationFrameEvents[event.args["data
"]["id"]]; | |
| 596 break; | |
| 597 | |
| 598 case recordTypes.ScheduleStyleRecalculation: | |
| 599 this._lastScheduleStyleRecalculation[event.args["frame"]] = event; | |
| 600 break; | |
| 601 | |
| 602 case recordTypes.RecalculateStyles: | |
| 603 this._invalidationTracker.didRecalcStyle(event); | |
| 604 event.initiator = this._lastScheduleStyleRecalculation[event.args["f
rame"]]; | |
| 605 this._lastRecalculateStylesEvent = event; | |
| 606 break; | |
| 607 | |
| 608 case recordTypes.StyleRecalcInvalidationTracking: | |
| 609 case recordTypes.LayoutInvalidationTracking: | |
| 610 case recordTypes.LayerInvalidationTracking: | |
| 611 case recordTypes.PaintInvalidationTracking: | |
| 612 this._invalidationTracker.addInvalidation(event); | |
| 613 break; | |
| 614 | |
| 615 case recordTypes.InvalidateLayout: | |
| 616 // Consider style recalculation as a reason for layout invalidation, | |
| 617 // but only if we had no earlier layout invalidation records. | |
| 618 var layoutInitator = event; | |
| 619 var frameId = event.args["frame"]; | |
| 620 if (!this._layoutInvalidate[frameId] && this._lastRecalculateStylesE
vent && this._lastRecalculateStylesEvent.endTime > event.startTime) | |
| 621 layoutInitator = this._lastRecalculateStylesEvent.initiator; | |
| 622 this._layoutInvalidate[frameId] = layoutInitator; | |
| 623 break; | |
| 624 | |
| 625 case recordTypes.Layout: | |
| 626 this._invalidationTracker.didLayout(event); | |
| 627 var frameId = event.args["beginData"]["frame"]; | |
| 628 event.initiator = this._layoutInvalidate[frameId]; | |
| 629 // In case we have no closing Layout event, endData is not available
. | |
| 630 if (event.args["endData"]) { | |
| 631 event.backendNodeId = event.args["endData"]["rootNode"]; | |
| 632 event.highlightQuad = event.args["endData"]["root"]; | |
| 633 } | |
| 634 this._layoutInvalidate[frameId] = null; | |
| 635 if (this._currentScriptEvent) | |
| 636 event.warning = WebInspector.UIString("Forced synchronous layout
is a possible performance bottleneck."); | |
| 637 break; | |
| 638 | |
| 639 case recordTypes.WebSocketCreate: | |
| 640 this._webSocketCreateEvents[event.args["data"]["identifier"]] = even
t; | |
| 641 break; | |
| 642 | |
| 643 case recordTypes.WebSocketSendHandshakeRequest: | |
| 644 case recordTypes.WebSocketReceiveHandshakeResponse: | |
| 645 case recordTypes.WebSocketDestroy: | |
| 646 event.initiator = this._webSocketCreateEvents[event.args["data"]["id
entifier"]]; | |
| 647 break; | |
| 648 | |
| 649 case recordTypes.EvaluateScript: | |
| 650 case recordTypes.FunctionCall: | |
| 651 if (!this._currentScriptEvent) | |
| 652 this._currentScriptEvent = event; | |
| 653 break; | |
| 654 | |
| 655 case recordTypes.SetLayerTreeId: | |
| 656 this._inspectedTargetLayerTreeId = event.args["layerTreeId"]; | |
| 657 break; | |
| 658 | |
| 659 case recordTypes.Paint: | |
| 660 this._invalidationTracker.didPaint(event); | |
| 661 event.highlightQuad = event.args["data"]["clip"]; | |
| 662 event.backendNodeId = event.args["data"]["nodeId"]; | |
| 663 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLay
er); | |
| 664 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== th
is._inspectedTargetLayerTreeId) | |
| 665 break; | |
| 666 // Only keep layer paint events, skip paints for subframes that get
painted to the same layer as parent. | |
| 667 if (!event.args["data"]["layerId"]) | |
| 668 break; | |
| 669 this._lastPaintForLayer[layerUpdateEvent.args["layerId"]] = event; | |
| 670 break; | |
| 671 | |
| 672 case recordTypes.PictureSnapshot: | |
| 673 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLay
er); | |
| 674 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== th
is._inspectedTargetLayerTreeId) | |
| 675 break; | |
| 676 var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["laye
rId"]]; | |
| 677 if (paintEvent) | |
| 678 paintEvent.picture = event; | |
| 679 break; | |
| 680 | |
| 681 case recordTypes.ScrollLayer: | |
| 682 event.backendNodeId = event.args["data"]["nodeId"]; | |
| 683 break; | |
| 684 | |
| 685 case recordTypes.PaintImage: | |
| 686 event.backendNodeId = event.args["data"]["nodeId"]; | |
| 687 event.imageURL = event.args["data"]["url"]; | |
| 688 break; | |
| 689 | |
| 690 case recordTypes.DecodeImage: | |
| 691 case recordTypes.ResizeImage: | |
| 692 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage
); | |
| 693 if (!paintImageEvent) { | |
| 694 var decodeLazyPixelRefEvent = this._findAncestorEvent(recordType
s.DecodeLazyPixelRef); | |
| 695 paintImageEvent = decodeLazyPixelRefEvent && this._paintImageEve
ntByPixelRefId[decodeLazyPixelRefEvent.args["LazyPixelRef"]]; | |
| 696 } | |
| 697 if (!paintImageEvent) | |
| 698 break; | |
| 699 event.backendNodeId = paintImageEvent.backendNodeId; | |
| 700 event.imageURL = paintImageEvent.imageURL; | |
| 701 break; | |
| 702 | |
| 703 case recordTypes.DrawLazyPixelRef: | |
| 704 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage
); | |
| 705 if (!paintImageEvent) | |
| 706 break; | |
| 707 this._paintImageEventByPixelRefId[event.args["LazyPixelRef"]] = pain
tImageEvent; | |
| 708 event.backendNodeId = paintImageEvent.backendNodeId; | |
| 709 event.imageURL = paintImageEvent.imageURL; | |
| 710 break; | |
| 711 } | |
| 712 }, | |
| 713 | |
| 714 /** | |
| 715 * @param {string} name | |
| 716 * @return {?WebInspector.TracingModel.Event} | |
| 717 */ | |
| 718 _findAncestorEvent: function(name) | |
| 719 { | |
| 720 for (var i = this._eventStack.length - 1; i >= 0; --i) { | |
| 721 var event = this._eventStack[i]; | |
| 722 if (event.name === name) | |
| 723 return event; | |
| 724 } | |
| 725 return null; | |
| 726 }, | |
| 727 | |
| 728 __proto__: WebInspector.TimelineModel.prototype | |
| 729 } | |
| 730 | |
| 731 /** | |
| 732 * @interface | |
| 733 */ | |
| 734 WebInspector.TracingTimelineModel.Filter = function() { } | |
| 735 | |
| 736 WebInspector.TracingTimelineModel.Filter.prototype = { | |
| 737 /** | |
| 738 * @param {!WebInspector.TracingModel.Event} event | |
| 739 * @return {boolean} | |
| 740 */ | |
| 741 accept: function(event) { } | |
| 742 } | |
| 743 | |
| 744 /** | |
| 745 * @constructor | |
| 746 * @implements {WebInspector.TracingTimelineModel.Filter} | |
| 747 * @param {!Array.<string>} eventNames | |
| 748 */ | |
| 749 WebInspector.TracingTimelineModel.EventNameFilter = function(eventNames) | |
| 750 { | |
| 751 this._eventNames = eventNames.keySet(); | |
| 752 } | |
| 753 | |
| 754 WebInspector.TracingTimelineModel.EventNameFilter.prototype = { | |
| 755 /** | |
| 756 * @param {!WebInspector.TracingModel.Event} event | |
| 757 * @return {boolean} | |
| 758 */ | |
| 759 accept: function(event) | |
| 760 { | |
| 761 throw new Error("Not implemented."); | |
| 762 } | |
| 763 } | |
| 764 | |
| 765 /** | |
| 766 * @constructor | |
| 767 * @extends {WebInspector.TracingTimelineModel.EventNameFilter} | |
| 768 * @param {!Array.<string>} includeNames | |
| 769 */ | |
| 770 WebInspector.TracingTimelineModel.InclusiveEventNameFilter = function(includeNam
es) | |
| 771 { | |
| 772 WebInspector.TracingTimelineModel.EventNameFilter.call(this, includeNames) | |
| 773 } | |
| 774 | |
| 775 WebInspector.TracingTimelineModel.InclusiveEventNameFilter.prototype = { | |
| 776 /** | |
| 777 * @override | |
| 778 * @param {!WebInspector.TracingModel.Event} event | |
| 779 * @return {boolean} | |
| 780 */ | |
| 781 accept: function(event) | |
| 782 { | |
| 783 return event.category === WebInspector.TracingModel.ConsoleEventCategory
|| !!this._eventNames[event.name]; | |
| 784 }, | |
| 785 __proto__: WebInspector.TracingTimelineModel.EventNameFilter.prototype | |
| 786 } | |
| 787 | |
| 788 /** | |
| 789 * @constructor | |
| 790 * @extends {WebInspector.TracingTimelineModel.EventNameFilter} | |
| 791 * @param {!Array.<string>} excludeNames | |
| 792 */ | |
| 793 WebInspector.TracingTimelineModel.ExclusiveEventNameFilter = function(excludeNam
es) | |
| 794 { | |
| 795 WebInspector.TracingTimelineModel.EventNameFilter.call(this, excludeNames) | |
| 796 } | |
| 797 | |
| 798 WebInspector.TracingTimelineModel.ExclusiveEventNameFilter.prototype = { | |
| 799 /** | |
| 800 * @override | |
| 801 * @param {!WebInspector.TracingModel.Event} event | |
| 802 * @return {boolean} | |
| 803 */ | |
| 804 accept: function(event) | |
| 805 { | |
| 806 return !this._eventNames[event.name]; | |
| 807 }, | |
| 808 __proto__: WebInspector.TracingTimelineModel.EventNameFilter.prototype | |
| 809 } | |
| 810 | |
| 811 /** | |
| 812 * @constructor | |
| 813 * @implements {WebInspector.TimelineModel.Record} | |
| 814 * @param {!WebInspector.TimelineModel} model | |
| 815 * @param {!WebInspector.TracingModel.Event} traceEvent | |
| 816 */ | |
| 817 WebInspector.TracingTimelineModel.TraceEventRecord = function(model, traceEvent) | |
| 818 { | |
| 819 this._model = model; | |
| 820 this._event = traceEvent; | |
| 821 traceEvent._timelineRecord = this; | |
| 822 this._children = []; | |
| 823 } | |
| 824 | |
| 825 WebInspector.TracingTimelineModel.TraceEventRecord.prototype = { | |
| 826 /** | |
| 827 * @return {?Array.<!ConsoleAgent.CallFrame>} | |
| 828 */ | |
| 829 callSiteStackTrace: function() | |
| 830 { | |
| 831 var initiator = this._event.initiator; | |
| 832 return initiator ? initiator.stackTrace : null; | |
| 833 }, | |
| 834 | |
| 835 /** | |
| 836 * @return {?WebInspector.TimelineModel.Record} | |
| 837 */ | |
| 838 initiator: function() | |
| 839 { | |
| 840 var initiator = this._event.initiator; | |
| 841 return initiator ? initiator._timelineRecord : null; | |
| 842 }, | |
| 843 | |
| 844 /** | |
| 845 * @return {?WebInspector.Target} | |
| 846 */ | |
| 847 target: function() | |
| 848 { | |
| 849 return this._event.thread.target(); | |
| 850 }, | |
| 851 | |
| 852 /** | |
| 853 * @return {number} | |
| 854 */ | |
| 855 selfTime: function() | |
| 856 { | |
| 857 return this._event.selfTime; | |
| 858 }, | |
| 859 | |
| 860 /** | |
| 861 * @return {!Array.<!WebInspector.TimelineModel.Record>} | |
| 862 */ | |
| 863 children: function() | |
| 864 { | |
| 865 return this._children; | |
| 866 }, | |
| 867 | |
| 868 /** | |
| 869 * @return {number} | |
| 870 */ | |
| 871 startTime: function() | |
| 872 { | |
| 873 return this._event.startTime; | |
| 874 }, | |
| 875 | |
| 876 /** | |
| 877 * @return {string} | |
| 878 */ | |
| 879 thread: function() | |
| 880 { | |
| 881 if (this._event.thread.name() === "CrRendererMain") | |
| 882 return WebInspector.TimelineModel.MainThreadName; | |
| 883 return this._event.thread.name(); | |
| 884 }, | |
| 885 | |
| 886 /** | |
| 887 * @return {number} | |
| 888 */ | |
| 889 endTime: function() | |
| 890 { | |
| 891 return this._endTime || this._event.endTime || this._event.startTime; | |
| 892 }, | |
| 893 | |
| 894 /** | |
| 895 * @param {number} endTime | |
| 896 */ | |
| 897 setEndTime: function(endTime) | |
| 898 { | |
| 899 this._endTime = endTime; | |
| 900 }, | |
| 901 | |
| 902 /** | |
| 903 * @return {!Object} | |
| 904 */ | |
| 905 data: function() | |
| 906 { | |
| 907 return this._event.args["data"]; | |
| 908 }, | |
| 909 | |
| 910 /** | |
| 911 * @return {string} | |
| 912 */ | |
| 913 type: function() | |
| 914 { | |
| 915 if (this._event.category === WebInspector.TracingModel.ConsoleEventCateg
ory) | |
| 916 return WebInspector.TracingTimelineModel.RecordType.ConsoleTime; | |
| 917 return this._event.name; | |
| 918 }, | |
| 919 | |
| 920 /** | |
| 921 * @return {string} | |
| 922 */ | |
| 923 frameId: function() | |
| 924 { | |
| 925 switch (this._event.name) { | |
| 926 case WebInspector.TracingTimelineModel.RecordType.ScheduleStyleRecalcula
tion: | |
| 927 case WebInspector.TracingTimelineModel.RecordType.RecalculateStyles: | |
| 928 case WebInspector.TracingTimelineModel.RecordType.InvalidateLayout: | |
| 929 return this._event.args["frameId"]; | |
| 930 case WebInspector.TracingTimelineModel.RecordType.Layout: | |
| 931 return this._event.args["beginData"]["frameId"]; | |
| 932 default: | |
| 933 var data = this._event.args["data"]; | |
| 934 return (data && data["frame"]) || ""; | |
| 935 } | |
| 936 }, | |
| 937 | |
| 938 /** | |
| 939 * @return {?Array.<!ConsoleAgent.CallFrame>} | |
| 940 */ | |
| 941 stackTrace: function() | |
| 942 { | |
| 943 return this._event.stackTrace; | |
| 944 }, | |
| 945 | |
| 946 /** | |
| 947 * @param {string} key | |
| 948 * @return {?Object} | |
| 949 */ | |
| 950 getUserObject: function(key) | |
| 951 { | |
| 952 if (key === "TimelineUIUtils::preview-element") | |
| 953 return this._event.previewElement; | |
| 954 throw new Error("Unexpected key: " + key); | |
| 955 }, | |
| 956 | |
| 957 /** | |
| 958 * @param {string} key | |
| 959 * @param {?Object|undefined} value | |
| 960 */ | |
| 961 setUserObject: function(key, value) | |
| 962 { | |
| 963 if (key !== "TimelineUIUtils::preview-element") | |
| 964 throw new Error("Unexpected key: " + key); | |
| 965 this._event.previewElement = /** @type {?Element} */ (value); | |
| 966 }, | |
| 967 | |
| 968 /** | |
| 969 * @return {?Array.<string>} | |
| 970 */ | |
| 971 warnings: function() | |
| 972 { | |
| 973 if (this._event.warning) | |
| 974 return [this._event.warning]; | |
| 975 return null; | |
| 976 }, | |
| 977 | |
| 978 /** | |
| 979 * @return {!WebInspector.TracingModel.Event} | |
| 980 */ | |
| 981 traceEvent: function() | |
| 982 { | |
| 983 return this._event; | |
| 984 }, | |
| 985 | |
| 986 /** | |
| 987 * @param {!WebInspector.TracingTimelineModel.TraceEventRecord} child | |
| 988 */ | |
| 989 _addChild: function(child) | |
| 990 { | |
| 991 this._children.push(child); | |
| 992 child.parent = this; | |
| 993 }, | |
| 994 | |
| 995 /** | |
| 996 * @return {!WebInspector.TimelineModel} | |
| 997 */ | |
| 998 timelineModel: function() | |
| 999 { | |
| 1000 return this._model; | |
| 1001 } | |
| 1002 } | |
| 1003 | |
| 1004 | |
| 1005 | |
| 1006 /** | |
| 1007 * @constructor | |
| 1008 * @implements {WebInspector.OutputStream} | |
| 1009 * @param {!WebInspector.TracingTimelineModel} model | |
| 1010 * @param {!{cancel: function()}} reader | |
| 1011 * @param {!WebInspector.Progress} progress | |
| 1012 */ | |
| 1013 WebInspector.TracingModelLoader = function(model, reader, progress) | |
| 1014 { | |
| 1015 this._model = model; | |
| 1016 this._reader = reader; | |
| 1017 this._progress = progress; | |
| 1018 this._buffer = ""; | |
| 1019 this._firstChunk = true; | |
| 1020 this._loader = new WebInspector.TracingModel.Loader(model._tracingModel); | |
| 1021 } | |
| 1022 | |
| 1023 WebInspector.TracingModelLoader.prototype = { | |
| 1024 /** | |
| 1025 * @param {string} chunk | |
| 1026 */ | |
| 1027 write: function(chunk) | |
| 1028 { | |
| 1029 var data = this._buffer + chunk; | |
| 1030 var lastIndex = 0; | |
| 1031 var index; | |
| 1032 do { | |
| 1033 index = lastIndex; | |
| 1034 lastIndex = WebInspector.TextUtils.findBalancedCurlyBrackets(data, i
ndex); | |
| 1035 } while (lastIndex !== -1) | |
| 1036 | |
| 1037 var json = data.slice(0, index) + "]"; | |
| 1038 this._buffer = data.slice(index); | |
| 1039 | |
| 1040 if (!index) | |
| 1041 return; | |
| 1042 | |
| 1043 if (this._firstChunk) { | |
| 1044 this._model._startCollectingTraceEvents(true); | |
| 1045 } else { | |
| 1046 var commaIndex = json.indexOf(","); | |
| 1047 if (commaIndex !== -1) | |
| 1048 json = json.slice(commaIndex + 1); | |
| 1049 json = "[" + json; | |
| 1050 } | |
| 1051 | |
| 1052 var items; | |
| 1053 try { | |
| 1054 items = /** @type {!Array.<!WebInspector.TracingManager.EventPayload
>} */ (JSON.parse(json)); | |
| 1055 } catch (e) { | |
| 1056 this._reportErrorAndCancelLoading("Malformed timeline data: " + e); | |
| 1057 return; | |
| 1058 } | |
| 1059 | |
| 1060 if (this._firstChunk) { | |
| 1061 this._firstChunk = false; | |
| 1062 if (this._looksLikeAppVersion(items[0])) { | |
| 1063 this._reportErrorAndCancelLoading("Old Timeline format is not su
pported."); | |
| 1064 return; | |
| 1065 } | |
| 1066 } | |
| 1067 | |
| 1068 try { | |
| 1069 this._loader.loadNextChunk(items); | |
| 1070 } catch(e) { | |
| 1071 this._reportErrorAndCancelLoading("Malformed timeline data: " + e); | |
| 1072 return; | |
| 1073 } | |
| 1074 }, | |
| 1075 | |
| 1076 _reportErrorAndCancelLoading: function(messsage) | |
| 1077 { | |
| 1078 WebInspector.console.error(messsage); | |
| 1079 this._model._onTracingComplete(); | |
| 1080 this._model.reset(); | |
| 1081 this._reader.cancel(); | |
| 1082 this._progress.done(); | |
| 1083 }, | |
| 1084 | |
| 1085 _looksLikeAppVersion: function(item) | |
| 1086 { | |
| 1087 return typeof item === "string" && item.indexOf("Chrome") !== -1; | |
| 1088 }, | |
| 1089 | |
| 1090 close: function() | |
| 1091 { | |
| 1092 this._loader.finish(); | |
| 1093 this._model._onTracingComplete(); | |
| 1094 } | |
| 1095 } | |
| 1096 | |
| 1097 /** | |
| 1098 * @constructor | |
| 1099 * @param {!WebInspector.OutputStream} stream | |
| 1100 * @implements {WebInspector.OutputStreamDelegate} | |
| 1101 */ | |
| 1102 WebInspector.TracingTimelineSaver = function(stream) | |
| 1103 { | |
| 1104 this._stream = stream; | |
| 1105 } | |
| 1106 | |
| 1107 WebInspector.TracingTimelineSaver.prototype = { | |
| 1108 onTransferStarted: function() | |
| 1109 { | |
| 1110 this._stream.write("["); | |
| 1111 }, | |
| 1112 | |
| 1113 onTransferFinished: function() | |
| 1114 { | |
| 1115 this._stream.write("]"); | |
| 1116 }, | |
| 1117 | |
| 1118 /** | |
| 1119 * @param {!WebInspector.ChunkedReader} reader | |
| 1120 */ | |
| 1121 onChunkTransferred: function(reader) { }, | |
| 1122 | |
| 1123 /** | |
| 1124 * @param {!WebInspector.ChunkedReader} reader | |
| 1125 * @param {!Event} event | |
| 1126 */ | |
| 1127 onError: function(reader, event) { }, | |
| 1128 } | |
| 1129 | |
| 1130 /** | |
| 1131 * @constructor | |
| 1132 * @param {!WebInspector.TracingModel.Event} event | |
| 1133 */ | |
| 1134 WebInspector.InvalidationTrackingEvent = function(event) | |
| 1135 { | |
| 1136 this.type = event.name; | |
| 1137 this.frameId = event.args["data"]["frame"]; | |
| 1138 this.nodeId = event.args["data"]["nodeId"]; | |
| 1139 this.nodeName = event.args["data"]["nodeName"]; | |
| 1140 this.paintId = event.args["data"]["paintId"]; | |
| 1141 this.reason = event.args["data"]["reason"]; | |
| 1142 this.stackTrace = event.args["data"]["stackTrace"]; | |
| 1143 } | |
| 1144 | |
| 1145 /** | |
| 1146 * @constructor | |
| 1147 */ | |
| 1148 WebInspector.InvalidationTracker = function() | |
| 1149 { | |
| 1150 this._initializePerFrameState(); | |
| 1151 } | |
| 1152 | |
| 1153 WebInspector.InvalidationTracker.prototype = { | |
| 1154 /** | |
| 1155 * @param {!WebInspector.TracingModel.Event} event | |
| 1156 */ | |
| 1157 addInvalidation: function(event) | |
| 1158 { | |
| 1159 var invalidation = new WebInspector.InvalidationTrackingEvent(event); | |
| 1160 | |
| 1161 this._startNewFrameIfNeeded(); | |
| 1162 if (!invalidation.nodeId && !invalidation.paintId) { | |
| 1163 console.error("Invalidation lacks node information."); | |
| 1164 console.error(invalidation); | |
| 1165 } | |
| 1166 | |
| 1167 // Record the paintIds for style recalc or layout invalidations. | |
| 1168 // FIXME: This O(n^2) loop could be optimized with a map. | |
| 1169 var recordTypes = WebInspector.TracingTimelineModel.RecordType; | |
| 1170 if (invalidation.type == recordTypes.PaintInvalidationTracking) | |
| 1171 this._invalidationEvents.forEach(updatePaintId); | |
| 1172 else | |
| 1173 this._invalidationEvents.push(invalidation); | |
| 1174 | |
| 1175 function updatePaintId(invalidationToUpdate) | |
| 1176 { | |
| 1177 if (invalidationToUpdate.nodeId !== invalidation.nodeId) | |
| 1178 return; | |
| 1179 if (invalidationToUpdate.type === recordTypes.StyleRecalcInvalidatio
nTracking | |
| 1180 || invalidationToUpdate.type === recordTypes.LayoutInvalidat
ionTracking) { | |
| 1181 invalidationToUpdate.paintId = invalidation.paintId; | |
| 1182 } | |
| 1183 } | |
| 1184 }, | |
| 1185 | |
| 1186 /** | |
| 1187 * @param {!WebInspector.TracingModel.Event} styleRecalcEvent | |
| 1188 */ | |
| 1189 didRecalcStyle: function(styleRecalcEvent) | |
| 1190 { | |
| 1191 var recalcFrameId = styleRecalcEvent.args["frame"]; | |
| 1192 var index = this._lastStyleRecalcEventIndex; | |
| 1193 var invalidationCount = this._invalidationEvents.length; | |
| 1194 for (; index < invalidationCount; index++) { | |
| 1195 var invalidation = this._invalidationEvents[index]; | |
| 1196 if (invalidation.type !== WebInspector.TracingTimelineModel.RecordTy
pe.StyleRecalcInvalidationTracking) | |
| 1197 continue; | |
| 1198 if (invalidation.frameId === recalcFrameId) | |
| 1199 this._addInvalidationTrackingEvent(styleRecalcEvent, invalidatio
n); | |
| 1200 } | |
| 1201 | |
| 1202 this._lastStyleRecalcEventIndex = invalidationCount; | |
| 1203 }, | |
| 1204 | |
| 1205 /** | |
| 1206 * @param {!WebInspector.TracingModel.Event} layoutEvent | |
| 1207 */ | |
| 1208 didLayout: function(layoutEvent) | |
| 1209 { | |
| 1210 var layoutFrameId = layoutEvent.args["beginData"]["frame"]; | |
| 1211 var index = this._lastLayoutEventIndex; | |
| 1212 var invalidationCount = this._invalidationEvents.length; | |
| 1213 for (; index < invalidationCount; index++) { | |
| 1214 var invalidation = this._invalidationEvents[index]; | |
| 1215 if (invalidation.type !== WebInspector.TracingTimelineModel.RecordTy
pe.LayoutInvalidationTracking) | |
| 1216 continue; | |
| 1217 if (invalidation.frameId === layoutFrameId) | |
| 1218 this._addInvalidationTrackingEvent(layoutEvent, invalidation); | |
| 1219 } | |
| 1220 | |
| 1221 this._lastLayoutEventIndex = invalidationCount; | |
| 1222 }, | |
| 1223 | |
| 1224 /** | |
| 1225 * @param {!WebInspector.TracingModel.Event} paintEvent | |
| 1226 */ | |
| 1227 didPaint: function(paintEvent) | |
| 1228 { | |
| 1229 this._didPaint = true; | |
| 1230 | |
| 1231 // If a paint doesn't have a corresponding graphics layer id, it paints | |
| 1232 // into its parent so add an effectivePaintId to these events. | |
| 1233 var layerId = paintEvent.args["data"]["layerId"]; | |
| 1234 if (layerId) | |
| 1235 this._lastPaintWithLayer = paintEvent; | |
| 1236 if (!this._lastPaintWithLayer) { | |
| 1237 console.error("Failed to find the paint container for a paint event.
"); | |
| 1238 return; | |
| 1239 } | |
| 1240 | |
| 1241 var effectivePaintId = this._lastPaintWithLayer.args["data"]["nodeId"]; | |
| 1242 var frameId = paintEvent.args["data"]["frame"]; | |
| 1243 this._invalidationEvents.forEach(recordInvalidationForPaint.bind(this)); | |
| 1244 | |
| 1245 /** | |
| 1246 * @param {!WebInspector.InvalidationTrackingEvent} invalidation | |
| 1247 * @this {WebInspector.InvalidationTracker} | |
| 1248 */ | |
| 1249 function recordInvalidationForPaint(invalidation) | |
| 1250 { | |
| 1251 if (invalidation.paintId === effectivePaintId && invalidation.frameI
d === frameId) | |
| 1252 this._addInvalidationTrackingEvent(paintEvent, invalidation); | |
| 1253 } | |
| 1254 }, | |
| 1255 | |
| 1256 /** | |
| 1257 * @param {!WebInspector.TracingModel.Event} event | |
| 1258 * @param {!WebInspector.InvalidationTrackingEvent} invalidation | |
| 1259 */ | |
| 1260 _addInvalidationTrackingEvent: function(event, invalidation) | |
| 1261 { | |
| 1262 if (!event.invalidationTrackingEvents) | |
| 1263 event.invalidationTrackingEvents = [ invalidation ]; | |
| 1264 else | |
| 1265 event.invalidationTrackingEvents.push(invalidation); | |
| 1266 }, | |
| 1267 | |
| 1268 _startNewFrameIfNeeded: function() | |
| 1269 { | |
| 1270 if (!this._didPaint) | |
| 1271 return; | |
| 1272 | |
| 1273 this._initializePerFrameState(); | |
| 1274 }, | |
| 1275 | |
| 1276 _initializePerFrameState: function() | |
| 1277 { | |
| 1278 /** @type {!Array.<!WebInspector.InvalidationTrackingEvent>} */ | |
| 1279 this._invalidationEvents = []; | |
| 1280 this._lastStyleRecalcEventIndex = 0; | |
| 1281 this._lastLayoutEventIndex = 0; | |
| 1282 this._lastPaintWithLayer = undefined; | |
| 1283 this._didPaint = false; | |
| 1284 } | |
| 1285 } | |
| OLD | NEW |