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_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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 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 * @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 }); |
OLD | NEW |