Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(865)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/sdk/TracingModel.js

Issue 2466123002: DevTools: reformat front-end code to match chromium style. (Closed)
Patch Set: all done Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2014 The Chromium Authors. All rights reserved. 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 3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file. 4 * found in the LICENSE file.
5 */ 5 */
6
7 /** 6 /**
8 * @constructor 7 * @unrestricted
9 * @param {!WebInspector.BackingStorage} backingStorage
10 */ 8 */
11 WebInspector.TracingModel = function(backingStorage) 9 WebInspector.TracingModel = class {
12 { 10 /**
11 * @param {!WebInspector.BackingStorage} backingStorage
12 */
13 constructor(backingStorage) {
13 this._backingStorage = backingStorage; 14 this._backingStorage = backingStorage;
14 // Avoid extra reset of the storage as it's expensive. 15 // Avoid extra reset of the storage as it's expensive.
15 this._firstWritePending = true; 16 this._firstWritePending = true;
16 this.reset(); 17 this.reset();
18 }
19
20 /**
21 * @param {string} phase
22 * @return {boolean}
23 */
24 static isNestableAsyncPhase(phase) {
25 return phase === 'b' || phase === 'e' || phase === 'n';
26 }
27
28 /**
29 * @param {string} phase
30 * @return {boolean}
31 */
32 static isAsyncBeginPhase(phase) {
33 return phase === 'S' || phase === 'b';
34 }
35
36 /**
37 * @param {string} phase
38 * @return {boolean}
39 */
40 static isAsyncPhase(phase) {
41 return WebInspector.TracingModel.isNestableAsyncPhase(phase) || phase === 'S ' || phase === 'T' || phase === 'F' ||
42 phase === 'p';
43 }
44
45 /**
46 * @param {string} phase
47 * @return {boolean}
48 */
49 static isFlowPhase(phase) {
50 return phase === 's' || phase === 't' || phase === 'f';
51 }
52
53 /**
54 * @param {!WebInspector.TracingModel.Event} event
55 * @return {boolean}
56 */
57 static isTopLevelEvent(event) {
58 return event.hasCategory(WebInspector.TracingModel.TopLevelEventCategory) ||
59 event.hasCategory(WebInspector.TracingModel.DevToolsMetadataEventCategor y) &&
60 event.name === 'Program'; // Older timelines may have this instead of t oplevel.
61 }
62
63 /**
64 * @param {!WebInspector.TracingManager.EventPayload} payload
65 * @return {string|undefined}
66 */
67 static _extractId(payload) {
68 var scope = payload.scope || '';
69 if (typeof payload.id2 === 'undefined')
70 return scope && payload.id ? `${scope}@${payload.id}` : payload.id;
71 var id2 = payload.id2;
72 if (typeof id2 === 'object' && ('global' in id2) !== ('local' in id2))
73 return typeof id2['global'] !== 'undefined' ? `:${scope}:${id2['global']}` :
74 `:${scope}:${payload.pid}:${ id2['local']}`;
75 console.error(
76 `Unexpected id2 field at ${payload.ts / 1000}, one and only one of 'loca l' and 'global' should be present.`);
77 }
78
79 /**
80 * @param {!WebInspector.TracingModel} tracingModel
81 * @return {?WebInspector.TracingModel.Thread}
82 *
83 * TODO: Move this to a better place. This is here just for convenience o
84 * re-use between modules. This really belongs to a higher level, since it
85 * is specific to chrome's usage of tracing.
86 */
87 static browserMainThread(tracingModel) {
88 var processes = tracingModel.sortedProcesses();
89 // Avoid warning for an empty model.
90 if (!processes.length)
91 return null;
92 var browserProcesses = [];
93 var crRendererMainThreads = [];
94 for (var process of processes) {
95 if (process.name().toLowerCase().endsWith('browser'))
96 browserProcesses.push(process);
97 crRendererMainThreads.push(...process.sortedThreads().filter(t => t.name() === 'CrBrowserMain'));
98 }
99 if (crRendererMainThreads.length === 1)
100 return crRendererMainThreads[0];
101 if (browserProcesses.length === 1)
102 return browserProcesses[0].threadByName('CrBrowserMain');
103 var tracingStartedInBrowser =
104 tracingModel.devToolsMetadataEvents().filter(e => e.name === 'TracingSta rtedInBrowser');
105 if (tracingStartedInBrowser.length === 1)
106 return tracingStartedInBrowser[0].thread;
107 WebInspector.console.error(
108 'Failed to find browser main thread in trace, some timeline features may be unavailable');
109 return null;
110 }
111
112 /**
113 * @return {!Array.<!WebInspector.TracingModel.Event>}
114 */
115 devToolsMetadataEvents() {
116 return this._devToolsMetadataEvents;
117 }
118
119 /**
120 * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
121 */
122 setEventsForTest(events) {
123 this.reset();
124 this.addEvents(events);
125 this.tracingComplete();
126 }
127
128 /**
129 * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
130 */
131 addEvents(events) {
132 for (var i = 0; i < events.length; ++i)
133 this._addEvent(events[i]);
134 }
135
136 tracingComplete() {
137 this._processPendingAsyncEvents();
138 this._backingStorage.appendString(this._firstWritePending ? '[]' : ']');
139 this._backingStorage.finishWriting();
140 this._firstWritePending = false;
141 for (var process of this._processById.values()) {
142 for (var thread of process._threads.values())
143 thread.tracingComplete();
144 }
145 }
146
147 reset() {
148 /** @type {!Map<(number|string), !WebInspector.TracingModel.Process>} */
149 this._processById = new Map();
150 this._processByName = new Map();
151 this._minimumRecordTime = 0;
152 this._maximumRecordTime = 0;
153 this._devToolsMetadataEvents = [];
154 if (!this._firstWritePending)
155 this._backingStorage.reset();
156
157 this._firstWritePending = true;
158 /** @type {!Array<!WebInspector.TracingModel.Event>} */
159 this._asyncEvents = [];
160 /** @type {!Map<string, !WebInspector.TracingModel.AsyncEvent>} */
161 this._openAsyncEvents = new Map();
162 /** @type {!Map<string, !Array<!WebInspector.TracingModel.AsyncEvent>>} */
163 this._openNestableAsyncEvents = new Map();
164 /** @type {!Map<string, !WebInspector.TracingModel.ProfileEventsGroup>} */
165 this._profileGroups = new Map();
166 /** @type {!Map<string, !Set<string>>} */
167 this._parsedCategories = new Map();
168 }
169
170 /**
171 * @param {!WebInspector.TracingManager.EventPayload} payload
172 */
173 _addEvent(payload) {
174 var process = this._processById.get(payload.pid);
175 if (!process) {
176 process = new WebInspector.TracingModel.Process(this, payload.pid);
177 this._processById.set(payload.pid, process);
178 }
179
180 var eventsDelimiter = ',\n';
181 this._backingStorage.appendString(this._firstWritePending ? '[' : eventsDeli miter);
182 this._firstWritePending = false;
183 var stringPayload = JSON.stringify(payload);
184 var isAccessible = payload.ph === WebInspector.TracingModel.Phase.SnapshotOb ject;
185 var backingStorage = null;
186 var keepStringsLessThan = 10000;
187 if (isAccessible && stringPayload.length > keepStringsLessThan)
188 backingStorage = this._backingStorage.appendAccessibleString(stringPayload );
189 else
190 this._backingStorage.appendString(stringPayload);
191
192 var timestamp = payload.ts / 1000;
193 // We do allow records for unrelated threads to arrive out-of-order,
194 // so there's a chance we're getting records from the past.
195 if (timestamp && (!this._minimumRecordTime || timestamp < this._minimumRecor dTime))
196 this._minimumRecordTime = timestamp;
197 var endTimeStamp = (payload.ts + (payload.dur || 0)) / 1000;
198 this._maximumRecordTime = Math.max(this._maximumRecordTime, endTimeStamp);
199 var event = process._addEvent(payload);
200 if (!event)
201 return;
202 if (payload.ph === WebInspector.TracingModel.Phase.Sample) {
203 this._addSampleEvent(event);
204 return;
205 }
206 // Build async event when we've got events from all threads & processes, so we can sort them and process in the
207 // chronological order. However, also add individual async events to the thr ead flow (above), so we can easily
208 // display them on the same chart as other events, should we choose so.
209 if (WebInspector.TracingModel.isAsyncPhase(payload.ph))
210 this._asyncEvents.push(event);
211 event._setBackingStorage(backingStorage);
212 if (event.hasCategory(WebInspector.TracingModel.DevToolsMetadataEventCategor y))
213 this._devToolsMetadataEvents.push(event);
214
215 if (payload.ph !== WebInspector.TracingModel.Phase.Metadata)
216 return;
217
218 switch (payload.name) {
219 case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex:
220 process._setSortIndex(payload.args['sort_index']);
221 break;
222 case WebInspector.TracingModel.MetadataEvent.ProcessName:
223 var processName = payload.args['name'];
224 process._setName(processName);
225 this._processByName.set(processName, process);
226 break;
227 case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex:
228 process.threadById(payload.tid)._setSortIndex(payload.args['sort_index'] );
229 break;
230 case WebInspector.TracingModel.MetadataEvent.ThreadName:
231 process.threadById(payload.tid)._setName(payload.args['name']);
232 break;
233 }
234 }
235
236 /**
237 * @param {!WebInspector.TracingModel.Event} event
238 */
239 _addSampleEvent(event) {
240 var group = this._profileGroups.get(event.id);
241 if (group)
242 group._addChild(event);
243 else
244 this._profileGroups.set(event.id, new WebInspector.TracingModel.ProfileEve ntsGroup(event));
245 }
246
247 /**
248 * @param {string} id
249 * @return {?WebInspector.TracingModel.ProfileEventsGroup}
250 */
251 profileGroup(id) {
252 return this._profileGroups.get(id) || null;
253 }
254
255 /**
256 * @return {number}
257 */
258 minimumRecordTime() {
259 return this._minimumRecordTime;
260 }
261
262 /**
263 * @return {number}
264 */
265 maximumRecordTime() {
266 return this._maximumRecordTime;
267 }
268
269 /**
270 * @return {!Array.<!WebInspector.TracingModel.Process>}
271 */
272 sortedProcesses() {
273 return WebInspector.TracingModel.NamedObject._sort(this._processById.valuesA rray());
274 }
275
276 /**
277 * @param {string} name
278 * @return {?WebInspector.TracingModel.Process}
279 */
280 processByName(name) {
281 return this._processByName.get(name);
282 }
283
284 /**
285 * @param {string} processName
286 * @param {string} threadName
287 * @return {?WebInspector.TracingModel.Thread}
288 */
289 threadByName(processName, threadName) {
290 var process = this.processByName(processName);
291 return process && process.threadByName(threadName);
292 }
293
294 _processPendingAsyncEvents() {
295 this._asyncEvents.stableSort(WebInspector.TracingModel.Event.compareStartTim e);
296 for (var i = 0; i < this._asyncEvents.length; ++i) {
297 var event = this._asyncEvents[i];
298 if (WebInspector.TracingModel.isNestableAsyncPhase(event.phase))
299 this._addNestableAsyncEvent(event);
300 else
301 this._addAsyncEvent(event);
302 }
303 this._asyncEvents = [];
304 this._closeOpenAsyncEvents();
305 }
306
307 _closeOpenAsyncEvents() {
308 for (var event of this._openAsyncEvents.values()) {
309 event.setEndTime(this._maximumRecordTime);
310 // FIXME: remove this once we figure a better way to convert async console
311 // events to sync [waterfall] timeline records.
312 event.steps[0].setEndTime(this._maximumRecordTime);
313 }
314 this._openAsyncEvents.clear();
315
316 for (var eventStack of this._openNestableAsyncEvents.values()) {
317 while (eventStack.length)
318 eventStack.pop().setEndTime(this._maximumRecordTime);
319 }
320 this._openNestableAsyncEvents.clear();
321 }
322
323 /**
324 * @param {!WebInspector.TracingModel.Event} event
325 */
326 _addNestableAsyncEvent(event) {
327 var phase = WebInspector.TracingModel.Phase;
328 var key = event.categoriesString + '.' + event.id;
329 var openEventsStack = this._openNestableAsyncEvents.get(key);
330
331 switch (event.phase) {
332 case phase.NestableAsyncBegin:
333 if (!openEventsStack) {
334 openEventsStack = [];
335 this._openNestableAsyncEvents.set(key, openEventsStack);
336 }
337 var asyncEvent = new WebInspector.TracingModel.AsyncEvent(event);
338 openEventsStack.push(asyncEvent);
339 event.thread._addAsyncEvent(asyncEvent);
340 break;
341
342 case phase.NestableAsyncInstant:
343 if (openEventsStack && openEventsStack.length)
344 openEventsStack.peekLast()._addStep(event);
345 break;
346
347 case phase.NestableAsyncEnd:
348 if (!openEventsStack || !openEventsStack.length)
349 break;
350 var top = openEventsStack.pop();
351 if (top.name !== event.name) {
352 console.error(
353 `Begin/end event mismatch for nestable async event, ${top.name} vs . ${event.name}, key: ${key}`);
354 break;
355 }
356 top._addStep(event);
357 }
358 }
359
360 /**
361 * @param {!WebInspector.TracingModel.Event} event
362 */
363 _addAsyncEvent(event) {
364 var phase = WebInspector.TracingModel.Phase;
365 var key = event.categoriesString + '.' + event.name + '.' + event.id;
366 var asyncEvent = this._openAsyncEvents.get(key);
367
368 if (event.phase === phase.AsyncBegin) {
369 if (asyncEvent) {
370 console.error(`Event ${event.name} has already been started`);
371 return;
372 }
373 asyncEvent = new WebInspector.TracingModel.AsyncEvent(event);
374 this._openAsyncEvents.set(key, asyncEvent);
375 event.thread._addAsyncEvent(asyncEvent);
376 return;
377 }
378 if (!asyncEvent) {
379 // Quietly ignore stray async events, we're probably too late for the star t.
380 return;
381 }
382 if (event.phase === phase.AsyncEnd) {
383 asyncEvent._addStep(event);
384 this._openAsyncEvents.delete(key);
385 return;
386 }
387 if (event.phase === phase.AsyncStepInto || event.phase === phase.AsyncStepPa st) {
388 var lastStep = asyncEvent.steps.peekLast();
389 if (lastStep.phase !== phase.AsyncBegin && lastStep.phase !== event.phase) {
390 console.assert(
391 false, 'Async event step phase mismatch: ' + lastStep.phase + ' at ' + lastStep.startTime + ' vs. ' +
392 event.phase + ' at ' + event.startTime);
393 return;
394 }
395 asyncEvent._addStep(event);
396 return;
397 }
398 console.assert(false, 'Invalid async event phase');
399 }
400
401 /**
402 * @param {string} str
403 * @return {!Set<string>}
404 */
405 _parsedCategoriesForString(str) {
406 var parsedCategories = this._parsedCategories.get(str);
407 if (!parsedCategories) {
408 parsedCategories = new Set(str.split(','));
409 this._parsedCategories.set(str, parsedCategories);
410 }
411 return parsedCategories;
412 }
17 }; 413 };
18 414
19 /** 415 /**
20 * @enum {string} 416 * @enum {string}
21 */ 417 */
22 WebInspector.TracingModel.Phase = { 418 WebInspector.TracingModel.Phase = {
23 Begin: "B", 419 Begin: 'B',
24 End: "E", 420 End: 'E',
25 Complete: "X", 421 Complete: 'X',
26 Instant: "I", 422 Instant: 'I',
27 AsyncBegin: "S", 423 AsyncBegin: 'S',
28 AsyncStepInto: "T", 424 AsyncStepInto: 'T',
29 AsyncStepPast: "p", 425 AsyncStepPast: 'p',
30 AsyncEnd: "F", 426 AsyncEnd: 'F',
31 NestableAsyncBegin: "b", 427 NestableAsyncBegin: 'b',
32 NestableAsyncEnd: "e", 428 NestableAsyncEnd: 'e',
33 NestableAsyncInstant: "n", 429 NestableAsyncInstant: 'n',
34 FlowBegin: "s", 430 FlowBegin: 's',
35 FlowStep: "t", 431 FlowStep: 't',
36 FlowEnd: "f", 432 FlowEnd: 'f',
37 Metadata: "M", 433 Metadata: 'M',
38 Counter: "C", 434 Counter: 'C',
39 Sample: "P", 435 Sample: 'P',
40 CreateObject: "N", 436 CreateObject: 'N',
41 SnapshotObject: "O", 437 SnapshotObject: 'O',
42 DeleteObject: "D" 438 DeleteObject: 'D'
43 }; 439 };
44 440
45 WebInspector.TracingModel.MetadataEvent = { 441 WebInspector.TracingModel.MetadataEvent = {
46 ProcessSortIndex: "process_sort_index", 442 ProcessSortIndex: 'process_sort_index',
47 ProcessName: "process_name", 443 ProcessName: 'process_name',
48 ThreadSortIndex: "thread_sort_index", 444 ThreadSortIndex: 'thread_sort_index',
49 ThreadName: "thread_name" 445 ThreadName: 'thread_name'
50 }; 446 };
51 447
52 WebInspector.TracingModel.TopLevelEventCategory = "toplevel"; 448 WebInspector.TracingModel.TopLevelEventCategory = 'toplevel';
53 WebInspector.TracingModel.DevToolsMetadataEventCategory = "disabled-by-default-d evtools.timeline"; 449 WebInspector.TracingModel.DevToolsMetadataEventCategory = 'disabled-by-default-d evtools.timeline';
54 WebInspector.TracingModel.DevToolsTimelineEventCategory = "disabled-by-default-d evtools.timeline"; 450 WebInspector.TracingModel.DevToolsTimelineEventCategory = 'disabled-by-default-d evtools.timeline';
55 451
56 WebInspector.TracingModel.FrameLifecycleEventCategory = "cc,devtools"; 452 WebInspector.TracingModel.FrameLifecycleEventCategory = 'cc,devtools';
57 453
58 /**
59 * @param {string} phase
60 * @return {boolean}
61 */
62 WebInspector.TracingModel.isNestableAsyncPhase = function(phase)
63 {
64 return phase === "b" || phase === "e" || phase === "n";
65 };
66
67 /**
68 * @param {string} phase
69 * @return {boolean}
70 */
71 WebInspector.TracingModel.isAsyncBeginPhase = function(phase)
72 {
73 return phase === "S" || phase === "b";
74 };
75
76 /**
77 * @param {string} phase
78 * @return {boolean}
79 */
80 WebInspector.TracingModel.isAsyncPhase = function(phase)
81 {
82 return WebInspector.TracingModel.isNestableAsyncPhase(phase) || phase === "S " || phase === "T" || phase === "F" || phase === "p";
83 };
84
85 /**
86 * @param {string} phase
87 * @return {boolean}
88 */
89 WebInspector.TracingModel.isFlowPhase = function(phase)
90 {
91 return phase === "s" || phase === "t" || phase === "f";
92 };
93
94 /**
95 * @param {!WebInspector.TracingModel.Event} event
96 * @return {boolean}
97 */
98 WebInspector.TracingModel.isTopLevelEvent = function(event)
99 {
100 return event.hasCategory(WebInspector.TracingModel.TopLevelEventCategory) ||
101 event.hasCategory(WebInspector.TracingModel.DevToolsMetadataEventCategor y) && event.name === "Program"; // Older timelines may have this instead of topl evel.
102 };
103
104 /**
105 * @param {!WebInspector.TracingManager.EventPayload} payload
106 * @return {string|undefined}
107 */
108 WebInspector.TracingModel._extractId = function(payload)
109 {
110 var scope = payload.scope || "";
111 if (typeof payload.id2 === "undefined")
112 return scope && payload.id ? `${scope}@${payload.id}` : payload.id;
113 var id2 = payload.id2;
114 if (typeof id2 === "object" && ("global" in id2) !== ("local" in id2))
115 return typeof id2["global"] !== "undefined" ? `:${scope}:${id2["global"] }` : `:${scope}:${payload.pid}:${id2["local"]}`;
116 console.error(`Unexpected id2 field at ${payload.ts / 1000}, one and only on e of 'local' and 'global' should be present.`);
117 };
118
119 /**
120 * @param {!WebInspector.TracingModel} tracingModel
121 * @return {?WebInspector.TracingModel.Thread}
122 *
123 * TODO: Move this to a better place. This is here just for convenience o
124 * re-use between modules. This really belongs to a higher level, since it
125 * is specific to chrome's usage of tracing.
126 */
127 WebInspector.TracingModel.browserMainThread = function(tracingModel)
128 {
129 var processes = tracingModel.sortedProcesses();
130 // Avoid warning for an empty model.
131 if (!processes.length)
132 return null;
133 var browserProcesses = [];
134 var crRendererMainThreads = [];
135 for (var process of processes) {
136 if (process.name().toLowerCase().endsWith("browser"))
137 browserProcesses.push(process);
138 crRendererMainThreads.push(...process.sortedThreads().filter(t => t.name () === "CrBrowserMain"));
139 }
140 if (crRendererMainThreads.length === 1)
141 return crRendererMainThreads[0];
142 if (browserProcesses.length === 1)
143 return browserProcesses[0].threadByName("CrBrowserMain");
144 var tracingStartedInBrowser = tracingModel.devToolsMetadataEvents().filter(e => e.name === "TracingStartedInBrowser");
145 if (tracingStartedInBrowser.length === 1)
146 return tracingStartedInBrowser[0].thread;
147 WebInspector.console.error("Failed to find browser main thread in trace, som e timeline features may be unavailable");
148 return null;
149 };
150 454
151 /** 455 /**
152 * @interface 456 * @interface
153 */ 457 */
154 WebInspector.BackingStorage = function() 458 WebInspector.BackingStorage = function() {};
155 { 459
460 WebInspector.BackingStorage.prototype = {
461 /**
462 * @param {string} string
463 */
464 appendString: function(string) {},
465
466 /**
467 * @param {string} string
468 * @return {function():!Promise.<?string>}
469 */
470 appendAccessibleString: function(string) {},
471
472 finishWriting: function() {},
473
474 reset: function() {},
156 }; 475 };
157 476
158 WebInspector.BackingStorage.prototype = {
159 /**
160 * @param {string} string
161 */
162 appendString: function(string) { },
163
164 /**
165 * @param {string} string
166 * @return {function():!Promise.<?string>}
167 */
168 appendAccessibleString: function(string) { },
169
170 finishWriting: function() { },
171
172 reset: function() { },
173 };
174
175
176 WebInspector.TracingModel.prototype = {
177 /**
178 * @return {!Array.<!WebInspector.TracingModel.Event>}
179 */
180 devToolsMetadataEvents: function()
181 {
182 return this._devToolsMetadataEvents;
183 },
184
185 /**
186 * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
187 */
188 setEventsForTest: function(events)
189 {
190 this.reset();
191 this.addEvents(events);
192 this.tracingComplete();
193 },
194
195 /**
196 * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
197 */
198 addEvents: function(events)
199 {
200 for (var i = 0; i < events.length; ++i)
201 this._addEvent(events[i]);
202 },
203
204 tracingComplete: function()
205 {
206 this._processPendingAsyncEvents();
207 this._backingStorage.appendString(this._firstWritePending ? "[]" : "]");
208 this._backingStorage.finishWriting();
209 this._firstWritePending = false;
210 for (var process of this._processById.values()) {
211 for (var thread of process._threads.values())
212 thread.tracingComplete();
213 }
214 },
215
216 reset: function()
217 {
218 /** @type {!Map<(number|string), !WebInspector.TracingModel.Process>} */
219 this._processById = new Map();
220 this._processByName = new Map();
221 this._minimumRecordTime = 0;
222 this._maximumRecordTime = 0;
223 this._devToolsMetadataEvents = [];
224 if (!this._firstWritePending)
225 this._backingStorage.reset();
226
227 this._firstWritePending = true;
228 /** @type {!Array<!WebInspector.TracingModel.Event>} */
229 this._asyncEvents = [];
230 /** @type {!Map<string, !WebInspector.TracingModel.AsyncEvent>} */
231 this._openAsyncEvents = new Map();
232 /** @type {!Map<string, !Array<!WebInspector.TracingModel.AsyncEvent>>} */
233 this._openNestableAsyncEvents = new Map();
234 /** @type {!Map<string, !WebInspector.TracingModel.ProfileEventsGroup>} */
235 this._profileGroups = new Map();
236 /** @type {!Map<string, !Set<string>>} */
237 this._parsedCategories = new Map();
238 },
239
240 /**
241 * @param {!WebInspector.TracingManager.EventPayload} payload
242 */
243 _addEvent: function(payload)
244 {
245 var process = this._processById.get(payload.pid);
246 if (!process) {
247 process = new WebInspector.TracingModel.Process(this, payload.pid);
248 this._processById.set(payload.pid, process);
249 }
250
251 var eventsDelimiter = ",\n";
252 this._backingStorage.appendString(this._firstWritePending ? "[" : events Delimiter);
253 this._firstWritePending = false;
254 var stringPayload = JSON.stringify(payload);
255 var isAccessible = payload.ph === WebInspector.TracingModel.Phase.Snapsh otObject;
256 var backingStorage = null;
257 var keepStringsLessThan = 10000;
258 if (isAccessible && stringPayload.length > keepStringsLessThan)
259 backingStorage = this._backingStorage.appendAccessibleString(stringP ayload);
260 else
261 this._backingStorage.appendString(stringPayload);
262
263 var timestamp = payload.ts / 1000;
264 // We do allow records for unrelated threads to arrive out-of-order,
265 // so there's a chance we're getting records from the past.
266 if (timestamp && (!this._minimumRecordTime || timestamp < this._minimumR ecordTime))
267 this._minimumRecordTime = timestamp;
268 var endTimeStamp = (payload.ts + (payload.dur || 0)) / 1000;
269 this._maximumRecordTime = Math.max(this._maximumRecordTime, endTimeStamp );
270 var event = process._addEvent(payload);
271 if (!event)
272 return;
273 if (payload.ph === WebInspector.TracingModel.Phase.Sample) {
274 this._addSampleEvent(event);
275 return;
276 }
277 // Build async event when we've got events from all threads & processes, so we can sort them and process in the
278 // chronological order. However, also add individual async events to the thread flow (above), so we can easily
279 // display them on the same chart as other events, should we choose so.
280 if (WebInspector.TracingModel.isAsyncPhase(payload.ph))
281 this._asyncEvents.push(event);
282 event._setBackingStorage(backingStorage);
283 if (event.hasCategory(WebInspector.TracingModel.DevToolsMetadataEventCat egory))
284 this._devToolsMetadataEvents.push(event);
285
286 if (payload.ph !== WebInspector.TracingModel.Phase.Metadata)
287 return;
288
289 switch (payload.name) {
290 case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex:
291 process._setSortIndex(payload.args["sort_index"]);
292 break;
293 case WebInspector.TracingModel.MetadataEvent.ProcessName:
294 var processName = payload.args["name"];
295 process._setName(processName);
296 this._processByName.set(processName, process);
297 break;
298 case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex:
299 process.threadById(payload.tid)._setSortIndex(payload.args["sort_ind ex"]);
300 break;
301 case WebInspector.TracingModel.MetadataEvent.ThreadName:
302 process.threadById(payload.tid)._setName(payload.args["name"]);
303 break;
304 }
305 },
306
307 /**
308 * @param {!WebInspector.TracingModel.Event} event
309 */
310 _addSampleEvent: function(event)
311 {
312 var group = this._profileGroups.get(event.id);
313 if (group)
314 group._addChild(event);
315 else
316 this._profileGroups.set(event.id, new WebInspector.TracingModel.Prof ileEventsGroup(event));
317 },
318
319 /**
320 * @param {string} id
321 * @return {?WebInspector.TracingModel.ProfileEventsGroup}
322 */
323 profileGroup: function(id)
324 {
325 return this._profileGroups.get(id) || null;
326 },
327
328 /**
329 * @return {number}
330 */
331 minimumRecordTime: function()
332 {
333 return this._minimumRecordTime;
334 },
335
336 /**
337 * @return {number}
338 */
339 maximumRecordTime: function()
340 {
341 return this._maximumRecordTime;
342 },
343
344 /**
345 * @return {!Array.<!WebInspector.TracingModel.Process>}
346 */
347 sortedProcesses: function()
348 {
349 return WebInspector.TracingModel.NamedObject._sort(this._processById.val uesArray());
350 },
351
352 /**
353 * @param {string} name
354 * @return {?WebInspector.TracingModel.Process}
355 */
356 processByName: function(name)
357 {
358 return this._processByName.get(name);
359 },
360
361 /**
362 * @param {string} processName
363 * @param {string} threadName
364 * @return {?WebInspector.TracingModel.Thread}
365 */
366 threadByName: function(processName, threadName)
367 {
368 var process = this.processByName(processName);
369 return process && process.threadByName(threadName);
370 },
371
372 _processPendingAsyncEvents: function()
373 {
374 this._asyncEvents.stableSort(WebInspector.TracingModel.Event.compareStar tTime);
375 for (var i = 0; i < this._asyncEvents.length; ++i) {
376 var event = this._asyncEvents[i];
377 if (WebInspector.TracingModel.isNestableAsyncPhase(event.phase))
378 this._addNestableAsyncEvent(event);
379 else
380 this._addAsyncEvent(event);
381 }
382 this._asyncEvents = [];
383 this._closeOpenAsyncEvents();
384 },
385
386 _closeOpenAsyncEvents: function()
387 {
388 for (var event of this._openAsyncEvents.values()) {
389 event.setEndTime(this._maximumRecordTime);
390 // FIXME: remove this once we figure a better way to convert async c onsole
391 // events to sync [waterfall] timeline records.
392 event.steps[0].setEndTime(this._maximumRecordTime);
393 }
394 this._openAsyncEvents.clear();
395
396 for (var eventStack of this._openNestableAsyncEvents.values()) {
397 while (eventStack.length)
398 eventStack.pop().setEndTime(this._maximumRecordTime);
399 }
400 this._openNestableAsyncEvents.clear();
401 },
402
403 /**
404 * @param {!WebInspector.TracingModel.Event} event
405 */
406 _addNestableAsyncEvent: function(event)
407 {
408 var phase = WebInspector.TracingModel.Phase;
409 var key = event.categoriesString + "." + event.id;
410 var openEventsStack = this._openNestableAsyncEvents.get(key);
411
412 switch (event.phase) {
413 case phase.NestableAsyncBegin:
414 if (!openEventsStack) {
415 openEventsStack = [];
416 this._openNestableAsyncEvents.set(key, openEventsStack);
417 }
418 var asyncEvent = new WebInspector.TracingModel.AsyncEvent(event);
419 openEventsStack.push(asyncEvent);
420 event.thread._addAsyncEvent(asyncEvent);
421 break;
422
423 case phase.NestableAsyncInstant:
424 if (openEventsStack && openEventsStack.length)
425 openEventsStack.peekLast()._addStep(event);
426 break;
427
428 case phase.NestableAsyncEnd:
429 if (!openEventsStack || !openEventsStack.length)
430 break;
431 var top = openEventsStack.pop();
432 if (top.name !== event.name) {
433 console.error(`Begin/end event mismatch for nestable async event , ${top.name} vs. ${event.name}, key: ${key}`);
434 break;
435 }
436 top._addStep(event);
437 }
438 },
439
440 /**
441 * @param {!WebInspector.TracingModel.Event} event
442 */
443 _addAsyncEvent: function(event)
444 {
445 var phase = WebInspector.TracingModel.Phase;
446 var key = event.categoriesString + "." + event.name + "." + event.id;
447 var asyncEvent = this._openAsyncEvents.get(key);
448
449 if (event.phase === phase.AsyncBegin) {
450 if (asyncEvent) {
451 console.error(`Event ${event.name} has already been started`);
452 return;
453 }
454 asyncEvent = new WebInspector.TracingModel.AsyncEvent(event);
455 this._openAsyncEvents.set(key, asyncEvent);
456 event.thread._addAsyncEvent(asyncEvent);
457 return;
458 }
459 if (!asyncEvent) {
460 // Quietly ignore stray async events, we're probably too late for th e start.
461 return;
462 }
463 if (event.phase === phase.AsyncEnd) {
464 asyncEvent._addStep(event);
465 this._openAsyncEvents.delete(key);
466 return;
467 }
468 if (event.phase === phase.AsyncStepInto || event.phase === phase.AsyncSt epPast) {
469 var lastStep = asyncEvent.steps.peekLast();
470 if (lastStep.phase !== phase.AsyncBegin && lastStep.phase !== event. phase) {
471 console.assert(false, "Async event step phase mismatch: " + last Step.phase + " at " + lastStep.startTime + " vs. " + event.phase + " at " + even t.startTime);
472 return;
473 }
474 asyncEvent._addStep(event);
475 return;
476 }
477 console.assert(false, "Invalid async event phase");
478 },
479
480 /**
481 * @param {string} str
482 * @return {!Set<string>}
483 */
484 _parsedCategoriesForString: function(str)
485 {
486 var parsedCategories = this._parsedCategories.get(str);
487 if (!parsedCategories) {
488 parsedCategories = new Set(str.split(","));
489 this._parsedCategories.set(str, parsedCategories);
490 }
491 return parsedCategories;
492 }
493 };
494
495 /** 477 /**
496 * @constructor 478 * @unrestricted
497 * @param {string} categories
498 * @param {string} name
499 * @param {!WebInspector.TracingModel.Phase} phase
500 * @param {number} startTime
501 * @param {!WebInspector.TracingModel.Thread} thread
502 */ 479 */
503 WebInspector.TracingModel.Event = function(categories, name, phase, startTime, t hread) 480 WebInspector.TracingModel.Event = class {
504 { 481 /**
482 * @param {string} categories
483 * @param {string} name
484 * @param {!WebInspector.TracingModel.Phase} phase
485 * @param {number} startTime
486 * @param {!WebInspector.TracingModel.Thread} thread
487 */
488 constructor(categories, name, phase, startTime, thread) {
505 /** @type {string} */ 489 /** @type {string} */
506 this.categoriesString = categories; 490 this.categoriesString = categories;
507 /** @type {!Set<string>} */ 491 /** @type {!Set<string>} */
508 this._parsedCategories = thread._model._parsedCategoriesForString(categories ); 492 this._parsedCategories = thread._model._parsedCategoriesForString(categories );
509 /** @type {string} */ 493 /** @type {string} */
510 this.name = name; 494 this.name = name;
511 /** @type {!WebInspector.TracingModel.Phase} */ 495 /** @type {!WebInspector.TracingModel.Phase} */
512 this.phase = phase; 496 this.phase = phase;
513 /** @type {number} */ 497 /** @type {number} */
514 this.startTime = startTime; 498 this.startTime = startTime;
(...skipping 10 matching lines...) Expand all
525 this.stackTrace = null; 509 this.stackTrace = null;
526 /** @type {?Element} */ 510 /** @type {?Element} */
527 this.previewElement = null; 511 this.previewElement = null;
528 /** @type {?string} */ 512 /** @type {?string} */
529 this.url = null; 513 this.url = null;
530 /** @type {number} */ 514 /** @type {number} */
531 this.backendNodeId = 0; 515 this.backendNodeId = 0;
532 516
533 /** @type {number} */ 517 /** @type {number} */
534 this.selfTime = 0; 518 this.selfTime = 0;
535 }; 519 }
536 520
537 /** 521 /**
538 * @param {!WebInspector.TracingManager.EventPayload} payload 522 * @param {!WebInspector.TracingManager.EventPayload} payload
539 * @param {!WebInspector.TracingModel.Thread} thread 523 * @param {!WebInspector.TracingModel.Thread} thread
540 * @return {!WebInspector.TracingModel.Event} 524 * @return {!WebInspector.TracingModel.Event}
541 */ 525 */
542 WebInspector.TracingModel.Event.fromPayload = function(payload, thread) 526 static fromPayload(payload, thread) {
543 { 527 var event = new WebInspector.TracingModel.Event(
544 var event = new WebInspector.TracingModel.Event(payload.cat, payload.name, / ** @type {!WebInspector.TracingModel.Phase} */ (payload.ph), payload.ts / 1000, thread); 528 payload.cat, payload.name, /** @type {!WebInspector.TracingModel.Phase} */ (payload.ph), payload.ts / 1000,
529 thread);
545 if (payload.args) 530 if (payload.args)
546 event.addArgs(payload.args); 531 event.addArgs(payload.args);
547 else 532 else
548 console.error("Missing mandatory event argument 'args' at " + payload.ts / 1000); 533 console.error('Missing mandatory event argument \'args\' at ' + payload.ts / 1000);
549 if (typeof payload.dur === "number") 534 if (typeof payload.dur === 'number')
550 event.setEndTime((payload.ts + payload.dur) / 1000); 535 event.setEndTime((payload.ts + payload.dur) / 1000);
551 var id = WebInspector.TracingModel._extractId(payload); 536 var id = WebInspector.TracingModel._extractId(payload);
552 if (typeof id !== "undefined") 537 if (typeof id !== 'undefined')
553 event.id = id; 538 event.id = id;
554 if (payload.bind_id) 539 if (payload.bind_id)
555 event.bind_id = payload.bind_id; 540 event.bind_id = payload.bind_id;
556 541
557 return event; 542 return event;
558 }; 543 }
559 544
560 WebInspector.TracingModel.Event.prototype = { 545 /**
561 /** 546 * @param {!WebInspector.TracingModel.Event} a
562 * @param {string} categoryName 547 * @param {!WebInspector.TracingModel.Event} b
563 * @return {boolean} 548 * @return {number}
564 */ 549 */
565 hasCategory: function(categoryName) 550 static compareStartTime(a, b) {
566 {
567 return this._parsedCategories.has(categoryName);
568 },
569
570 /**
571 * @param {number} endTime
572 */
573 setEndTime: function(endTime)
574 {
575 if (endTime < this.startTime) {
576 console.assert(false, "Event out of order: " + this.name);
577 return;
578 }
579 this.endTime = endTime;
580 this.duration = endTime - this.startTime;
581 },
582
583 /**
584 * @param {!Object} args
585 */
586 addArgs: function(args)
587 {
588 // Shallow copy args to avoid modifying original payload which may be sa ved to file.
589 for (var name in args) {
590 if (name in this.args)
591 console.error("Same argument name (" + name + ") is used for be gin and end phases of " + this.name);
592 this.args[name] = args[name];
593 }
594 },
595
596 /**
597 * @param {!WebInspector.TracingModel.Event} endEvent
598 */
599 _complete: function(endEvent)
600 {
601 if (endEvent.args)
602 this.addArgs(endEvent.args);
603 else
604 console.error("Missing mandatory event argument 'args' at " + endEve nt.startTime);
605 this.setEndTime(endEvent.startTime);
606 },
607
608 /**
609 * @param {?function():!Promise.<?string>} backingStorage
610 */
611 _setBackingStorage: function(backingStorage)
612 {
613 }
614 };
615
616 /**
617 * @param {!WebInspector.TracingModel.Event} a
618 * @param {!WebInspector.TracingModel.Event} b
619 * @return {number}
620 */
621 WebInspector.TracingModel.Event.compareStartTime = function(a, b)
622 {
623 return a.startTime - b.startTime; 551 return a.startTime - b.startTime;
624 }; 552 }
625 553
626 /** 554 /**
627 * @param {!WebInspector.TracingModel.Event} a 555 * @param {!WebInspector.TracingModel.Event} a
628 * @param {!WebInspector.TracingModel.Event} b 556 * @param {!WebInspector.TracingModel.Event} b
629 * @return {number} 557 * @return {number}
630 */ 558 */
631 WebInspector.TracingModel.Event.compareStartAndEndTime = function(a, b) 559 static compareStartAndEndTime(a, b) {
632 { 560 return a.startTime - b.startTime || (b.endTime !== undefined && a.endTime != = undefined && b.endTime - a.endTime) ||
633 return a.startTime - b.startTime || (b.endTime !== undefined && a.endTime != = undefined && b.endTime - a.endTime) || 0; 561 0;
634 }; 562 }
635 563
636 /** 564 /**
637 * @param {!WebInspector.TracingModel.Event} a 565 * @param {!WebInspector.TracingModel.Event} a
638 * @param {!WebInspector.TracingModel.Event} b 566 * @param {!WebInspector.TracingModel.Event} b
639 * @return {number} 567 * @return {number}
640 */ 568 */
641 WebInspector.TracingModel.Event.orderedCompareStartTime = function(a, b) 569 static orderedCompareStartTime(a, b) {
642 {
643 // Array.mergeOrdered coalesces objects if comparator returns 0. 570 // Array.mergeOrdered coalesces objects if comparator returns 0.
644 // To change this behavior this comparator return -1 in the case events 571 // To change this behavior this comparator return -1 in the case events
645 // startTime's are equal, so both events got placed into the result array. 572 // startTime's are equal, so both events got placed into the result array.
646 return a.startTime - b.startTime || a.ordinal - b.ordinal || -1; 573 return a.startTime - b.startTime || a.ordinal - b.ordinal || -1;
647 }; 574 }
648 575
649 /** 576 /**
650 * @constructor 577 * @param {string} categoryName
651 * @extends {WebInspector.TracingModel.Event} 578 * @return {boolean}
652 * @param {string} category 579 */
653 * @param {string} name 580 hasCategory(categoryName) {
654 * @param {number} startTime 581 return this._parsedCategories.has(categoryName);
655 * @param {!WebInspector.TracingModel.Thread} thread 582 }
656 */ 583
657 WebInspector.TracingModel.ObjectSnapshot = function(category, name, startTime, t hread) 584 /**
658 { 585 * @param {number} endTime
659 WebInspector.TracingModel.Event.call(this, category, name, WebInspector.Trac ingModel.Phase.SnapshotObject, startTime, thread); 586 */
660 }; 587 setEndTime(endTime) {
661 588 if (endTime < this.startTime) {
662 /** 589 console.assert(false, 'Event out of order: ' + this.name);
663 * @param {!WebInspector.TracingManager.EventPayload} payload 590 return;
664 * @param {!WebInspector.TracingModel.Thread} thread 591 }
665 * @return {!WebInspector.TracingModel.ObjectSnapshot} 592 this.endTime = endTime;
666 */ 593 this.duration = endTime - this.startTime;
667 WebInspector.TracingModel.ObjectSnapshot.fromPayload = function(payload, thread) 594 }
668 { 595
596 /**
597 * @param {!Object} args
598 */
599 addArgs(args) {
600 // Shallow copy args to avoid modifying original payload which may be saved to file.
601 for (var name in args) {
602 if (name in this.args)
603 console.error('Same argument name (' + name + ') is used for begin and e nd phases of ' + this.name);
604 this.args[name] = args[name];
605 }
606 }
607
608 /**
609 * @param {!WebInspector.TracingModel.Event} endEvent
610 */
611 _complete(endEvent) {
612 if (endEvent.args)
613 this.addArgs(endEvent.args);
614 else
615 console.error('Missing mandatory event argument \'args\' at ' + endEvent.s tartTime);
616 this.setEndTime(endEvent.startTime);
617 }
618
619 /**
620 * @param {?function():!Promise.<?string>} backingStorage
621 */
622 _setBackingStorage(backingStorage) {
623 }
624 };
625
626
627 /**
628 * @unrestricted
629 */
630 WebInspector.TracingModel.ObjectSnapshot = class extends WebInspector.TracingMod el.Event {
631 /**
632 * @param {string} category
633 * @param {string} name
634 * @param {number} startTime
635 * @param {!WebInspector.TracingModel.Thread} thread
636 */
637 constructor(category, name, startTime, thread) {
638 super(category, name, WebInspector.TracingModel.Phase.SnapshotObject, startT ime, thread);
639 }
640
641 /**
642 * @param {!WebInspector.TracingManager.EventPayload} payload
643 * @param {!WebInspector.TracingModel.Thread} thread
644 * @return {!WebInspector.TracingModel.ObjectSnapshot}
645 */
646 static fromPayload(payload, thread) {
669 var snapshot = new WebInspector.TracingModel.ObjectSnapshot(payload.cat, pay load.name, payload.ts / 1000, thread); 647 var snapshot = new WebInspector.TracingModel.ObjectSnapshot(payload.cat, pay load.name, payload.ts / 1000, thread);
670 var id = WebInspector.TracingModel._extractId(payload); 648 var id = WebInspector.TracingModel._extractId(payload);
671 if (typeof id !== "undefined") 649 if (typeof id !== 'undefined')
672 snapshot.id = id; 650 snapshot.id = id;
673 if (!payload.args || !payload.args["snapshot"]) { 651 if (!payload.args || !payload.args['snapshot']) {
674 console.error("Missing mandatory 'snapshot' argument at " + payload.ts / 1000); 652 console.error('Missing mandatory \'snapshot\' argument at ' + payload.ts / 1000);
675 return snapshot; 653 return snapshot;
676 } 654 }
677 if (payload.args) 655 if (payload.args)
678 snapshot.addArgs(payload.args); 656 snapshot.addArgs(payload.args);
679 return snapshot; 657 return snapshot;
680 }; 658 }
681 659
682 WebInspector.TracingModel.ObjectSnapshot.prototype = { 660 /**
661 * @param {function(?)} callback
662 */
663 requestObject(callback) {
664 var snapshot = this.args['snapshot'];
665 if (snapshot) {
666 callback(snapshot);
667 return;
668 }
669 this._backingStorage().then(onRead, callback.bind(null, null));
683 /** 670 /**
684 * @param {function(?)} callback 671 * @param {?string} result
685 */ 672 */
686 requestObject: function(callback) 673 function onRead(result) {
687 { 674 if (!result) {
688 var snapshot = this.args["snapshot"]; 675 callback(null);
689 if (snapshot) { 676 return;
690 callback(snapshot); 677 }
691 return; 678 try {
692 } 679 var payload = JSON.parse(result);
693 this._backingStorage().then(onRead, callback.bind(null, null)); 680 callback(payload['args']['snapshot']);
694 /** 681 } catch (e) {
695 * @param {?string} result 682 WebInspector.console.error('Malformed event data in backing storage');
696 */ 683 callback(null);
697 function onRead(result) 684 }
698 { 685 }
699 if (!result) { 686 }
700 callback(null); 687
701 return; 688 /**
702 } 689 * @return {!Promise<?>}
703 try { 690 */
704 var payload = JSON.parse(result); 691 objectPromise() {
705 callback(payload["args"]["snapshot"]); 692 if (!this._objectPromise)
706 } catch (e) { 693 this._objectPromise = new Promise(this.requestObject.bind(this));
707 WebInspector.console.error("Malformed event data in backing stor age"); 694 return this._objectPromise;
708 callback(null); 695 }
709 } 696
710 } 697 /**
711 }, 698 * @override
712 699 * @param {?function():!Promise.<?>} backingStorage
713 /** 700 */
714 * @return {!Promise<?>} 701 _setBackingStorage(backingStorage) {
715 */ 702 if (!backingStorage)
716 objectPromise: function() 703 return;
717 { 704 this._backingStorage = backingStorage;
718 if (!this._objectPromise) 705 this.args = {};
719 this._objectPromise = new Promise(this.requestObject.bind(this)); 706 }
720 return this._objectPromise; 707 };
721 }, 708
722 709
723 /** 710 /**
724 * @override 711 * @unrestricted
725 * @param {?function():!Promise.<?>} backingStorage 712 */
726 */ 713 WebInspector.TracingModel.AsyncEvent = class extends WebInspector.TracingModel.E vent {
727 _setBackingStorage: function(backingStorage) 714 /**
728 { 715 * @param {!WebInspector.TracingModel.Event} startEvent
729 if (!backingStorage) 716 */
730 return; 717 constructor(startEvent) {
731 this._backingStorage = backingStorage; 718 super(startEvent.categoriesString, startEvent.name, startEvent.phase, startE vent.startTime, startEvent.thread);
732 this.args = {};
733 },
734
735 __proto__: WebInspector.TracingModel.Event.prototype
736 };
737
738 /**
739 * @constructor
740 * @param {!WebInspector.TracingModel.Event} startEvent
741 * @extends {WebInspector.TracingModel.Event}
742 */
743 WebInspector.TracingModel.AsyncEvent = function(startEvent)
744 {
745 WebInspector.TracingModel.Event.call(this, startEvent.categoriesString, star tEvent.name, startEvent.phase, startEvent.startTime, startEvent.thread);
746 this.addArgs(startEvent.args); 719 this.addArgs(startEvent.args);
747 this.steps = [startEvent]; 720 this.steps = [startEvent];
748 }; 721 }
749 722
750 WebInspector.TracingModel.AsyncEvent.prototype = { 723 /**
751 /** 724 * @param {!WebInspector.TracingModel.Event} event
752 * @param {!WebInspector.TracingModel.Event} event 725 */
753 */ 726 _addStep(event) {
754 _addStep: function(event) 727 this.steps.push(event);
755 { 728 if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd ||
756 this.steps.push(event); 729 event.phase === WebInspector.TracingModel.Phase.NestableAsyncEnd) {
757 if (event.phase === WebInspector.TracingModel.Phase.AsyncEnd || event.ph ase === WebInspector.TracingModel.Phase.NestableAsyncEnd) { 730 this.setEndTime(event.startTime);
758 this.setEndTime(event.startTime); 731 // FIXME: ideally, we shouldn't do this, but this makes the logic of conve rting
759 // FIXME: ideally, we shouldn't do this, but this makes the logic of converting 732 // async console events to sync ones much simpler.
760 // async console events to sync ones much simpler. 733 this.steps[0].setEndTime(event.startTime);
761 this.steps[0].setEndTime(event.startTime); 734 }
762 } 735 }
763 }, 736 };
764 737
765 __proto__: WebInspector.TracingModel.Event.prototype 738 /**
766 }; 739 * @unrestricted
767 740 */
768 /** 741 WebInspector.TracingModel.ProfileEventsGroup = class {
769 * @constructor 742 /**
770 * @param {!WebInspector.TracingModel.Event} event 743 * @param {!WebInspector.TracingModel.Event} event
771 */ 744 */
772 WebInspector.TracingModel.ProfileEventsGroup = function(event) 745 constructor(event) {
773 {
774 /** @type {!Array<!WebInspector.TracingModel.Event>} */ 746 /** @type {!Array<!WebInspector.TracingModel.Event>} */
775 this.children = [event]; 747 this.children = [event];
776 }; 748 }
777 749
778 WebInspector.TracingModel.ProfileEventsGroup.prototype = { 750 /**
779 /** 751 * @param {!WebInspector.TracingModel.Event} event
780 * @param {!WebInspector.TracingModel.Event} event 752 */
781 */ 753 _addChild(event) {
782 _addChild: function(event) 754 this.children.push(event);
783 { 755 }
784 this.children.push(event); 756 };
785 } 757
786 }; 758 /**
787 759 * @unrestricted
788 /** 760 */
789 * @constructor 761 WebInspector.TracingModel.NamedObject = class {
790 */ 762 /**
791 WebInspector.TracingModel.NamedObject = function() 763 * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array
792 { 764 */
793 }; 765 static _sort(array) {
794
795 WebInspector.TracingModel.NamedObject.prototype =
796 {
797 /**
798 * @param {string} name
799 */
800 _setName: function(name)
801 {
802 this._name = name;
803 },
804
805 /**
806 * @return {string}
807 */
808 name: function()
809 {
810 return this._name;
811 },
812
813 /**
814 * @param {number} sortIndex
815 */
816 _setSortIndex: function(sortIndex)
817 {
818 this._sortIndex = sortIndex;
819 },
820 };
821
822 /**
823 * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array
824 */
825 WebInspector.TracingModel.NamedObject._sort = function(array)
826 {
827 /** 766 /**
828 * @param {!WebInspector.TracingModel.NamedObject} a 767 * @param {!WebInspector.TracingModel.NamedObject} a
829 * @param {!WebInspector.TracingModel.NamedObject} b 768 * @param {!WebInspector.TracingModel.NamedObject} b
830 */ 769 */
831 function comparator(a, b) 770 function comparator(a, b) {
832 { 771 return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.nam e().localeCompare(b.name());
833 return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.n ame().localeCompare(b.name());
834 } 772 }
835 return array.sort(comparator); 773 return array.sort(comparator);
836 }; 774 }
837 775
838 /** 776 /**
839 * @constructor 777 * @param {string} name
840 * @extends {WebInspector.TracingModel.NamedObject} 778 */
841 * @param {!WebInspector.TracingModel} model 779 _setName(name) {
842 * @param {number} id 780 this._name = name;
843 */ 781 }
844 WebInspector.TracingModel.Process = function(model, id) 782
845 { 783 /**
846 WebInspector.TracingModel.NamedObject.call(this); 784 * @return {string}
847 this._setName("Process " + id); 785 */
786 name() {
787 return this._name;
788 }
789
790 /**
791 * @param {number} sortIndex
792 */
793 _setSortIndex(sortIndex) {
794 this._sortIndex = sortIndex;
795 }
796 };
797
798
799 /**
800 * @unrestricted
801 */
802 WebInspector.TracingModel.Process = class extends WebInspector.TracingModel.Name dObject {
803 /**
804 * @param {!WebInspector.TracingModel} model
805 * @param {number} id
806 */
807 constructor(model, id) {
808 super();
809 this._setName('Process ' + id);
848 this._id = id; 810 this._id = id;
849 /** @type {!Map<number, !WebInspector.TracingModel.Thread>} */ 811 /** @type {!Map<number, !WebInspector.TracingModel.Thread>} */
850 this._threads = new Map(); 812 this._threads = new Map();
851 this._threadByName = new Map(); 813 this._threadByName = new Map();
852 this._model = model; 814 this._model = model;
853 }; 815 }
854 816
855 WebInspector.TracingModel.Process.prototype = { 817 /**
856 /** 818 * @return {number}
857 * @return {number} 819 */
858 */ 820 id() {
859 id: function() 821 return this._id;
860 { 822 }
861 return this._id; 823
862 }, 824 /**
863 825 * @param {number} id
864 /** 826 * @return {!WebInspector.TracingModel.Thread}
865 * @param {number} id 827 */
866 * @return {!WebInspector.TracingModel.Thread} 828 threadById(id) {
867 */ 829 var thread = this._threads.get(id);
868 threadById: function(id) 830 if (!thread) {
869 { 831 thread = new WebInspector.TracingModel.Thread(this, id);
870 var thread = this._threads.get(id); 832 this._threads.set(id, thread);
871 if (!thread) { 833 }
872 thread = new WebInspector.TracingModel.Thread(this, id); 834 return thread;
873 this._threads.set(id, thread); 835 }
874 } 836
875 return thread; 837 /**
876 }, 838 * @param {string} name
877 839 * @return {?WebInspector.TracingModel.Thread}
878 /** 840 */
879 * @param {string} name 841 threadByName(name) {
880 * @return {?WebInspector.TracingModel.Thread} 842 return this._threadByName.get(name) || null;
881 */ 843 }
882 threadByName: function(name) 844
883 { 845 /**
884 return this._threadByName.get(name) || null; 846 * @param {string} name
885 }, 847 * @param {!WebInspector.TracingModel.Thread} thread
886 848 */
887 /** 849 _setThreadByName(name, thread) {
888 * @param {string} name 850 this._threadByName.set(name, thread);
889 * @param {!WebInspector.TracingModel.Thread} thread 851 }
890 */ 852
891 _setThreadByName: function(name, thread) 853 /**
892 { 854 * @param {!WebInspector.TracingManager.EventPayload} payload
893 this._threadByName.set(name, thread); 855 * @return {?WebInspector.TracingModel.Event} event
894 }, 856 */
895 857 _addEvent(payload) {
896 /** 858 return this.threadById(payload.tid)._addEvent(payload);
897 * @param {!WebInspector.TracingManager.EventPayload} payload 859 }
898 * @return {?WebInspector.TracingModel.Event} event 860
899 */ 861 /**
900 _addEvent: function(payload) 862 * @return {!Array.<!WebInspector.TracingModel.Thread>}
901 { 863 */
902 return this.threadById(payload.tid)._addEvent(payload); 864 sortedThreads() {
903 }, 865 return WebInspector.TracingModel.NamedObject._sort(this._threads.valuesArray ());
904 866 }
905 /** 867 };
906 * @return {!Array.<!WebInspector.TracingModel.Thread>} 868
907 */ 869 /**
908 sortedThreads: function() 870 * @unrestricted
909 { 871 */
910 return WebInspector.TracingModel.NamedObject._sort(this._threads.valuesA rray()); 872 WebInspector.TracingModel.Thread = class extends WebInspector.TracingModel.Named Object {
911 }, 873 /**
912 874 * @param {!WebInspector.TracingModel.Process} process
913 __proto__: WebInspector.TracingModel.NamedObject.prototype 875 * @param {number} id
914 }; 876 */
915 877 constructor(process, id) {
916 /** 878 super();
917 * @constructor
918 * @extends {WebInspector.TracingModel.NamedObject}
919 * @param {!WebInspector.TracingModel.Process} process
920 * @param {number} id
921 */
922 WebInspector.TracingModel.Thread = function(process, id)
923 {
924 WebInspector.TracingModel.NamedObject.call(this);
925 this._process = process; 879 this._process = process;
926 this._setName("Thread " + id); 880 this._setName('Thread ' + id);
927 this._events = []; 881 this._events = [];
928 this._asyncEvents = []; 882 this._asyncEvents = [];
929 this._id = id; 883 this._id = id;
930 this._model = process._model; 884 this._model = process._model;
931 }; 885 }
932 886
933 WebInspector.TracingModel.Thread.prototype = { 887 tracingComplete() {
934 tracingComplete: function() 888 this._asyncEvents.stableSort(WebInspector.TracingModel.Event.compareStartAnd EndTime);
935 { 889 this._events.stableSort(WebInspector.TracingModel.Event.compareStartTime);
936 this._asyncEvents.stableSort(WebInspector.TracingModel.Event.compareStar tAndEndTime); 890 var phases = WebInspector.TracingModel.Phase;
937 this._events.stableSort(WebInspector.TracingModel.Event.compareStartTime ); 891 var stack = [];
938 var phases = WebInspector.TracingModel.Phase; 892 for (var i = 0; i < this._events.length; ++i) {
939 var stack = []; 893 var e = this._events[i];
940 for (var i = 0; i < this._events.length; ++i) { 894 e.ordinal = i;
941 var e = this._events[i]; 895 switch (e.phase) {
942 e.ordinal = i; 896 case phases.End:
943 switch (e.phase) { 897 this._events[i] = null; // Mark for removal.
944 case phases.End: 898 // Quietly ignore unbalanced close events, they're legit (we could hav e missed start one).
945 this._events[i] = null; // Mark for removal. 899 if (!stack.length)
946 // Quietly ignore unbalanced close events, they're legit (we cou ld have missed start one). 900 continue;
947 if (!stack.length) 901 var top = stack.pop();
948 continue; 902 if (top.name !== e.name || top.categoriesString !== e.categoriesString )
949 var top = stack.pop(); 903 console.error(
950 if (top.name !== e.name || top.categoriesString !== e.categories String) 904 'B/E events mismatch at ' + top.startTime + ' (' + top.name + ') vs. ' + e.startTime + ' (' + e.name +
951 console.error("B/E events mismatch at " + top.startTime + " (" + top.name + ") vs. " + e.startTime + " (" + e.name + ")"); 905 ')');
952 else 906 else
953 top._complete(e); 907 top._complete(e);
954 break; 908 break;
955 case phases.Begin: 909 case phases.Begin:
956 stack.push(e); 910 stack.push(e);
957 break; 911 break;
958 } 912 }
959 } 913 }
960 while (stack.length) 914 while (stack.length)
961 stack.pop().setEndTime(this._model.maximumRecordTime()); 915 stack.pop().setEndTime(this._model.maximumRecordTime());
962 this._events.remove(null, false); 916 this._events.remove(null, false);
963 }, 917 }
964 918
965 /** 919 /**
966 * @param {!WebInspector.TracingManager.EventPayload} payload 920 * @param {!WebInspector.TracingManager.EventPayload} payload
967 * @return {?WebInspector.TracingModel.Event} event 921 * @return {?WebInspector.TracingModel.Event} event
968 */ 922 */
969 _addEvent: function(payload) 923 _addEvent(payload) {
970 { 924 var event = payload.ph === WebInspector.TracingModel.Phase.SnapshotObject ?
971 var event = payload.ph === WebInspector.TracingModel.Phase.SnapshotObjec t 925 WebInspector.TracingModel.ObjectSnapshot.fromPayload(payload, this) :
972 ? WebInspector.TracingModel.ObjectSnapshot.fromPayload(payload, this ) 926 WebInspector.TracingModel.Event.fromPayload(payload, this);
973 : WebInspector.TracingModel.Event.fromPayload(payload, this); 927 if (WebInspector.TracingModel.isTopLevelEvent(event)) {
974 if (WebInspector.TracingModel.isTopLevelEvent(event)) { 928 // Discard nested "top-level" events.
975 // Discard nested "top-level" events. 929 if (this._lastTopLevelEvent && this._lastTopLevelEvent.endTime > event.sta rtTime)
976 if (this._lastTopLevelEvent && this._lastTopLevelEvent.endTime > eve nt.startTime) 930 return null;
977 return null; 931 this._lastTopLevelEvent = event;
978 this._lastTopLevelEvent = event; 932 }
979 } 933 this._events.push(event);
980 this._events.push(event); 934 return event;
981 return event; 935 }
982 }, 936
983 937 /**
984 /** 938 * @param {!WebInspector.TracingModel.AsyncEvent} asyncEvent
985 * @param {!WebInspector.TracingModel.AsyncEvent} asyncEvent 939 */
986 */ 940 _addAsyncEvent(asyncEvent) {
987 _addAsyncEvent: function(asyncEvent) 941 this._asyncEvents.push(asyncEvent);
988 { 942 }
989 this._asyncEvents.push(asyncEvent); 943
990 }, 944 /**
991 945 * @override
992 /** 946 * @param {string} name
993 * @override 947 */
994 * @param {string} name 948 _setName(name) {
995 */ 949 super._setName(name);
996 _setName: function(name) 950 this._process._setThreadByName(name, this);
997 { 951 }
998 WebInspector.TracingModel.NamedObject.prototype._setName.call(this, name ); 952
999 this._process._setThreadByName(name, this); 953 /**
1000 }, 954 * @return {number}
1001 955 */
1002 /** 956 id() {
1003 * @return {number} 957 return this._id;
1004 */ 958 }
1005 id: function() 959
1006 { 960 /**
1007 return this._id; 961 * @return {!WebInspector.TracingModel.Process}
1008 }, 962 */
1009 963 process() {
1010 /** 964 return this._process;
1011 * @return {!WebInspector.TracingModel.Process} 965 }
1012 */ 966
1013 process: function() 967 /**
1014 { 968 * @return {!Array.<!WebInspector.TracingModel.Event>}
1015 return this._process; 969 */
1016 }, 970 events() {
1017 971 return this._events;
1018 /** 972 }
1019 * @return {!Array.<!WebInspector.TracingModel.Event>} 973
1020 */ 974 /**
1021 events: function() 975 * @return {!Array.<!WebInspector.TracingModel.AsyncEvent>}
1022 { 976 */
1023 return this._events; 977 asyncEvents() {
1024 }, 978 return this._asyncEvents;
1025 979 }
1026 /** 980 };
1027 * @return {!Array.<!WebInspector.TracingModel.AsyncEvent>}
1028 */
1029 asyncEvents: function()
1030 {
1031 return this._asyncEvents;
1032 },
1033
1034 __proto__: WebInspector.TracingModel.NamedObject.prototype
1035 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698