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

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

Issue 9706010: about:tracing support for TRACE_ASYNC_START/FINISH events. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: try again Created 8 years, 9 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 5
6 /** 6 /**
7 * @fileoverview TimelineModel is a parsed representation of the 7 * @fileoverview TimelineModel is a parsed representation of the
8 * TraceEvents obtained from base/trace_event in which the begin-end 8 * TraceEvents obtained from base/trace_event in which the begin-end
9 * tokens are converted into a hierarchy of processes, threads, 9 * tokens are converted into a hierarchy of processes, threads,
10 * subrows, and slices. 10 * subrows, and slices.
11 * 11 *
12 * The building block of the model is a slice. A slice is roughly 12 * The building block of the model is a slice. A slice is roughly
13 * equivalent to function call executing on a specific thread. As a 13 * equivalent to function call executing on a specific thread. As a
14 * result, slices may have one or more subslices. 14 * result, slices may have one or more subslices.
15 * 15 *
16 * A thread contains one or more subrows of slices. Row 0 corresponds to 16 * A thread contains one or more subrows of slices. Row 0 corresponds to
17 * the "root" slices, e.g. the topmost slices. Row 1 contains slices that 17 * the "root" slices, e.g. the topmost slices. Row 1 contains slices that
18 * are nested 1 deep in the stack, and so on. We use these subrows to draw 18 * are nested 1 deep in the stack, and so on. We use these subrows to draw
19 * nesting tasks. 19 * nesting tasks.
20 * 20 *
21 */ 21 */
22 cr.define('tracing', function() { 22 cr.define('tracing', function() {
23 /** 23 /**
24 * A TimelineSlice represents an interval of time on a given resource plus 24 * A TimelineSlice represents an interval of time plus parameters associated
25 * parameters associated with that interval. 25 * with that interval.
26 *
27 * A slice is typically associated with a specific trace event pair on a
28 * specific thread.
29 * For example,
30 * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
31 * TRACE_EVENT_END() at time=0.3ms
32 * This results in a single timeline slice from 0.1 with duration 0.2 on a
33 * specific thread.
34 *
35 * A slice can also be an interval of time on a Cpu on a TimelineCpu.
36 * 26 *
37 * All time units are stored in milliseconds. 27 * All time units are stored in milliseconds.
38 * @constructor 28 * @constructor
39 */ 29 */
40 function TimelineSlice(title, colorId, start, args, opt_duration) { 30 function TimelineSlice(title, colorId, start, args, opt_duration) {
41 this.title = title; 31 this.title = title;
42 this.start = start; 32 this.start = start;
43 this.colorId = colorId; 33 this.colorId = colorId;
44 this.args = args; 34 this.args = args;
45 this.didNotFinish = false; 35 this.didNotFinish = false;
46 this.subSlices = [];
47 if (opt_duration !== undefined) 36 if (opt_duration !== undefined)
48 this.duration = opt_duration; 37 this.duration = opt_duration;
49 } 38 }
50 39
51 TimelineSlice.prototype = { 40 TimelineSlice.prototype = {
52 selected: false, 41 selected: false,
53 42
54 duration: undefined, 43 duration: undefined,
55 44
56 get end() { 45 get end() {
57 return this.start + this.duration; 46 return this.start + this.duration;
58 } 47 }
59 }; 48 };
60 49
61 /** 50 /**
51 * A TimelineThreadSlice represents an interval of time on a thread resource
52 * with associated nestinged slice information.
53 *
54 * ThreadSlices are typically associated with a specific trace event pair on a
55 * specific thread.
56 * For example,
57 * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
58 * TRACE_EVENT_END0() at time=0.3ms
59 * This results in a single timeline slice from 0.1 with duration 0.2 on a
60 * specific thread.
61 *
62 * @constructor
63 */
64 function TimelineThreadSlice(title, colorId, start, args, opt_duration) {
65 TimelineSlice.call(this, title, colorId, start, args, opt_duration);
66 this.subSlices = [];
67 }
68
69 TimelineThreadSlice.prototype = {
70 __proto__: TimelineSlice.prototype
71 };
72
73 /**
74 * A TimelineAsyncSlice represents an interval of time during which an
75 * asynchronous operation is in progress. An AsyncSlice consumes no CPU time
76 * itself and so is only associated with Threads at its start and end point.
77 *
78 * @constructor
79 */
80 function TimelineAsyncSlice(title, colorId, start, args) {
81 TimelineSlice.call(this, title, colorId, start, args);
82 };
83
84 TimelineAsyncSlice.prototype = {
85 __proto__: TimelineSlice.prototype,
86
87 id: undefined,
88
89 startThread: undefined,
90
91 endThread: undefined
92 };
93
94 /**
62 * A TimelineThread stores all the trace events collected for a particular 95 * A TimelineThread stores all the trace events collected for a particular
63 * thread. We organize the slices on a thread by "subrows," where subrow 0 96 * thread. We organize the synchronous slices on a thread by "subrows," where
64 * has all the root slices, subrow 1 those nested 1 deep, and so on. There 97 * subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on.
65 * is also a set of non-nested subrows. 98 * The asynchronous slices are stored in an TimelineAsyncSliceGroup object.
99 *
100 * The slices stored on a TimelineThread should be instances of
101 * TimelineThreadSlice.
66 * 102 *
67 * @constructor 103 * @constructor
68 */ 104 */
69 function TimelineThread(parent, tid) { 105 function TimelineThread(parent, tid) {
106 if (!parent)
107 throw 'Parent must be provided.';
70 this.parent = parent; 108 this.parent = parent;
71 this.tid = tid; 109 this.tid = tid;
72 this.subRows = [[]]; 110 this.subRows = [[]];
73 this.nonNestedSubRows = []; 111 this.asyncSlices = new TimelineAsyncSliceGroup(this.ptid);
112 }
113
114 var ptidMap = {};
115
116 /**
117 * @return {String} A string that can be used as a unique key for a specific
118 * thread within a process.
119 */
120 TimelineThread.getPTIDFromPidAndTid = function(pid, tid) {
121 if (!ptidMap[pid])
122 ptidMap[pid] = {};
123 if (!ptidMap[pid][tid])
124 ptidMap[pid][tid] = pid + ':' + tid;
125 return ptidMap[pid][tid];
74 } 126 }
75 127
76 TimelineThread.prototype = { 128 TimelineThread.prototype = {
77 /** 129 /**
78 * Name of the thread, if present. 130 * Name of the thread, if present.
79 */ 131 */
80 name: undefined, 132 name: undefined,
81 133
134 /**
135 * @return {string} A concatenation of the parent id and the thread's
136 * tid. Can be used to uniquely identify a thread.
137 */
138 get ptid() {
139 return TimelineThread.getPTIDFromPidAndTid(this.tid, this.parent.pid);
140 },
141
82 getSubrow: function(i) { 142 getSubrow: function(i) {
83 while (i >= this.subRows.length) 143 while (i >= this.subRows.length)
84 this.subRows.push([]); 144 this.subRows.push([]);
85 return this.subRows[i]; 145 return this.subRows[i];
86 }, 146 },
87 147
88 addNonNestedSlice: function(slice) { 148
89 for (var i = 0; i < this.nonNestedSubRows.length; i++) { 149 shiftSubRow_: function(subRow, amount) {
90 var currSubRow = this.nonNestedSubRows[i]; 150 for (var tS = 0; tS < subRow.length; tS++) {
91 var lastSlice = currSubRow[currSubRow.length - 1]; 151 var slice = subRow[tS];
92 if (slice.start >= lastSlice.start + lastSlice.duration) { 152 slice.start = (slice.start + amount);
93 currSubRow.push(slice);
94 return;
95 }
96 } 153 }
97 this.nonNestedSubRows.push([slice]); 154 },
155
156 /**
157 * Shifts all the timestamps inside this thread forward by the amount
158 * specified.
159 */
160 shiftTimestampsForward: function(amount) {
161 if (this.cpuSlices)
162 this.shiftSubRow_(this.cpuSlices, amount);
163
164 for (var tSR = 0; tSR < this.subRows.length; tSR++) {
165 this.shiftSubRow_(this.subRows[tSR], amount);
166 }
167
168 this.asyncSlices.shiftTimestampsForward(amount);
98 }, 169 },
99 170
100 /** 171 /**
101 * Updates the minTimestamp and maxTimestamp fields based on the 172 * Updates the minTimestamp and maxTimestamp fields based on the
102 * current slices and nonNestedSubRows attached to the thread. 173 * current objects associated with the thread.
103 */ 174 */
104 updateBounds: function() { 175 updateBounds: function() {
105 var values = []; 176 var values = [];
106 var slices; 177 var slices;
107 if (this.subRows[0].length != 0) { 178 if (this.subRows[0].length != 0) {
108 slices = this.subRows[0]; 179 slices = this.subRows[0];
109 values.push(slices[0].start); 180 values.push(slices[0].start);
110 values.push(slices[slices.length - 1].end); 181 values.push(slices[slices.length - 1].end);
111 } 182 }
112 for (var i = 0; i < this.nonNestedSubRows.length; ++i) { 183 if (this.asyncSlices.slices.length) {
113 slices = this.nonNestedSubRows[i]; 184 this.asyncSlices.updateBounds();
114 values.push(slices[0].start); 185 values.push(this.asyncSlices.minTimestamp);
115 values.push(slices[slices.length - 1].end); 186 values.push(this.asyncSlices.maxTimestamp);
116 } 187 }
117 if (values.length) { 188 if (values.length) {
118 this.minTimestamp = Math.min.apply(Math, values); 189 this.minTimestamp = Math.min.apply(Math, values);
119 this.maxTimestamp = Math.max.apply(Math, values); 190 this.maxTimestamp = Math.max.apply(Math, values);
120 } else { 191 } else {
121 this.minTimestamp = undefined; 192 this.minTimestamp = undefined;
122 this.maxTimestamp = undefined; 193 this.maxTimestamp = undefined;
123 } 194 }
124 }, 195 },
125 196
126 /** 197 /**
127 * @return {String} A user-friendly name for this thread. 198 * @return {String} A user-friendly name for this thread.
128 */ 199 */
129 get userFriendlyName() { 200 get userFriendlyName() {
130 var tname = this.name || this.tid; 201 var tname = this.name || this.tid;
131 return this.parent.pid + ': ' + tname; 202 return this.parent.pid + ': ' + tname;
132 }, 203 },
133 204
134 /** 205 /**
135 * @return {String} User friendly details about this thread. 206 * @return {String} User friendly details about this thread.
136 */ 207 */
137 get userFriendlyDetials() { 208 get userFriendlyDetails() {
138 return 'pid: ' + this.parent.pid + 209 return 'pid: ' + this.parent.pid +
139 ', tid: ' + this.tid + 210 ', tid: ' + this.tid +
140 (this.name ? ', name: ' + this.name : ''); 211 (this.name ? ', name: ' + this.name : '');
141 } 212 }
142 213
143 }; 214 };
144 215
145 /** 216 /**
146 * Comparison between threads that orders first by pid, 217 * Comparison between threads that orders first by pid,
147 * then by names, then by tid. 218 * then by names, then by tid.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 255
185 get numSeries() { 256 get numSeries() {
186 return this.seriesNames.length; 257 return this.seriesNames.length;
187 }, 258 },
188 259
189 get numSamples() { 260 get numSamples() {
190 return this.timestamps.length; 261 return this.timestamps.length;
191 }, 262 },
192 263
193 /** 264 /**
265 * Shifts all the timestamps inside this counter forward by the amount
266 * specified.
267 */
268 shiftTimestampsForward: function(amount) {
269 for (var sI = 0; sI < this.timestamps.length; sI++)
270 this.timestamps[sI] = (this.timestamps[sI] + amount);
271 },
272
273 /**
194 * Updates the bounds for this counter based on the samples it contains. 274 * Updates the bounds for this counter based on the samples it contains.
195 */ 275 */
196 updateBounds: function() { 276 updateBounds: function() {
197 if (this.seriesNames.length != this.seriesColors.length) 277 if (this.seriesNames.length != this.seriesColors.length)
198 throw 'seriesNames.length must match seriesColors.length'; 278 throw 'seriesNames.length must match seriesColors.length';
199 if (this.numSeries * this.numSamples != this.samples.length) 279 if (this.numSeries * this.numSamples != this.samples.length)
200 throw 'samples.length must be a multiple of numSamples.'; 280 throw 'samples.length must be a multiple of numSamples.';
201 281
202 this.totals = []; 282 this.totals = [];
203 if (this.samples.length == 0) { 283 if (this.samples.length == 0) {
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
253 TimelineProcess.prototype = { 333 TimelineProcess.prototype = {
254 get numThreads() { 334 get numThreads() {
255 var n = 0; 335 var n = 0;
256 for (var p in this.threads) { 336 for (var p in this.threads) {
257 n++; 337 n++;
258 } 338 }
259 return n; 339 return n;
260 }, 340 },
261 341
262 /** 342 /**
343 * Shifts all the timestamps inside this process forward by the amount
344 * specified.
345 */
346 shiftTimestampsForward: function(amount) {
347 for (var tid in this.threads)
348 this.threads[tid].shiftTimestampsForward(amount);
349 for (var id in this.counters)
350 this.counters[id].shiftTimestampsForward(amount);
351 },
352
353 /**
263 * @return {TimlineThread} The thread identified by tid on this process, 354 * @return {TimlineThread} The thread identified by tid on this process,
264 * creating it if it doesn't exist. 355 * creating it if it doesn't exist.
265 */ 356 */
266 getOrCreateThread: function(tid) { 357 getOrCreateThread: function(tid) {
267 if (!this.threads[tid]) 358 if (!this.threads[tid])
268 this.threads[tid] = new TimelineThread(this, tid); 359 this.threads[tid] = new TimelineThread(this, tid);
269 return this.threads[tid]; 360 return this.threads[tid];
270 }, 361 },
271 362
272 /** 363 /**
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
308 if (cat.length) 399 if (cat.length)
309 id = cat + '.' + name; 400 id = cat + '.' + name;
310 else 401 else
311 id = name; 402 id = name;
312 if (!this.counters[id]) 403 if (!this.counters[id])
313 this.counters[id] = new TimelineCounter(this, id, name); 404 this.counters[id] = new TimelineCounter(this, id, name);
314 return this.counters[id]; 405 return this.counters[id];
315 }, 406 },
316 407
317 /** 408 /**
409 * Shifts all the timestamps inside this CPU forward by the amount
410 * specified.
411 */
412 shiftTimestampsForward: function(amount) {
413 for (var sI = 0; sI < this.slices.length; sI++)
414 this.slices[sI].start = (this.slices[sI].start + amount);
415 for (var id in this.counters)
416 this.counters[id].shiftTimestampsForward(amount);
417 },
418
419 /**
318 * Updates the minTimestamp and maxTimestamp fields based on the 420 * Updates the minTimestamp and maxTimestamp fields based on the
319 * current slices attached to the cpu. 421 * current slices attached to the cpu.
320 */ 422 */
321 updateBounds: function() { 423 updateBounds: function() {
322 var values = []; 424 var values = [];
323 if (this.slices.length) { 425 if (this.slices.length) {
324 this.minTimestamp = this.slices[0].start; 426 this.minTimestamp = this.slices[0].start;
325 this.maxTimestamp = this.slices[this.slices.length - 1].end; 427 this.maxTimestamp = this.slices[this.slices.length - 1].end;
326 } else { 428 } else {
327 this.minTimestamp = undefined; 429 this.minTimestamp = undefined;
328 this.maxTimestamp = undefined; 430 this.maxTimestamp = undefined;
329 } 431 }
330 } 432 }
331 }; 433 };
332 434
333 /** 435 /**
334 * Comparison between processes that orders by cpuNumber. 436 * Comparison between processes that orders by cpuNumber.
335 */ 437 */
336 TimelineCpu.compare = function(x, y) { 438 TimelineCpu.compare = function(x, y) {
337 return x.cpuNumber - y.cpuNumber; 439 return x.cpuNumber - y.cpuNumber;
338 }; 440 };
339 441
442 /**
443 * A group of AsyncSlices.
444 * @constructor
445 */
446 function TimelineAsyncSliceGroup(name) {
447 this.name = name;
448 this.slices = [];
449 }
450
451 TimelineAsyncSliceGroup.prototype = {
452 __proto__: Object.prototype,
453
454 /**
455 * Helper function that pushes the provided slice onto the slices array.
456 */
457 push: function(slice) {
458 this.slices.push(slice);
459 },
460
461 /**
462 * @return {Number} The number of slices in this group.
463 */
464 get length() {
465 return this.slices.length;
466 },
467
468 /**
469 * Built automatically by rebuildSubRows().
470 */
471 subRows_: undefined,
472
473 /**
474 * Updates the bounds for this group based on the slices it contains.
475 */
476 sortSlices_: function() {
477 this.slices.sort(function(x, y) {
478 return x.start - y.start;
479 });
480 },
481
482 /**
483 * Shifts all the timestamps inside this group forward by the amount
484 * specified.
485 */
486 shiftTimestampsForward: function(amount) {
487 for (var sI = 0; sI < this.slices.length; sI++)
488 this.slices[sI].start = (this.slices[sI].start + amount);
489 },
490
491 /**
492 * Updates the bounds for this group based on the slices it contains.
493 */
494 updateBounds: function() {
495 this.sortSlices_();
496 if (this.slices.length) {
497 this.minTimestamp = this.slices[0].start;
498 this.maxTimestamp = this.slices[this.slices.length - 1].end;
499 } else {
500 this.minTimestamp = undefined;
501 this.maxTimestamp = undefined;
502 }
503 this.subRows_ = undefined;
504 },
505
506 get subRows() {
507 if (!this.subRows_)
508 this.rebuildSubRows_();
509 return this.subRows_;
510 },
511
512 /**
513 * Breaks up the list of slices into N rows, each of which is a list of
514 * slices that are non overlapping.
515 *
516 * It uses a very simple approach: walk through the slices in sorted order
517 * by start time. For each slice, try to fit it in an existing subRow. If it
518 * doesn't fit in any subrow, make another subRow.
519 */
520 rebuildSubRows_: function() {
521 this.sortSlices_();
522 var subRows = [];
523 for (var i = 0; i < this.slices.length; i++) {
524 var slice = this.slices[i];
525
526 var found = false;
527 for (var j = 0; j < subRows.length; j++) {
528 var subRow = subRows[j];
529 var lastSliceInSubRow = subRow[subRow.length - 1];
530 if (slice.start >= lastSliceInSubRow.end) {
531 found = true;
532 subRow.push(slice);
533 }
534 }
535 if (!found) {
536 subRows.push([slice]);
537 }
538 }
539 this.subRows_ = subRows;
540 },
541
542 /**
543 * Breaks up this group into slices based on start thread.
544 *
545 * @return {Array} An array of TimelineAsyncSliceGroups where each group has
546 * slices that started on the same thread.
547 **/
548 computeSubGroups: function() {
549 var subGroupsByPTID = {};
550 for (var i = 0; i < this.slices.length; ++i) {
551 var slice = this.slices[i];
552 var slicePTID = slice.startThread.ptid;
553 if (!subGroupsByPTID[slicePTID])
554 subGroupsByPTID[slicePTID] = new TimelineAsyncSliceGroup(this.name);
555 subGroupsByPTID[slicePTID].slices.push(slice);
556 }
557 var groups = [];
558 for (var ptid in subGroupsByPTID) {
559 var group = subGroupsByPTID[ptid];
560 group.updateBounds();
561 groups.push(group);
562 }
563 return groups;
564 }
565
566 };
567
568 /**
569 * Comparison between counters that orders by pid, then name.
570 */
571 TimelineCounter.compare = function(x, y) {
572 if (x.parent.pid != y.parent.pid) {
573 return TimelineProcess.compare(x.parent, y.parent.pid);
574 }
575 var tmp = x.name.localeCompare(y.name);
576 if (tmp == 0)
577 return x.tid - y.tid;
578 return tmp;
579 };
580
340 // The color pallette is split in half, with the upper 581 // The color pallette is split in half, with the upper
341 // half of the pallette being the "highlighted" verison 582 // half of the pallette being the "highlighted" verison
342 // of the base color. So, color 7's highlighted form is 583 // of the base color. So, color 7's highlighted form is
343 // 7 + (pallette.length / 2). 584 // 7 + (pallette.length / 2).
344 // 585 //
345 // These bright versions of colors are automatically generated 586 // These bright versions of colors are automatically generated
346 // from the base colors. 587 // from the base colors.
347 // 588 //
348 // Within the color pallette, there are "regular" colors, 589 // Within the color pallette, there are "regular" colors,
349 // which can be used for random color selection, and 590 // which can be used for random color selection, and
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
480 * See TimelineModel.importEvents for details and more advanced ways to 721 * See TimelineModel.importEvents for details and more advanced ways to
481 * import data. 722 * import data.
482 * @param {bool=} opt_zeroAndBoost Whether to align to zero and boost the 723 * @param {bool=} opt_zeroAndBoost Whether to align to zero and boost the
483 * by 15%. Defaults to true. 724 * by 15%. Defaults to true.
484 * @constructor 725 * @constructor
485 */ 726 */
486 function TimelineModel(opt_eventData, opt_zeroAndBoost) { 727 function TimelineModel(opt_eventData, opt_zeroAndBoost) {
487 this.cpus = {}; 728 this.cpus = {};
488 this.processes = {}; 729 this.processes = {};
489 this.importErrors = []; 730 this.importErrors = [];
731 this.asyncSliceGroups = {};
490 732
491 if (opt_eventData) 733 if (opt_eventData)
492 this.importEvents(opt_eventData, opt_zeroAndBoost); 734 this.importEvents(opt_eventData, opt_zeroAndBoost);
493 } 735 }
494 736
495 var importerConstructors = []; 737 var importerConstructors = [];
496 738
497 /** 739 /**
498 * Registers an importer. All registered importers are considered 740 * Registers an importer. All registered importers are considered
499 * when processing an import request. 741 * when processing an import request.
(...skipping 12 matching lines...) Expand all
512 return true; 754 return true;
513 if (typeof(eventData) === 'string' || eventData instanceof String) { 755 if (typeof(eventData) === 'string' || eventData instanceof String) {
514 return eventData.length == 0; 756 return eventData.length == 0;
515 } 757 }
516 return false; 758 return false;
517 }; 759 };
518 760
519 TimelineModelEmptyImporter.prototype = { 761 TimelineModelEmptyImporter.prototype = {
520 __proto__: Object.prototype, 762 __proto__: Object.prototype,
521 763
522 importEvents : function() { 764 importEvents: function() {
765 },
766 finalizeImport: function() {
523 } 767 }
524 }; 768 };
525 769
526 TimelineModel.registerImporter(TimelineModelEmptyImporter); 770 TimelineModel.registerImporter(TimelineModelEmptyImporter);
527 771
528 TimelineModel.prototype = { 772 TimelineModel.prototype = {
529 __proto__: cr.EventTarget.prototype, 773 __proto__: cr.EventTarget.prototype,
530 774
531 get numProcesses() { 775 get numProcesses() {
532 var n = 0; 776 var n = 0;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
572 816
573 // Begin-events without matching end events leave a thread in a state 817 // Begin-events without matching end events leave a thread in a state
574 // where the toplevel subrows are empty but child subrows have 818 // where the toplevel subrows are empty but child subrows have
575 // entries. The autocloser will fix this up later. But, for the 819 // entries. The autocloser will fix this up later. But, for the
576 // purposes of pruning, such threads need to be treated as having 820 // purposes of pruning, such threads need to be treated as having
577 // content. 821 // content.
578 var hasNonEmptySubrow = false; 822 var hasNonEmptySubrow = false;
579 for (var s = 0; s < thread.subRows.length; s++) 823 for (var s = 0; s < thread.subRows.length; s++)
580 hasNonEmptySubrow |= thread.subRows[s].length > 0; 824 hasNonEmptySubrow |= thread.subRows[s].length > 0;
581 825
582 if (hasNonEmptySubrow || thread.nonNestedSubRows.legnth) 826 if (hasNonEmptySubrow || thread.asyncSlices.length > 0)
583 prunedThreads[tid] = thread; 827 prunedThreads[tid] = thread;
584 } 828 }
585 process.threads = prunedThreads; 829 process.threads = prunedThreads;
586 } 830 }
587 }, 831 },
588 832
589 updateBounds: function() { 833 updateBounds: function() {
590 var wmin = Infinity; 834 var wmin = Infinity;
591 var wmax = -wmin; 835 var wmax = -wmin;
592 var hasData = false; 836 var hasData = false;
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
631 } else { 875 } else {
632 this.maxTimestamp = undefined; 876 this.maxTimestamp = undefined;
633 this.minTimestamp = undefined; 877 this.minTimestamp = undefined;
634 } 878 }
635 }, 879 },
636 880
637 shiftWorldToZero: function() { 881 shiftWorldToZero: function() {
638 if (this.minTimestamp === undefined) 882 if (this.minTimestamp === undefined)
639 return; 883 return;
640 var timeBase = this.minTimestamp; 884 var timeBase = this.minTimestamp;
641 var threads = this.getAllThreads(); 885 for (var pid in this.processes)
642 for (var tI = 0; tI < threads.length; tI++) { 886 this.processes[pid].shiftTimestampsForward(-timeBase);
643 var thread = threads[tI]; 887 for (var cpuNumber in this.cpus)
644 var shiftSubRow = function(subRow) { 888 this.cpus[cpuNumber].shiftTimestampsForward(-timeBase);
645 for (var tS = 0; tS < subRow.length; tS++) {
646 var slice = subRow[tS];
647 slice.start = (slice.start - timeBase);
648 }
649 };
650
651 if (thread.cpuSlices)
652 shiftSubRow(thread.cpuSlices);
653
654 for (var tSR = 0; tSR < thread.subRows.length; tSR++) {
655 shiftSubRow(thread.subRows[tSR]);
656 }
657 for (var tSR = 0; tSR < thread.nonNestedSubRows.length; tSR++) {
658 shiftSubRow(thread.nonNestedSubRows[tSR]);
659 }
660 }
661 var counters = this.getAllCounters();
662 for (var tI = 0; tI < counters.length; tI++) {
663 var counter = counters[tI];
664 for (var sI = 0; sI < counter.timestamps.length; sI++)
665 counter.timestamps[sI] = (counter.timestamps[sI] - timeBase);
666 }
667 var cpus = this.getAllCpus();
668 for (var tI = 0; tI < cpus.length; tI++) {
669 var cpu = cpus[tI];
670 for (var sI = 0; sI < cpu.slices.length; sI++)
671 cpu.slices[sI].start = (cpu.slices[sI].start - timeBase);
672 }
673 this.updateBounds(); 889 this.updateBounds();
674 }, 890 },
675 891
676 getAllThreads: function() { 892 getAllThreads: function() {
677 var threads = []; 893 var threads = [];
678 for (var pid in this.processes) { 894 for (var pid in this.processes) {
679 var process = this.processes[pid]; 895 var process = this.processes[pid];
680 for (var tid in process.threads) { 896 for (var tid in process.threads) {
681 threads.push(process.threads[tid]); 897 threads.push(process.threads[tid]);
682 } 898 }
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
725 941
726 /** 942 /**
727 * Imports the provided events into the model. The eventData type 943 * Imports the provided events into the model. The eventData type
728 * is undefined and will be passed to all the timeline importers registered 944 * is undefined and will be passed to all the timeline importers registered
729 * via TimelineModel.registerImporter. The first importer that returns true 945 * via TimelineModel.registerImporter. The first importer that returns true
730 * for canImport(events) will be used to import the events. 946 * for canImport(events) will be used to import the events.
731 * 947 *
732 * @param {Object} events Events to import. 948 * @param {Object} events Events to import.
733 * @param {boolean} isChildImport True the eventData being imported is an 949 * @param {boolean} isChildImport True the eventData being imported is an
734 * additional trace after the primary eventData. 950 * additional trace after the primary eventData.
951 * @return {TimelineModelImporter} The importer used for the eventData.
735 */ 952 */
736 importOneTrace_: function(eventData, isAdditionalImport) { 953 importOneTrace_: function(eventData, isAdditionalImport) {
737 var importerConstructor; 954 var importerConstructor;
738 for (var i = 0; i < importerConstructors.length; ++i) { 955 for (var i = 0; i < importerConstructors.length; ++i) {
739 if (importerConstructors[i].canImport(eventData)) { 956 if (importerConstructors[i].canImport(eventData)) {
740 importerConstructor = importerConstructors[i]; 957 importerConstructor = importerConstructors[i];
741 break; 958 break;
742 } 959 }
743 } 960 }
744 if (!importerConstructor) 961 if (!importerConstructor)
745 throw 'Could not find an importer for the provided eventData.'; 962 throw 'Could not find an importer for the provided eventData.';
746 963
747 var importer = new importerConstructor( 964 var importer = new importerConstructor(
748 this, eventData, isAdditionalImport); 965 this, eventData, isAdditionalImport);
749 importer.importEvents(); 966 importer.importEvents();
750 this.pruneEmptyThreads(); 967 return importer;
751 }, 968 },
752 969
753 /** 970 /**
754 * Imports the provided traces into the model. The eventData type 971 * Imports the provided traces into the model. The eventData type
755 * is undefined and will be passed to all the timeline importers registered 972 * is undefined and will be passed to all the timeline importers registered
756 * via TimelineModel.registerImporter. The first importer that returns true 973 * via TimelineModel.registerImporter. The first importer that returns true
757 * for canImport(events) will be used to import the events. 974 * for canImport(events) will be used to import the events.
758 * 975 *
759 * The primary trace is provided via the eventData variable. If multiple 976 * The primary trace is provided via the eventData variable. If multiple
760 * traces are to be imported, specify the first one as events, and the 977 * traces are to be imported, specify the first one as events, and the
761 * remainder in the opt_additionalEventData array. 978 * remainder in the opt_additionalEventData array.
762 * 979 *
763 * @param {Object} eventData Events to import. 980 * @param {Object} eventData Events to import.
764 * @param {bool=} opt_zeroAndBoost Whether to align to zero and boost the 981 * @param {bool=} opt_zeroAndBoost Whether to align to zero and boost the
765 * by 15%. Defaults to true. 982 * by 15%. Defaults to true.
766 * @param {Array=} opt_additionalEventData An array of eventData objects 983 * @param {Array=} opt_additionalEventData An array of eventData objects
767 * (e.g. array of arrays) to 984 * (e.g. array of arrays) to
768 * import after importing the primary events. 985 * import after importing the primary events.
769 */ 986 */
770 importEvents: function(eventData, 987 importEvents: function(eventData,
771 opt_zeroAndBoost, opt_additionalEventData) { 988 opt_zeroAndBoost, opt_additionalEventData) {
772 if (opt_zeroAndBoost === undefined) 989 if (opt_zeroAndBoost === undefined)
773 opt_zeroAndBoost = true; 990 opt_zeroAndBoost = true;
774 991
775 this.importOneTrace_(eventData, false); 992 activeImporters = [];
993 var importer = this.importOneTrace_(eventData, false);
994 activeImporters.push(importer);
776 if (opt_additionalEventData) { 995 if (opt_additionalEventData) {
777 for (var i = 0; i < opt_additionalEventData.length; ++i) { 996 for (var i = 0; i < opt_additionalEventData.length; ++i) {
778 this.importOneTrace_(opt_additionalEventData[i], true); 997 importer = this.importOneTrace_(opt_additionalEventData[i], true);
998 activeImporters.push(importer);
779 } 999 }
780 } 1000 }
1001 for (var i = 0; i < activeImporters.length; ++i)
1002 activeImporters[i].finalizeImport();
1003
1004 for (var i = 0; i < activeImporters.length; ++i)
1005 this.pruneEmptyThreads();
781 1006
782 this.updateBounds(); 1007 this.updateBounds();
783 1008
784 if (opt_zeroAndBoost) 1009 if (opt_zeroAndBoost)
785 this.shiftWorldToZero(); 1010 this.shiftWorldToZero();
786 1011
787 if (opt_zeroAndBoost && 1012 if (opt_zeroAndBoost &&
788 this.minTimestamp !== undefined && 1013 this.minTimestamp !== undefined &&
789 this.maxTimestamp !== undefined) { 1014 this.maxTimestamp !== undefined) {
790 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; 1015 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15;
791 this.minTimestamp = this.minTimestamp - boost; 1016 this.minTimestamp = this.minTimestamp - boost;
792 this.maxTimestamp = this.maxTimestamp + boost; 1017 this.maxTimestamp = this.maxTimestamp + boost;
793 } 1018 }
794 } 1019 }
795 }; 1020 };
796 1021
797 return { 1022 return {
798 getPallette: getPallette, 1023 getPallette: getPallette,
799 getPalletteHighlightIdBoost: getPalletteHighlightIdBoost, 1024 getPalletteHighlightIdBoost: getPalletteHighlightIdBoost,
800 getColorIdByName: getColorIdByName, 1025 getColorIdByName: getColorIdByName,
801 getStringHash: getStringHash, 1026 getStringHash: getStringHash,
802 getStringColorId: getStringColorId, 1027 getStringColorId: getStringColorId,
803 1028
804 TimelineSlice: TimelineSlice, 1029 TimelineSlice: TimelineSlice,
1030 TimelineThreadSlice: TimelineThreadSlice,
1031 TimelineAsyncSlice: TimelineAsyncSlice,
805 TimelineThread: TimelineThread, 1032 TimelineThread: TimelineThread,
806 TimelineCounter: TimelineCounter, 1033 TimelineCounter: TimelineCounter,
807 TimelineProcess: TimelineProcess, 1034 TimelineProcess: TimelineProcess,
808 TimelineCpu: TimelineCpu, 1035 TimelineCpu: TimelineCpu,
1036 TimelineAsyncSliceGroup: TimelineAsyncSliceGroup,
809 TimelineModel: TimelineModel 1037 TimelineModel: TimelineModel
810 }; 1038 };
811 1039
812 }); 1040 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/tracing/timeline.js ('k') | chrome/browser/resources/tracing/timeline_model_test.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698