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 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
113 this.maxTimestamp = undefined; | 113 this.maxTimestamp = undefined; |
114 } | 114 } |
115 } | 115 } |
116 | 116 |
117 }; | 117 }; |
118 | 118 |
119 /** | 119 /** |
120 * Comparison between threads that orders first by pid, | 120 * Comparison between threads that orders first by pid, |
121 * then by names, then by tid. | 121 * then by names, then by tid. |
122 */ | 122 */ |
123 TimelineThread.compare = function(x,y) { | 123 TimelineThread.compare = function(x, y) { |
124 if(x.parent.pid != y.parent.pid) { | 124 if (x.parent.pid != y.parent.pid) { |
125 return x.parent.pid - y.parent.pid; | 125 return x.parent.pid - y.parent.pid; |
126 } | 126 } |
127 | 127 |
128 if (x.name && y.name) { | 128 if (x.name && y.name) { |
129 var tmp = x.name.localeCompare(y.name); | 129 var tmp = x.name.localeCompare(y.name); |
130 if (tmp == 0) | 130 if (tmp == 0) |
131 return x.tid - y.tid; | 131 return x.tid - y.tid; |
132 return tmp; | 132 return tmp; |
133 } else if (x.name) { | 133 } else if (x.name) { |
134 return -1; | 134 return -1; |
135 } else if (y.name){ | 135 } else if (y.name) { |
136 return 1; | 136 return 1; |
137 } else { | 137 } else { |
138 return x.tid - y.tid; | 138 return x.tid - y.tid; |
139 } | 139 } |
140 }; | 140 }; |
141 | 141 |
142 | 142 |
143 /** | 143 /** |
144 * The TimelineProcess represents a single process in the | 144 * The TimelineProcess represents a single process in the |
145 * trace. Right now, we keep this around purely for bookkeeping | 145 * trace. Right now, we keep this around purely for bookkeeping |
146 * reasons. | 146 * reasons. |
147 * @constructor | 147 * @constructor |
148 */ | 148 */ |
149 function TimelineProcess(pid) { | 149 function TimelineProcess(pid) { |
150 this.pid = pid; | 150 this.pid = pid; |
151 this.threads = {}; | 151 this.threads = {}; |
152 }; | 152 }; |
153 | 153 |
154 TimelineProcess.prototype = { | 154 TimelineProcess.prototype = { |
155 getThread: function(tid) { | 155 get numThreads() { |
156 var n = 0; | |
157 for (var p in this.threads) | |
arv (Not doing code reviews)
2011/10/21 17:09:18
Missing {}
nduca
2011/10/21 19:50:40
Done.
| |
158 n++; | |
159 return n; | |
160 }, | |
161 | |
162 getOrCreateThread: function(tid) { | |
156 if (!this.threads[tid]) | 163 if (!this.threads[tid]) |
157 this.threads[tid] = new TimelineThread(this, tid); | 164 this.threads[tid] = new TimelineThread(this, tid); |
158 return this.threads[tid]; | 165 return this.threads[tid]; |
159 } | 166 } |
160 }; | 167 }; |
161 | 168 |
162 /** | 169 /** |
170 * Computes a simplistic hashcode of the provide name. Used to chose colors | |
171 * for slices. | |
172 * @param {string} name The string to hash. | |
173 */ | |
174 function getStringHash(name) { | |
arv (Not doing code reviews)
2011/10/21 17:09:18
This looks like it might be overkill for choosing
nduca
2011/10/21 19:50:40
Do you have any suggestions? The goal is to get a
arv (Not doing code reviews)
2011/10/21 23:43:53
I don't have any better idea. Are the strings shor
| |
175 var hash = 0; | |
176 for (var i = 0; i < name.length; ++i) | |
177 hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF; | |
178 return hash; | |
179 } | |
180 | |
181 /** | |
163 * Builds a model from an array of TraceEvent objects. | 182 * Builds a model from an array of TraceEvent objects. |
164 * @param {Array} events An array of TraceEvents created by | 183 * @param {Array} events An array of TraceEvents created by |
165 * TraceEvent.ToJSON(). | 184 * TraceEvent.ToJSON(). |
166 * @constructor | 185 * @constructor |
167 */ | 186 */ |
168 function TimelineModel(events) { | 187 function TimelineModel(events) { |
169 this.processes = {}; | 188 this.processes = {}; |
170 this.importErrors = []; | 189 this.importErrors = []; |
171 | 190 |
172 if (events) | 191 if (events) |
173 this.importEvents(events); | 192 this.importEvents(events); |
174 } | 193 } |
175 | 194 |
176 TimelineModel.prototype = { | 195 TimelineModel.prototype = { |
177 __proto__: cr.EventTarget.prototype, | 196 __proto__: cr.EventTarget.prototype, |
178 | 197 |
179 getProcess: function(pid) { | 198 get numProcesses() { |
199 var n = 0; | |
200 for (var p in this.processes) | |
201 n++; | |
202 return n; | |
203 }, | |
204 | |
205 getOrCreateProcess: function(pid) { | |
180 if (!this.processes[pid]) | 206 if (!this.processes[pid]) |
181 this.processes[pid] = new TimelineProcess(pid); | 207 this.processes[pid] = new TimelineProcess(pid); |
182 return this.processes[pid]; | 208 return this.processes[pid]; |
183 }, | 209 }, |
184 | 210 |
185 /** | 211 /** |
186 * The import takes an array of json-ified TraceEvents and adds them into | 212 * The import takes an array of json-ified TraceEvents and adds them into |
187 * the TimelineModel as processes, threads, and slices. | 213 * the TimelineModel as processes, threads, and slices. |
188 */ | 214 */ |
189 importEvents: function(events) { | 215 importEvents: function(events) { |
190 // A ptid is a pid and tid joined together x:y fashion, eg 1024:130 | 216 // A ptid is a pid and tid joined together x:y fashion, eg 1024:130 |
191 // The ptid is a unique key for a thread in the trace. | 217 // The ptid is a unique key for a thread in the trace. |
192 this.importErrors = []; | 218 this.importErrors = []; |
193 | 219 |
194 // Threadstate | 220 // Threadstate |
195 const numColorIds = 30; | 221 const numColorIds = 30; |
196 function ThreadState(tid) { | 222 function ThreadState(tid) { |
197 this.openSlices = []; | 223 this.openSlices = []; |
198 this.openNonNestedSlices = {}; | 224 this.openNonNestedSlices = {}; |
199 } | 225 } |
200 var threadStateByPTID = {}; | 226 var threadStateByPTID = {}; |
201 | 227 |
202 var nameToColorMap = {}; | 228 var nameToColorMap = {}; |
203 function getColor(name) { | 229 function getColor(name) { |
204 if (!(name in nameToColorMap)) { | 230 if (!(name in nameToColorMap)) { |
205 // Compute a simplistic hashcode of the string so we get consistent | 231 var hash = getStringHash(name); |
206 // coloring across traces. | |
207 var hash = 0; | |
208 for (var i = 0; i < name.length; ++i) | |
209 hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF; | |
210 nameToColorMap[name] = hash % numColorIds; | 232 nameToColorMap[name] = hash % numColorIds; |
211 } | 233 } |
212 return nameToColorMap[name]; | 234 return nameToColorMap[name]; |
213 } | 235 } |
214 | 236 |
215 var self = this; | 237 var self = this; |
216 | 238 |
217 /** | 239 /** |
218 * Helper to process a 'begin' event (e.g. initiate a slice). | 240 * Helper to process a 'begin' event (e.g. initiate a slice). |
219 * @param {ThreadState} state Thread state (holds slices). | 241 * @param {ThreadState} state Thread state (holds slices). |
(...skipping 26 matching lines...) Expand all Loading... | |
246 if (event.args['ui-nest'] === '0') { | 268 if (event.args['ui-nest'] === '0') { |
247 var sliceID = event.name; | 269 var sliceID = event.name; |
248 for (var x in event.args) | 270 for (var x in event.args) |
249 sliceID += ';' + event.args[x]; | 271 sliceID += ';' + event.args[x]; |
250 var slice = state.openNonNestedSlices[sliceID]; | 272 var slice = state.openNonNestedSlices[sliceID]; |
251 if (!slice) | 273 if (!slice) |
252 return; | 274 return; |
253 slice.slice.duration = event.ts - slice.slice.start; | 275 slice.slice.duration = event.ts - slice.slice.start; |
254 | 276 |
255 // Store the slice in a non-nested subrow. | 277 // Store the slice in a non-nested subrow. |
256 var thread = self.getProcess(event.pid).getThread(event.tid); | 278 var thread = self.getOrCreateProcess(event.pid) |
279 .getOrCreateThread(event.tid); | |
arv (Not doing code reviews)
2011/10/21 17:09:18
binop before newline
nduca
2011/10/21 19:50:40
Done.
| |
257 thread.addNonNestedSlice(slice.slice); | 280 thread.addNonNestedSlice(slice.slice); |
258 delete state.openNonNestedSlices[name]; | 281 delete state.openNonNestedSlices[name]; |
259 } else { | 282 } else { |
260 if (state.openSlices.length == 0) { | 283 if (state.openSlices.length == 0) { |
261 // Ignore E events that are unmatched. | 284 // Ignore E events that are unmatched. |
262 return; | 285 return; |
263 } | 286 } |
264 var slice = state.openSlices.pop().slice; | 287 var slice = state.openSlices.pop().slice; |
265 slice.duration = event.ts - slice.start; | 288 slice.duration = event.ts - slice.start; |
266 | 289 |
267 // Store the slice on the correct subrow. | 290 // Store the slice on the correct subrow. |
268 var thread = self.getProcess(event.pid).getThread(event.tid); | 291 var thread = self.getOrCreateProcess(event.pid) |
292 .getOrCreateThread(event.tid); | |
269 var subRowIndex = state.openSlices.length; | 293 var subRowIndex = state.openSlices.length; |
270 thread.getSubrow(subRowIndex).push(slice); | 294 thread.getSubrow(subRowIndex).push(slice); |
271 | 295 |
272 // Add the slice to the subSlices array of its parent. | 296 // Add the slice to the subSlices array of its parent. |
273 if (state.openSlices.length) { | 297 if (state.openSlices.length) { |
274 var parentSlice = state.openSlices[state.openSlices.length - 1]; | 298 var parentSlice = state.openSlices[state.openSlices.length - 1]; |
275 parentSlice.slice.subSlices.push(slice); | 299 parentSlice.slice.subSlices.push(slice); |
276 } | 300 } |
277 } | 301 } |
278 } | 302 } |
(...skipping 11 matching lines...) Expand all Loading... | |
290 processBegin(state, event); | 314 processBegin(state, event); |
291 } else if (event.ph == 'E') { | 315 } else if (event.ph == 'E') { |
292 processEnd(state, event); | 316 processEnd(state, event); |
293 } else if (event.ph == 'I') { | 317 } else if (event.ph == 'I') { |
294 // Treat an Instant event as a duration 0 slice. | 318 // Treat an Instant event as a duration 0 slice. |
295 // TimelineSliceTrack's redraw() knows how to handle this. | 319 // TimelineSliceTrack's redraw() knows how to handle this. |
296 processBegin(state, event); | 320 processBegin(state, event); |
297 processEnd(state, event); | 321 processEnd(state, event); |
298 } else if (event.ph == 'M') { | 322 } else if (event.ph == 'M') { |
299 if (event.name == 'thread_name') { | 323 if (event.name == 'thread_name') { |
300 var thread = this.getProcess(event.pid).getThread(event.tid); | 324 var thread = this.getOrCreateProcess(event.pid) |
325 .getOrCreateThread(event.tid); | |
301 thread.name = event.args.name; | 326 thread.name = event.args.name; |
302 } else { | 327 } else { |
303 this.importErrors.push('Unrecognized metadata name: ' + event.name); | 328 this.importErrors.push('Unrecognized metadata name: ' + event.name); |
304 } | 329 } |
305 } else { | 330 } else { |
306 this.importErrors.push('Unrecognized event phase: ' + event.ph + | 331 this.importErrors.push('Unrecognized event phase: ' + event.ph + |
307 '(' + event.name + ')'); | 332 '(' + event.name + ')'); |
308 } | 333 } |
309 } | 334 } |
310 this.pruneEmptyThreads(); | 335 this.pruneEmptyThreads(); |
311 this.updateBounds(); | 336 this.updateBounds(); |
312 | 337 |
313 // Add end events for any events that are still on the stack. These | 338 // Add end events for any events that are still on the stack. These |
314 // are events that were still open when trace was ended, and can often | 339 // are events that were still open when trace was ended, and can often |
315 // indicate deadlock behavior. | 340 // indicate deadlock behavior. |
316 for (var ptid in threadStateByPTID) { | 341 for (var ptid in threadStateByPTID) { |
317 var state = threadStateByPTID[ptid]; | 342 var state = threadStateByPTID[ptid]; |
318 while (state.openSlices.length > 0) { | 343 while (state.openSlices.length > 0) { |
319 var slice = state.openSlices.pop(); | 344 var slice = state.openSlices.pop(); |
320 slice.slice.duration = this.maxTimestamp - slice.slice.start; | 345 slice.slice.duration = this.maxTimestamp - slice.slice.start; |
321 slice.slice.didNotFinish = true; | 346 slice.slice.didNotFinish = true; |
322 var event = events[slice.index]; | 347 var event = events[slice.index]; |
323 | 348 |
324 // Store the slice on the correct subrow. | 349 // Store the slice on the correct subrow. |
325 var thread = this.getProcess(event.pid).getThread(event.tid); | 350 var thread = this.getOrCreateProcess(event.pid) |
351 .getOrCreateThread(event.tid); | |
326 var subRowIndex = state.openSlices.length; | 352 var subRowIndex = state.openSlices.length; |
327 thread.getSubrow(subRowIndex).push(slice.slice); | 353 thread.getSubrow(subRowIndex).push(slice.slice); |
328 | 354 |
329 // Add the slice to the subSlices array of its parent. | 355 // Add the slice to the subSlices array of its parent. |
330 if (state.openSlices.length) { | 356 if (state.openSlices.length) { |
331 var parentSlice = state.openSlices[state.openSlices.length - 1]; | 357 var parentSlice = state.openSlices[state.openSlices.length - 1]; |
332 parentSlice.slice.subSlices.push(slice.slice); | 358 parentSlice.slice.subSlices.push(slice.slice); |
333 } | 359 } |
334 } | 360 } |
335 } | 361 } |
336 | 362 |
337 this.shiftWorldToMicroseconds(); | 363 this.shiftWorldToMicroseconds(); |
338 | 364 |
339 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; | 365 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; |
340 this.minTimestamp = this.minTimestamp - boost; | 366 this.minTimestamp = this.minTimestamp - boost; |
341 this.maxTimestamp = this.maxTimestamp + boost; | 367 this.maxTimestamp = this.maxTimestamp + boost; |
342 }, | 368 }, |
343 | 369 |
344 /** | 370 /** |
345 * Removes threads from the model that have no subrows. | 371 * Removes threads from the model that have no subrows. |
346 */ | 372 */ |
347 pruneEmptyThreads: function() { | 373 pruneEmptyThreads: function() { |
348 for (var pid in this.processes) { | 374 for (var pid in this.processes) { |
349 var process = this.processes[pid]; | 375 var process = this.processes[pid]; |
350 var prunedThreads = []; | 376 var prunedThreads = {}; |
351 for (var tid in process.threads) { | 377 for (var tid in process.threads) { |
352 var thread = process.threads[tid]; | 378 var thread = process.threads[tid]; |
353 if (thread.subRows[0].length || thread.nonNestedSubRows.legnth) | 379 if (thread.subRows[0].length || thread.nonNestedSubRows.legnth) |
354 prunedThreads.push(thread); | 380 prunedThreads[tid] = thread; |
355 } | 381 } |
356 process.threads = prunedThreads; | 382 process.threads = prunedThreads; |
357 } | 383 } |
358 }, | 384 }, |
359 | 385 |
360 updateBounds: function() { | 386 updateBounds: function() { |
361 var wmin = Infinity; | 387 var wmin = Infinity; |
362 var wmax = -wmin; | 388 var wmax = -wmin; |
363 var threads = this.getAllThreads(); | 389 var threads = this.getAllThreads(); |
364 for (var tI = 0; tI < threads.length; tI++) { | 390 for (var tI = 0; tI < threads.length; tI++) { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
404 for (var tid in process.threads) { | 430 for (var tid in process.threads) { |
405 threads.push(process.threads[tid]); | 431 threads.push(process.threads[tid]); |
406 } | 432 } |
407 } | 433 } |
408 return threads; | 434 return threads; |
409 } | 435 } |
410 | 436 |
411 }; | 437 }; |
412 | 438 |
413 return { | 439 return { |
440 getStringHash: getStringHash, | |
414 TimelineSlice: TimelineSlice, | 441 TimelineSlice: TimelineSlice, |
415 TimelineThread: TimelineThread, | 442 TimelineThread: TimelineThread, |
416 TimelineProcess: TimelineProcess, | 443 TimelineProcess: TimelineProcess, |
417 TimelineModel: TimelineModel | 444 TimelineModel: TimelineModel |
418 }; | 445 }; |
419 }); | 446 }); |
OLD | NEW |