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._active = false; | |
178 if (!this._pendingStopCallback) | |
179 return; | |
180 this._pendingStopCallback(); | |
181 this._pendingStopCallback = null; | |
182 }, | |
183 | |
184 reset: function() | |
185 { | |
186 this._processById = {}; | |
187 this._minimumRecordTime = null; | |
188 this._maximumRecordTime = null; | |
189 this._sessionId = null; | |
190 this._inspectedTargetProcessId = null; | |
191 this._inspectedTargetMainThread = null; | |
192 this._inspectedTargetMainThreadEvents = []; | |
193 this._inspectedTargetLayerTreeHostId = 0; | |
194 this._frameLifecycleEvents = []; | |
195 }, | |
196 | |
197 /** | |
198 * @param {!WebInspector.TracingModel.EventPayload} payload | |
199 */ | |
200 _addEvent: function(payload) | |
201 { | |
202 var process = this._processById[payload.pid]; | |
203 if (!process) { | |
204 process = new WebInspector.TracingModel.Process(payload.pid); | |
205 this._processById[payload.pid] = process; | |
206 } | |
207 if (payload.ph === WebInspector.TracingModel.Phase.SnapshotObject) { | |
208 process.addObject(payload); | |
209 return; | |
210 } | |
211 var thread = process.threadById(payload.tid); | |
212 if (payload.ph !== WebInspector.TracingModel.Phase.Metadata) { | |
213 var timestamp = payload.ts; | |
214 // We do allow records for unrelated threads to arrive out-of-order, | |
215 // so there's a chance we're getting records from the past. | |
216 if (timestamp && (!this._minimumRecordTime || timestamp < this._mini
mumRecordTime)) | |
217 this._minimumRecordTime = timestamp; | |
218 if (!this._maximumRecordTime || timestamp > this._maximumRecordTime) | |
219 this._maximumRecordTime = timestamp; | |
220 if (payload.cat === WebInspector.TracingModel.DevToolsMetadataEventC
ategory) | |
221 this._processDevToolsMetadataEvent(payload); | |
222 var event = thread.addEvent(payload); | |
223 if (!event) | |
224 return; | |
225 if (thread === this._inspectedTargetMainThread) | |
226 this._inspectedTargetMainThreadEvents.push(event); | |
227 if (payload.cat === WebInspector.TracingModel.FrameLifecycleEventCat
egory && payload.pid === this._inspectedTargetProcessId && | |
228 payload.args && payload.args["layerTreeId"] === this._inspectedT
argetLayerTreeId) { | |
229 this._frameLifecycleEvents.push(event); | |
230 } | |
231 return; | |
232 } | |
233 switch (payload.name) { | |
234 case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex: | |
235 process._setSortIndex(payload.args["sort_index"]); | |
236 break; | |
237 case WebInspector.TracingModel.MetadataEvent.ProcessName: | |
238 process._setName(payload.args["name"]); | |
239 break; | |
240 case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex: | |
241 thread._setSortIndex(payload.args["sort_index"]); | |
242 break; | |
243 case WebInspector.TracingModel.MetadataEvent.ThreadName: | |
244 thread._setName(payload.args["name"]); | |
245 break; | |
246 } | |
247 }, | |
248 | |
249 /** | |
250 * @param {!WebInspector.TracingModel.EventPayload} payload | |
251 */ | |
252 _processDevToolsMetadataEvent: function(payload) | |
253 { | |
254 if (payload.args["sessionId"] !== this._sessionId) | |
255 return; | |
256 if (payload.name === WebInspector.TracingModel.DevToolsMetadataEvent.Tra
cingStartedInPage) { | |
257 var thread = this._processById[payload.pid].threadById(payload.tid) | |
258 this._inspectedTargetProcessId = payload.pid; | |
259 this._inspectedTargetMainThread = thread; | |
260 this._inspectedTargetMainThreadEvents = this._inspectedTargetMainThr
eadEvents.concat(thread.events()); | |
261 } else if (payload.name === WebInspector.TracingModel.DevToolsMetadataEv
ent.SetLayerTreeId) { | |
262 this._inspectedTargetLayerTreeId = payload.args["layerTreeId"]; | |
263 } | |
264 }, | |
265 | |
266 /** | |
267 * @return {?number} | |
268 */ | |
269 minimumRecordTime: function() | |
270 { | |
271 return this._minimumRecordTime; | |
272 }, | |
273 | |
274 /** | |
275 * @return {?number} | |
276 */ | |
277 maximumRecordTime: function() | |
278 { | |
279 return this._maximumRecordTime; | |
280 }, | |
281 | |
282 /** | |
283 * @return {!Array.<!WebInspector.TracingModel.Process>} | |
284 */ | |
285 sortedProcesses: function() | |
286 { | |
287 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._p
rocessById)); | |
288 }, | |
289 | |
290 __proto__: WebInspector.Object.prototype | |
291 } | |
292 | |
293 /** | |
294 * @constructor | |
295 * @param {!WebInspector.TracingModel.EventPayload} payload | |
296 * @param {number} level | |
297 */ | |
298 WebInspector.TracingModel.Event = function(payload, level) | |
299 { | |
300 this.name = payload.name; | |
301 this.category = payload.cat; | |
302 this.startTime = payload.ts; | |
303 this.args = payload.args; | |
304 this.phase = payload.ph; | |
305 this.level = level; | |
306 } | |
307 | |
308 WebInspector.TracingModel.Event.prototype = { | |
309 /** | |
310 * @param {number} duration | |
311 */ | |
312 _setDuration: function(duration) | |
313 { | |
314 this.endTime = this.startTime + duration; | |
315 this.duration = duration; | |
316 }, | |
317 | |
318 /** | |
319 * @param {!WebInspector.TracingModel.EventPayload} payload | |
320 */ | |
321 _complete: function(payload) | |
322 { | |
323 if (this.name !== payload.name) { | |
324 console.assert(false, "Open/close event mismatch: " + this.name + "
vs. " + payload.name); | |
325 return; | |
326 } | |
327 var duration = payload.ts - this.startTime; | |
328 if (duration < 0) { | |
329 console.assert(false, "Event out of order: " + this.name); | |
330 return; | |
331 } | |
332 this._setDuration(duration); | |
333 } | |
334 } | |
335 | |
336 /** | |
337 * @constructor | |
338 */ | |
339 WebInspector.TracingModel.NamedObject = function() | |
340 { | |
341 } | |
342 | |
343 WebInspector.TracingModel.NamedObject.prototype = | |
344 { | |
345 /** | |
346 * @param {string} name | |
347 */ | |
348 _setName: function(name) | |
349 { | |
350 this._name = name; | |
351 }, | |
352 | |
353 /** | |
354 * @return {string} | |
355 */ | |
356 name: function() | |
357 { | |
358 return this._name; | |
359 }, | |
360 | |
361 /** | |
362 * @param {number} sortIndex | |
363 */ | |
364 _setSortIndex: function(sortIndex) | |
365 { | |
366 this._sortIndex = sortIndex; | |
367 }, | |
368 } | |
369 | |
370 /** | |
371 * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array | |
372 */ | |
373 WebInspector.TracingModel.NamedObject._sort = function(array) | |
374 { | |
375 /** | |
376 * @param {!WebInspector.TracingModel.NamedObject} a | |
377 * @param {!WebInspector.TracingModel.NamedObject} b | |
378 */ | |
379 function comparator(a, b) | |
380 { | |
381 return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.n
ame().localeCompare(b.name()); | |
382 } | |
383 return array.sort(comparator); | |
384 } | |
385 | |
386 /** | |
387 * @constructor | |
388 * @extends {WebInspector.TracingModel.NamedObject} | |
389 * @param {number} id | |
390 */ | |
391 WebInspector.TracingModel.Process = function(id) | |
392 { | |
393 WebInspector.TracingModel.NamedObject.call(this); | |
394 this._setName("Process " + id); | |
395 this._threads = {}; | |
396 this._objects = {}; | |
397 } | |
398 | |
399 WebInspector.TracingModel.Process.prototype = { | |
400 /** | |
401 * @param {number} id | |
402 * @return {!WebInspector.TracingModel.Thread} | |
403 */ | |
404 threadById: function(id) | |
405 { | |
406 var thread = this._threads[id]; | |
407 if (!thread) { | |
408 thread = new WebInspector.TracingModel.Thread(id); | |
409 this._threads[id] = thread; | |
410 } | |
411 return thread; | |
412 }, | |
413 | |
414 /** | |
415 * @param {!WebInspector.TracingModel.EventPayload} event | |
416 */ | |
417 addObject: function(event) | |
418 { | |
419 this.objectsByName(event.name).push(new WebInspector.TracingModel.Event(
event, 0)); | |
420 }, | |
421 | |
422 /** | |
423 * @param {string} name | |
424 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
425 */ | |
426 objectsByName: function(name) | |
427 { | |
428 var objects = this._objects[name]; | |
429 if (!objects) { | |
430 objects = []; | |
431 this._objects[name] = objects; | |
432 } | |
433 return objects; | |
434 }, | |
435 | |
436 /** | |
437 * @return {!Array.<string>} | |
438 */ | |
439 sortedObjectNames: function() | |
440 { | |
441 return Object.keys(this._objects).sort(); | |
442 }, | |
443 | |
444 /** | |
445 * @return {!Array.<!WebInspector.TracingModel.Thread>} | |
446 */ | |
447 sortedThreads: function() | |
448 { | |
449 return WebInspector.TracingModel.NamedObject._sort(Object.values(this._t
hreads)); | |
450 }, | |
451 | |
452 __proto__: WebInspector.TracingModel.NamedObject.prototype | |
453 } | |
454 | |
455 /** | |
456 * @constructor | |
457 * @extends {WebInspector.TracingModel.NamedObject} | |
458 * @param {number} id | |
459 */ | |
460 WebInspector.TracingModel.Thread = function(id) | |
461 { | |
462 WebInspector.TracingModel.NamedObject.call(this); | |
463 this._setName("Thread " + id); | |
464 this._events = []; | |
465 this._stack = []; | |
466 this._maxStackDepth = 0; | |
467 } | |
468 | |
469 WebInspector.TracingModel.Thread.prototype = { | |
470 /** | |
471 * @param {!WebInspector.TracingModel.EventPayload} payload | |
472 * @return {?WebInspector.TracingModel.Event} event | |
473 */ | |
474 addEvent: function(payload) | |
475 { | |
476 for (var top = this._stack.peekLast(); top && top.endTime && top.endTime
<= payload.ts;) { | |
477 this._stack.pop(); | |
478 top = this._stack.peekLast(); | |
479 } | |
480 if (payload.ph === WebInspector.TracingModel.Phase.End) { | |
481 var openEvent = this._stack.pop(); | |
482 // Quietly ignore unbalanced close events, they're legit (we could h
ave missed start one). | |
483 if (openEvent) | |
484 openEvent._complete(payload); | |
485 return null; | |
486 } | |
487 | |
488 var event = new WebInspector.TracingModel.Event(payload, this._stack.len
gth); | |
489 if (payload.ph === WebInspector.TracingModel.Phase.Begin || payload.ph =
== WebInspector.TracingModel.Phase.Complete) { | |
490 if (payload.ph === WebInspector.TracingModel.Phase.Complete) | |
491 event._setDuration(payload.dur); | |
492 this._stack.push(event); | |
493 if (this._maxStackDepth < this._stack.length) | |
494 this._maxStackDepth = this._stack.length; | |
495 } | |
496 if (this._events.length && this._events.peekLast().startTime > event.sta
rtTime) | |
497 console.assert(false, "Event is our of order: " + event.name); | |
498 this._events.push(event); | |
499 return event; | |
500 }, | |
501 | |
502 /** | |
503 * @return {!Array.<!WebInspector.TracingModel.Event>} | |
504 */ | |
505 events: function() | |
506 { | |
507 return this._events; | |
508 }, | |
509 | |
510 /** | |
511 * @return {number} | |
512 */ | |
513 maxStackDepth: function() | |
514 { | |
515 // Reserve one for non-container events. | |
516 return this._maxStackDepth + 1; | |
517 }, | |
518 | |
519 __proto__: WebInspector.TracingModel.NamedObject.prototype | |
520 } | |
521 | |
522 | |
523 /** | |
524 * @constructor | |
525 * @implements {TracingAgent.Dispatcher} | |
526 * @param {!WebInspector.TracingModel} tracingModel | |
527 */ | |
528 WebInspector.TracingDispatcher = function(tracingModel) | |
529 { | |
530 this._tracingModel = tracingModel; | |
531 } | |
532 | |
533 WebInspector.TracingDispatcher.prototype = { | |
534 /** | |
535 * @param {number} usage | |
536 */ | |
537 bufferUsage: function(usage) | |
538 { | |
539 this._tracingModel._bufferUsage(usage); | |
540 }, | |
541 | |
542 /** | |
543 * @param {!Array.<!WebInspector.TracingModel.EventPayload>} data | |
544 */ | |
545 dataCollected: function(data) | |
546 { | |
547 this._tracingModel._eventsCollected(data); | |
548 }, | |
549 | |
550 tracingComplete: function() | |
551 { | |
552 this._tracingModel._tracingComplete(); | |
553 } | |
554 } | |
OLD | NEW |