OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 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 |
(...skipping 12 matching lines...) Expand all Loading... |
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 | 30 |
31 /** | 31 /** |
32 * @constructor | 32 * @constructor |
| 33 * @param {!WebInspector.TracingManager} tracingManager |
| 34 * @param {!WebInspector.TracingModel} tracingModel |
| 35 * @param {!WebInspector.TimelineModel.Filter} recordFilter |
33 * @extends {WebInspector.Object} | 36 * @extends {WebInspector.Object} |
34 */ | 37 */ |
35 WebInspector.TimelineModel = function() | 38 WebInspector.TimelineModel = function(tracingManager, tracingModel, recordFilter
) |
36 { | 39 { |
37 WebInspector.Object.call(this); | 40 WebInspector.Object.call(this); |
38 this._filters = []; | 41 this._filters = []; |
| 42 this._tracingManager = tracingManager; |
| 43 this._tracingModel = tracingModel; |
| 44 this._recordFilter = recordFilter; |
| 45 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.Tra
cingStarted, this._onTracingStarted, this); |
| 46 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.Eve
ntsCollected, this._onEventsCollected, this); |
| 47 this._tracingManager.addEventListener(WebInspector.TracingManager.Events.Tra
cingComplete, this._onTracingComplete, this); |
| 48 this.reset(); |
39 } | 49 } |
40 | 50 |
41 WebInspector.TimelineModel.RecordType = { | 51 WebInspector.TimelineModel.RecordType = { |
42 Root: "Root", | |
43 Program: "Program", | 52 Program: "Program", |
44 EventDispatch: "EventDispatch", | 53 EventDispatch: "EventDispatch", |
45 | 54 |
46 GPUTask: "GPUTask", | 55 GPUTask: "GPUTask", |
47 | 56 |
48 RequestMainThreadFrame: "RequestMainThreadFrame", | 57 RequestMainThreadFrame: "RequestMainThreadFrame", |
49 BeginFrame: "BeginFrame", | 58 BeginFrame: "BeginFrame", |
| 59 BeginMainThreadFrame: "BeginMainThreadFrame", |
50 ActivateLayerTree: "ActivateLayerTree", | 60 ActivateLayerTree: "ActivateLayerTree", |
51 DrawFrame: "DrawFrame", | 61 DrawFrame: "DrawFrame", |
52 ScheduleStyleRecalculation: "ScheduleStyleRecalculation", | 62 ScheduleStyleRecalculation: "ScheduleStyleRecalculation", |
53 RecalculateStyles: "RecalculateStyles", | 63 RecalculateStyles: "RecalculateStyles", |
54 InvalidateLayout: "InvalidateLayout", | 64 InvalidateLayout: "InvalidateLayout", |
55 Layout: "Layout", | 65 Layout: "Layout", |
| 66 UpdateLayer: "UpdateLayer", |
56 UpdateLayerTree: "UpdateLayerTree", | 67 UpdateLayerTree: "UpdateLayerTree", |
57 PaintSetup: "PaintSetup", | 68 PaintSetup: "PaintSetup", |
58 Paint: "Paint", | 69 Paint: "Paint", |
| 70 PaintImage: "PaintImage", |
59 Rasterize: "Rasterize", | 71 Rasterize: "Rasterize", |
| 72 RasterTask: "RasterTask", |
60 ScrollLayer: "ScrollLayer", | 73 ScrollLayer: "ScrollLayer", |
61 DecodeImage: "DecodeImage", | |
62 ResizeImage: "ResizeImage", | |
63 CompositeLayers: "CompositeLayers", | 74 CompositeLayers: "CompositeLayers", |
64 | 75 |
| 76 StyleRecalcInvalidationTracking: "StyleRecalcInvalidationTracking", |
| 77 LayoutInvalidationTracking: "LayoutInvalidationTracking", |
| 78 LayerInvalidationTracking: "LayerInvalidationTracking", |
| 79 PaintInvalidationTracking: "PaintInvalidationTracking", |
| 80 |
65 ParseHTML: "ParseHTML", | 81 ParseHTML: "ParseHTML", |
66 | 82 |
67 TimerInstall: "TimerInstall", | 83 TimerInstall: "TimerInstall", |
68 TimerRemove: "TimerRemove", | 84 TimerRemove: "TimerRemove", |
69 TimerFire: "TimerFire", | 85 TimerFire: "TimerFire", |
70 | 86 |
71 XHRReadyStateChange: "XHRReadyStateChange", | 87 XHRReadyStateChange: "XHRReadyStateChange", |
72 XHRLoad: "XHRLoad", | 88 XHRLoad: "XHRLoad", |
73 EvaluateScript: "EvaluateScript", | 89 EvaluateScript: "EvaluateScript", |
74 | 90 |
75 MarkLoad: "MarkLoad", | 91 MarkLoad: "MarkLoad", |
76 MarkDOMContent: "MarkDOMContent", | 92 MarkDOMContent: "MarkDOMContent", |
77 MarkFirstPaint: "MarkFirstPaint", | 93 MarkFirstPaint: "MarkFirstPaint", |
78 | 94 |
79 TimeStamp: "TimeStamp", | 95 TimeStamp: "TimeStamp", |
80 ConsoleTime: "ConsoleTime", | 96 ConsoleTime: "ConsoleTime", |
81 | 97 |
82 ResourceSendRequest: "ResourceSendRequest", | 98 ResourceSendRequest: "ResourceSendRequest", |
83 ResourceReceiveResponse: "ResourceReceiveResponse", | 99 ResourceReceiveResponse: "ResourceReceiveResponse", |
84 ResourceReceivedData: "ResourceReceivedData", | 100 ResourceReceivedData: "ResourceReceivedData", |
85 ResourceFinish: "ResourceFinish", | 101 ResourceFinish: "ResourceFinish", |
86 | 102 |
87 FunctionCall: "FunctionCall", | 103 FunctionCall: "FunctionCall", |
88 GCEvent: "GCEvent", | 104 GCEvent: "GCEvent", |
| 105 JSFrame: "JSFrame", |
| 106 JSSample: "JSSample", |
89 | 107 |
90 UpdateCounters: "UpdateCounters", | 108 UpdateCounters: "UpdateCounters", |
91 | 109 |
92 RequestAnimationFrame: "RequestAnimationFrame", | 110 RequestAnimationFrame: "RequestAnimationFrame", |
93 CancelAnimationFrame: "CancelAnimationFrame", | 111 CancelAnimationFrame: "CancelAnimationFrame", |
94 FireAnimationFrame: "FireAnimationFrame", | 112 FireAnimationFrame: "FireAnimationFrame", |
95 | 113 |
96 WebSocketCreate : "WebSocketCreate", | 114 WebSocketCreate : "WebSocketCreate", |
97 WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest", | 115 WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest", |
98 WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse", | 116 WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse", |
99 WebSocketDestroy : "WebSocketDestroy", | 117 WebSocketDestroy : "WebSocketDestroy", |
100 | 118 |
101 EmbedderCallback : "EmbedderCallback", | 119 EmbedderCallback : "EmbedderCallback", |
| 120 |
| 121 CallStack: "CallStack", |
| 122 SetLayerTreeId: "SetLayerTreeId", |
| 123 TracingStartedInPage: "TracingStartedInPage", |
| 124 TracingSessionIdForWorker: "TracingSessionIdForWorker", |
| 125 |
| 126 DecodeImage: "Decode Image", |
| 127 ResizeImage: "Resize Image", |
| 128 DrawLazyPixelRef: "Draw LazyPixelRef", |
| 129 DecodeLazyPixelRef: "Decode LazyPixelRef", |
| 130 |
| 131 LazyPixelRef: "LazyPixelRef", |
| 132 LayerTreeHostImplSnapshot: "cc::LayerTreeHostImpl", |
| 133 PictureSnapshot: "cc::Picture", |
| 134 |
| 135 // CpuProfile is a virtual event created on frontend to support |
| 136 // serialization of CPU Profiles within tracing timeline data. |
| 137 CpuProfile: "CpuProfile" |
102 } | 138 } |
103 | 139 |
104 WebInspector.TimelineModel.Events = { | 140 WebInspector.TimelineModel.Events = { |
105 RecordsCleared: "RecordsCleared", | 141 RecordsCleared: "RecordsCleared", |
106 RecordingStarted: "RecordingStarted", | 142 RecordingStarted: "RecordingStarted", |
107 RecordingStopped: "RecordingStopped", | 143 RecordingStopped: "RecordingStopped", |
108 RecordFilterChanged: "RecordFilterChanged" | 144 RecordFilterChanged: "RecordFilterChanged" |
109 } | 145 } |
110 | 146 |
111 WebInspector.TimelineModel.MainThreadName = "main"; | 147 WebInspector.TimelineModel.MainThreadName = "main"; |
(...skipping 22 matching lines...) Expand all Loading... |
134 if (postOrderCallback && postOrderCallback(record, depth)) | 170 if (postOrderCallback && postOrderCallback(record, depth)) |
135 return true; | 171 return true; |
136 } | 172 } |
137 return false; | 173 return false; |
138 } | 174 } |
139 return processRecords(recordsArray, 0); | 175 return processRecords(recordsArray, 0); |
140 } | 176 } |
141 | 177 |
142 WebInspector.TimelineModel.TransferChunkLengthBytes = 5000000; | 178 WebInspector.TimelineModel.TransferChunkLengthBytes = 5000000; |
143 | 179 |
| 180 /** |
| 181 * @constructor |
| 182 * @param {string} name |
| 183 */ |
| 184 WebInspector.TimelineModel.VirtualThread = function(name) |
| 185 { |
| 186 this.name = name; |
| 187 /** @type {!Array.<!WebInspector.TracingModel.Event>} */ |
| 188 this.events = []; |
| 189 /** @type {!Array.<!Array.<!WebInspector.TracingModel.Event>>} */ |
| 190 this.asyncEvents = []; |
| 191 } |
| 192 |
| 193 /** |
| 194 * @constructor |
| 195 * @param {!WebInspector.TimelineModel} model |
| 196 * @param {!WebInspector.TracingModel.Event} traceEvent |
| 197 */ |
| 198 WebInspector.TimelineModel.Record = function(model, traceEvent) |
| 199 { |
| 200 this._model = model; |
| 201 this._event = traceEvent; |
| 202 traceEvent._timelineRecord = this; |
| 203 this._children = []; |
| 204 } |
| 205 |
| 206 WebInspector.TimelineModel.Record.prototype = { |
| 207 /** |
| 208 * @return {?Array.<!ConsoleAgent.CallFrame>} |
| 209 */ |
| 210 callSiteStackTrace: function() |
| 211 { |
| 212 var initiator = this._event.initiator; |
| 213 return initiator ? initiator.stackTrace : null; |
| 214 }, |
| 215 |
| 216 /** |
| 217 * @return {?WebInspector.TimelineModel.Record} |
| 218 */ |
| 219 initiator: function() |
| 220 { |
| 221 var initiator = this._event.initiator; |
| 222 return initiator ? initiator._timelineRecord : null; |
| 223 }, |
| 224 |
| 225 /** |
| 226 * @return {?WebInspector.Target} |
| 227 */ |
| 228 target: function() |
| 229 { |
| 230 return this._event.thread.target(); |
| 231 }, |
| 232 |
| 233 /** |
| 234 * @return {number} |
| 235 */ |
| 236 selfTime: function() |
| 237 { |
| 238 return this._event.selfTime; |
| 239 }, |
| 240 |
| 241 /** |
| 242 * @return {!Array.<!WebInspector.TimelineModel.Record>} |
| 243 */ |
| 244 children: function() |
| 245 { |
| 246 return this._children; |
| 247 }, |
| 248 |
| 249 /** |
| 250 * @return {number} |
| 251 */ |
| 252 startTime: function() |
| 253 { |
| 254 return this._event.startTime; |
| 255 }, |
| 256 |
| 257 /** |
| 258 * @return {string} |
| 259 */ |
| 260 thread: function() |
| 261 { |
| 262 if (this._event.thread.name() === "CrRendererMain") |
| 263 return WebInspector.TimelineModel.MainThreadName; |
| 264 return this._event.thread.name(); |
| 265 }, |
| 266 |
| 267 /** |
| 268 * @return {number} |
| 269 */ |
| 270 endTime: function() |
| 271 { |
| 272 return this._endTime || this._event.endTime || this._event.startTime; |
| 273 }, |
| 274 |
| 275 /** |
| 276 * @param {number} endTime |
| 277 */ |
| 278 setEndTime: function(endTime) |
| 279 { |
| 280 this._endTime = endTime; |
| 281 }, |
| 282 |
| 283 /** |
| 284 * @return {!Object} |
| 285 */ |
| 286 data: function() |
| 287 { |
| 288 return this._event.args["data"]; |
| 289 }, |
| 290 |
| 291 /** |
| 292 * @return {string} |
| 293 */ |
| 294 type: function() |
| 295 { |
| 296 if (this._event.category === WebInspector.TracingModel.ConsoleEventCateg
ory) |
| 297 return WebInspector.TimelineModel.RecordType.ConsoleTime; |
| 298 return this._event.name; |
| 299 }, |
| 300 |
| 301 /** |
| 302 * @return {string} |
| 303 */ |
| 304 frameId: function() |
| 305 { |
| 306 switch (this._event.name) { |
| 307 case WebInspector.TimelineModel.RecordType.ScheduleStyleRecalculation: |
| 308 case WebInspector.TimelineModel.RecordType.RecalculateStyles: |
| 309 case WebInspector.TimelineModel.RecordType.InvalidateLayout: |
| 310 return this._event.args["frameId"]; |
| 311 case WebInspector.TimelineModel.RecordType.Layout: |
| 312 return this._event.args["beginData"]["frameId"]; |
| 313 default: |
| 314 var data = this._event.args["data"]; |
| 315 return (data && data["frame"]) || ""; |
| 316 } |
| 317 }, |
| 318 |
| 319 /** |
| 320 * @return {?Array.<!ConsoleAgent.CallFrame>} |
| 321 */ |
| 322 stackTrace: function() |
| 323 { |
| 324 return this._event.stackTrace; |
| 325 }, |
| 326 |
| 327 /** |
| 328 * @param {string} key |
| 329 * @return {?Object} |
| 330 */ |
| 331 getUserObject: function(key) |
| 332 { |
| 333 if (key === "TimelineUIUtils::preview-element") |
| 334 return this._event.previewElement; |
| 335 throw new Error("Unexpected key: " + key); |
| 336 }, |
| 337 |
| 338 /** |
| 339 * @param {string} key |
| 340 * @param {?Object|undefined} value |
| 341 */ |
| 342 setUserObject: function(key, value) |
| 343 { |
| 344 if (key !== "TimelineUIUtils::preview-element") |
| 345 throw new Error("Unexpected key: " + key); |
| 346 this._event.previewElement = /** @type {?Element} */ (value); |
| 347 }, |
| 348 |
| 349 /** |
| 350 * @return {?Array.<string>} |
| 351 */ |
| 352 warnings: function() |
| 353 { |
| 354 if (this._event.warning) |
| 355 return [this._event.warning]; |
| 356 return null; |
| 357 }, |
| 358 |
| 359 /** |
| 360 * @return {!WebInspector.TracingModel.Event} |
| 361 */ |
| 362 traceEvent: function() |
| 363 { |
| 364 return this._event; |
| 365 }, |
| 366 |
| 367 /** |
| 368 * @param {!WebInspector.TimelineModel.Record} child |
| 369 */ |
| 370 _addChild: function(child) |
| 371 { |
| 372 this._children.push(child); |
| 373 child.parent = this; |
| 374 }, |
| 375 |
| 376 /** |
| 377 * @return {!WebInspector.TimelineModel} |
| 378 */ |
| 379 timelineModel: function() |
| 380 { |
| 381 return this._model; |
| 382 } |
| 383 } |
| 384 |
144 WebInspector.TimelineModel.prototype = { | 385 WebInspector.TimelineModel.prototype = { |
145 /** | 386 /** |
146 * @param {boolean} captureCauses | 387 * @param {boolean} captureCauses |
| 388 * @param {boolean} enableJSSampling |
147 * @param {boolean} captureMemory | 389 * @param {boolean} captureMemory |
148 * @param {boolean} capturePictures | 390 * @param {boolean} capturePictures |
149 */ | 391 */ |
150 startRecording: function(captureCauses, captureMemory, capturePictures) | 392 startRecording: function(captureCauses, enableJSSampling, captureMemory, cap
turePictures) |
151 { | 393 { |
| 394 function disabledByDefault(category) |
| 395 { |
| 396 return "disabled-by-default-" + category; |
| 397 } |
| 398 var categoriesArray = [ |
| 399 "-*", |
| 400 disabledByDefault("devtools.timeline"), |
| 401 disabledByDefault("devtools.timeline.frame"), |
| 402 WebInspector.TracingModel.ConsoleEventCategory |
| 403 ]; |
| 404 if (captureCauses || enableJSSampling) |
| 405 categoriesArray.push(disabledByDefault("devtools.timeline.stack")); |
| 406 if (enableJSSampling) |
| 407 this._startCpuProfilingOnAllTargets(); |
| 408 if (captureCauses && Runtime.experiments.isEnabled("timelineInvalidation
Tracking")) |
| 409 categoriesArray.push(disabledByDefault("devtools.timeline.invalidati
onTracking")); |
| 410 if (capturePictures) { |
| 411 categoriesArray = categoriesArray.concat([ |
| 412 disabledByDefault("devtools.timeline.layers"), |
| 413 disabledByDefault("devtools.timeline.picture"), |
| 414 disabledByDefault("blink.graphics_context_annotations")]); |
| 415 } |
| 416 var categories = categoriesArray.join(","); |
| 417 this._startRecordingWithCategories(categories); |
152 }, | 418 }, |
153 | 419 |
154 stopRecording: function() | 420 stopRecording: function() |
155 { | 421 { |
156 }, | 422 this._stopCallbackBarrier = new CallbackBarrier(); |
157 | 423 this._stopProfilingOnAllTargets(); |
158 /** | 424 this._tracingManager.stop(); |
| 425 }, |
| 426 |
| 427 /** |
159 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspe
ctor.TimelineModel.Record,number)} preOrderCallback | 428 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspe
ctor.TimelineModel.Record,number)} preOrderCallback |
160 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspect
or.TimelineModel.Record,number)=} postOrderCallback | 429 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspect
or.TimelineModel.Record,number)=} postOrderCallback |
161 */ | 430 */ |
162 forAllRecords: function(preOrderCallback, postOrderCallback) | 431 forAllRecords: function(preOrderCallback, postOrderCallback) |
163 { | 432 { |
164 WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback
, postOrderCallback); | 433 WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback
, postOrderCallback); |
165 }, | 434 }, |
166 | 435 |
167 /** | 436 /** |
168 * @param {!WebInspector.TimelineModel.Filter} filter | 437 * @param {!WebInspector.TimelineModel.Filter} filter |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
223 | 492 |
224 /** | 493 /** |
225 * @return {!Array.<!WebInspector.TimelineModel.Record>} | 494 * @return {!Array.<!WebInspector.TimelineModel.Record>} |
226 */ | 495 */ |
227 records: function() | 496 records: function() |
228 { | 497 { |
229 return this._records; | 498 return this._records; |
230 }, | 499 }, |
231 | 500 |
232 /** | 501 /** |
| 502 * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events |
| 503 */ |
| 504 setEventsForTest: function(events) |
| 505 { |
| 506 this._startCollectingTraceEvents(false); |
| 507 this._tracingModel.addEvents(events); |
| 508 this._onTracingComplete(); |
| 509 }, |
| 510 |
| 511 _startCpuProfilingOnAllTargets: function() |
| 512 { |
| 513 this._profilingTargets = WebInspector.targetManager.targets(); |
| 514 for (var i = 0; i < this._profilingTargets.length; ++i) { |
| 515 var target = this._profilingTargets[i]; |
| 516 this._configureCpuProfilerSamplingInterval(target); |
| 517 target.profilerAgent().start(); |
| 518 } |
| 519 }, |
| 520 |
| 521 _stopProfilingOnAllTargets: function() |
| 522 { |
| 523 if (!this._profilingTargets) |
| 524 return; |
| 525 for (var i = 0; i < this._profilingTargets.length; ++i) { |
| 526 var target = this._profilingTargets[i]; |
| 527 target.profilerAgent().stop(this._stopCallbackBarrier.createCallback
(this._didStopRecordingJSSamples.bind(this, target))); |
| 528 } |
| 529 this._profilingTargets = null; |
| 530 }, |
| 531 |
| 532 /** |
| 533 * @param {!WebInspector.Target} target |
| 534 */ |
| 535 _configureCpuProfilerSamplingInterval: function(target) |
| 536 { |
| 537 var intervalUs = WebInspector.settings.highResolutionCpuProfiling.get()
? 100 : 1000; |
| 538 target.profilerAgent().setSamplingInterval(intervalUs, didChangeInterval
); |
| 539 |
| 540 function didChangeInterval(error) |
| 541 { |
| 542 if (error) |
| 543 WebInspector.console.error(error); |
| 544 } |
| 545 }, |
| 546 |
| 547 /** |
| 548 * @param {string} categories |
| 549 */ |
| 550 _startRecordingWithCategories: function(categories) |
| 551 { |
| 552 this._tracingManager.start(categories, ""); |
| 553 }, |
| 554 |
| 555 _onTracingStarted: function() |
| 556 { |
| 557 this._startCollectingTraceEvents(false); |
| 558 }, |
| 559 |
| 560 /** |
| 561 * @param {boolean} fromFile |
| 562 */ |
| 563 _startCollectingTraceEvents: function(fromFile) |
| 564 { |
| 565 this.reset(); |
| 566 this._tracingModel.reset(); |
| 567 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStarted, { fromFile: fromFile }); |
| 568 }, |
| 569 |
| 570 /** |
| 571 * @param {!WebInspector.Event} event |
| 572 */ |
| 573 _onEventsCollected: function(event) |
| 574 { |
| 575 var traceEvents = /** @type {!Array.<!WebInspector.TracingManager.EventP
ayload>} */ (event.data); |
| 576 this._tracingModel.addEvents(traceEvents); |
| 577 }, |
| 578 |
| 579 _onTracingComplete: function() |
| 580 { |
| 581 if (this._stopCallbackBarrier) { |
| 582 this._stopCallbackBarrier.callWhenDone(this._didStopRecordingTraceEv
ents.bind(this)); |
| 583 this._stopCallbackBarrier = null; |
| 584 } else { |
| 585 this._didStopRecordingTraceEvents(); |
| 586 } |
| 587 }, |
| 588 |
| 589 /** |
| 590 * @param {!WebInspector.Target} target |
| 591 * @param {?Protocol.Error} error |
| 592 * @param {?ProfilerAgent.CPUProfile} cpuProfile |
| 593 */ |
| 594 _didStopRecordingJSSamples: function(target, error, cpuProfile) |
| 595 { |
| 596 if (error) |
| 597 WebInspector.console.error(error); |
| 598 if (!this._cpuProfiles) |
| 599 this._cpuProfiles = {}; |
| 600 this._cpuProfiles[target.id()] = cpuProfile; |
| 601 }, |
| 602 |
| 603 _didStopRecordingTraceEvents: function() |
| 604 { |
| 605 this._tracingModel.tracingComplete(); |
| 606 |
| 607 var events = this._tracingModel.devtoolsPageMetadataEvents(); |
| 608 var workerMetadataEvents = this._tracingModel.devtoolsWorkerMetadataEven
ts(); |
| 609 |
| 610 this._resetProcessingState(); |
| 611 for (var i = 0, length = events.length; i < length; i++) { |
| 612 var event = events[i]; |
| 613 var process = event.thread.process(); |
| 614 var startTime = event.startTime; |
| 615 |
| 616 var endTime = Infinity; |
| 617 if (i + 1 < length) |
| 618 endTime = events[i + 1].startTime; |
| 619 |
| 620 var threads = process.sortedThreads(); |
| 621 for (var j = 0; j < threads.length; j++) { |
| 622 var thread = threads[j]; |
| 623 if (thread.name() === "WebCore: Worker" && workerMetadataEvents.
every(function(e) { return e.args["data"]["workerThreadId"] !== thread.id(); })) |
| 624 continue; |
| 625 this._processThreadEvents(startTime, endTime, event.thread, thre
ad); |
| 626 } |
| 627 } |
| 628 this._resetProcessingState(); |
| 629 |
| 630 this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compare
StartTime); |
| 631 |
| 632 this._cpuProfiles = null; |
| 633 |
| 634 this._buildTimelineRecords(); |
| 635 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStopped); |
| 636 }, |
| 637 |
| 638 /** |
| 639 * @param {!ProfilerAgent.CPUProfile} cpuProfile |
| 640 */ |
| 641 _injectCpuProfileEvent: function(cpuProfile) |
| 642 { |
| 643 var metaEvent = this._tracingModel.devtoolsPageMetadataEvents().peekLast
(); |
| 644 if (!metaEvent) |
| 645 return; |
| 646 var cpuProfileEvent = /** @type {!WebInspector.TracingManager.EventPaylo
ad} */ ({ |
| 647 cat: WebInspector.TracingModel.DevToolsMetadataEventCategory, |
| 648 ph: WebInspector.TracingModel.Phase.Instant, |
| 649 ts: this._tracingModel.maximumRecordTime() * 1000, |
| 650 pid: metaEvent.thread.process().id(), |
| 651 tid: metaEvent.thread.id(), |
| 652 name: WebInspector.TimelineModel.RecordType.CpuProfile, |
| 653 args: { data: { cpuProfile: cpuProfile } } |
| 654 }); |
| 655 this._tracingModel.addEvents([cpuProfileEvent]); |
| 656 }, |
| 657 |
| 658 _buildTimelineRecords: function() |
| 659 { |
| 660 var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThrea
dEvents()); |
| 661 |
| 662 /** |
| 663 * @param {!WebInspector.TimelineModel.Record} a |
| 664 * @param {!WebInspector.TimelineModel.Record} b |
| 665 * @return {number} |
| 666 */ |
| 667 function compareRecordStartTime(a, b) |
| 668 { |
| 669 // Never return 0 as otherwise equal records would be merged. |
| 670 return (a.startTime() <= b.startTime()) ? -1 : +1; |
| 671 } |
| 672 |
| 673 /** |
| 674 * @param {!WebInspector.TimelineModel.VirtualThread} virtualThread |
| 675 * @this {!WebInspector.TimelineModel} |
| 676 */ |
| 677 function processVirtualThreadEvents(virtualThread) |
| 678 { |
| 679 var threadRecords = this._buildTimelineRecordsForThread(virtualThrea
d.events); |
| 680 topLevelRecords = topLevelRecords.mergeOrdered(threadRecords, compar
eRecordStartTime); |
| 681 } |
| 682 this.virtualThreads().forEach(processVirtualThreadEvents.bind(this)); |
| 683 |
| 684 |
| 685 for (var i = 0; i < topLevelRecords.length; i++) { |
| 686 var record = topLevelRecords[i]; |
| 687 if (record.type() === WebInspector.TimelineModel.RecordType.Program) |
| 688 this._mainThreadTasks.push(record); |
| 689 if (record.type() === WebInspector.TimelineModel.RecordType.GPUTask) |
| 690 this._gpuThreadTasks.push(record); |
| 691 } |
| 692 this._records = topLevelRecords; |
| 693 }, |
| 694 |
| 695 /** |
| 696 * @param {!Array.<!WebInspector.TracingModel.Event>} threadEvents |
| 697 * @return {!Array.<!WebInspector.TimelineModel.Record>} |
| 698 */ |
| 699 _buildTimelineRecordsForThread: function(threadEvents) |
| 700 { |
| 701 var recordStack = []; |
| 702 var topLevelRecords = []; |
| 703 |
| 704 for (var i = 0, size = threadEvents.length; i < size; ++i) { |
| 705 var event = threadEvents[i]; |
| 706 for (var top = recordStack.peekLast(); top && top._event.endTime <=
event.startTime; top = recordStack.peekLast()) { |
| 707 recordStack.pop(); |
| 708 if (!recordStack.length) |
| 709 topLevelRecords.push(top); |
| 710 } |
| 711 if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd || even
t.phase === WebInspector.TracingModel.Phase.NestableAsyncEnd) |
| 712 continue; |
| 713 var parentRecord = recordStack.peekLast(); |
| 714 // Maintain the back-end logic of old timeline, skip console.time()
/ console.timeEnd() that are not properly nested. |
| 715 if (WebInspector.TracingModel.isAsyncBeginPhase(event.phase) && pare
ntRecord && event.endTime > parentRecord._event.endTime) |
| 716 continue; |
| 717 var record = new WebInspector.TimelineModel.Record(this, event); |
| 718 if (WebInspector.TimelineUIUtils.isMarkerEvent(event)) |
| 719 this._eventDividerRecords.push(record); |
| 720 if (!this._recordFilter.accept(record)) |
| 721 continue; |
| 722 if (parentRecord) |
| 723 parentRecord._addChild(record); |
| 724 if (event.endTime) |
| 725 recordStack.push(record); |
| 726 } |
| 727 |
| 728 if (recordStack.length) |
| 729 topLevelRecords.push(recordStack[0]); |
| 730 |
| 731 return topLevelRecords; |
| 732 }, |
| 733 |
| 734 _resetProcessingState: function() |
| 735 { |
| 736 this._sendRequestEvents = {}; |
| 737 this._timerEvents = {}; |
| 738 this._requestAnimationFrameEvents = {}; |
| 739 this._invalidationTracker = new WebInspector.InvalidationTracker(); |
| 740 this._layoutInvalidate = {}; |
| 741 this._lastScheduleStyleRecalculation = {}; |
| 742 this._webSocketCreateEvents = {}; |
| 743 this._paintImageEventByPixelRefId = {}; |
| 744 this._lastPaintForLayer = {}; |
| 745 this._lastRecalculateStylesEvent = null; |
| 746 this._currentScriptEvent = null; |
| 747 this._eventStack = []; |
| 748 }, |
| 749 |
| 750 /** |
| 751 * @param {number} startTime |
| 752 * @param {?number} endTime |
| 753 * @param {!WebInspector.TracingModel.Thread} mainThread |
| 754 * @param {!WebInspector.TracingModel.Thread} thread |
| 755 */ |
| 756 _processThreadEvents: function(startTime, endTime, mainThread, thread) |
| 757 { |
| 758 var events = thread.events(); |
| 759 var length = events.length; |
| 760 var i = events.lowerBound(startTime, function (time, event) { return tim
e - event.startTime }); |
| 761 |
| 762 var threadEvents; |
| 763 var virtualThread = null; |
| 764 if (thread === mainThread) { |
| 765 threadEvents = this._mainThreadEvents; |
| 766 this._mainThreadAsyncEvents = this._mainThreadAsyncEvents.concat(thr
ead.asyncEvents()); |
| 767 } else { |
| 768 virtualThread = new WebInspector.TimelineModel.VirtualThread(thread.
name()); |
| 769 threadEvents = virtualThread.events; |
| 770 virtualThread.asyncEvents = virtualThread.asyncEvents.concat(thread.
asyncEvents()); |
| 771 this._virtualThreads.push(virtualThread); |
| 772 } |
| 773 |
| 774 this._eventStack = []; |
| 775 for (; i < length; i++) { |
| 776 var event = events[i]; |
| 777 if (endTime && event.startTime >= endTime) |
| 778 break; |
| 779 this._processEvent(event); |
| 780 threadEvents.push(event); |
| 781 this._inspectedTargetEvents.push(event); |
| 782 } |
| 783 |
| 784 if (this._cpuProfiles && thread.target()) { |
| 785 var cpuProfile = this._cpuProfiles[thread.target().id()]; |
| 786 if (cpuProfile) { |
| 787 var jsSamples = WebInspector.TimelineJSProfileProcessor.generate
TracingEventsFromCpuProfile(cpuProfile, thread); |
| 788 var mergedEvents = threadEvents.mergeOrdered(jsSamples, WebInspe
ctor.TracingModel.Event.orderedCompareStartTime); |
| 789 var jsFrameEvents = WebInspector.TimelineJSProfileProcessor.gene
rateJSFrameEvents(mergedEvents); |
| 790 mergedEvents = jsFrameEvents.mergeOrdered(mergedEvents, WebInspe
ctor.TracingModel.Event.orderedCompareStartTime); |
| 791 if (virtualThread) |
| 792 virtualThread.events = mergedEvents; |
| 793 else |
| 794 this._mainThreadEvents = mergedEvents; |
| 795 this._inspectedTargetEvents = this._inspectedTargetEvents.concat
(jsSamples, jsFrameEvents); |
| 796 } |
| 797 } |
| 798 }, |
| 799 |
| 800 /** |
| 801 * @param {!WebInspector.TracingModel.Event} event |
| 802 */ |
| 803 _processEvent: function(event) |
| 804 { |
| 805 var recordTypes = WebInspector.TimelineModel.RecordType; |
| 806 |
| 807 var eventStack = this._eventStack; |
| 808 while (eventStack.length && eventStack.peekLast().endTime < event.startT
ime) |
| 809 eventStack.pop(); |
| 810 var duration = event.duration; |
| 811 if (duration) { |
| 812 if (eventStack.length) { |
| 813 var parent = eventStack.peekLast(); |
| 814 parent.selfTime -= duration; |
| 815 } |
| 816 event.selfTime = duration; |
| 817 eventStack.push(event); |
| 818 } |
| 819 |
| 820 if (this._currentScriptEvent && event.startTime > this._currentScriptEve
nt.endTime) |
| 821 this._currentScriptEvent = null; |
| 822 |
| 823 switch (event.name) { |
| 824 case recordTypes.CallStack: |
| 825 var lastMainThreadEvent = this.mainThreadEvents().peekLast(); |
| 826 if (lastMainThreadEvent && event.args["stack"] && event.args["stack"
].length) |
| 827 lastMainThreadEvent.stackTrace = event.args["stack"]; |
| 828 break; |
| 829 |
| 830 case recordTypes.CpuProfile: |
| 831 this._cpuProfile = event.args["data"]["cpuProfile"]; |
| 832 break; |
| 833 |
| 834 case recordTypes.ResourceSendRequest: |
| 835 this._sendRequestEvents[event.args["data"]["requestId"]] = event; |
| 836 event.imageURL = event.args["data"]["url"]; |
| 837 break; |
| 838 |
| 839 case recordTypes.ResourceReceiveResponse: |
| 840 case recordTypes.ResourceReceivedData: |
| 841 case recordTypes.ResourceFinish: |
| 842 event.initiator = this._sendRequestEvents[event.args["data"]["reques
tId"]]; |
| 843 if (event.initiator) |
| 844 event.imageURL = event.initiator.imageURL; |
| 845 break; |
| 846 |
| 847 case recordTypes.TimerInstall: |
| 848 this._timerEvents[event.args["data"]["timerId"]] = event; |
| 849 break; |
| 850 |
| 851 case recordTypes.TimerFire: |
| 852 event.initiator = this._timerEvents[event.args["data"]["timerId"]]; |
| 853 break; |
| 854 |
| 855 case recordTypes.RequestAnimationFrame: |
| 856 this._requestAnimationFrameEvents[event.args["data"]["id"]] = event; |
| 857 break; |
| 858 |
| 859 case recordTypes.FireAnimationFrame: |
| 860 event.initiator = this._requestAnimationFrameEvents[event.args["data
"]["id"]]; |
| 861 break; |
| 862 |
| 863 case recordTypes.ScheduleStyleRecalculation: |
| 864 this._lastScheduleStyleRecalculation[event.args["frame"]] = event; |
| 865 break; |
| 866 |
| 867 case recordTypes.RecalculateStyles: |
| 868 this._invalidationTracker.didRecalcStyle(event); |
| 869 event.initiator = this._lastScheduleStyleRecalculation[event.args["f
rame"]]; |
| 870 this._lastRecalculateStylesEvent = event; |
| 871 break; |
| 872 |
| 873 case recordTypes.StyleRecalcInvalidationTracking: |
| 874 case recordTypes.LayoutInvalidationTracking: |
| 875 case recordTypes.LayerInvalidationTracking: |
| 876 case recordTypes.PaintInvalidationTracking: |
| 877 this._invalidationTracker.addInvalidation(event); |
| 878 break; |
| 879 |
| 880 case recordTypes.InvalidateLayout: |
| 881 // Consider style recalculation as a reason for layout invalidation, |
| 882 // but only if we had no earlier layout invalidation records. |
| 883 var layoutInitator = event; |
| 884 var frameId = event.args["frame"]; |
| 885 if (!this._layoutInvalidate[frameId] && this._lastRecalculateStylesE
vent && this._lastRecalculateStylesEvent.endTime > event.startTime) |
| 886 layoutInitator = this._lastRecalculateStylesEvent.initiator; |
| 887 this._layoutInvalidate[frameId] = layoutInitator; |
| 888 break; |
| 889 |
| 890 case recordTypes.Layout: |
| 891 this._invalidationTracker.didLayout(event); |
| 892 var frameId = event.args["beginData"]["frame"]; |
| 893 event.initiator = this._layoutInvalidate[frameId]; |
| 894 // In case we have no closing Layout event, endData is not available
. |
| 895 if (event.args["endData"]) { |
| 896 event.backendNodeId = event.args["endData"]["rootNode"]; |
| 897 event.highlightQuad = event.args["endData"]["root"]; |
| 898 } |
| 899 this._layoutInvalidate[frameId] = null; |
| 900 if (this._currentScriptEvent) |
| 901 event.warning = WebInspector.UIString("Forced synchronous layout
is a possible performance bottleneck."); |
| 902 break; |
| 903 |
| 904 case recordTypes.WebSocketCreate: |
| 905 this._webSocketCreateEvents[event.args["data"]["identifier"]] = even
t; |
| 906 break; |
| 907 |
| 908 case recordTypes.WebSocketSendHandshakeRequest: |
| 909 case recordTypes.WebSocketReceiveHandshakeResponse: |
| 910 case recordTypes.WebSocketDestroy: |
| 911 event.initiator = this._webSocketCreateEvents[event.args["data"]["id
entifier"]]; |
| 912 break; |
| 913 |
| 914 case recordTypes.EvaluateScript: |
| 915 case recordTypes.FunctionCall: |
| 916 if (!this._currentScriptEvent) |
| 917 this._currentScriptEvent = event; |
| 918 break; |
| 919 |
| 920 case recordTypes.SetLayerTreeId: |
| 921 this._inspectedTargetLayerTreeId = event.args["layerTreeId"]; |
| 922 break; |
| 923 |
| 924 case recordTypes.Paint: |
| 925 this._invalidationTracker.didPaint(event); |
| 926 event.highlightQuad = event.args["data"]["clip"]; |
| 927 event.backendNodeId = event.args["data"]["nodeId"]; |
| 928 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLay
er); |
| 929 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== th
is._inspectedTargetLayerTreeId) |
| 930 break; |
| 931 // Only keep layer paint events, skip paints for subframes that get
painted to the same layer as parent. |
| 932 if (!event.args["data"]["layerId"]) |
| 933 break; |
| 934 this._lastPaintForLayer[layerUpdateEvent.args["layerId"]] = event; |
| 935 break; |
| 936 |
| 937 case recordTypes.PictureSnapshot: |
| 938 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLay
er); |
| 939 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== th
is._inspectedTargetLayerTreeId) |
| 940 break; |
| 941 var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["laye
rId"]]; |
| 942 if (paintEvent) |
| 943 paintEvent.picture = event; |
| 944 break; |
| 945 |
| 946 case recordTypes.ScrollLayer: |
| 947 event.backendNodeId = event.args["data"]["nodeId"]; |
| 948 break; |
| 949 |
| 950 case recordTypes.PaintImage: |
| 951 event.backendNodeId = event.args["data"]["nodeId"]; |
| 952 event.imageURL = event.args["data"]["url"]; |
| 953 break; |
| 954 |
| 955 case recordTypes.DecodeImage: |
| 956 case recordTypes.ResizeImage: |
| 957 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage
); |
| 958 if (!paintImageEvent) { |
| 959 var decodeLazyPixelRefEvent = this._findAncestorEvent(recordType
s.DecodeLazyPixelRef); |
| 960 paintImageEvent = decodeLazyPixelRefEvent && this._paintImageEve
ntByPixelRefId[decodeLazyPixelRefEvent.args["LazyPixelRef"]]; |
| 961 } |
| 962 if (!paintImageEvent) |
| 963 break; |
| 964 event.backendNodeId = paintImageEvent.backendNodeId; |
| 965 event.imageURL = paintImageEvent.imageURL; |
| 966 break; |
| 967 |
| 968 case recordTypes.DrawLazyPixelRef: |
| 969 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage
); |
| 970 if (!paintImageEvent) |
| 971 break; |
| 972 this._paintImageEventByPixelRefId[event.args["LazyPixelRef"]] = pain
tImageEvent; |
| 973 event.backendNodeId = paintImageEvent.backendNodeId; |
| 974 event.imageURL = paintImageEvent.imageURL; |
| 975 break; |
| 976 } |
| 977 }, |
| 978 |
| 979 /** |
| 980 * @param {string} name |
| 981 * @return {?WebInspector.TracingModel.Event} |
| 982 */ |
| 983 _findAncestorEvent: function(name) |
| 984 { |
| 985 for (var i = this._eventStack.length - 1; i >= 0; --i) { |
| 986 var event = this._eventStack[i]; |
| 987 if (event.name === name) |
| 988 return event; |
| 989 } |
| 990 return null; |
| 991 }, |
| 992 |
| 993 /** |
233 * @param {!Blob} file | 994 * @param {!Blob} file |
234 * @param {!WebInspector.Progress} progress | 995 * @param {!WebInspector.Progress} progress |
235 */ | 996 */ |
236 loadFromFile: function(file, progress) | 997 loadFromFile: function(file, progress) |
237 { | 998 { |
238 var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this,
progress); | 999 var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this,
progress); |
239 var fileReader = this._createFileReader(file, delegate); | 1000 var fileReader = this._createFileReader(file, delegate); |
240 var loader = this.createLoader(fileReader, progress); | 1001 var loader = this.createLoader(fileReader, progress); |
241 fileReader.start(loader); | 1002 fileReader.start(loader); |
242 }, | 1003 }, |
243 | 1004 |
244 /** | |
245 * @param {!WebInspector.ChunkedFileReader} fileReader | |
246 * @param {!WebInspector.Progress} progress | |
247 * @return {!WebInspector.OutputStream} | |
248 */ | |
249 createLoader: function(fileReader, progress) | |
250 { | |
251 throw new Error("Not implemented."); | |
252 }, | |
253 | |
254 _createFileReader: function(file, delegate) | 1005 _createFileReader: function(file, delegate) |
255 { | 1006 { |
256 return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineMod
el.TransferChunkLengthBytes, delegate); | 1007 return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineMod
el.TransferChunkLengthBytes, delegate); |
257 }, | 1008 }, |
258 | 1009 |
259 _createFileWriter: function() | 1010 _createFileWriter: function() |
260 { | 1011 { |
261 return new WebInspector.FileOutputStream(); | 1012 return new WebInspector.FileOutputStream(); |
262 }, | 1013 }, |
263 | 1014 |
264 saveToFile: function() | 1015 saveToFile: function() |
265 { | 1016 { |
266 var now = new Date(); | 1017 var now = new Date(); |
267 var fileName = "TimelineRawData-" + now.toISO8601Compact() + ".json"; | 1018 var fileName = "TimelineRawData-" + now.toISO8601Compact() + ".json"; |
268 var stream = this._createFileWriter(); | 1019 var stream = this._createFileWriter(); |
269 | 1020 |
270 /** | 1021 /** |
271 * @param {boolean} accepted | 1022 * @param {boolean} accepted |
272 * @this {WebInspector.TimelineModel} | 1023 * @this {WebInspector.TimelineModel} |
273 */ | 1024 */ |
274 function callback(accepted) | 1025 function callback(accepted) |
275 { | 1026 { |
276 if (!accepted) | 1027 if (!accepted) |
277 return; | 1028 return; |
278 this.writeToStream(stream); | 1029 this.writeToStream(stream); |
279 } | 1030 } |
280 stream.open(fileName, callback.bind(this)); | 1031 stream.open(fileName, callback.bind(this)); |
281 }, | 1032 }, |
282 | 1033 |
283 /** | |
284 * @param {!WebInspector.OutputStream} stream | |
285 */ | |
286 writeToStream: function(stream) | |
287 { | |
288 throw new Error("Not implemented."); | |
289 }, | |
290 | |
291 reset: function() | 1034 reset: function() |
292 { | 1035 { |
| 1036 this._virtualThreads = []; |
| 1037 this._mainThreadEvents = []; |
| 1038 this._mainThreadAsyncEvents = []; |
| 1039 this._inspectedTargetEvents = []; |
| 1040 |
293 this._records = []; | 1041 this._records = []; |
294 this._minimumRecordTime = 0; | |
295 this._maximumRecordTime = 0; | |
296 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ | 1042 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ |
297 this._mainThreadTasks = []; | 1043 this._mainThreadTasks = []; |
298 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ | 1044 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ |
299 this._gpuThreadTasks = []; | 1045 this._gpuThreadTasks = []; |
300 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ | 1046 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ |
301 this._eventDividerRecords = []; | 1047 this._eventDividerRecords = []; |
302 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsC
leared); | 1048 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsC
leared); |
303 }, | 1049 }, |
304 | 1050 |
305 /** | 1051 /** |
306 * @return {number} | 1052 * @return {number} |
307 */ | 1053 */ |
308 minimumRecordTime: function() | 1054 minimumRecordTime: function() |
309 { | 1055 { |
310 throw new Error("Not implemented."); | 1056 return this._tracingModel.minimumRecordTime(); |
311 }, | 1057 }, |
312 | 1058 |
313 /** | 1059 /** |
314 * @return {number} | 1060 * @return {number} |
315 */ | 1061 */ |
316 maximumRecordTime: function() | 1062 maximumRecordTime: function() |
317 { | 1063 { |
318 throw new Error("Not implemented."); | 1064 return this._tracingModel.maximumRecordTime(); |
| 1065 }, |
| 1066 |
| 1067 /** |
| 1068 * @return {!Array.<!WebInspector.TracingModel.Event>} |
| 1069 */ |
| 1070 inspectedTargetEvents: function() |
| 1071 { |
| 1072 return this._inspectedTargetEvents; |
| 1073 }, |
| 1074 |
| 1075 /** |
| 1076 * @return {!Array.<!WebInspector.TracingModel.Event>} |
| 1077 */ |
| 1078 mainThreadEvents: function() |
| 1079 { |
| 1080 return this._mainThreadEvents; |
| 1081 }, |
| 1082 |
| 1083 /** |
| 1084 * @param {!Array.<!WebInspector.TracingModel.Event>} events |
| 1085 */ |
| 1086 _setMainThreadEvents: function(events) |
| 1087 { |
| 1088 this._mainThreadEvents = events; |
| 1089 }, |
| 1090 |
| 1091 /** |
| 1092 * @return {!Array.<!Array.<!WebInspector.TracingModel.Event>>} |
| 1093 */ |
| 1094 mainThreadAsyncEvents: function() |
| 1095 { |
| 1096 return this._mainThreadAsyncEvents; |
| 1097 }, |
| 1098 |
| 1099 /** |
| 1100 * @return {!Array.<!WebInspector.TimelineModel.VirtualThread>} |
| 1101 */ |
| 1102 virtualThreads: function() |
| 1103 { |
| 1104 return this._virtualThreads; |
| 1105 }, |
| 1106 |
| 1107 /** |
| 1108 * @param {!WebInspector.ChunkedFileReader} fileReader |
| 1109 * @param {!WebInspector.Progress} progress |
| 1110 * @return {!WebInspector.OutputStream} |
| 1111 */ |
| 1112 createLoader: function(fileReader, progress) |
| 1113 { |
| 1114 return new WebInspector.TracingModelLoader(this, fileReader, progress); |
| 1115 }, |
| 1116 |
| 1117 /** |
| 1118 * @param {!WebInspector.OutputStream} stream |
| 1119 */ |
| 1120 writeToStream: function(stream) |
| 1121 { |
| 1122 var saver = new WebInspector.TracingTimelineSaver(stream); |
| 1123 this._tracingModel.writeToStream(stream, saver); |
319 }, | 1124 }, |
320 | 1125 |
321 /** | 1126 /** |
322 * @return {boolean} | 1127 * @return {boolean} |
323 */ | 1128 */ |
324 isEmpty: function() | 1129 isEmpty: function() |
325 { | 1130 { |
326 return this.minimumRecordTime() === 0 && this.maximumRecordTime() === 0; | 1131 return this.minimumRecordTime() === 0 && this.maximumRecordTime() === 0; |
327 }, | 1132 }, |
328 | 1133 |
(...skipping 14 matching lines...) Expand all Loading... |
343 }, | 1148 }, |
344 | 1149 |
345 /** | 1150 /** |
346 * @return {!Array.<!WebInspector.TimelineModel.Record>} | 1151 * @return {!Array.<!WebInspector.TimelineModel.Record>} |
347 */ | 1152 */ |
348 eventDividerRecords: function() | 1153 eventDividerRecords: function() |
349 { | 1154 { |
350 return this._eventDividerRecords; | 1155 return this._eventDividerRecords; |
351 }, | 1156 }, |
352 | 1157 |
| 1158 |
353 __proto__: WebInspector.Object.prototype | 1159 __proto__: WebInspector.Object.prototype |
354 } | 1160 } |
355 | 1161 |
356 /** | 1162 /** |
357 * @interface | |
358 */ | |
359 WebInspector.TimelineModel.Record = function() | |
360 { | |
361 } | |
362 | |
363 WebInspector.TimelineModel.Record.prototype = { | |
364 /** | |
365 * @return {?Array.<!ConsoleAgent.CallFrame>} | |
366 */ | |
367 callSiteStackTrace: function() { }, | |
368 | |
369 /** | |
370 * @return {?WebInspector.TimelineModel.Record} | |
371 */ | |
372 initiator: function() { }, | |
373 | |
374 /** | |
375 * @return {?WebInspector.Target} | |
376 */ | |
377 target: function() { }, | |
378 | |
379 /** | |
380 * @return {number} | |
381 */ | |
382 selfTime: function() { }, | |
383 | |
384 /** | |
385 * @return {!Array.<!WebInspector.TimelineModel.Record>} | |
386 */ | |
387 children: function() { }, | |
388 | |
389 /** | |
390 * @return {number} | |
391 */ | |
392 startTime: function() { }, | |
393 | |
394 /** | |
395 * @return {string} | |
396 */ | |
397 thread: function() { }, | |
398 | |
399 /** | |
400 * @return {number} | |
401 */ | |
402 endTime: function() { }, | |
403 | |
404 /** | |
405 * @param {number} endTime | |
406 */ | |
407 setEndTime: function(endTime) { }, | |
408 | |
409 /** | |
410 * @return {!Object} | |
411 */ | |
412 data: function() { }, | |
413 | |
414 /** | |
415 * @return {string} | |
416 */ | |
417 type: function() { }, | |
418 | |
419 /** | |
420 * @return {string} | |
421 */ | |
422 frameId: function() { }, | |
423 | |
424 /** | |
425 * @return {?Array.<!ConsoleAgent.CallFrame>} | |
426 */ | |
427 stackTrace: function() { }, | |
428 | |
429 /** | |
430 * @param {string} key | |
431 * @return {?Object} | |
432 */ | |
433 getUserObject: function(key) { }, | |
434 | |
435 /** | |
436 * @param {string} key | |
437 * @param {?Object|undefined} value | |
438 */ | |
439 setUserObject: function(key, value) { }, | |
440 | |
441 /** | |
442 * @return {?Array.<string>} | |
443 */ | |
444 warnings: function() { } | |
445 } | |
446 | |
447 /** | |
448 * @constructor | 1163 * @constructor |
449 */ | 1164 */ |
450 WebInspector.TimelineModel.Filter = function() | 1165 WebInspector.TimelineModel.Filter = function() |
451 { | 1166 { |
452 /** @type {!WebInspector.TimelineModel} */ | 1167 /** @type {!WebInspector.TimelineModel} */ |
453 this._model; | 1168 this._model; |
454 } | 1169 } |
455 | 1170 |
456 WebInspector.TimelineModel.Filter.prototype = { | 1171 WebInspector.TimelineModel.Filter.prototype = { |
457 /** | 1172 /** |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
610 case FileError.NOT_READABLE_ERR: | 1325 case FileError.NOT_READABLE_ERR: |
611 WebInspector.console.error(WebInspector.UIString("File \"%s\" is not
readable", reader.fileName())); | 1326 WebInspector.console.error(WebInspector.UIString("File \"%s\" is not
readable", reader.fileName())); |
612 break; | 1327 break; |
613 case FileError.ABORT_ERR: | 1328 case FileError.ABORT_ERR: |
614 break; | 1329 break; |
615 default: | 1330 default: |
616 WebInspector.console.error(WebInspector.UIString("An error occurred
while reading the file \"%s\"", reader.fileName())); | 1331 WebInspector.console.error(WebInspector.UIString("An error occurred
while reading the file \"%s\"", reader.fileName())); |
617 } | 1332 } |
618 } | 1333 } |
619 } | 1334 } |
| 1335 |
| 1336 |
| 1337 /** |
| 1338 * @interface |
| 1339 */ |
| 1340 WebInspector.TraceEventFilter = function() { } |
| 1341 |
| 1342 WebInspector.TraceEventFilter.prototype = { |
| 1343 /** |
| 1344 * @param {!WebInspector.TracingModel.Event} event |
| 1345 * @return {boolean} |
| 1346 */ |
| 1347 accept: function(event) { } |
| 1348 } |
| 1349 |
| 1350 /** |
| 1351 * @constructor |
| 1352 * @implements {WebInspector.TraceEventFilter} |
| 1353 * @param {!Array.<string>} eventNames |
| 1354 */ |
| 1355 WebInspector.TraceEventNameFilter = function(eventNames) |
| 1356 { |
| 1357 this._eventNames = eventNames.keySet(); |
| 1358 } |
| 1359 |
| 1360 WebInspector.TraceEventNameFilter.prototype = { |
| 1361 /** |
| 1362 * @param {!WebInspector.TracingModel.Event} event |
| 1363 * @return {boolean} |
| 1364 */ |
| 1365 accept: function(event) |
| 1366 { |
| 1367 throw new Error("Not implemented."); |
| 1368 } |
| 1369 } |
| 1370 |
| 1371 /** |
| 1372 * @constructor |
| 1373 * @extends {WebInspector.TraceEventNameFilter} |
| 1374 * @param {!Array.<string>} includeNames |
| 1375 */ |
| 1376 WebInspector.InclusiveTraceEventNameFilter = function(includeNames) |
| 1377 { |
| 1378 WebInspector.TraceEventNameFilter.call(this, includeNames) |
| 1379 } |
| 1380 |
| 1381 WebInspector.InclusiveTraceEventNameFilter.prototype = { |
| 1382 /** |
| 1383 * @override |
| 1384 * @param {!WebInspector.TracingModel.Event} event |
| 1385 * @return {boolean} |
| 1386 */ |
| 1387 accept: function(event) |
| 1388 { |
| 1389 return event.category === WebInspector.TracingModel.ConsoleEventCategory
|| !!this._eventNames[event.name]; |
| 1390 }, |
| 1391 __proto__: WebInspector.TraceEventNameFilter.prototype |
| 1392 } |
| 1393 |
| 1394 /** |
| 1395 * @constructor |
| 1396 * @extends {WebInspector.TraceEventNameFilter} |
| 1397 * @param {!Array.<string>} excludeNames |
| 1398 */ |
| 1399 WebInspector.ExclusiveTraceEventNameFilter = function(excludeNames) |
| 1400 { |
| 1401 WebInspector.TraceEventNameFilter.call(this, excludeNames) |
| 1402 } |
| 1403 |
| 1404 WebInspector.ExclusiveTraceEventNameFilter.prototype = { |
| 1405 /** |
| 1406 * @override |
| 1407 * @param {!WebInspector.TracingModel.Event} event |
| 1408 * @return {boolean} |
| 1409 */ |
| 1410 accept: function(event) |
| 1411 { |
| 1412 return !this._eventNames[event.name]; |
| 1413 }, |
| 1414 __proto__: WebInspector.TraceEventNameFilter.prototype |
| 1415 } |
| 1416 |
| 1417 /** |
| 1418 * @constructor |
| 1419 * @implements {WebInspector.OutputStream} |
| 1420 * @param {!WebInspector.TimelineModel} model |
| 1421 * @param {!{cancel: function()}} reader |
| 1422 * @param {!WebInspector.Progress} progress |
| 1423 */ |
| 1424 WebInspector.TracingModelLoader = function(model, reader, progress) |
| 1425 { |
| 1426 this._model = model; |
| 1427 this._reader = reader; |
| 1428 this._progress = progress; |
| 1429 this._buffer = ""; |
| 1430 this._firstChunk = true; |
| 1431 this._loader = new WebInspector.TracingModel.Loader(model._tracingModel); |
| 1432 } |
| 1433 |
| 1434 WebInspector.TracingModelLoader.prototype = { |
| 1435 /** |
| 1436 * @param {string} chunk |
| 1437 */ |
| 1438 write: function(chunk) |
| 1439 { |
| 1440 var data = this._buffer + chunk; |
| 1441 var lastIndex = 0; |
| 1442 var index; |
| 1443 do { |
| 1444 index = lastIndex; |
| 1445 lastIndex = WebInspector.TextUtils.findBalancedCurlyBrackets(data, i
ndex); |
| 1446 } while (lastIndex !== -1) |
| 1447 |
| 1448 var json = data.slice(0, index) + "]"; |
| 1449 this._buffer = data.slice(index); |
| 1450 |
| 1451 if (!index) |
| 1452 return; |
| 1453 |
| 1454 if (this._firstChunk) { |
| 1455 this._model._startCollectingTraceEvents(true); |
| 1456 } else { |
| 1457 var commaIndex = json.indexOf(","); |
| 1458 if (commaIndex !== -1) |
| 1459 json = json.slice(commaIndex + 1); |
| 1460 json = "[" + json; |
| 1461 } |
| 1462 |
| 1463 var items; |
| 1464 try { |
| 1465 items = /** @type {!Array.<!WebInspector.TracingManager.EventPayload
>} */ (JSON.parse(json)); |
| 1466 } catch (e) { |
| 1467 this._reportErrorAndCancelLoading("Malformed timeline data: " + e); |
| 1468 return; |
| 1469 } |
| 1470 |
| 1471 if (this._firstChunk) { |
| 1472 this._firstChunk = false; |
| 1473 if (this._looksLikeAppVersion(items[0])) { |
| 1474 this._reportErrorAndCancelLoading("Old Timeline format is not su
pported."); |
| 1475 return; |
| 1476 } |
| 1477 } |
| 1478 |
| 1479 try { |
| 1480 this._loader.loadNextChunk(items); |
| 1481 } catch(e) { |
| 1482 this._reportErrorAndCancelLoading("Malformed timeline data: " + e); |
| 1483 return; |
| 1484 } |
| 1485 }, |
| 1486 |
| 1487 _reportErrorAndCancelLoading: function(messsage) |
| 1488 { |
| 1489 WebInspector.console.error(messsage); |
| 1490 this._model._onTracingComplete(); |
| 1491 this._model.reset(); |
| 1492 this._reader.cancel(); |
| 1493 this._progress.done(); |
| 1494 }, |
| 1495 |
| 1496 _looksLikeAppVersion: function(item) |
| 1497 { |
| 1498 return typeof item === "string" && item.indexOf("Chrome") !== -1; |
| 1499 }, |
| 1500 |
| 1501 close: function() |
| 1502 { |
| 1503 this._loader.finish(); |
| 1504 this._model._onTracingComplete(); |
| 1505 } |
| 1506 } |
| 1507 |
| 1508 /** |
| 1509 * @constructor |
| 1510 * @param {!WebInspector.OutputStream} stream |
| 1511 * @implements {WebInspector.OutputStreamDelegate} |
| 1512 */ |
| 1513 WebInspector.TracingTimelineSaver = function(stream) |
| 1514 { |
| 1515 this._stream = stream; |
| 1516 } |
| 1517 |
| 1518 WebInspector.TracingTimelineSaver.prototype = { |
| 1519 onTransferStarted: function() |
| 1520 { |
| 1521 this._stream.write("["); |
| 1522 }, |
| 1523 |
| 1524 onTransferFinished: function() |
| 1525 { |
| 1526 this._stream.write("]"); |
| 1527 }, |
| 1528 |
| 1529 /** |
| 1530 * @param {!WebInspector.ChunkedReader} reader |
| 1531 */ |
| 1532 onChunkTransferred: function(reader) { }, |
| 1533 |
| 1534 /** |
| 1535 * @param {!WebInspector.ChunkedReader} reader |
| 1536 * @param {!Event} event |
| 1537 */ |
| 1538 onError: function(reader, event) { }, |
| 1539 } |
| 1540 |
| 1541 /** |
| 1542 * @constructor |
| 1543 * @param {!WebInspector.TracingModel.Event} event |
| 1544 */ |
| 1545 WebInspector.InvalidationTrackingEvent = function(event) |
| 1546 { |
| 1547 this.type = event.name; |
| 1548 this.frameId = event.args["data"]["frame"]; |
| 1549 this.nodeId = event.args["data"]["nodeId"]; |
| 1550 this.nodeName = event.args["data"]["nodeName"]; |
| 1551 this.paintId = event.args["data"]["paintId"]; |
| 1552 this.reason = event.args["data"]["reason"]; |
| 1553 this.stackTrace = event.args["data"]["stackTrace"]; |
| 1554 } |
| 1555 |
| 1556 /** |
| 1557 * @constructor |
| 1558 */ |
| 1559 WebInspector.InvalidationTracker = function() |
| 1560 { |
| 1561 this._initializePerFrameState(); |
| 1562 } |
| 1563 |
| 1564 WebInspector.InvalidationTracker.prototype = { |
| 1565 /** |
| 1566 * @param {!WebInspector.TracingModel.Event} event |
| 1567 */ |
| 1568 addInvalidation: function(event) |
| 1569 { |
| 1570 var invalidation = new WebInspector.InvalidationTrackingEvent(event); |
| 1571 |
| 1572 this._startNewFrameIfNeeded(); |
| 1573 if (!invalidation.nodeId && !invalidation.paintId) { |
| 1574 console.error("Invalidation lacks node information."); |
| 1575 console.error(invalidation); |
| 1576 } |
| 1577 |
| 1578 // Record the paintIds for style recalc or layout invalidations. |
| 1579 // FIXME: This O(n^2) loop could be optimized with a map. |
| 1580 var recordTypes = WebInspector.TimelineModel.RecordType; |
| 1581 if (invalidation.type == recordTypes.PaintInvalidationTracking) |
| 1582 this._invalidationEvents.forEach(updatePaintId); |
| 1583 else |
| 1584 this._invalidationEvents.push(invalidation); |
| 1585 |
| 1586 function updatePaintId(invalidationToUpdate) |
| 1587 { |
| 1588 if (invalidationToUpdate.nodeId !== invalidation.nodeId) |
| 1589 return; |
| 1590 if (invalidationToUpdate.type === recordTypes.StyleRecalcInvalidatio
nTracking |
| 1591 || invalidationToUpdate.type === recordTypes.LayoutInvalidat
ionTracking) { |
| 1592 invalidationToUpdate.paintId = invalidation.paintId; |
| 1593 } |
| 1594 } |
| 1595 }, |
| 1596 |
| 1597 /** |
| 1598 * @param {!WebInspector.TracingModel.Event} styleRecalcEvent |
| 1599 */ |
| 1600 didRecalcStyle: function(styleRecalcEvent) |
| 1601 { |
| 1602 var recalcFrameId = styleRecalcEvent.args["frame"]; |
| 1603 var index = this._lastStyleRecalcEventIndex; |
| 1604 var invalidationCount = this._invalidationEvents.length; |
| 1605 for (; index < invalidationCount; index++) { |
| 1606 var invalidation = this._invalidationEvents[index]; |
| 1607 if (invalidation.type !== WebInspector.TimelineModel.RecordType.Styl
eRecalcInvalidationTracking) |
| 1608 continue; |
| 1609 if (invalidation.frameId === recalcFrameId) |
| 1610 this._addInvalidationTrackingEvent(styleRecalcEvent, invalidatio
n); |
| 1611 } |
| 1612 |
| 1613 this._lastStyleRecalcEventIndex = invalidationCount; |
| 1614 }, |
| 1615 |
| 1616 /** |
| 1617 * @param {!WebInspector.TracingModel.Event} layoutEvent |
| 1618 */ |
| 1619 didLayout: function(layoutEvent) |
| 1620 { |
| 1621 var layoutFrameId = layoutEvent.args["beginData"]["frame"]; |
| 1622 var index = this._lastLayoutEventIndex; |
| 1623 var invalidationCount = this._invalidationEvents.length; |
| 1624 for (; index < invalidationCount; index++) { |
| 1625 var invalidation = this._invalidationEvents[index]; |
| 1626 if (invalidation.type !== WebInspector.TimelineModel.RecordType.Layo
utInvalidationTracking) |
| 1627 continue; |
| 1628 if (invalidation.frameId === layoutFrameId) |
| 1629 this._addInvalidationTrackingEvent(layoutEvent, invalidation); |
| 1630 } |
| 1631 |
| 1632 this._lastLayoutEventIndex = invalidationCount; |
| 1633 }, |
| 1634 |
| 1635 /** |
| 1636 * @param {!WebInspector.TracingModel.Event} paintEvent |
| 1637 */ |
| 1638 didPaint: function(paintEvent) |
| 1639 { |
| 1640 this._didPaint = true; |
| 1641 |
| 1642 // If a paint doesn't have a corresponding graphics layer id, it paints |
| 1643 // into its parent so add an effectivePaintId to these events. |
| 1644 var layerId = paintEvent.args["data"]["layerId"]; |
| 1645 if (layerId) |
| 1646 this._lastPaintWithLayer = paintEvent; |
| 1647 if (!this._lastPaintWithLayer) { |
| 1648 console.error("Failed to find the paint container for a paint event.
"); |
| 1649 return; |
| 1650 } |
| 1651 |
| 1652 var effectivePaintId = this._lastPaintWithLayer.args["data"]["nodeId"]; |
| 1653 var frameId = paintEvent.args["data"]["frame"]; |
| 1654 this._invalidationEvents.forEach(recordInvalidationForPaint.bind(this)); |
| 1655 |
| 1656 /** |
| 1657 * @param {!WebInspector.InvalidationTrackingEvent} invalidation |
| 1658 * @this {WebInspector.InvalidationTracker} |
| 1659 */ |
| 1660 function recordInvalidationForPaint(invalidation) |
| 1661 { |
| 1662 if (invalidation.paintId === effectivePaintId && invalidation.frameI
d === frameId) |
| 1663 this._addInvalidationTrackingEvent(paintEvent, invalidation); |
| 1664 } |
| 1665 }, |
| 1666 |
| 1667 /** |
| 1668 * @param {!WebInspector.TracingModel.Event} event |
| 1669 * @param {!WebInspector.InvalidationTrackingEvent} invalidation |
| 1670 */ |
| 1671 _addInvalidationTrackingEvent: function(event, invalidation) |
| 1672 { |
| 1673 if (!event.invalidationTrackingEvents) |
| 1674 event.invalidationTrackingEvents = [ invalidation ]; |
| 1675 else |
| 1676 event.invalidationTrackingEvents.push(invalidation); |
| 1677 }, |
| 1678 |
| 1679 _startNewFrameIfNeeded: function() |
| 1680 { |
| 1681 if (!this._didPaint) |
| 1682 return; |
| 1683 |
| 1684 this._initializePerFrameState(); |
| 1685 }, |
| 1686 |
| 1687 _initializePerFrameState: function() |
| 1688 { |
| 1689 /** @type {!Array.<!WebInspector.InvalidationTrackingEvent>} */ |
| 1690 this._invalidationEvents = []; |
| 1691 this._lastStyleRecalcEventIndex = 0; |
| 1692 this._lastLayoutEventIndex = 0; |
| 1693 this._lastPaintWithLayer = undefined; |
| 1694 this._didPaint = false; |
| 1695 } |
| 1696 } |
OLD | NEW |