OLD | NEW |
---|---|
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_END() at time=0.3ms | |
jbates
2012/03/15 00:17:15
nit: TRACE_EVENT_END0("x") so noobs aren't confuse
| |
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 /** | |
75 * A TimelineAsyncSlice represents an interval of time during which an | |
76 * asynchronous operation is in progress. An AsyncSlice consumes no CPU time | |
77 * itself and so is only associated with Threads at its start and end point. | |
78 * | |
79 * @constructor | |
80 */ | |
81 function TimelineAsyncSlice(title, colorId, start, args) { | |
82 TimelineSlice.call(this, title, colorId, start, args); | |
83 }; | |
84 | |
85 TimelineAsyncSlice.prototype = { | |
86 __proto__: TimelineSlice.prototype, | |
87 | |
88 id: undefined, | |
89 | |
90 startThread: undefined, | |
91 | |
92 endThread: undefined | |
93 }; | |
94 | |
95 /** | |
62 * A TimelineThread stores all the trace events collected for a particular | 96 * 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 | 97 * 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 | 98 * 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. | 99 * The asynchronous slices ar stored in an TimelineAsyncSliceGroup object. |
jbates
2012/03/15 00:17:15
ar->are
| |
100 * | |
101 * The slices stored on a TimelineThread should be instances of | |
102 * TimelineThreadSlice. | |
66 * | 103 * |
67 * @constructor | 104 * @constructor |
68 */ | 105 */ |
69 function TimelineThread(parent, tid) { | 106 function TimelineThread(parent, tid) { |
107 if (!parent) | |
108 throw "parent must be provided."; | |
70 this.parent = parent; | 109 this.parent = parent; |
71 this.tid = tid; | 110 this.tid = tid; |
72 this.subRows = [[]]; | 111 this.subRows = [[]]; |
73 this.nonNestedSubRows = []; | 112 this.asyncSlices = new TimelineAsyncSliceGroup( |
113 "AsyncSliceGroup for " + this.ptid); | |
jbates
2012/03/15 00:17:15
Is "AsyncSliceGroup for ptid" the title presented
| |
114 } | |
115 | |
116 var ptidMap = {}; | |
117 | |
118 /** | |
119 * @return {String} A string that can be used as a unique key for a specific | |
120 * thread within a process. | |
121 */ | |
122 TimelineThread.getPTIDFromPidAndTid = function(pid, tid) { | |
123 if (!ptidMap[pid]) | |
124 ptidMap[pid] = {}; | |
125 if (!ptidMap[pid][tid]) | |
126 ptidMap[pid][tid] = pid + ':' + tid;; | |
127 return ptidMap[pid][tid]; | |
74 } | 128 } |
75 | 129 |
76 TimelineThread.prototype = { | 130 TimelineThread.prototype = { |
77 /** | 131 /** |
78 * Name of the thread, if present. | 132 * Name of the thread, if present. |
79 */ | 133 */ |
80 name: undefined, | 134 name: undefined, |
81 | 135 |
136 /** | |
137 * @return {string} A concatenation of the parent id and the thread's | |
138 * tid. Can be used to uniquely identify a thread. | |
139 */ | |
140 get ptid() { | |
141 return TimelineThread.getPTIDFromPidAndTid(this.tid, this.parent.pid); | |
142 }, | |
143 | |
82 getSubrow: function(i) { | 144 getSubrow: function(i) { |
83 while (i >= this.subRows.length) | 145 while (i >= this.subRows.length) |
84 this.subRows.push([]); | 146 this.subRows.push([]); |
85 return this.subRows[i]; | 147 return this.subRows[i]; |
86 }, | 148 }, |
87 | 149 |
88 addNonNestedSlice: function(slice) { | 150 |
89 for (var i = 0; i < this.nonNestedSubRows.length; i++) { | 151 shiftSubRow_: function(subRow, amount) { |
90 var currSubRow = this.nonNestedSubRows[i]; | 152 for (var tS = 0; tS < subRow.length; tS++) { |
91 var lastSlice = currSubRow[currSubRow.length - 1]; | 153 var slice = subRow[tS]; |
92 if (slice.start >= lastSlice.start + lastSlice.duration) { | 154 slice.start = (slice.start + amount); |
93 currSubRow.push(slice); | |
94 return; | |
95 } | |
96 } | 155 } |
97 this.nonNestedSubRows.push([slice]); | 156 }, |
157 | |
158 /** | |
159 * Shifts all the timestamps inside this thread forward by the amount | |
160 * specified. | |
161 */ | |
162 shiftTimestampsForward: function(amount) { | |
163 if (this.cpuSlices) | |
164 this.shiftSubRow_(this.cpuSlices, amount); | |
165 | |
166 for (var tSR = 0; tSR < this.subRows.length; tSR++) { | |
167 this.shiftSubRow_(this.subRows[tSR], amount); | |
168 } | |
169 | |
170 this.asyncSlices.shiftTimestampsForward(amount); | |
98 }, | 171 }, |
99 | 172 |
100 /** | 173 /** |
101 * Updates the minTimestamp and maxTimestamp fields based on the | 174 * Updates the minTimestamp and maxTimestamp fields based on the |
102 * current slices and nonNestedSubRows attached to the thread. | 175 * current objects associated with the thread. |
103 */ | 176 */ |
104 updateBounds: function() { | 177 updateBounds: function() { |
105 var values = []; | 178 var values = []; |
106 var slices; | 179 var slices; |
107 if (this.subRows[0].length != 0) { | 180 if (this.subRows[0].length != 0) { |
108 slices = this.subRows[0]; | 181 slices = this.subRows[0]; |
109 values.push(slices[0].start); | 182 values.push(slices[0].start); |
110 values.push(slices[slices.length - 1].end); | 183 values.push(slices[slices.length - 1].end); |
111 } | 184 } |
112 for (var i = 0; i < this.nonNestedSubRows.length; ++i) { | 185 if (this.asyncSlices.slices.length) { |
113 slices = this.nonNestedSubRows[i]; | 186 this.asyncSlices.updateBounds(); |
114 values.push(slices[0].start); | 187 values.push(this.asyncSlices.minTimestamp); |
115 values.push(slices[slices.length - 1].end); | 188 values.push(this.asyncSlices.maxTimestamp); |
116 } | 189 } |
117 if (values.length) { | 190 if (values.length) { |
118 this.minTimestamp = Math.min.apply(Math, values); | 191 this.minTimestamp = Math.min.apply(Math, values); |
119 this.maxTimestamp = Math.max.apply(Math, values); | 192 this.maxTimestamp = Math.max.apply(Math, values); |
120 } else { | 193 } else { |
121 this.minTimestamp = undefined; | 194 this.minTimestamp = undefined; |
122 this.maxTimestamp = undefined; | 195 this.maxTimestamp = undefined; |
123 } | 196 } |
124 }, | 197 }, |
125 | 198 |
126 /** | 199 /** |
127 * @return {String} A user-friendly name for this thread. | 200 * @return {String} A user-friendly name for this thread. |
128 */ | 201 */ |
129 get userFriendlyName() { | 202 get userFriendlyName() { |
130 var tname = this.name || this.tid; | 203 var tname = this.name || this.tid; |
131 return this.parent.pid + ': ' + tname; | 204 return this.parent.pid + ': ' + tname; |
132 }, | 205 }, |
133 | 206 |
134 /** | 207 /** |
135 * @return {String} User friendly details about this thread. | 208 * @return {String} User friendly details about this thread. |
136 */ | 209 */ |
137 get userFriendlyDetials() { | 210 get userFriendlyDetails() { |
138 return 'pid: ' + this.parent.pid + | 211 return 'pid: ' + this.parent.pid + |
139 ', tid: ' + this.tid + | 212 ', tid: ' + this.tid + |
140 (this.name ? ', name: ' + this.name : ''); | 213 (this.name ? ', name: ' + this.name : ''); |
141 } | 214 } |
142 | 215 |
143 }; | 216 }; |
144 | 217 |
145 /** | 218 /** |
146 * Comparison between threads that orders first by pid, | 219 * Comparison between threads that orders first by pid, |
147 * then by names, then by tid. | 220 * then by names, then by tid. |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
184 | 257 |
185 get numSeries() { | 258 get numSeries() { |
186 return this.seriesNames.length; | 259 return this.seriesNames.length; |
187 }, | 260 }, |
188 | 261 |
189 get numSamples() { | 262 get numSamples() { |
190 return this.timestamps.length; | 263 return this.timestamps.length; |
191 }, | 264 }, |
192 | 265 |
193 /** | 266 /** |
267 * Shifts all the timestamps inside this counter forward by the amount | |
268 * specified. | |
269 */ | |
270 shiftTimestampsForward: function(amount) { | |
271 for (var sI = 0; sI < this.timestamps.length; sI++) | |
272 this.timestamps[sI] = (this.timestamps[sI] + amount); | |
273 }, | |
274 | |
275 /** | |
194 * Updates the bounds for this counter based on the samples it contains. | 276 * Updates the bounds for this counter based on the samples it contains. |
195 */ | 277 */ |
196 updateBounds: function() { | 278 updateBounds: function() { |
197 if (this.seriesNames.length != this.seriesColors.length) | 279 if (this.seriesNames.length != this.seriesColors.length) |
198 throw 'seriesNames.length must match seriesColors.length'; | 280 throw 'seriesNames.length must match seriesColors.length'; |
199 if (this.numSeries * this.numSamples != this.samples.length) | 281 if (this.numSeries * this.numSamples != this.samples.length) |
200 throw 'samples.length must be a multiple of numSamples.'; | 282 throw 'samples.length must be a multiple of numSamples.'; |
201 | 283 |
202 this.totals = []; | 284 this.totals = []; |
203 if (this.samples.length == 0) { | 285 if (this.samples.length == 0) { |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
253 TimelineProcess.prototype = { | 335 TimelineProcess.prototype = { |
254 get numThreads() { | 336 get numThreads() { |
255 var n = 0; | 337 var n = 0; |
256 for (var p in this.threads) { | 338 for (var p in this.threads) { |
257 n++; | 339 n++; |
258 } | 340 } |
259 return n; | 341 return n; |
260 }, | 342 }, |
261 | 343 |
262 /** | 344 /** |
345 * Shifts all the timestamps inside this process forward by the amount | |
346 * specified. | |
347 */ | |
348 shiftTimestampsForward: function(amount) { | |
349 for (var tid in this.threads) | |
350 this.threads[tid].shiftTimestampsForward(amount); | |
351 for (var id in this.counters) | |
352 this.counters[id].shiftTimestampsForward(amount); | |
353 }, | |
354 | |
355 /** | |
263 * @return {TimlineThread} The thread identified by tid on this process, | 356 * @return {TimlineThread} The thread identified by tid on this process, |
264 * creating it if it doesn't exist. | 357 * creating it if it doesn't exist. |
265 */ | 358 */ |
266 getOrCreateThread: function(tid) { | 359 getOrCreateThread: function(tid) { |
267 if (!this.threads[tid]) | 360 if (!this.threads[tid]) |
268 this.threads[tid] = new TimelineThread(this, tid); | 361 this.threads[tid] = new TimelineThread(this, tid); |
269 return this.threads[tid]; | 362 return this.threads[tid]; |
270 }, | 363 }, |
271 | 364 |
272 /** | 365 /** |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
308 if (cat.length) | 401 if (cat.length) |
309 id = cat + '.' + name; | 402 id = cat + '.' + name; |
310 else | 403 else |
311 id = name; | 404 id = name; |
312 if (!this.counters[id]) | 405 if (!this.counters[id]) |
313 this.counters[id] = new TimelineCounter(this, id, name); | 406 this.counters[id] = new TimelineCounter(this, id, name); |
314 return this.counters[id]; | 407 return this.counters[id]; |
315 }, | 408 }, |
316 | 409 |
317 /** | 410 /** |
411 * Shifts all the timestamps inside this CPU forward by the amount | |
412 * specified. | |
413 */ | |
414 shiftTimestampsForward: function(amount) { | |
415 for (var sI = 0; sI < this.slices.length; sI++) | |
416 this.slices[sI].start = (this.slices[sI].start + amount); | |
417 for (var id in this.counters) | |
418 this.counters[id].shiftTimestampsForward(amount); | |
419 }, | |
420 | |
421 /** | |
318 * Updates the minTimestamp and maxTimestamp fields based on the | 422 * Updates the minTimestamp and maxTimestamp fields based on the |
319 * current slices attached to the cpu. | 423 * current slices attached to the cpu. |
320 */ | 424 */ |
321 updateBounds: function() { | 425 updateBounds: function() { |
322 var values = []; | 426 var values = []; |
323 if (this.slices.length) { | 427 if (this.slices.length) { |
324 this.minTimestamp = this.slices[0].start; | 428 this.minTimestamp = this.slices[0].start; |
325 this.maxTimestamp = this.slices[this.slices.length - 1].end; | 429 this.maxTimestamp = this.slices[this.slices.length - 1].end; |
326 } else { | 430 } else { |
327 this.minTimestamp = undefined; | 431 this.minTimestamp = undefined; |
328 this.maxTimestamp = undefined; | 432 this.maxTimestamp = undefined; |
329 } | 433 } |
330 } | 434 } |
331 }; | 435 }; |
332 | 436 |
333 /** | 437 /** |
334 * Comparison between processes that orders by cpuNumber. | 438 * Comparison between processes that orders by cpuNumber. |
335 */ | 439 */ |
336 TimelineCpu.compare = function(x, y) { | 440 TimelineCpu.compare = function(x, y) { |
337 return x.cpuNumber - y.cpuNumber; | 441 return x.cpuNumber - y.cpuNumber; |
338 }; | 442 }; |
339 | 443 |
444 /** | |
445 * A group of AsyncSlices. | |
446 * @constructor | |
447 */ | |
448 function TimelineAsyncSliceGroup(name) { | |
449 this.name = name; | |
450 this.slices = []; | |
451 } | |
452 | |
453 TimelineAsyncSliceGroup.prototype = { | |
454 __proto__: Object.prototype, | |
455 | |
456 /** | |
457 * Helper function that pushes the provided slice onto the slices array. | |
458 */ | |
459 push: function(slice) { | |
460 this.slices.push(slice); | |
461 }, | |
462 | |
463 /** | |
464 * @return {Number} The number of slices in this group. | |
465 */ | |
466 get length() { | |
467 return this.slices.length; | |
468 }, | |
469 | |
470 /** | |
471 * Built automatically rebuildSubRows() | |
jbates
2012/03/15 00:17:15
Built automatically by rebuildSubRows
| |
472 */ | |
473 subRows_: undefined, | |
474 | |
475 /** | |
476 * Updates the bounds for this group based on the slices it contains. | |
477 */ | |
478 sortSlices_: function() { | |
479 this.slices.sort(function(x,y) { | |
480 return x.start - y.start; | |
481 }); | |
482 }, | |
483 | |
484 /** | |
485 * Shifts all the timestamps inside this group forward by the amount | |
486 * specified. | |
487 */ | |
488 shiftTimestampsForward: function(amount) { | |
489 for (var sI = 0; sI < this.slices.length; sI++) | |
490 this.slices[sI].start = (this.slices[sI].start + amount); | |
491 }, | |
492 | |
493 /** | |
494 * Updates the bounds for this group based on the slices it contains. | |
495 */ | |
496 updateBounds: function() { | |
497 this.sortSlices_(); | |
498 if (this.slices.length) { | |
499 this.minTimestamp = this.slices[0].start; | |
500 this.maxTimestamp = this.slices[this.slices.length - 1].end; | |
501 } else { | |
502 this.minTimestamp = undefined; | |
503 this.maxTimestamp = undefined; | |
504 } | |
505 this.subRows_ = undefined; | |
506 }, | |
507 | |
508 get subRows() { | |
509 if (!this.subRows_) | |
510 this.rebuildSubRows_(); | |
511 return this.subRows_; | |
512 }, | |
513 | |
514 /** | |
515 * Breaks up the list of slices into N rows, each of which is a list of | |
516 * slices that are non overlapping. | |
517 * | |
518 * It uses a very simple approach: walk through the slices in sorted order | |
519 * by start time. For each slice, try to fit it in an existing subRow. If it | |
520 * doesn't fit in any subrow, make another subRow. | |
521 */ | |
522 rebuildSubRows_: function() { | |
523 this.sortSlices_(); | |
524 var subRows = []; | |
525 for (var i = 0; i < this.slices.length; i++) { | |
526 var slice = this.slices[i]; | |
527 | |
528 var found = false; | |
529 for (var j = 0; j < subRows.length; j++) { | |
530 var subRow = subRows[j]; | |
531 var lastSliceInSubRow = subRow[subRow.length - 1]; | |
532 if (slice.start >= lastSliceInSubRow.end) { | |
533 found = true; | |
534 subRow.push(slice); | |
535 } | |
536 } | |
537 if (!found) { | |
538 subRows.push([slice]); | |
539 } | |
540 } | |
541 this.subRows_ = subRows; | |
542 }, | |
543 | |
544 /** | |
545 * Breaks up this group into slices based on start thread. | |
546 * | |
547 * @return {Array} An array of TimelineAsyncSliceGroups where each group has | |
548 * slices that started on the same thread. | |
549 **/ | |
550 computeSubGroups: function() { | |
551 var subGroupsByPTID = {}; | |
552 for (var i = 0; i < this.slices.length; ++i) { | |
553 var slice = this.slices[i]; | |
554 var slicePTID = slice.startThread.ptid; | |
555 if (!subGroupsByPTID[slicePTID]) | |
556 subGroupsByPTID[slicePTID] = new TimelineAsyncSliceGroup(this.name) | |
557 subGroupsByPTID[slicePTID].slices.push(slice); | |
558 } | |
559 var groups = [] | |
560 for (var ptid in subGroupsByPTID) { | |
561 var group = subGroupsByPTID[ptid]; | |
562 group.updateBounds() | |
563 groups.push(group); | |
564 } | |
565 return groups; | |
566 } | |
567 | |
568 }; | |
569 | |
570 /** | |
571 * Comparison between counters that orders by pid, then name. | |
572 */ | |
573 TimelineCounter.compare = function(x, y) { | |
574 if (x.parent.pid != y.parent.pid) { | |
575 return TimelineProcess.compare(x.parent, y.parent.pid); | |
576 } | |
577 var tmp = x.name.localeCompare(y.name); | |
578 if (tmp == 0) | |
579 return x.tid - y.tid; | |
580 return tmp; | |
581 }; | |
582 | |
340 // The color pallette is split in half, with the upper | 583 // The color pallette is split in half, with the upper |
341 // half of the pallette being the "highlighted" verison | 584 // half of the pallette being the "highlighted" verison |
342 // of the base color. So, color 7's highlighted form is | 585 // of the base color. So, color 7's highlighted form is |
343 // 7 + (pallette.length / 2). | 586 // 7 + (pallette.length / 2). |
344 // | 587 // |
345 // These bright versions of colors are automatically generated | 588 // These bright versions of colors are automatically generated |
346 // from the base colors. | 589 // from the base colors. |
347 // | 590 // |
348 // Within the color pallette, there are "regular" colors, | 591 // Within the color pallette, there are "regular" colors, |
349 // which can be used for random color selection, and | 592 // which can be used for random color selection, and |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
480 * See TimelineModel.importEvents for details and more advanced ways to | 723 * See TimelineModel.importEvents for details and more advanced ways to |
481 * import data. | 724 * import data. |
482 * @param {bool=} opt_zeroAndBoost Whether to align to zero and boost the | 725 * @param {bool=} opt_zeroAndBoost Whether to align to zero and boost the |
483 * by 15%. Defaults to true. | 726 * by 15%. Defaults to true. |
484 * @constructor | 727 * @constructor |
485 */ | 728 */ |
486 function TimelineModel(opt_eventData, opt_zeroAndBoost) { | 729 function TimelineModel(opt_eventData, opt_zeroAndBoost) { |
487 this.cpus = {}; | 730 this.cpus = {}; |
488 this.processes = {}; | 731 this.processes = {}; |
489 this.importErrors = []; | 732 this.importErrors = []; |
733 this.asyncSliceGroups = {}; | |
490 | 734 |
491 if (opt_eventData) | 735 if (opt_eventData) |
492 this.importEvents(opt_eventData, opt_zeroAndBoost); | 736 this.importEvents(opt_eventData, opt_zeroAndBoost); |
493 } | 737 } |
494 | 738 |
495 var importerConstructors = []; | 739 var importerConstructors = []; |
496 | 740 |
497 /** | 741 /** |
498 * Registers an importer. All registered importers are considered | 742 * Registers an importer. All registered importers are considered |
499 * when processing an import request. | 743 * when processing an import request. |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 Loading... | |
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 Loading... | |
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 * @returns {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 }); |
OLD | NEW |