| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2014 The Chromium Authors. All rights reserved. | |
| 3 * Use of this source code is governed by a BSD-style license that can be | |
| 4 * found in the LICENSE file. | |
| 5 */ | |
| 6 | |
| 7 /** | |
| 8 * @constructor | |
| 9 * @extends {WebInspector.Object} | |
| 10 */ | |
| 11 WebInspector.TracingModel = function() | |
| 12 { | |
| 13 WebInspector.Object.call(this); | |
| 14 this.reset(); | |
| 15 this._active = false; | |
| 16 InspectorBackend.registerTracingDispatcher(new WebInspector.TracingDispatche
r(this)); | |
| 17 } | |
| 18 | |
| 19 WebInspector.TracingModel.Events = { | |
| 20 "BufferUsage": "BufferUsage" | |
| 21 } | |
| 22 | |
| 23 /** @typedef {!{ | |
| 24 cat: string, | |
| 25 pid: number, | |
| 26 tid: number, | |
| 27 ts: number, | |
| 28 ph: string, | |
| 29 name: string, | |
| 30 args: !Object, | |
| 31 dur: number, | |
| 32 id: number, | |
| 33 s: string | |
| 34 }} | |
| 35 */ | |
| 36 WebInspector.TracingModel.EventPayload; | |
| 37 | |
| 38 /** | |
| 39 * @enum {string} | |
| 40 */ | |
| 41 WebInspector.TracingModel.Phase = { | |
| 42 Begin: "B", | |
| 43 End: "E", | |
| 44 Complete: "X", | |
| 45 Instant: "i", | |
| 46 AsyncBegin: "S", | |
| 47 AsyncStepInto: "T", | |
| 48 AsyncStepPast: "p", | |
| 49 AsyncEnd: "F", | |
| 50 FlowBegin: "s", | |
| 51 FlowStep: "t", | |
| 52 FlowEnd: "f", | |
| 53 Metadata: "M", | |
| 54 Counter: "C", | |
| 55 Sample: "P", | |
| 56 CreateObject: "N", | |
| 57 SnapshotObject: "O", | |
| 58 DeleteObject: "D" | |
| 59 }; | |
| 60 | |
| 61 WebInspector.TracingModel.MetadataEvent = { | |
| 62 ProcessSortIndex: "process_sort_index", | |
| 63 ProcessName: "process_name", | |
| 64 ThreadSortIndex: "thread_sort_index", | |
| 65 ThreadName: "thread_name" | |
| 66 } | |
| 67 | |
| 68 WebInspector.TracingModel.DevToolsMetadataEventCategory = "disabled-by-default-d
evtools.timeline"; | |
| 69 | |
| 70 WebInspector.TracingModel.FrameLifecycleEventCategory = "cc,devtools"; | |
| 71 | |
| 72 WebInspector.TracingModel.DevToolsMetadataEvent = { | |
| 73 TracingStartedInPage: "TracingStartedInPage", | |
| 74 SetLayerTreeId: "SetLayerTreeId" | |
| 75 }; | |
| 76 | |
| 77 WebInspector.TracingModel.TraceEventName = { | |
| 78 ActivateLayerTree: "ActivateLayerTree", | |
| 79 BeginFrame: "BeginFrame", | |
| 80 BeginMainThreadFrame: "BeginMainThreadFrame", | |
| 81 CompositeLayers: "CompositeLayers", | |
| 82 DrawFrame: "DrawFrame", | |
| 83 PaintSetup: "PaintSetup", | |
| 84 RasterTask: "RasterTask", | |
| 85 RequestMainThreadFrame: "RequestMainThreadFrame" | |
| 86 }; | |
| 87 | |
| 88 WebInspector.TracingModel.prototype = { | |
| 89 /** | |
| 90 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
| 91 */ | |
| 92 inspectedTargetMainThreadEvents: function() | |
| 93 { | |
| 94 return this._inspectedTargetMainThreadEvents; | |
| 95 }, | |
| 96 | |
| 97 /** | |
| 98 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
| 99 */ | |
| 100 frameLifecycleEvents: function() | |
| 101 { | |
| 102 /** | |
| 103 * @param {!WebInspector.TracingModel.Event} a | |
| 104 * @param {!WebInspector.TracingModel.Event} b | |
| 105 */ | |
| 106 function compareStartTime(a, b) | |
| 107 { | |
| 108 return a.startTime - b.startTime; | |
| 109 } | |
| 110 return this._frameLifecycleEvents.sort(compareStartTime); | |
| 111 }, | |
| 112 | |
| 113 /** | |
| 114 * @param {string} categoryFilter | |
| 115 * @param {string} options | |
| 116 * @param {function(?string)=} callback | |
| 117 */ | |
| 118 start: function(categoryFilter, options, callback) | |
| 119 { | |
| 120 this.reset(); | |
| 121 var bufferUsageReportingIntervalMs = 500; | |
| 122 /** | |
| 123 * @param {?string} error | |
| 124 * @param {string} sessionId | |
| 125 * @this {WebInspector.TracingModel} | |
| 126 */ | |
| 127 function callbackWrapper(error, sessionId) | |
| 128 { | |
| 129 this._sessionId = sessionId; | |
| 130 if (callback) | |
| 131 callback(error); | |
| 132 } | |
| 133 TracingAgent.start(categoryFilter, options, bufferUsageReportingInterval
Ms, callbackWrapper.bind(this)); | |
| 134 this._active = true; | |
| 135 }, | |
| 136 | |
| 137 /** | |
| 138 * @param {function()} callback | |
| 139 */ | |
| 140 stop: function(callback) | |
| 141 { | |
| 142 if (!this._active) { | |
| 143 callback(); | |
| 144 return; | |
| 145 } | |
| 146 this._pendingStopCallback = callback; | |
| 147 TracingAgent.end(); | |
| 148 }, | |
| 149 | |
| 150 /** | |
| 151 * @return {?string} | |
| 152 */ | |
| 153 sessionId: function() | |
| 154 { | |
| 155 return this._sessionId; | |
| 156 }, | |
| 157 | |
| 158 /** | |
| 159 * @param {number} usage | |
| 160 */ | |
| 161 _bufferUsage: function(usage) | |
| 162 { | |
| 163 this.dispatchEventToListeners(WebInspector.TracingModel.Events.BufferUsa
ge, usage); | |
| 164 }, | |
| 165 | |
| 166 /** | |
| 167 * @param {!Array.<!WebInspector.TracingModel.EventPayload>} events | |
| 168 */ | |
| 169 _eventsCollected: function(events) | |
| 170 { | |
| 171 for (var i = 0; i < events.length; ++i) | |
| 172 this._addEvent(events[i]); | |
| 173 }, | |
| 174 | |
| 175 _tracingComplete: function() | |
| 176 { | |
| 177 this._bindings = new WebInspector.TracingModel.EventBindings(this); | |
| 178 this._active = false; | |
| 179 if (!this._pendingStopCallback) | |
| 180 return; | |
| 181 this._pendingStopCallback(); | |
| 182 this._pendingStopCallback = null; | |
| 183 }, | |
| 184 | |
| 185 /** | |
| 186 * @return {!WebInspector.TracingModel.EventBindings} | |
| 187 */ | |
| 188 bindings: function() | |
| 189 { | |
| 190 return this._bindings; | |
| 191 }, | |
| 192 | |
| 193 reset: function() | |
| 194 { | |
| 195 this._processById = {}; | |
| 196 this._minimumRecordTime = null; | |
| 197 this._maximumRecordTime = null; | |
| 198 this._sessionId = null; | |
| 199 this._inspectedTargetProcessId = null; | |
| 200 this._inspectedTargetMainThread = null; | |
| 201 this._inspectedTargetMainThreadEvents = []; | |
| 202 this._inspectedTargetLayerTreeHostId = 0; | |
| 203 this._frameLifecycleEvents = []; | |
| 204 this._bindings = null; | |
| 205 }, | |
| 206 | |
| 207 /** | |
| 208 * @param {!WebInspector.TracingModel.EventPayload} payload | |
| 209 */ | |
| 210 _addEvent: function(payload) | |
| 211 { | |
| 212 var process = this._processById[payload.pid]; | |
| 213 if (!process) { | |
| 214 process = new WebInspector.TracingModel.Process(payload.pid); | |
| 215 this._processById[payload.pid] = process; | |
| 216 } | |
| 217 if (payload.ph === WebInspector.TracingModel.Phase.SnapshotObject) { | |
| 218 process.addObject(payload); | |
| 219 return; | |
| 220 } | |
| 221 var thread = process.threadById(payload.tid); | |
| 222 if (payload.ph !== WebInspector.TracingModel.Phase.Metadata) { | |
| 223 var timestamp = payload.ts; | |
| 224 // We do allow records for unrelated threads to arrive out-of-order, | |
| 225 // so there's a chance we're getting records from the past. | |
| 226 if (timestamp && (!this._minimumRecordTime || timestamp < this._mini
mumRecordTime)) | |
| 227 this._minimumRecordTime = timestamp; | |
| 228 if (!this._maximumRecordTime || timestamp > this._maximumRecordTime) | |
| 229 this._maximumRecordTime = timestamp; | |
| 230 if (payload.cat === WebInspector.TracingModel.DevToolsMetadataEventC
ategory) | |
| 231 this._processDevToolsMetadataEvent(payload); | |
| 232 var event = thread.addEvent(payload); | |
| 233 if (!event) | |
| 234 return; | |
| 235 if (thread === this._inspectedTargetMainThread) | |
| 236 this._inspectedTargetMainThreadEvents.push(event); | |
| 237 if (payload.cat === WebInspector.TracingModel.FrameLifecycleEventCat
egory && payload.pid === this._inspectedTargetProcessId && | |
| 238 payload.args && payload.args["layerTreeId"] === this._inspectedT
argetLayerTreeId) { | |
| 239 this._frameLifecycleEvents.push(event); | |
| 240 } | |
| 241 return; | |
| 242 } | |
| 243 switch (payload.name) { | |
| 244 case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex: | |
| 245 process._setSortIndex(payload.args["sort_index"]); | |
| 246 break; | |
| 247 case WebInspector.TracingModel.MetadataEvent.ProcessName: | |
| 248 process._setName(payload.args["name"]); | |
| 249 break; | |
| 250 case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex: | |
| 251 thread._setSortIndex(payload.args["sort_index"]); | |
| 252 break; | |
| 253 case WebInspector.TracingModel.MetadataEvent.ThreadName: | |
| 254 thread._setName(payload.args["name"]); | |
| 255 break; | |
| 256 } | |
| 257 }, | |
| 258 | |
| 259 /** | |
| 260 * @param {!WebInspector.TracingModel.EventPayload} payload | |
| 261 */ | |
| 262 _processDevToolsMetadataEvent: function(payload) | |
| 263 { | |
| 264 if (payload.args["sessionId"] !== this._sessionId) | |
| 265 return; | |
| 266 if (payload.name === WebInspector.TracingModel.DevToolsMetadataEvent.Tra
cingStartedInPage) { | |
| 267 var thread = this._processById[payload.pid].threadById(payload.tid) | |
| 268 this._inspectedTargetProcessId = payload.pid; | |
| 269 this._inspectedTargetMainThread = thread; | |
| 270 this._inspectedTargetMainThreadEvents = this._inspectedTargetMainThr
eadEvents.concat(thread.events()); | |
| 271 } else if (payload.name === WebInspector.TracingModel.DevToolsMetadataEv
ent.SetLayerTreeId) { | |
| 272 this._inspectedTargetLayerTreeId = payload.args["layerTreeId"]; | |
| 273 } | |
| 274 }, | |
| 275 | |
| 276 /** | |
| 277 * @return {?number} | |
| 278 */ | |
| 279 minimumRecordTime: function() | |
| 280 { | |
| 281 return this._minimumRecordTime; | |
| 282 }, | |
| 283 | |
| 284 /** | |
| 285 * @return {?number} | |
| 286 */ | |
| 287 maximumRecordTime: function() | |
| 288 { | |
| 289 return this._maximumRecordTime; | |
| 290 }, | |
| 291 | |
| 292 /** | |
| 293 * @return {!Array.<!WebInspector.TracingModel.Process>} | |
| 294 */ | |
| 295 sortedProcesses: function() | |
| 296 { | |
| 297 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._p
rocessById)); | |
| 298 }, | |
| 299 | |
| 300 __proto__: WebInspector.Object.prototype | |
| 301 } | |
| 302 | |
| 303 /** | |
| 304 * @param {!WebInspector.TracingModel} model | |
| 305 * @constructor | |
| 306 */ | |
| 307 WebInspector.TracingModel.EventBindings = function(model) | |
| 308 { | |
| 309 this._eventToWarning = new Map(); | |
| 310 this._model = model; | |
| 311 this._calculateWarnings(); | |
| 312 } | |
| 313 | |
| 314 WebInspector.TracingModel.EventBindings.prototype = { | |
| 315 /** | |
| 316 * @param {!WebInspector.TracingModel.Event} event | |
| 317 * @return {string|undefined} | |
| 318 */ | |
| 319 eventWarning: function(event) | |
| 320 { | |
| 321 return this._eventToWarning.get(event); | |
| 322 }, | |
| 323 | |
| 324 _calculateWarnings: function() | |
| 325 { | |
| 326 var events = this._model.inspectedTargetMainThreadEvents(); | |
| 327 var currentScriptEvent = null; | |
| 328 for (var i = 0, length = events.length; i < length; i++) { | |
| 329 var event = events[i]; | |
| 330 if (currentScriptEvent && event.startTime > currentScriptEvent.endTi
me) | |
| 331 currentScriptEvent = null; | |
| 332 if (event.name === WebInspector.TimelineModel.RecordType.Layout && c
urrentScriptEvent) | |
| 333 this._eventToWarning.put(event, WebInspector.UIString("Forced sy
nchronous layout is a possible performance bottleneck.")); | |
| 334 if (!currentScriptEvent && (event.name === WebInspector.TimelineMode
l.RecordType.EvaluateScript || event.name === WebInspector.TimelineModel.RecordT
ype.FunctionCall)) | |
| 335 currentScriptEvent = event; | |
| 336 } | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 /** | |
| 341 * @constructor | |
| 342 * @param {!WebInspector.TracingModel.EventPayload} payload | |
| 343 * @param {number} level | |
| 344 */ | |
| 345 WebInspector.TracingModel.Event = function(payload, level) | |
| 346 { | |
| 347 this.name = payload.name; | |
| 348 this.category = payload.cat; | |
| 349 this.startTime = payload.ts; | |
| 350 this.args = payload.args; | |
| 351 this.phase = payload.ph; | |
| 352 this.level = level; | |
| 353 } | |
| 354 | |
| 355 WebInspector.TracingModel.Event.prototype = { | |
| 356 /** | |
| 357 * @param {number} duration | |
| 358 */ | |
| 359 _setDuration: function(duration) | |
| 360 { | |
| 361 this.endTime = this.startTime + duration; | |
| 362 this.duration = duration; | |
| 363 }, | |
| 364 | |
| 365 /** | |
| 366 * @param {!WebInspector.TracingModel.EventPayload} payload | |
| 367 */ | |
| 368 _complete: function(payload) | |
| 369 { | |
| 370 if (this.name !== payload.name) { | |
| 371 console.assert(false, "Open/close event mismatch: " + this.name + "
vs. " + payload.name); | |
| 372 return; | |
| 373 } | |
| 374 var duration = payload.ts - this.startTime; | |
| 375 if (duration < 0) { | |
| 376 console.assert(false, "Event out of order: " + this.name); | |
| 377 return; | |
| 378 } | |
| 379 this._setDuration(duration); | |
| 380 } | |
| 381 } | |
| 382 | |
| 383 /** | |
| 384 * @constructor | |
| 385 */ | |
| 386 WebInspector.TracingModel.NamedObject = function() | |
| 387 { | |
| 388 } | |
| 389 | |
| 390 WebInspector.TracingModel.NamedObject.prototype = | |
| 391 { | |
| 392 /** | |
| 393 * @param {string} name | |
| 394 */ | |
| 395 _setName: function(name) | |
| 396 { | |
| 397 this._name = name; | |
| 398 }, | |
| 399 | |
| 400 /** | |
| 401 * @return {string} | |
| 402 */ | |
| 403 name: function() | |
| 404 { | |
| 405 return this._name; | |
| 406 }, | |
| 407 | |
| 408 /** | |
| 409 * @param {number} sortIndex | |
| 410 */ | |
| 411 _setSortIndex: function(sortIndex) | |
| 412 { | |
| 413 this._sortIndex = sortIndex; | |
| 414 }, | |
| 415 } | |
| 416 | |
| 417 /** | |
| 418 * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array | |
| 419 */ | |
| 420 WebInspector.TracingModel.NamedObject._sort = function(array) | |
| 421 { | |
| 422 /** | |
| 423 * @param {!WebInspector.TracingModel.NamedObject} a | |
| 424 * @param {!WebInspector.TracingModel.NamedObject} b | |
| 425 */ | |
| 426 function comparator(a, b) | |
| 427 { | |
| 428 return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.n
ame().localeCompare(b.name()); | |
| 429 } | |
| 430 return array.sort(comparator); | |
| 431 } | |
| 432 | |
| 433 /** | |
| 434 * @constructor | |
| 435 * @extends {WebInspector.TracingModel.NamedObject} | |
| 436 * @param {number} id | |
| 437 */ | |
| 438 WebInspector.TracingModel.Process = function(id) | |
| 439 { | |
| 440 WebInspector.TracingModel.NamedObject.call(this); | |
| 441 this._setName("Process " + id); | |
| 442 this._threads = {}; | |
| 443 this._objects = {}; | |
| 444 } | |
| 445 | |
| 446 WebInspector.TracingModel.Process.prototype = { | |
| 447 /** | |
| 448 * @param {number} id | |
| 449 * @return {!WebInspector.TracingModel.Thread} | |
| 450 */ | |
| 451 threadById: function(id) | |
| 452 { | |
| 453 var thread = this._threads[id]; | |
| 454 if (!thread) { | |
| 455 thread = new WebInspector.TracingModel.Thread(id); | |
| 456 this._threads[id] = thread; | |
| 457 } | |
| 458 return thread; | |
| 459 }, | |
| 460 | |
| 461 /** | |
| 462 * @param {!WebInspector.TracingModel.EventPayload} event | |
| 463 */ | |
| 464 addObject: function(event) | |
| 465 { | |
| 466 this.objectsByName(event.name).push(new WebInspector.TracingModel.Event(
event, 0)); | |
| 467 }, | |
| 468 | |
| 469 /** | |
| 470 * @param {string} name | |
| 471 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
| 472 */ | |
| 473 objectsByName: function(name) | |
| 474 { | |
| 475 var objects = this._objects[name]; | |
| 476 if (!objects) { | |
| 477 objects = []; | |
| 478 this._objects[name] = objects; | |
| 479 } | |
| 480 return objects; | |
| 481 }, | |
| 482 | |
| 483 /** | |
| 484 * @return {!Array.<string>} | |
| 485 */ | |
| 486 sortedObjectNames: function() | |
| 487 { | |
| 488 return Object.keys(this._objects).sort(); | |
| 489 }, | |
| 490 | |
| 491 /** | |
| 492 * @return {!Array.<!WebInspector.TracingModel.Thread>} | |
| 493 */ | |
| 494 sortedThreads: function() | |
| 495 { | |
| 496 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._t
hreads)); | |
| 497 }, | |
| 498 | |
| 499 __proto__: WebInspector.TracingModel.NamedObject.prototype | |
| 500 } | |
| 501 | |
| 502 /** | |
| 503 * @constructor | |
| 504 * @extends {WebInspector.TracingModel.NamedObject} | |
| 505 * @param {number} id | |
| 506 */ | |
| 507 WebInspector.TracingModel.Thread = function(id) | |
| 508 { | |
| 509 WebInspector.TracingModel.NamedObject.call(this); | |
| 510 this._setName("Thread " + id); | |
| 511 this._events = []; | |
| 512 this._stack = []; | |
| 513 this._maxStackDepth = 0; | |
| 514 } | |
| 515 | |
| 516 WebInspector.TracingModel.Thread.prototype = { | |
| 517 /** | |
| 518 * @param {!WebInspector.TracingModel.EventPayload} payload | |
| 519 * @return {?WebInspector.TracingModel.Event} event | |
| 520 */ | |
| 521 addEvent: function(payload) | |
| 522 { | |
| 523 for (var top = this._stack.peekLast(); top && top.endTime && top.endTime
<= payload.ts;) { | |
| 524 this._stack.pop(); | |
| 525 top = this._stack.peekLast(); | |
| 526 } | |
| 527 if (payload.ph === WebInspector.TracingModel.Phase.End) { | |
| 528 var openEvent = this._stack.pop(); | |
| 529 // Quietly ignore unbalanced close events, they're legit (we could h
ave missed start one). | |
| 530 if (openEvent) | |
| 531 openEvent._complete(payload); | |
| 532 return null; | |
| 533 } | |
| 534 | |
| 535 var event = new WebInspector.TracingModel.Event(payload, this._stack.len
gth); | |
| 536 if (payload.ph === WebInspector.TracingModel.Phase.Begin || payload.ph =
== WebInspector.TracingModel.Phase.Complete) { | |
| 537 if (payload.ph === WebInspector.TracingModel.Phase.Complete) | |
| 538 event._setDuration(payload.dur); | |
| 539 this._stack.push(event); | |
| 540 if (this._maxStackDepth < this._stack.length) | |
| 541 this._maxStackDepth = this._stack.length; | |
| 542 } | |
| 543 if (this._events.length && this._events.peekLast().startTime > event.sta
rtTime) | |
| 544 console.assert(false, "Event is our of order: " + event.name); | |
| 545 this._events.push(event); | |
| 546 return event; | |
| 547 }, | |
| 548 | |
| 549 /** | |
| 550 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
| 551 */ | |
| 552 events: function() | |
| 553 { | |
| 554 return this._events; | |
| 555 }, | |
| 556 | |
| 557 /** | |
| 558 * @return {number} | |
| 559 */ | |
| 560 maxStackDepth: function() | |
| 561 { | |
| 562 // Reserve one for non-container events. | |
| 563 return this._maxStackDepth + 1; | |
| 564 }, | |
| 565 | |
| 566 __proto__: WebInspector.TracingModel.NamedObject.prototype | |
| 567 } | |
| 568 | |
| 569 | |
| 570 /** | |
| 571 * @constructor | |
| 572 * @implements {TracingAgent.Dispatcher} | |
| 573 * @param {!WebInspector.TracingModel} tracingModel | |
| 574 */ | |
| 575 WebInspector.TracingDispatcher = function(tracingModel) | |
| 576 { | |
| 577 this._tracingModel = tracingModel; | |
| 578 } | |
| 579 | |
| 580 WebInspector.TracingDispatcher.prototype = { | |
| 581 /** | |
| 582 * @param {number} usage | |
| 583 */ | |
| 584 bufferUsage: function(usage) | |
| 585 { | |
| 586 this._tracingModel._bufferUsage(usage); | |
| 587 }, | |
| 588 | |
| 589 /** | |
| 590 * @param {!Array.<!WebInspector.TracingModel.EventPayload>} data | |
| 591 */ | |
| 592 dataCollected: function(data) | |
| 593 { | |
| 594 this._tracingModel._eventsCollected(data); | |
| 595 }, | |
| 596 | |
| 597 tracingComplete: function() | |
| 598 { | |
| 599 this._tracingModel._tracingComplete(); | |
| 600 } | |
| 601 } | |
| OLD | NEW |