OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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. |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
58 * @constructor | 58 * @constructor |
59 */ | 59 */ |
60 function TimelineThread(parent, tid) { | 60 function TimelineThread(parent, tid) { |
61 this.parent = parent; | 61 this.parent = parent; |
62 this.tid = tid; | 62 this.tid = tid; |
63 this.subRows = [[]]; | 63 this.subRows = [[]]; |
64 this.nonNestedSubRows = []; | 64 this.nonNestedSubRows = []; |
65 } | 65 } |
66 | 66 |
67 TimelineThread.prototype = { | 67 TimelineThread.prototype = { |
| 68 /** |
| 69 * Name of the thread, if present. |
| 70 */ |
| 71 name: undefined, |
| 72 |
68 getSubrow: function(i) { | 73 getSubrow: function(i) { |
69 while (i >= this.subRows.length) | 74 while (i >= this.subRows.length) |
70 this.subRows.push([]); | 75 this.subRows.push([]); |
71 return this.subRows[i]; | 76 return this.subRows[i]; |
72 }, | 77 }, |
73 | 78 |
74 addNonNestedSlice: function(slice) { | 79 addNonNestedSlice: function(slice) { |
75 for (var i = 0; i < this.nonNestedSubRows.length; i++) { | 80 for (var i = 0; i < this.nonNestedSubRows.length; i++) { |
76 var currSubRow = this.nonNestedSubRows[i]; | 81 var currSubRow = this.nonNestedSubRows[i]; |
77 var lastSlice = currSubRow[currSubRow.length - 1]; | 82 var lastSlice = currSubRow[currSubRow.length - 1]; |
78 if (slice.start >= lastSlice.start + lastSlice.duration) { | 83 if (slice.start >= lastSlice.start + lastSlice.duration) { |
79 currSubRow.push(slice); | 84 currSubRow.push(slice); |
80 return; | 85 return; |
81 } | 86 } |
82 } | 87 } |
83 this.nonNestedSubRows.push([slice]); | 88 this.nonNestedSubRows.push([slice]); |
84 }, | 89 }, |
85 | 90 |
| 91 /** |
| 92 * Updates the minTimestamp and maxTimestamp fields based on the |
| 93 * current slices and nonNestedSubRows attached to the thread. |
| 94 */ |
86 updateBounds: function() { | 95 updateBounds: function() { |
87 var slices = this.subRows[0]; | 96 var values = []; |
88 if (slices.length != 0) { | 97 var slices; |
89 this.minTimestamp = slices[0].start; | 98 if (this.subRows[0].length != 0) { |
90 this.maxTimestamp = slices[slices.length - 1].end; | 99 slices = this.subRows[0]; |
| 100 values.push(slices[0].start); |
| 101 values.push(slices[slices.length - 1].end); |
| 102 } |
| 103 for (var i = 0; i < this.nonNestedSubRows.length; ++i) { |
| 104 slices = this.nonNestedSubRows[i]; |
| 105 values.push(slices[0].start); |
| 106 values.push(slices[slices.length - 1].end); |
| 107 } |
| 108 if (values.length) { |
| 109 this.minTimestamp = Math.min.apply(Math, values); |
| 110 this.maxTimestamp = Math.max.apply(Math, values); |
91 } else { | 111 } else { |
92 this.minTimestamp = undefined; | 112 this.minTimestamp = undefined; |
93 this.maxTimestamp = undefined; | 113 this.maxTimestamp = undefined; |
94 } | 114 } |
95 } | 115 } |
96 | 116 |
97 }; | 117 }; |
98 | 118 |
99 /** | 119 /** |
| 120 * Comparison between threads that orders first by pid, |
| 121 * then by names, then by tid. |
| 122 */ |
| 123 TimelineThread.compare = function(x,y) { |
| 124 if(x.parent.pid != y.parent.pid) { |
| 125 return x.parent.pid - y.parent.pid; |
| 126 } |
| 127 |
| 128 if (x.name && y.name) { |
| 129 var tmp = x.name.localeCompare(y.name); |
| 130 if (tmp == 0) |
| 131 return x.tid - y.tid; |
| 132 return tmp; |
| 133 } else if (x.name) { |
| 134 return -1; |
| 135 } else if (y.name){ |
| 136 return 1; |
| 137 } else { |
| 138 return x.tid - y.tid; |
| 139 } |
| 140 }; |
| 141 |
| 142 |
| 143 /** |
100 * The TimelineProcess represents a single process in the | 144 * The TimelineProcess represents a single process in the |
101 * trace. Right now, we keep this around purely for bookkeeping | 145 * trace. Right now, we keep this around purely for bookkeeping |
102 * reasons. | 146 * reasons. |
103 * @constructor | 147 * @constructor |
104 */ | 148 */ |
105 function TimelineProcess(pid) { | 149 function TimelineProcess(pid) { |
106 this.pid = pid; | 150 this.pid = pid; |
107 this.threads = {}; | 151 this.threads = {}; |
108 }; | 152 }; |
109 | 153 |
110 TimelineProcess.prototype = { | 154 TimelineProcess.prototype = { |
111 getThread: function(tid) { | 155 getThread: function(tid) { |
112 if (!this.threads[tid]) | 156 if (!this.threads[tid]) |
113 this.threads[tid] = new TimelineThread(this, tid); | 157 this.threads[tid] = new TimelineThread(this, tid); |
114 return this.threads[tid]; | 158 return this.threads[tid]; |
115 } | 159 } |
116 }; | 160 }; |
117 | 161 |
118 /** | 162 /** |
119 * Builds a model from an array of TraceEvent objects. | 163 * Builds a model from an array of TraceEvent objects. |
120 * @param {Array} events An array of TraceEvents created by | 164 * @param {Array} events An array of TraceEvents created by |
121 * TraceEvent.ToJSON(). | 165 * TraceEvent.ToJSON(). |
122 * @constructor | 166 * @constructor |
123 */ | 167 */ |
124 function TimelineModel(events) { | 168 function TimelineModel(events) { |
125 this.processes = {}; | 169 this.processes = {}; |
| 170 this.importErrors = []; |
126 | 171 |
127 if (events) | 172 if (events) |
128 this.importEvents(events); | 173 this.importEvents(events); |
129 } | 174 } |
130 | 175 |
131 TimelineModel.prototype = { | 176 TimelineModel.prototype = { |
132 __proto__: cr.EventTarget.prototype, | 177 __proto__: cr.EventTarget.prototype, |
133 | 178 |
134 getProcess: function(pid) { | 179 getProcess: function(pid) { |
135 if (!this.processes[pid]) | 180 if (!this.processes[pid]) |
136 this.processes[pid] = new TimelineProcess(pid); | 181 this.processes[pid] = new TimelineProcess(pid); |
137 return this.processes[pid]; | 182 return this.processes[pid]; |
138 }, | 183 }, |
139 | 184 |
140 /** | 185 /** |
141 * The import takes an array of json-ified TraceEvents and adds them into | 186 * The import takes an array of json-ified TraceEvents and adds them into |
142 * the TimelineModel as processes, threads, and slices. | 187 * the TimelineModel as processes, threads, and slices. |
143 */ | 188 */ |
144 importEvents: function(events) { | 189 importEvents: function(events) { |
145 // A ptid is a pid and tid joined together x:y fashion, eg 1024:130 | 190 // A ptid is a pid and tid joined together x:y fashion, eg 1024:130 |
146 // The ptid is a unique key for a thread in the trace. | 191 // The ptid is a unique key for a thread in the trace. |
147 | 192 this.importErrors = []; |
148 | 193 |
149 // Threadstate | 194 // Threadstate |
150 const numColorIds = 30; | 195 const numColorIds = 30; |
151 function ThreadState(tid) { | 196 function ThreadState(tid) { |
152 this.openSlices = []; | 197 this.openSlices = []; |
153 this.openNonNestedSlices = {}; | 198 this.openNonNestedSlices = {}; |
154 } | 199 } |
155 var threadStateByPTID = {}; | 200 var threadStateByPTID = {}; |
156 | 201 |
157 var nameToColorMap = {}; | 202 var nameToColorMap = {}; |
(...skipping 20 matching lines...) Expand all Loading... |
178 var colorId = getColor(event.name); | 223 var colorId = getColor(event.name); |
179 var slice = | 224 var slice = |
180 { index: eI, | 225 { index: eI, |
181 slice: new TimelineSlice(event.name, colorId, event.ts, | 226 slice: new TimelineSlice(event.name, colorId, event.ts, |
182 event.args) }; | 227 event.args) }; |
183 if (event.args['ui-nest'] === '0') { | 228 if (event.args['ui-nest'] === '0') { |
184 var sliceID = event.name; | 229 var sliceID = event.name; |
185 for (var x in event.args) | 230 for (var x in event.args) |
186 sliceID += ';' + event.args[x]; | 231 sliceID += ';' + event.args[x]; |
187 if (state.openNonNestedSlices[sliceID]) | 232 if (state.openNonNestedSlices[sliceID]) |
188 console.log('Event ' + sliceID + ' already open.'); | 233 this.importErrors.push('Event ' + sliceID + ' already open.'); |
189 state.openNonNestedSlices[sliceID] = slice; | 234 state.openNonNestedSlices[sliceID] = slice; |
190 } else { | 235 } else { |
191 state.openSlices.push(slice); | 236 state.openSlices.push(slice); |
192 } | 237 } |
193 } | 238 } |
194 | 239 |
195 /** | 240 /** |
196 * Helper to process an 'end' event (e.g. close a slice). | 241 * Helper to process an 'end' event (e.g. close a slice). |
197 * @param {ThreadState} state Thread state (holds slices). | 242 * @param {ThreadState} state Thread state (holds slices). |
198 * @param {Object} event The current trace event. | 243 * @param {Object} event The current trace event. |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 | 288 |
244 if (event.ph == 'B') { | 289 if (event.ph == 'B') { |
245 processBegin(state, event); | 290 processBegin(state, event); |
246 } else if (event.ph == 'E') { | 291 } else if (event.ph == 'E') { |
247 processEnd(state, event); | 292 processEnd(state, event); |
248 } else if (event.ph == 'I') { | 293 } else if (event.ph == 'I') { |
249 // Treat an Instant event as a duration 0 slice. | 294 // Treat an Instant event as a duration 0 slice. |
250 // TimelineSliceTrack's redraw() knows how to handle this. | 295 // TimelineSliceTrack's redraw() knows how to handle this. |
251 processBegin(state, event); | 296 processBegin(state, event); |
252 processEnd(state, event); | 297 processEnd(state, event); |
| 298 } else if (event.ph == 'M') { |
| 299 if (event.name == 'thread_name') { |
| 300 var thread = this.getProcess(event.pid).getThread(event.tid); |
| 301 thread.name = event.args.name; |
| 302 } else { |
| 303 this.importErrors.push('Unrecognized metadata name: ' + event.name); |
| 304 } |
253 } else { | 305 } else { |
254 throw new Error('Unrecognized event phase: ' + event.ph + | 306 this.importErrors.push('Unrecognized event phase: ' + event.ph + |
255 '(' + event.name + ')'); | 307 '(' + event.name + ')'); |
256 } | 308 } |
257 } | 309 } |
258 | 310 this.pruneEmptyThreads(); |
259 this.updateBounds(); | 311 this.updateBounds(); |
260 | 312 |
261 // Add end events for any events that are still on the stack. These | 313 // Add end events for any events that are still on the stack. These |
262 // are events that were still open when trace was ended, and can often | 314 // are events that were still open when trace was ended, and can often |
263 // indicate deadlock behavior. | 315 // indicate deadlock behavior. |
264 for (var ptid in threadStateByPTID) { | 316 for (var ptid in threadStateByPTID) { |
265 var state = threadStateByPTID[ptid]; | 317 var state = threadStateByPTID[ptid]; |
266 while (state.openSlices.length > 0) { | 318 while (state.openSlices.length > 0) { |
267 var slice = state.openSlices.pop(); | 319 var slice = state.openSlices.pop(); |
268 slice.slice.duration = this.maxTimestamp - slice.slice.start; | 320 slice.slice.duration = this.maxTimestamp - slice.slice.start; |
(...skipping 13 matching lines...) Expand all Loading... |
282 } | 334 } |
283 } | 335 } |
284 | 336 |
285 this.shiftWorldToMicroseconds(); | 337 this.shiftWorldToMicroseconds(); |
286 | 338 |
287 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; | 339 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; |
288 this.minTimestamp = this.minTimestamp - boost; | 340 this.minTimestamp = this.minTimestamp - boost; |
289 this.maxTimestamp = this.maxTimestamp + boost; | 341 this.maxTimestamp = this.maxTimestamp + boost; |
290 }, | 342 }, |
291 | 343 |
| 344 /** |
| 345 * Removes threads from the model that have no subrows. |
| 346 */ |
| 347 pruneEmptyThreads: function() { |
| 348 for (var pid in this.processes) { |
| 349 var process = this.processes[pid]; |
| 350 var prunedThreads = []; |
| 351 for (var tid in process.threads) { |
| 352 var thread = process.threads[tid]; |
| 353 if (thread.subRows[0].length || thread.nonNestedSubRows.legnth) |
| 354 prunedThreads.push(thread); |
| 355 } |
| 356 process.threads = prunedThreads; |
| 357 } |
| 358 }, |
| 359 |
292 updateBounds: function() { | 360 updateBounds: function() { |
293 var wmin = Infinity; | 361 var wmin = Infinity; |
294 var wmax = -wmin; | 362 var wmax = -wmin; |
295 var threads = this.getAllThreads(); | 363 var threads = this.getAllThreads(); |
296 for (var tI = 0; tI < threads.length; tI++) { | 364 for (var tI = 0; tI < threads.length; tI++) { |
297 var thread = threads[tI]; | 365 var thread = threads[tI]; |
298 thread.updateBounds(); | 366 thread.updateBounds(); |
299 if (thread.minTimestamp && thread.maxTimestamp) { | 367 if (thread.minTimestamp != undefined && |
| 368 thread.maxTimestamp != undefined) { |
300 wmin = Math.min(wmin, thread.minTimestamp); | 369 wmin = Math.min(wmin, thread.minTimestamp); |
301 wmax = Math.max(wmax, thread.maxTimestamp); | 370 wmax = Math.max(wmax, thread.maxTimestamp); |
302 } | 371 } |
303 } | 372 } |
304 this.minTimestamp = wmin; | 373 this.minTimestamp = wmin; |
305 this.maxTimestamp = wmax; | 374 this.maxTimestamp = wmax; |
306 }, | 375 }, |
307 | 376 |
308 shiftWorldToMicroseconds: function() { | 377 shiftWorldToMicroseconds: function() { |
309 var timeBase = this.minTimestamp; | 378 var timeBase = this.minTimestamp; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 | 410 |
342 }; | 411 }; |
343 | 412 |
344 return { | 413 return { |
345 TimelineSlice: TimelineSlice, | 414 TimelineSlice: TimelineSlice, |
346 TimelineThread: TimelineThread, | 415 TimelineThread: TimelineThread, |
347 TimelineProcess: TimelineProcess, | 416 TimelineProcess: TimelineProcess, |
348 TimelineModel: TimelineModel | 417 TimelineModel: TimelineModel |
349 }; | 418 }; |
350 }); | 419 }); |
OLD | NEW |