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._jsProfilerStarted = true; |
| 408 this._currentTarget = WebInspector.context.flavor(WebInspector.Targe
t); |
| 409 this._configureCpuProfilerSamplingInterval(); |
| 410 this._currentTarget.profilerAgent().start(); |
| 411 } |
| 412 if (captureCauses && Runtime.experiments.isEnabled("timelineInvalidation
Tracking")) |
| 413 categoriesArray.push(disabledByDefault("devtools.timeline.invalidati
onTracking")); |
| 414 if (capturePictures) { |
| 415 categoriesArray = categoriesArray.concat([ |
| 416 disabledByDefault("devtools.timeline.layers"), |
| 417 disabledByDefault("devtools.timeline.picture"), |
| 418 disabledByDefault("blink.graphics_context_annotations")]); |
| 419 } |
| 420 var categories = categoriesArray.join(","); |
| 421 this._startRecordingWithCategories(categories); |
152 }, | 422 }, |
153 | 423 |
154 stopRecording: function() | 424 stopRecording: function() |
155 { | 425 { |
156 }, | 426 if (this._jsProfilerStarted) { |
157 | 427 this._stopCallbackBarrier = new CallbackBarrier(); |
158 /** | 428 this._currentTarget.profilerAgent().stop(this._stopCallbackBarrier.c
reateCallback(this._didStopRecordingJSSamples.bind(this))); |
| 429 this._jsProfilerStarted = false; |
| 430 } |
| 431 this._tracingManager.stop(); |
| 432 }, |
| 433 |
| 434 /** |
159 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspe
ctor.TimelineModel.Record,number)} preOrderCallback | 435 * @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 | 436 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspect
or.TimelineModel.Record,number)=} postOrderCallback |
161 */ | 437 */ |
162 forAllRecords: function(preOrderCallback, postOrderCallback) | 438 forAllRecords: function(preOrderCallback, postOrderCallback) |
163 { | 439 { |
164 WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback
, postOrderCallback); | 440 WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback
, postOrderCallback); |
165 }, | 441 }, |
166 | 442 |
167 /** | 443 /** |
168 * @param {!WebInspector.TimelineModel.Filter} filter | 444 * @param {!WebInspector.TimelineModel.Filter} filter |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
223 | 499 |
224 /** | 500 /** |
225 * @return {!Array.<!WebInspector.TimelineModel.Record>} | 501 * @return {!Array.<!WebInspector.TimelineModel.Record>} |
226 */ | 502 */ |
227 records: function() | 503 records: function() |
228 { | 504 { |
229 return this._records; | 505 return this._records; |
230 }, | 506 }, |
231 | 507 |
232 /** | 508 /** |
| 509 * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events |
| 510 */ |
| 511 setEventsForTest: function(events) |
| 512 { |
| 513 this._startCollectingTraceEvents(false); |
| 514 this._tracingModel.addEvents(events); |
| 515 this._onTracingComplete(); |
| 516 }, |
| 517 |
| 518 _configureCpuProfilerSamplingInterval: function() |
| 519 { |
| 520 var intervalUs = WebInspector.settings.highResolutionCpuProfiling.get()
? 100 : 1000; |
| 521 this._currentTarget.profilerAgent().setSamplingInterval(intervalUs, didC
hangeInterval); |
| 522 |
| 523 function didChangeInterval(error) |
| 524 { |
| 525 if (error) |
| 526 WebInspector.console.error(error); |
| 527 } |
| 528 }, |
| 529 |
| 530 /** |
| 531 * @param {string} categories |
| 532 */ |
| 533 _startRecordingWithCategories: function(categories) |
| 534 { |
| 535 this._tracingManager.start(categories, ""); |
| 536 }, |
| 537 |
| 538 _onTracingStarted: function() |
| 539 { |
| 540 this._startCollectingTraceEvents(false); |
| 541 }, |
| 542 |
| 543 /** |
| 544 * @param {boolean} fromFile |
| 545 */ |
| 546 _startCollectingTraceEvents: function(fromFile) |
| 547 { |
| 548 this.reset(); |
| 549 this._tracingModel.reset(); |
| 550 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStarted, { fromFile: fromFile }); |
| 551 }, |
| 552 |
| 553 /** |
| 554 * @param {!WebInspector.Event} event |
| 555 */ |
| 556 _onEventsCollected: function(event) |
| 557 { |
| 558 var traceEvents = /** @type {!Array.<!WebInspector.TracingManager.EventP
ayload>} */ (event.data); |
| 559 this._tracingModel.addEvents(traceEvents); |
| 560 }, |
| 561 |
| 562 _onTracingComplete: function() |
| 563 { |
| 564 if (this._stopCallbackBarrier) |
| 565 this._stopCallbackBarrier.callWhenDone(this._didStopRecordingTraceEv
ents.bind(this)); |
| 566 else |
| 567 this._didStopRecordingTraceEvents(); |
| 568 }, |
| 569 |
| 570 /** |
| 571 * @param {?Protocol.Error} error |
| 572 * @param {?ProfilerAgent.CPUProfile} cpuProfile |
| 573 */ |
| 574 _didStopRecordingJSSamples: function(error, cpuProfile) |
| 575 { |
| 576 if (error) |
| 577 WebInspector.console.error(error); |
| 578 this._recordedCpuProfile = cpuProfile; |
| 579 }, |
| 580 |
| 581 _didStopRecordingTraceEvents: function() |
| 582 { |
| 583 this._stopCallbackBarrier = null; |
| 584 |
| 585 if (this._recordedCpuProfile) { |
| 586 this._injectCpuProfileEvent(this._recordedCpuProfile); |
| 587 this._recordedCpuProfile = null; |
| 588 } |
| 589 this._tracingModel.tracingComplete(); |
| 590 |
| 591 var events = this._tracingModel.devtoolsPageMetadataEvents(); |
| 592 var workerMetadataEvents = this._tracingModel.devtoolsWorkerMetadataEven
ts(); |
| 593 |
| 594 this._resetProcessingState(); |
| 595 for (var i = 0, length = events.length; i < length; i++) { |
| 596 var event = events[i]; |
| 597 var process = event.thread.process(); |
| 598 var startTime = event.startTime; |
| 599 |
| 600 var endTime = Infinity; |
| 601 if (i + 1 < length) |
| 602 endTime = events[i + 1].startTime; |
| 603 |
| 604 var threads = process.sortedThreads(); |
| 605 for (var j = 0; j < threads.length; j++) { |
| 606 var thread = threads[j]; |
| 607 if (thread.name() === "WebCore: Worker" && !workerMetadataEvents
.some(function(e) { return e.args["data"]["workerThreadId"] === thread.id(); })) |
| 608 continue; |
| 609 this._processThreadEvents(startTime, endTime, event.thread, thre
ad); |
| 610 } |
| 611 } |
| 612 this._resetProcessingState(); |
| 613 |
| 614 this._inspectedTargetEvents.sort(WebInspector.TracingModel.Event.compare
StartTime); |
| 615 |
| 616 if (this._cpuProfile) { |
| 617 this._processCpuProfile(this._cpuProfile); |
| 618 this._cpuProfile = null; |
| 619 } |
| 620 this._buildTimelineRecords(); |
| 621 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStopped); |
| 622 }, |
| 623 |
| 624 /** |
| 625 * @param {!ProfilerAgent.CPUProfile} cpuProfile |
| 626 */ |
| 627 _injectCpuProfileEvent: function(cpuProfile) |
| 628 { |
| 629 var metaEvent = this._tracingModel.devtoolsPageMetadataEvents().peekLast
(); |
| 630 if (!metaEvent) |
| 631 return; |
| 632 var cpuProfileEvent = /** @type {!WebInspector.TracingManager.EventPaylo
ad} */ ({ |
| 633 cat: WebInspector.TracingModel.DevToolsMetadataEventCategory, |
| 634 ph: WebInspector.TracingModel.Phase.Instant, |
| 635 ts: this._tracingModel.maximumRecordTime() * 1000, |
| 636 pid: metaEvent.thread.process().id(), |
| 637 tid: metaEvent.thread.id(), |
| 638 name: WebInspector.TimelineModel.RecordType.CpuProfile, |
| 639 args: { data: { cpuProfile: cpuProfile } } |
| 640 }); |
| 641 this._tracingModel.addEvents([cpuProfileEvent]); |
| 642 }, |
| 643 |
| 644 /** |
| 645 * @param {!ProfilerAgent.CPUProfile} cpuProfile |
| 646 */ |
| 647 _processCpuProfile: function(cpuProfile) |
| 648 { |
| 649 var jsSamples = WebInspector.TimelineJSProfileProcessor.generateTracingE
ventsFromCpuProfile(this, cpuProfile); |
| 650 this._inspectedTargetEvents = this._inspectedTargetEvents.mergeOrdered(j
sSamples, WebInspector.TracingModel.Event.orderedCompareStartTime); |
| 651 this._setMainThreadEvents(this.mainThreadEvents().mergeOrdered(jsSamples
, WebInspector.TracingModel.Event.orderedCompareStartTime)); |
| 652 var jsFrameEvents = WebInspector.TimelineJSProfileProcessor.generateJSFr
ameEvents(this.mainThreadEvents()); |
| 653 this._setMainThreadEvents(jsFrameEvents.mergeOrdered(this.mainThreadEven
ts(), WebInspector.TracingModel.Event.orderedCompareStartTime)); |
| 654 this._inspectedTargetEvents = jsFrameEvents.mergeOrdered(this._inspected
TargetEvents, WebInspector.TracingModel.Event.orderedCompareStartTime); |
| 655 }, |
| 656 |
| 657 _buildTimelineRecords: function() |
| 658 { |
| 659 var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThrea
dEvents()); |
| 660 |
| 661 /** |
| 662 * @param {!WebInspector.TimelineModel.Record} a |
| 663 * @param {!WebInspector.TimelineModel.Record} b |
| 664 * @return {number} |
| 665 */ |
| 666 function compareRecordStartTime(a, b) |
| 667 { |
| 668 // Never return 0 as otherwise equal records would be merged. |
| 669 return (a.startTime() <= b.startTime()) ? -1 : +1; |
| 670 } |
| 671 |
| 672 /** |
| 673 * @param {!WebInspector.TimelineModel.VirtualThread} virtualThread |
| 674 * @this {!WebInspector.TimelineModel} |
| 675 */ |
| 676 function processVirtualThreadEvents(virtualThread) |
| 677 { |
| 678 var threadRecords = this._buildTimelineRecordsForThread(virtualThrea
d.events); |
| 679 topLevelRecords = topLevelRecords.mergeOrdered(threadRecords, compar
eRecordStartTime); |
| 680 } |
| 681 this.virtualThreads().forEach(processVirtualThreadEvents.bind(this)); |
| 682 |
| 683 |
| 684 for (var i = 0; i < topLevelRecords.length; i++) { |
| 685 var record = topLevelRecords[i]; |
| 686 if (record.type() === WebInspector.TimelineModel.RecordType.Program) |
| 687 this._mainThreadTasks.push(record); |
| 688 if (record.type() === WebInspector.TimelineModel.RecordType.GPUTask) |
| 689 this._gpuThreadTasks.push(record); |
| 690 } |
| 691 this._records = topLevelRecords; |
| 692 }, |
| 693 |
| 694 /** |
| 695 * @param {!Array.<!WebInspector.TracingModel.Event>} threadEvents |
| 696 * @return {!Array.<!WebInspector.TimelineModel.Record>} |
| 697 */ |
| 698 _buildTimelineRecordsForThread: function(threadEvents) |
| 699 { |
| 700 var recordStack = []; |
| 701 var topLevelRecords = []; |
| 702 |
| 703 for (var i = 0, size = threadEvents.length; i < size; ++i) { |
| 704 var event = threadEvents[i]; |
| 705 for (var top = recordStack.peekLast(); top && top._event.endTime <=
event.startTime; top = recordStack.peekLast()) { |
| 706 recordStack.pop(); |
| 707 if (!recordStack.length) |
| 708 topLevelRecords.push(top); |
| 709 } |
| 710 if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd || even
t.phase === WebInspector.TracingModel.Phase.NestableAsyncEnd) |
| 711 continue; |
| 712 var parentRecord = recordStack.peekLast(); |
| 713 // Maintain the back-end logic of old timeline, skip console.time()
/ console.timeEnd() that are not properly nested. |
| 714 if (WebInspector.TracingModel.isAsyncBeginPhase(event.phase) && pare
ntRecord && event.endTime > parentRecord._event.endTime) |
| 715 continue; |
| 716 var record = new WebInspector.TimelineModel.Record(this, event); |
| 717 if (WebInspector.TimelineUIUtils.isMarkerEvent(event)) |
| 718 this._eventDividerRecords.push(record); |
| 719 if (!this._recordFilter.accept(record)) |
| 720 continue; |
| 721 if (parentRecord) |
| 722 parentRecord._addChild(record); |
| 723 if (event.endTime) |
| 724 recordStack.push(record); |
| 725 } |
| 726 |
| 727 if (recordStack.length) |
| 728 topLevelRecords.push(recordStack[0]); |
| 729 |
| 730 return topLevelRecords; |
| 731 }, |
| 732 |
| 733 _resetProcessingState: function() |
| 734 { |
| 735 this._sendRequestEvents = {}; |
| 736 this._timerEvents = {}; |
| 737 this._requestAnimationFrameEvents = {}; |
| 738 this._invalidationTracker = new WebInspector.InvalidationTracker(); |
| 739 this._layoutInvalidate = {}; |
| 740 this._lastScheduleStyleRecalculation = {}; |
| 741 this._webSocketCreateEvents = {}; |
| 742 this._paintImageEventByPixelRefId = {}; |
| 743 this._lastPaintForLayer = {}; |
| 744 this._lastRecalculateStylesEvent = null; |
| 745 this._currentScriptEvent = null; |
| 746 this._eventStack = []; |
| 747 }, |
| 748 |
| 749 /** |
| 750 * @param {number} startTime |
| 751 * @param {?number} endTime |
| 752 * @param {!WebInspector.TracingModel.Thread} mainThread |
| 753 * @param {!WebInspector.TracingModel.Thread} thread |
| 754 */ |
| 755 _processThreadEvents: function(startTime, endTime, mainThread, thread) |
| 756 { |
| 757 var events = thread.events(); |
| 758 var length = events.length; |
| 759 var i = events.lowerBound(startTime, function (time, event) { return tim
e - event.startTime }); |
| 760 |
| 761 var threadEvents; |
| 762 if (thread === mainThread) { |
| 763 threadEvents = this._mainThreadEvents; |
| 764 this._mainThreadAsyncEvents = this._mainThreadAsyncEvents.concat(thr
ead.asyncEvents()); |
| 765 } else { |
| 766 var virtualThread = new WebInspector.TimelineModel.VirtualThread(thr
ead.name()); |
| 767 threadEvents = virtualThread.events; |
| 768 virtualThread.asyncEvents = virtualThread.asyncEvents.concat(thread.
asyncEvents()); |
| 769 this._virtualThreads.push(virtualThread); |
| 770 } |
| 771 |
| 772 this._eventStack = []; |
| 773 for (; i < length; i++) { |
| 774 var event = events[i]; |
| 775 if (endTime && event.startTime >= endTime) |
| 776 break; |
| 777 this._processEvent(event); |
| 778 threadEvents.push(event); |
| 779 this._inspectedTargetEvents.push(event); |
| 780 } |
| 781 }, |
| 782 |
| 783 /** |
| 784 * @param {!WebInspector.TracingModel.Event} event |
| 785 */ |
| 786 _processEvent: function(event) |
| 787 { |
| 788 var recordTypes = WebInspector.TimelineModel.RecordType; |
| 789 |
| 790 var eventStack = this._eventStack; |
| 791 while (eventStack.length && eventStack.peekLast().endTime < event.startT
ime) |
| 792 eventStack.pop(); |
| 793 var duration = event.duration; |
| 794 if (duration) { |
| 795 if (eventStack.length) { |
| 796 var parent = eventStack.peekLast(); |
| 797 parent.selfTime -= duration; |
| 798 } |
| 799 event.selfTime = duration; |
| 800 eventStack.push(event); |
| 801 } |
| 802 |
| 803 if (this._currentScriptEvent && event.startTime > this._currentScriptEve
nt.endTime) |
| 804 this._currentScriptEvent = null; |
| 805 |
| 806 switch (event.name) { |
| 807 case recordTypes.CallStack: |
| 808 var lastMainThreadEvent = this.mainThreadEvents().peekLast(); |
| 809 if (lastMainThreadEvent && event.args["stack"] && event.args["stack"
].length) |
| 810 lastMainThreadEvent.stackTrace = event.args["stack"]; |
| 811 break; |
| 812 |
| 813 case recordTypes.CpuProfile: |
| 814 this._cpuProfile = event.args["data"]["cpuProfile"]; |
| 815 break; |
| 816 |
| 817 case recordTypes.ResourceSendRequest: |
| 818 this._sendRequestEvents[event.args["data"]["requestId"]] = event; |
| 819 event.imageURL = event.args["data"]["url"]; |
| 820 break; |
| 821 |
| 822 case recordTypes.ResourceReceiveResponse: |
| 823 case recordTypes.ResourceReceivedData: |
| 824 case recordTypes.ResourceFinish: |
| 825 event.initiator = this._sendRequestEvents[event.args["data"]["reques
tId"]]; |
| 826 if (event.initiator) |
| 827 event.imageURL = event.initiator.imageURL; |
| 828 break; |
| 829 |
| 830 case recordTypes.TimerInstall: |
| 831 this._timerEvents[event.args["data"]["timerId"]] = event; |
| 832 break; |
| 833 |
| 834 case recordTypes.TimerFire: |
| 835 event.initiator = this._timerEvents[event.args["data"]["timerId"]]; |
| 836 break; |
| 837 |
| 838 case recordTypes.RequestAnimationFrame: |
| 839 this._requestAnimationFrameEvents[event.args["data"]["id"]] = event; |
| 840 break; |
| 841 |
| 842 case recordTypes.FireAnimationFrame: |
| 843 event.initiator = this._requestAnimationFrameEvents[event.args["data
"]["id"]]; |
| 844 break; |
| 845 |
| 846 case recordTypes.ScheduleStyleRecalculation: |
| 847 this._lastScheduleStyleRecalculation[event.args["frame"]] = event; |
| 848 break; |
| 849 |
| 850 case recordTypes.RecalculateStyles: |
| 851 this._invalidationTracker.didRecalcStyle(event); |
| 852 event.initiator = this._lastScheduleStyleRecalculation[event.args["f
rame"]]; |
| 853 this._lastRecalculateStylesEvent = event; |
| 854 break; |
| 855 |
| 856 case recordTypes.StyleRecalcInvalidationTracking: |
| 857 case recordTypes.LayoutInvalidationTracking: |
| 858 case recordTypes.LayerInvalidationTracking: |
| 859 case recordTypes.PaintInvalidationTracking: |
| 860 this._invalidationTracker.addInvalidation(event); |
| 861 break; |
| 862 |
| 863 case recordTypes.InvalidateLayout: |
| 864 // Consider style recalculation as a reason for layout invalidation, |
| 865 // but only if we had no earlier layout invalidation records. |
| 866 var layoutInitator = event; |
| 867 var frameId = event.args["frame"]; |
| 868 if (!this._layoutInvalidate[frameId] && this._lastRecalculateStylesE
vent && this._lastRecalculateStylesEvent.endTime > event.startTime) |
| 869 layoutInitator = this._lastRecalculateStylesEvent.initiator; |
| 870 this._layoutInvalidate[frameId] = layoutInitator; |
| 871 break; |
| 872 |
| 873 case recordTypes.Layout: |
| 874 this._invalidationTracker.didLayout(event); |
| 875 var frameId = event.args["beginData"]["frame"]; |
| 876 event.initiator = this._layoutInvalidate[frameId]; |
| 877 // In case we have no closing Layout event, endData is not available
. |
| 878 if (event.args["endData"]) { |
| 879 event.backendNodeId = event.args["endData"]["rootNode"]; |
| 880 event.highlightQuad = event.args["endData"]["root"]; |
| 881 } |
| 882 this._layoutInvalidate[frameId] = null; |
| 883 if (this._currentScriptEvent) |
| 884 event.warning = WebInspector.UIString("Forced synchronous layout
is a possible performance bottleneck."); |
| 885 break; |
| 886 |
| 887 case recordTypes.WebSocketCreate: |
| 888 this._webSocketCreateEvents[event.args["data"]["identifier"]] = even
t; |
| 889 break; |
| 890 |
| 891 case recordTypes.WebSocketSendHandshakeRequest: |
| 892 case recordTypes.WebSocketReceiveHandshakeResponse: |
| 893 case recordTypes.WebSocketDestroy: |
| 894 event.initiator = this._webSocketCreateEvents[event.args["data"]["id
entifier"]]; |
| 895 break; |
| 896 |
| 897 case recordTypes.EvaluateScript: |
| 898 case recordTypes.FunctionCall: |
| 899 if (!this._currentScriptEvent) |
| 900 this._currentScriptEvent = event; |
| 901 break; |
| 902 |
| 903 case recordTypes.SetLayerTreeId: |
| 904 this._inspectedTargetLayerTreeId = event.args["layerTreeId"]; |
| 905 break; |
| 906 |
| 907 case recordTypes.Paint: |
| 908 this._invalidationTracker.didPaint(event); |
| 909 event.highlightQuad = event.args["data"]["clip"]; |
| 910 event.backendNodeId = event.args["data"]["nodeId"]; |
| 911 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLay
er); |
| 912 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== th
is._inspectedTargetLayerTreeId) |
| 913 break; |
| 914 // Only keep layer paint events, skip paints for subframes that get
painted to the same layer as parent. |
| 915 if (!event.args["data"]["layerId"]) |
| 916 break; |
| 917 this._lastPaintForLayer[layerUpdateEvent.args["layerId"]] = event; |
| 918 break; |
| 919 |
| 920 case recordTypes.PictureSnapshot: |
| 921 var layerUpdateEvent = this._findAncestorEvent(recordTypes.UpdateLay
er); |
| 922 if (!layerUpdateEvent || layerUpdateEvent.args["layerTreeId"] !== th
is._inspectedTargetLayerTreeId) |
| 923 break; |
| 924 var paintEvent = this._lastPaintForLayer[layerUpdateEvent.args["laye
rId"]]; |
| 925 if (paintEvent) |
| 926 paintEvent.picture = event; |
| 927 break; |
| 928 |
| 929 case recordTypes.ScrollLayer: |
| 930 event.backendNodeId = event.args["data"]["nodeId"]; |
| 931 break; |
| 932 |
| 933 case recordTypes.PaintImage: |
| 934 event.backendNodeId = event.args["data"]["nodeId"]; |
| 935 event.imageURL = event.args["data"]["url"]; |
| 936 break; |
| 937 |
| 938 case recordTypes.DecodeImage: |
| 939 case recordTypes.ResizeImage: |
| 940 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage
); |
| 941 if (!paintImageEvent) { |
| 942 var decodeLazyPixelRefEvent = this._findAncestorEvent(recordType
s.DecodeLazyPixelRef); |
| 943 paintImageEvent = decodeLazyPixelRefEvent && this._paintImageEve
ntByPixelRefId[decodeLazyPixelRefEvent.args["LazyPixelRef"]]; |
| 944 } |
| 945 if (!paintImageEvent) |
| 946 break; |
| 947 event.backendNodeId = paintImageEvent.backendNodeId; |
| 948 event.imageURL = paintImageEvent.imageURL; |
| 949 break; |
| 950 |
| 951 case recordTypes.DrawLazyPixelRef: |
| 952 var paintImageEvent = this._findAncestorEvent(recordTypes.PaintImage
); |
| 953 if (!paintImageEvent) |
| 954 break; |
| 955 this._paintImageEventByPixelRefId[event.args["LazyPixelRef"]] = pain
tImageEvent; |
| 956 event.backendNodeId = paintImageEvent.backendNodeId; |
| 957 event.imageURL = paintImageEvent.imageURL; |
| 958 break; |
| 959 } |
| 960 }, |
| 961 |
| 962 /** |
| 963 * @param {string} name |
| 964 * @return {?WebInspector.TracingModel.Event} |
| 965 */ |
| 966 _findAncestorEvent: function(name) |
| 967 { |
| 968 for (var i = this._eventStack.length - 1; i >= 0; --i) { |
| 969 var event = this._eventStack[i]; |
| 970 if (event.name === name) |
| 971 return event; |
| 972 } |
| 973 return null; |
| 974 }, |
| 975 |
| 976 /** |
233 * @param {!Blob} file | 977 * @param {!Blob} file |
234 * @param {!WebInspector.Progress} progress | 978 * @param {!WebInspector.Progress} progress |
235 */ | 979 */ |
236 loadFromFile: function(file, progress) | 980 loadFromFile: function(file, progress) |
237 { | 981 { |
238 var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this,
progress); | 982 var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this,
progress); |
239 var fileReader = this._createFileReader(file, delegate); | 983 var fileReader = this._createFileReader(file, delegate); |
240 var loader = this.createLoader(fileReader, progress); | 984 var loader = this.createLoader(fileReader, progress); |
241 fileReader.start(loader); | 985 fileReader.start(loader); |
242 }, | 986 }, |
243 | 987 |
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) | 988 _createFileReader: function(file, delegate) |
255 { | 989 { |
256 return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineMod
el.TransferChunkLengthBytes, delegate); | 990 return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineMod
el.TransferChunkLengthBytes, delegate); |
257 }, | 991 }, |
258 | 992 |
259 _createFileWriter: function() | 993 _createFileWriter: function() |
260 { | 994 { |
261 return new WebInspector.FileOutputStream(); | 995 return new WebInspector.FileOutputStream(); |
262 }, | 996 }, |
263 | 997 |
264 saveToFile: function() | 998 saveToFile: function() |
265 { | 999 { |
266 var now = new Date(); | 1000 var now = new Date(); |
267 var fileName = "TimelineRawData-" + now.toISO8601Compact() + ".json"; | 1001 var fileName = "TimelineRawData-" + now.toISO8601Compact() + ".json"; |
268 var stream = this._createFileWriter(); | 1002 var stream = this._createFileWriter(); |
269 | 1003 |
270 /** | 1004 /** |
271 * @param {boolean} accepted | 1005 * @param {boolean} accepted |
272 * @this {WebInspector.TimelineModel} | 1006 * @this {WebInspector.TimelineModel} |
273 */ | 1007 */ |
274 function callback(accepted) | 1008 function callback(accepted) |
275 { | 1009 { |
276 if (!accepted) | 1010 if (!accepted) |
277 return; | 1011 return; |
278 this.writeToStream(stream); | 1012 this.writeToStream(stream); |
279 } | 1013 } |
280 stream.open(fileName, callback.bind(this)); | 1014 stream.open(fileName, callback.bind(this)); |
281 }, | 1015 }, |
282 | 1016 |
283 /** | |
284 * @param {!WebInspector.OutputStream} stream | |
285 */ | |
286 writeToStream: function(stream) | |
287 { | |
288 throw new Error("Not implemented."); | |
289 }, | |
290 | |
291 reset: function() | 1017 reset: function() |
292 { | 1018 { |
| 1019 this._virtualThreads = []; |
| 1020 this._mainThreadEvents = []; |
| 1021 this._mainThreadAsyncEvents = []; |
| 1022 this._inspectedTargetEvents = []; |
| 1023 |
293 this._records = []; | 1024 this._records = []; |
294 this._minimumRecordTime = 0; | |
295 this._maximumRecordTime = 0; | |
296 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ | 1025 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ |
297 this._mainThreadTasks = []; | 1026 this._mainThreadTasks = []; |
298 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ | 1027 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ |
299 this._gpuThreadTasks = []; | 1028 this._gpuThreadTasks = []; |
300 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ | 1029 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */ |
301 this._eventDividerRecords = []; | 1030 this._eventDividerRecords = []; |
302 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsC
leared); | 1031 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsC
leared); |
303 }, | 1032 }, |
304 | 1033 |
305 /** | 1034 /** |
306 * @return {number} | 1035 * @return {number} |
307 */ | 1036 */ |
308 minimumRecordTime: function() | 1037 minimumRecordTime: function() |
309 { | 1038 { |
310 throw new Error("Not implemented."); | 1039 return this._tracingModel.minimumRecordTime(); |
311 }, | 1040 }, |
312 | 1041 |
313 /** | 1042 /** |
314 * @return {number} | 1043 * @return {number} |
315 */ | 1044 */ |
316 maximumRecordTime: function() | 1045 maximumRecordTime: function() |
317 { | 1046 { |
318 throw new Error("Not implemented."); | 1047 return this._tracingModel.maximumRecordTime(); |
| 1048 }, |
| 1049 |
| 1050 /** |
| 1051 * @return {!Array.<!WebInspector.TracingModel.Event>} |
| 1052 */ |
| 1053 inspectedTargetEvents: function() |
| 1054 { |
| 1055 return this._inspectedTargetEvents; |
| 1056 }, |
| 1057 |
| 1058 /** |
| 1059 * @return {!Array.<!WebInspector.TracingModel.Event>} |
| 1060 */ |
| 1061 mainThreadEvents: function() |
| 1062 { |
| 1063 return this._mainThreadEvents; |
| 1064 }, |
| 1065 |
| 1066 /** |
| 1067 * @param {!Array.<!WebInspector.TracingModel.Event>} events |
| 1068 */ |
| 1069 _setMainThreadEvents: function(events) |
| 1070 { |
| 1071 this._mainThreadEvents = events; |
| 1072 }, |
| 1073 |
| 1074 /** |
| 1075 * @return {!Array.<!Array.<!WebInspector.TracingModel.Event>>} |
| 1076 */ |
| 1077 mainThreadAsyncEvents: function() |
| 1078 { |
| 1079 return this._mainThreadAsyncEvents; |
| 1080 }, |
| 1081 |
| 1082 /** |
| 1083 * @return {!Array.<!WebInspector.TimelineModel.VirtualThread>} |
| 1084 */ |
| 1085 virtualThreads: function() |
| 1086 { |
| 1087 return this._virtualThreads; |
| 1088 }, |
| 1089 |
| 1090 /** |
| 1091 * @param {!WebInspector.ChunkedFileReader} fileReader |
| 1092 * @param {!WebInspector.Progress} progress |
| 1093 * @return {!WebInspector.OutputStream} |
| 1094 */ |
| 1095 createLoader: function(fileReader, progress) |
| 1096 { |
| 1097 return new WebInspector.TracingModelLoader(this, fileReader, progress); |
| 1098 }, |
| 1099 |
| 1100 /** |
| 1101 * @param {!WebInspector.OutputStream} stream |
| 1102 */ |
| 1103 writeToStream: function(stream) |
| 1104 { |
| 1105 var saver = new WebInspector.TracingTimelineSaver(stream); |
| 1106 this._tracingModel.writeToStream(stream, saver); |
319 }, | 1107 }, |
320 | 1108 |
321 /** | 1109 /** |
322 * @return {boolean} | 1110 * @return {boolean} |
323 */ | 1111 */ |
324 isEmpty: function() | 1112 isEmpty: function() |
325 { | 1113 { |
326 return this.minimumRecordTime() === 0 && this.maximumRecordTime() === 0; | 1114 return this.minimumRecordTime() === 0 && this.maximumRecordTime() === 0; |
327 }, | 1115 }, |
328 | 1116 |
(...skipping 14 matching lines...) Expand all Loading... |
343 }, | 1131 }, |
344 | 1132 |
345 /** | 1133 /** |
346 * @return {!Array.<!WebInspector.TimelineModel.Record>} | 1134 * @return {!Array.<!WebInspector.TimelineModel.Record>} |
347 */ | 1135 */ |
348 eventDividerRecords: function() | 1136 eventDividerRecords: function() |
349 { | 1137 { |
350 return this._eventDividerRecords; | 1138 return this._eventDividerRecords; |
351 }, | 1139 }, |
352 | 1140 |
| 1141 |
353 __proto__: WebInspector.Object.prototype | 1142 __proto__: WebInspector.Object.prototype |
354 } | 1143 } |
355 | 1144 |
356 /** | 1145 /** |
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 | 1146 * @constructor |
449 */ | 1147 */ |
450 WebInspector.TimelineModel.Filter = function() | 1148 WebInspector.TimelineModel.Filter = function() |
451 { | 1149 { |
452 /** @type {!WebInspector.TimelineModel} */ | 1150 /** @type {!WebInspector.TimelineModel} */ |
453 this._model; | 1151 this._model; |
454 } | 1152 } |
455 | 1153 |
456 WebInspector.TimelineModel.Filter.prototype = { | 1154 WebInspector.TimelineModel.Filter.prototype = { |
457 /** | 1155 /** |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
610 case FileError.NOT_READABLE_ERR: | 1308 case FileError.NOT_READABLE_ERR: |
611 WebInspector.console.error(WebInspector.UIString("File \"%s\" is not
readable", reader.fileName())); | 1309 WebInspector.console.error(WebInspector.UIString("File \"%s\" is not
readable", reader.fileName())); |
612 break; | 1310 break; |
613 case FileError.ABORT_ERR: | 1311 case FileError.ABORT_ERR: |
614 break; | 1312 break; |
615 default: | 1313 default: |
616 WebInspector.console.error(WebInspector.UIString("An error occurred
while reading the file \"%s\"", reader.fileName())); | 1314 WebInspector.console.error(WebInspector.UIString("An error occurred
while reading the file \"%s\"", reader.fileName())); |
617 } | 1315 } |
618 } | 1316 } |
619 } | 1317 } |
| 1318 |
| 1319 |
| 1320 /** |
| 1321 * @interface |
| 1322 */ |
| 1323 WebInspector.TraceEventFilter = function() { } |
| 1324 |
| 1325 WebInspector.TraceEventFilter.prototype = { |
| 1326 /** |
| 1327 * @param {!WebInspector.TracingModel.Event} event |
| 1328 * @return {boolean} |
| 1329 */ |
| 1330 accept: function(event) { } |
| 1331 } |
| 1332 |
| 1333 /** |
| 1334 * @constructor |
| 1335 * @implements {WebInspector.TraceEventFilter} |
| 1336 * @param {!Array.<string>} eventNames |
| 1337 */ |
| 1338 WebInspector.TraceEventNameFilter = function(eventNames) |
| 1339 { |
| 1340 this._eventNames = eventNames.keySet(); |
| 1341 } |
| 1342 |
| 1343 WebInspector.TraceEventNameFilter.prototype = { |
| 1344 /** |
| 1345 * @param {!WebInspector.TracingModel.Event} event |
| 1346 * @return {boolean} |
| 1347 */ |
| 1348 accept: function(event) |
| 1349 { |
| 1350 throw new Error("Not implemented."); |
| 1351 } |
| 1352 } |
| 1353 |
| 1354 /** |
| 1355 * @constructor |
| 1356 * @extends {WebInspector.TraceEventNameFilter} |
| 1357 * @param {!Array.<string>} includeNames |
| 1358 */ |
| 1359 WebInspector.InclusiveTraceEventNameFilter = function(includeNames) |
| 1360 { |
| 1361 WebInspector.TraceEventNameFilter.call(this, includeNames) |
| 1362 } |
| 1363 |
| 1364 WebInspector.InclusiveTraceEventNameFilter.prototype = { |
| 1365 /** |
| 1366 * @override |
| 1367 * @param {!WebInspector.TracingModel.Event} event |
| 1368 * @return {boolean} |
| 1369 */ |
| 1370 accept: function(event) |
| 1371 { |
| 1372 return event.category === WebInspector.TracingModel.ConsoleEventCategory
|| !!this._eventNames[event.name]; |
| 1373 }, |
| 1374 __proto__: WebInspector.TraceEventNameFilter.prototype |
| 1375 } |
| 1376 |
| 1377 /** |
| 1378 * @constructor |
| 1379 * @extends {WebInspector.TraceEventNameFilter} |
| 1380 * @param {!Array.<string>} excludeNames |
| 1381 */ |
| 1382 WebInspector.ExclusiveTraceEventNameFilter = function(excludeNames) |
| 1383 { |
| 1384 WebInspector.TraceEventNameFilter.call(this, excludeNames) |
| 1385 } |
| 1386 |
| 1387 WebInspector.ExclusiveTraceEventNameFilter.prototype = { |
| 1388 /** |
| 1389 * @override |
| 1390 * @param {!WebInspector.TracingModel.Event} event |
| 1391 * @return {boolean} |
| 1392 */ |
| 1393 accept: function(event) |
| 1394 { |
| 1395 return !this._eventNames[event.name]; |
| 1396 }, |
| 1397 __proto__: WebInspector.TraceEventNameFilter.prototype |
| 1398 } |
| 1399 |
| 1400 /** |
| 1401 * @constructor |
| 1402 * @implements {WebInspector.OutputStream} |
| 1403 * @param {!WebInspector.TimelineModel} model |
| 1404 * @param {!{cancel: function()}} reader |
| 1405 * @param {!WebInspector.Progress} progress |
| 1406 */ |
| 1407 WebInspector.TracingModelLoader = function(model, reader, progress) |
| 1408 { |
| 1409 this._model = model; |
| 1410 this._reader = reader; |
| 1411 this._progress = progress; |
| 1412 this._buffer = ""; |
| 1413 this._firstChunk = true; |
| 1414 this._loader = new WebInspector.TracingModel.Loader(model._tracingModel); |
| 1415 } |
| 1416 |
| 1417 WebInspector.TracingModelLoader.prototype = { |
| 1418 /** |
| 1419 * @param {string} chunk |
| 1420 */ |
| 1421 write: function(chunk) |
| 1422 { |
| 1423 var data = this._buffer + chunk; |
| 1424 var lastIndex = 0; |
| 1425 var index; |
| 1426 do { |
| 1427 index = lastIndex; |
| 1428 lastIndex = WebInspector.TextUtils.findBalancedCurlyBrackets(data, i
ndex); |
| 1429 } while (lastIndex !== -1) |
| 1430 |
| 1431 var json = data.slice(0, index) + "]"; |
| 1432 this._buffer = data.slice(index); |
| 1433 |
| 1434 if (!index) |
| 1435 return; |
| 1436 |
| 1437 if (this._firstChunk) { |
| 1438 this._model._startCollectingTraceEvents(true); |
| 1439 } else { |
| 1440 var commaIndex = json.indexOf(","); |
| 1441 if (commaIndex !== -1) |
| 1442 json = json.slice(commaIndex + 1); |
| 1443 json = "[" + json; |
| 1444 } |
| 1445 |
| 1446 var items; |
| 1447 try { |
| 1448 items = /** @type {!Array.<!WebInspector.TracingManager.EventPayload
>} */ (JSON.parse(json)); |
| 1449 } catch (e) { |
| 1450 this._reportErrorAndCancelLoading("Malformed timeline data: " + e); |
| 1451 return; |
| 1452 } |
| 1453 |
| 1454 if (this._firstChunk) { |
| 1455 this._firstChunk = false; |
| 1456 if (this._looksLikeAppVersion(items[0])) { |
| 1457 this._reportErrorAndCancelLoading("Old Timeline format is not su
pported."); |
| 1458 return; |
| 1459 } |
| 1460 } |
| 1461 |
| 1462 try { |
| 1463 this._loader.loadNextChunk(items); |
| 1464 } catch(e) { |
| 1465 this._reportErrorAndCancelLoading("Malformed timeline data: " + e); |
| 1466 return; |
| 1467 } |
| 1468 }, |
| 1469 |
| 1470 _reportErrorAndCancelLoading: function(messsage) |
| 1471 { |
| 1472 WebInspector.console.error(messsage); |
| 1473 this._model._onTracingComplete(); |
| 1474 this._model.reset(); |
| 1475 this._reader.cancel(); |
| 1476 this._progress.done(); |
| 1477 }, |
| 1478 |
| 1479 _looksLikeAppVersion: function(item) |
| 1480 { |
| 1481 return typeof item === "string" && item.indexOf("Chrome") !== -1; |
| 1482 }, |
| 1483 |
| 1484 close: function() |
| 1485 { |
| 1486 this._loader.finish(); |
| 1487 this._model._onTracingComplete(); |
| 1488 } |
| 1489 } |
| 1490 |
| 1491 /** |
| 1492 * @constructor |
| 1493 * @param {!WebInspector.OutputStream} stream |
| 1494 * @implements {WebInspector.OutputStreamDelegate} |
| 1495 */ |
| 1496 WebInspector.TracingTimelineSaver = function(stream) |
| 1497 { |
| 1498 this._stream = stream; |
| 1499 } |
| 1500 |
| 1501 WebInspector.TracingTimelineSaver.prototype = { |
| 1502 onTransferStarted: function() |
| 1503 { |
| 1504 this._stream.write("["); |
| 1505 }, |
| 1506 |
| 1507 onTransferFinished: function() |
| 1508 { |
| 1509 this._stream.write("]"); |
| 1510 }, |
| 1511 |
| 1512 /** |
| 1513 * @param {!WebInspector.ChunkedReader} reader |
| 1514 */ |
| 1515 onChunkTransferred: function(reader) { }, |
| 1516 |
| 1517 /** |
| 1518 * @param {!WebInspector.ChunkedReader} reader |
| 1519 * @param {!Event} event |
| 1520 */ |
| 1521 onError: function(reader, event) { }, |
| 1522 } |
| 1523 |
| 1524 /** |
| 1525 * @constructor |
| 1526 * @param {!WebInspector.TracingModel.Event} event |
| 1527 */ |
| 1528 WebInspector.InvalidationTrackingEvent = function(event) |
| 1529 { |
| 1530 this.type = event.name; |
| 1531 this.frameId = event.args["data"]["frame"]; |
| 1532 this.nodeId = event.args["data"]["nodeId"]; |
| 1533 this.nodeName = event.args["data"]["nodeName"]; |
| 1534 this.paintId = event.args["data"]["paintId"]; |
| 1535 this.reason = event.args["data"]["reason"]; |
| 1536 this.stackTrace = event.args["data"]["stackTrace"]; |
| 1537 } |
| 1538 |
| 1539 /** |
| 1540 * @constructor |
| 1541 */ |
| 1542 WebInspector.InvalidationTracker = function() |
| 1543 { |
| 1544 this._initializePerFrameState(); |
| 1545 } |
| 1546 |
| 1547 WebInspector.InvalidationTracker.prototype = { |
| 1548 /** |
| 1549 * @param {!WebInspector.TracingModel.Event} event |
| 1550 */ |
| 1551 addInvalidation: function(event) |
| 1552 { |
| 1553 var invalidation = new WebInspector.InvalidationTrackingEvent(event); |
| 1554 |
| 1555 this._startNewFrameIfNeeded(); |
| 1556 if (!invalidation.nodeId && !invalidation.paintId) { |
| 1557 console.error("Invalidation lacks node information."); |
| 1558 console.error(invalidation); |
| 1559 } |
| 1560 |
| 1561 // Record the paintIds for style recalc or layout invalidations. |
| 1562 // FIXME: This O(n^2) loop could be optimized with a map. |
| 1563 var recordTypes = WebInspector.TimelineModel.RecordType; |
| 1564 if (invalidation.type == recordTypes.PaintInvalidationTracking) |
| 1565 this._invalidationEvents.forEach(updatePaintId); |
| 1566 else |
| 1567 this._invalidationEvents.push(invalidation); |
| 1568 |
| 1569 function updatePaintId(invalidationToUpdate) |
| 1570 { |
| 1571 if (invalidationToUpdate.nodeId !== invalidation.nodeId) |
| 1572 return; |
| 1573 if (invalidationToUpdate.type === recordTypes.StyleRecalcInvalidatio
nTracking |
| 1574 || invalidationToUpdate.type === recordTypes.LayoutInvalidat
ionTracking) { |
| 1575 invalidationToUpdate.paintId = invalidation.paintId; |
| 1576 } |
| 1577 } |
| 1578 }, |
| 1579 |
| 1580 /** |
| 1581 * @param {!WebInspector.TracingModel.Event} styleRecalcEvent |
| 1582 */ |
| 1583 didRecalcStyle: function(styleRecalcEvent) |
| 1584 { |
| 1585 var recalcFrameId = styleRecalcEvent.args["frame"]; |
| 1586 var index = this._lastStyleRecalcEventIndex; |
| 1587 var invalidationCount = this._invalidationEvents.length; |
| 1588 for (; index < invalidationCount; index++) { |
| 1589 var invalidation = this._invalidationEvents[index]; |
| 1590 if (invalidation.type !== WebInspector.TimelineModel.RecordType.Styl
eRecalcInvalidationTracking) |
| 1591 continue; |
| 1592 if (invalidation.frameId === recalcFrameId) |
| 1593 this._addInvalidationTrackingEvent(styleRecalcEvent, invalidatio
n); |
| 1594 } |
| 1595 |
| 1596 this._lastStyleRecalcEventIndex = invalidationCount; |
| 1597 }, |
| 1598 |
| 1599 /** |
| 1600 * @param {!WebInspector.TracingModel.Event} layoutEvent |
| 1601 */ |
| 1602 didLayout: function(layoutEvent) |
| 1603 { |
| 1604 var layoutFrameId = layoutEvent.args["beginData"]["frame"]; |
| 1605 var index = this._lastLayoutEventIndex; |
| 1606 var invalidationCount = this._invalidationEvents.length; |
| 1607 for (; index < invalidationCount; index++) { |
| 1608 var invalidation = this._invalidationEvents[index]; |
| 1609 if (invalidation.type !== WebInspector.TimelineModel.RecordType.Layo
utInvalidationTracking) |
| 1610 continue; |
| 1611 if (invalidation.frameId === layoutFrameId) |
| 1612 this._addInvalidationTrackingEvent(layoutEvent, invalidation); |
| 1613 } |
| 1614 |
| 1615 this._lastLayoutEventIndex = invalidationCount; |
| 1616 }, |
| 1617 |
| 1618 /** |
| 1619 * @param {!WebInspector.TracingModel.Event} paintEvent |
| 1620 */ |
| 1621 didPaint: function(paintEvent) |
| 1622 { |
| 1623 this._didPaint = true; |
| 1624 |
| 1625 // If a paint doesn't have a corresponding graphics layer id, it paints |
| 1626 // into its parent so add an effectivePaintId to these events. |
| 1627 var layerId = paintEvent.args["data"]["layerId"]; |
| 1628 if (layerId) |
| 1629 this._lastPaintWithLayer = paintEvent; |
| 1630 if (!this._lastPaintWithLayer) { |
| 1631 console.error("Failed to find the paint container for a paint event.
"); |
| 1632 return; |
| 1633 } |
| 1634 |
| 1635 var effectivePaintId = this._lastPaintWithLayer.args["data"]["nodeId"]; |
| 1636 var frameId = paintEvent.args["data"]["frame"]; |
| 1637 this._invalidationEvents.forEach(recordInvalidationForPaint.bind(this)); |
| 1638 |
| 1639 /** |
| 1640 * @param {!WebInspector.InvalidationTrackingEvent} invalidation |
| 1641 * @this {WebInspector.InvalidationTracker} |
| 1642 */ |
| 1643 function recordInvalidationForPaint(invalidation) |
| 1644 { |
| 1645 if (invalidation.paintId === effectivePaintId && invalidation.frameI
d === frameId) |
| 1646 this._addInvalidationTrackingEvent(paintEvent, invalidation); |
| 1647 } |
| 1648 }, |
| 1649 |
| 1650 /** |
| 1651 * @param {!WebInspector.TracingModel.Event} event |
| 1652 * @param {!WebInspector.InvalidationTrackingEvent} invalidation |
| 1653 */ |
| 1654 _addInvalidationTrackingEvent: function(event, invalidation) |
| 1655 { |
| 1656 if (!event.invalidationTrackingEvents) |
| 1657 event.invalidationTrackingEvents = [ invalidation ]; |
| 1658 else |
| 1659 event.invalidationTrackingEvents.push(invalidation); |
| 1660 }, |
| 1661 |
| 1662 _startNewFrameIfNeeded: function() |
| 1663 { |
| 1664 if (!this._didPaint) |
| 1665 return; |
| 1666 |
| 1667 this._initializePerFrameState(); |
| 1668 }, |
| 1669 |
| 1670 _initializePerFrameState: function() |
| 1671 { |
| 1672 /** @type {!Array.<!WebInspector.InvalidationTrackingEvent>} */ |
| 1673 this._invalidationEvents = []; |
| 1674 this._lastStyleRecalcEventIndex = 0; |
| 1675 this._lastLayoutEventIndex = 0; |
| 1676 this._lastPaintWithLayer = undefined; |
| 1677 this._didPaint = false; |
| 1678 } |
| 1679 } |
OLD | NEW |