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

Side by Side Diff: chrome/browser/resources/tracing/trace_event_importer.js

Issue 10543144: Remove old tracing code now that it has moved to third_party. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years, 6 months 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
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview TraceEventImporter imports TraceEvent-formatted data
7 * into the provided timeline model.
8 */
9 cr.define('tracing', function() {
10 function ThreadState(tid) {
11 this.openSlices = [];
12 }
13
14 function TraceEventImporter(model, eventData) {
15 this.model_ = model;
16
17 if (typeof(eventData) === 'string' || eventData instanceof String) {
18 // If the event data begins with a [, then we know it should end with a ].
19 // The reason we check for this is because some tracing implementations
20 // cannot guarantee that a ']' gets written to the trace file. So, we are
21 // forgiving and if this is obviously the case, we fix it up before
22 // throwing the string at JSON.parse.
23 if (eventData[0] == '[') {
24 n = eventData.length;
25 if (eventData[n - 1] != ']' && eventData[n - 1] != '\n') {
26 eventData = eventData + ']';
27 } else if (eventData[n - 2] != ']' && eventData[n - 1] == '\n') {
28 eventData = eventData + ']';
29 } else if (eventData[n - 3] != ']' && eventData[n - 2] == '\r' &&
30 eventData[n - 1] == '\n') {
31 eventData = eventData + ']';
32 }
33 }
34 this.events_ = JSON.parse(eventData);
35
36 } else {
37 this.events_ = eventData;
38 }
39
40 // Some trace_event implementations put the actual trace events
41 // inside a container. E.g { ... , traceEvents: [ ] }
42 //
43 // If we see that, just pull out the trace events.
44 if (this.events_.traceEvents)
45 this.events_ = this.events_.traceEvents;
46
47 // To allow simple indexing of threads, we store all the threads by a
48 // PTID. A ptid is a pid and tid joined together x:y fashion, eg
49 // 1024:130. The ptid is a unique key for a thread in the trace.
50 this.threadStateByPTID_ = {};
51
52 // Async events need to be processed durign finalizeEvents
53 this.allAsyncEvents_ = [];
54 }
55
56 /**
57 * @return {boolean} Whether obj is a TraceEvent array.
58 */
59 TraceEventImporter.canImport = function(eventData) {
60 // May be encoded JSON. But we dont want to parse it fully yet.
61 // Use a simple heuristic:
62 // - eventData that starts with [ are probably trace_event
63 // - eventData that starts with { are probably trace_event
64 // May be encoded JSON. Treat files that start with { as importable by us.
65 if (typeof(eventData) === 'string' || eventData instanceof String) {
66 return eventData[0] == '{' || eventData[0] == '[';
67 }
68
69 // Might just be an array of events
70 if (eventData instanceof Array && eventData.length && eventData[0].ph)
71 return true;
72
73 // Might be an object with a traceEvents field in it.
74 if (eventData.traceEvents)
75 return eventData.traceEvents instanceof Array &&
76 eventData.traceEvents[0].ph;
77
78 return false;
79 };
80
81 TraceEventImporter.prototype = {
82
83 __proto__: Object.prototype,
84
85 /**
86 * Helper to process a 'begin' event (e.g. initiate a slice).
87 * @param {ThreadState} state Thread state (holds slices).
88 * @param {Object} event The current trace event.
89 */
90 processBeginEvent: function(index, state, event) {
91 var colorId = tracing.getStringColorId(event.name);
92 var slice =
93 { index: index,
94 slice: new tracing.TimelineThreadSlice(event.name, colorId,
95 event.ts / 1000,
96 event.args) };
97
98 if (event.uts)
99 slice.slice.startInUserTime = event.uts / 1000;
100
101 if (event.args['ui-nest'] === '0') {
102 this.model_.importErrors.push('ui-nest no longer supported.');
103 return;
104 }
105
106 state.openSlices.push(slice);
107 },
108
109 /**
110 * Helper to process an 'end' event (e.g. close a slice).
111 * @param {ThreadState} state Thread state (holds slices).
112 * @param {Object} event The current trace event.
113 */
114 processEndEvent: function(state, event) {
115 if (event.args['ui-nest'] === '0') {
116 this.model_.importErrors.push('ui-nest no longer supported.');
117 return;
118 }
119 if (state.openSlices.length == 0) {
120 // Ignore E events that are unmatched.
121 return;
122 }
123 var slice = state.openSlices.pop().slice;
124 slice.duration = (event.ts / 1000) - slice.start;
125 if (event.uts)
126 slice.durationInUserTime = (event.uts / 1000) - slice.startInUserTime;
127 for (var arg in event.args)
128 slice.args[arg] = event.args[arg];
129
130 // Store the slice on the correct subrow.
131 var thread = this.model_.getOrCreateProcess(event.pid).
132 getOrCreateThread(event.tid);
133 var subRowIndex = state.openSlices.length;
134 thread.getSubrow(subRowIndex).push(slice);
135
136 // Add the slice to the subSlices array of its parent.
137 if (state.openSlices.length) {
138 var parentSlice = state.openSlices[state.openSlices.length - 1];
139 parentSlice.slice.subSlices.push(slice);
140 }
141 },
142
143 /**
144 * Helper to process an 'async finish' event, which will close an open slice
145 * on a TimelineAsyncSliceGroup object.
146 **/
147 processAsyncEvent: function(index, state, event) {
148 var thread = this.model_.getOrCreateProcess(event.pid).
149 getOrCreateThread(event.tid);
150 this.allAsyncEvents_.push({
151 event: event,
152 thread: thread});
153 },
154
155 /**
156 * Helper function that closes any open slices. This happens when a trace
157 * ends before an 'E' phase event can get posted. When that happens, this
158 * closes the slice at the highest timestamp we recorded and sets the
159 * didNotFinish flag to true.
160 */
161 autoCloseOpenSlices: function() {
162 // We need to know the model bounds in order to assign an end-time to
163 // the open slices.
164 this.model_.updateBounds();
165
166 // The model's max value in the trace is wrong at this point if there are
167 // un-closed events. To close those events, we need the true global max
168 // value. To compute this, build a list of timestamps that weren't
169 // included in the max calculation, then compute the real maximum based on
170 // that.
171 var openTimestamps = [];
172 for (var ptid in this.threadStateByPTID_) {
173 var state = this.threadStateByPTID_[ptid];
174 for (var i = 0; i < state.openSlices.length; i++) {
175 var slice = state.openSlices[i];
176 openTimestamps.push(slice.slice.start);
177 for (var s = 0; s < slice.slice.subSlices.length; s++) {
178 var subSlice = slice.slice.subSlices[s];
179 openTimestamps.push(subSlice.start);
180 if (subSlice.duration)
181 openTimestamps.push(subSlice.end);
182 }
183 }
184 }
185
186 // Figure out the maximum value of model.maxTimestamp and
187 // Math.max(openTimestamps). Made complicated by the fact that the model
188 // timestamps might be undefined.
189 var realMaxTimestamp;
190 if (this.model_.maxTimestamp) {
191 realMaxTimestamp = Math.max(this.model_.maxTimestamp,
192 Math.max.apply(Math, openTimestamps));
193 } else {
194 realMaxTimestamp = Math.max.apply(Math, openTimestamps);
195 }
196
197 // Automatically close any slices are still open. These occur in a number
198 // of reasonable situations, e.g. deadlock. This pass ensures the open
199 // slices make it into the final model.
200 for (var ptid in this.threadStateByPTID_) {
201 var state = this.threadStateByPTID_[ptid];
202 while (state.openSlices.length > 0) {
203 var slice = state.openSlices.pop();
204 slice.slice.duration = realMaxTimestamp - slice.slice.start;
205 slice.slice.didNotFinish = true;
206 var event = this.events_[slice.index];
207
208 // Store the slice on the correct subrow.
209 var thread = this.model_.getOrCreateProcess(event.pid)
210 .getOrCreateThread(event.tid);
211 var subRowIndex = state.openSlices.length;
212 thread.getSubrow(subRowIndex).push(slice.slice);
213
214 // Add the slice to the subSlices array of its parent.
215 if (state.openSlices.length) {
216 var parentSlice = state.openSlices[state.openSlices.length - 1];
217 parentSlice.slice.subSlices.push(slice.slice);
218 }
219 }
220 }
221 },
222
223 /**
224 * Helper that creates and adds samples to a TimelineCounter object based on
225 * 'C' phase events.
226 */
227 processCounterEvent: function(event) {
228 var ctr_name;
229 if (event.id !== undefined)
230 ctr_name = event.name + '[' + event.id + ']';
231 else
232 ctr_name = event.name;
233
234 var ctr = this.model_.getOrCreateProcess(event.pid)
235 .getOrCreateCounter(event.cat, ctr_name);
236 // Initialize the counter's series fields if needed.
237 if (ctr.numSeries == 0) {
238 for (var seriesName in event.args) {
239 ctr.seriesNames.push(seriesName);
240 ctr.seriesColors.push(
241 tracing.getStringColorId(ctr.name + '.' + seriesName));
242 }
243 if (ctr.numSeries == 0) {
244 this.model_.importErrors.push('Expected counter ' + event.name +
245 ' to have at least one argument to use as a value.');
246 // Drop the counter.
247 delete ctr.parent.counters[ctr.name];
248 return;
249 }
250 }
251
252 // Add the sample values.
253 ctr.timestamps.push(event.ts / 1000);
254 for (var i = 0; i < ctr.numSeries; i++) {
255 var seriesName = ctr.seriesNames[i];
256 if (event.args[seriesName] === undefined) {
257 ctr.samples.push(0);
258 continue;
259 }
260 ctr.samples.push(event.args[seriesName]);
261 }
262 },
263
264 /**
265 * Walks through the events_ list and outputs the structures discovered to
266 * model_.
267 */
268 importEvents: function() {
269 // Walk through events
270 var events = this.events_;
271 // Some events cannot be handled until we have done a first pass over the
272 // data set. So, accumulate them into a temporary data structure.
273 var second_pass_events = [];
274 for (var eI = 0; eI < events.length; eI++) {
275 var event = events[eI];
276 var ptid = tracing.TimelineThread.getPTIDFromPidAndTid(
277 event.pid, event.tid);
278
279 if (!(ptid in this.threadStateByPTID_))
280 this.threadStateByPTID_[ptid] = new ThreadState();
281 var state = this.threadStateByPTID_[ptid];
282
283 if (event.ph == 'B') {
284 this.processBeginEvent(eI, state, event);
285 } else if (event.ph == 'E') {
286 this.processEndEvent(state, event);
287 } else if (event.ph == 'S') {
288 this.processAsyncEvent(eI, state, event);
289 } else if (event.ph == 'F') {
290 this.processAsyncEvent(eI, state, event);
291 } else if (event.ph == 'T') {
292 this.processAsyncEvent(eI, state, event);
293 } else if (event.ph == 'I') {
294 // Treat an Instant event as a duration 0 slice.
295 // TimelineSliceTrack's redraw() knows how to handle this.
296 this.processBeginEvent(eI, state, event);
297 this.processEndEvent(state, event);
298 } else if (event.ph == 'C') {
299 this.processCounterEvent(event);
300 } else if (event.ph == 'M') {
301 if (event.name == 'thread_name') {
302 var thread = this.model_.getOrCreateProcess(event.pid)
303 .getOrCreateThread(event.tid);
304 thread.name = event.args.name;
305 } else {
306 this.model_.importErrors.push(
307 'Unrecognized metadata name: ' + event.name);
308 }
309 } else {
310 this.model_.importErrors.push(
311 'Unrecognized event phase: ' + event.ph +
312 '(' + event.name + ')');
313 }
314 }
315
316 // Autoclose any open slices.
317 var hasOpenSlices = false;
318 for (var ptid in this.threadStateByPTID_) {
319 var state = this.threadStateByPTID_[ptid];
320 hasOpenSlices |= state.openSlices.length > 0;
321 }
322 if (hasOpenSlices)
323 this.autoCloseOpenSlices();
324 },
325
326 /**
327 * Called by the TimelineModel after all other importers have imported their
328 * events. This function creates async slices for any async events we saw.
329 */
330 finalizeImport: function() {
331 if (this.allAsyncEvents_.length == 0)
332 return;
333
334 this.allAsyncEvents_.sort(function(x, y) {
335 return x.event.ts - y.event.ts;
336 });
337
338 var asyncEventStatesByNameThenID = {};
339
340 var allAsyncEvents = this.allAsyncEvents_;
341 for (var i = 0; i < allAsyncEvents.length; i++) {
342 var asyncEventState = allAsyncEvents[i];
343
344 var event = asyncEventState.event;
345 var name = event.name;
346 if (name === undefined) {
347 this.model_.importErrors.push(
348 'Async events (ph: S, T or F) require an name parameter.');
349 continue;
350 }
351
352 var id = event.id;
353 if (id === undefined) {
354 this.model_.importErrors.push(
355 'Async events (ph: S, T or F) require an id parameter.');
356 continue;
357 }
358
359 // TODO(simonjam): Add a synchronous tick on the appropriate thread.
360
361 if (event.ph == 'S') {
362 if (asyncEventStatesByNameThenID[name] === undefined)
363 asyncEventStatesByNameThenID[name] = {};
364 if (asyncEventStatesByNameThenID[name][id]) {
365 this.model_.importErrors.push(
366 'At ' + event.ts + ', an slice of the same id ' + id +
367 ' was alrady open.');
368 continue;
369 }
370 asyncEventStatesByNameThenID[name][id] = [];
371 asyncEventStatesByNameThenID[name][id].push(asyncEventState);
372 } else {
373 if (asyncEventStatesByNameThenID[name] === undefined) {
374 this.model_.importErrors.push(
375 'At ' + event.ts + ', no slice named ' + name +
376 ' was open.');
377 continue;
378 }
379 if (asyncEventStatesByNameThenID[name][id] === undefined) {
380 this.model_.importErrors.push(
381 'At ' + event.ts + ', no slice named ' + name +
382 ' with id=' + id + ' was open.');
383 continue;
384 }
385 var events = asyncEventStatesByNameThenID[name][id];
386 events.push(asyncEventState);
387
388 if (event.ph == 'F') {
389 // Create a slice from start to end.
390 var slice = new tracing.TimelineAsyncSlice(
391 name,
392 tracing.getStringColorId(name),
393 events[0].event.ts / 1000);
394
395 slice.duration = (event.ts / 1000) - (events[0].event.ts / 1000);
396
397 slice.startThread = events[0].thread;
398 slice.endThread = asyncEventState.thread;
399 slice.id = id;
400 slice.args = events[0].event.args;
401 slice.subSlices = [];
402
403 // Create subSlices for each step.
404 for (var j = 1; j < events.length; ++j) {
405 var subName = name;
406 if (events[j - 1].event.ph == 'T')
407 subName = name + ':' + events[j - 1].event.args.step;
408 var subSlice = new tracing.TimelineAsyncSlice(
409 subName,
410 tracing.getStringColorId(name + j),
411 events[j - 1].event.ts / 1000);
412
413 subSlice.duration =
414 (events[j].event.ts / 1000) - (events[j - 1].event.ts / 1000);
415
416 subSlice.startThread = events[j - 1].thread;
417 subSlice.endThread = events[j].thread;
418 subSlice.id = id;
419 subSlice.args = events[j - 1].event.args;
420
421 slice.subSlices.push(subSlice);
422 }
423
424 // The args for the finish event go in the last subSlice.
425 var lastSlice = slice.subSlices[slice.subSlices.length - 1];
426 for (var arg in event.args)
427 lastSlice.args[arg] = event.args[arg];
428
429 // Add |slice| to the start-thread's asyncSlices.
430 slice.startThread.asyncSlices.push(slice);
431 delete asyncEventStatesByNameThenID[name][id];
432 }
433 }
434 }
435 }
436 };
437
438 tracing.TimelineModel.registerImporter(TraceEventImporter);
439
440 return {
441 TraceEventImporter: TraceEventImporter
442 };
443 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698