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