Chromium Code Reviews| 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 |