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 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, | |
|
James Hawkins
2011/08/01 18:28:49
Is this intended to be private?
nduca
2011/08/01 20:52:22
Nope, its public. Should I be exposing this via a
James Hawkins
2011/08/01 21:01:24
Hmm, likely though I won't ask you to do that. It'
| |
| 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 |
| 86 updateBounds: function() { | 91 updateBounds: function() { |
|
James Hawkins
2011/08/01 18:28:49
Document this method.
nduca
2011/08/01 20:52:22
Done.
| |
| 87 var slices = this.subRows[0]; | 92 var values = []; |
| 88 if (slices.length != 0) { | 93 var slices; |
| 89 this.minTimestamp = slices[0].start; | 94 if (this.subRows[0].length != 0) { |
| 90 this.maxTimestamp = slices[slices.length - 1].end; | 95 slices = this.subRows[0]; |
| 96 values.push(slices[0].start); | |
| 97 values.push(slices[slices.length - 1].end); | |
| 98 } | |
| 99 for (var i = 0; i < this.nonNestedSubRows.length; ++i) { | |
| 100 slices = this.nonNestedSubRows[i]; | |
| 101 values.push(slices[0].start); | |
| 102 values.push(slices[slices.length - 1].end); | |
| 103 } | |
| 104 if (values.length) { | |
| 105 this.minTimestamp = Math.min.apply(Math, values); | |
| 106 this.maxTimestamp = Math.max.apply(Math, values); | |
| 91 } else { | 107 } else { |
| 92 this.minTimestamp = undefined; | 108 this.minTimestamp = undefined; |
| 93 this.maxTimestamp = undefined; | 109 this.maxTimestamp = undefined; |
| 94 } | 110 } |
| 95 } | 111 } |
| 96 | 112 |
| 97 }; | 113 }; |
| 114 /** | |
|
James Hawkins
2011/08/01 18:28:49
Add blank line between methods.
nduca
2011/08/01 20:52:22
Done.
| |
| 115 * Comparison between threads that orders first by pid, | |
| 116 * then by names, then by tid. | |
| 117 */ | |
| 118 TimelineThread.compare = function(x,y) { | |
| 119 if(x.parent.pid == y.parent.pid) { | |
|
James Hawkins
2011/08/01 18:28:49
You could save a lot of indentation by reversing t
James Hawkins
2011/08/01 18:28:49
Space after if.
nduca
2011/08/01 20:52:22
Done.
| |
| 120 if (x.name && y.name) { | |
| 121 var tmp = x.name.localeCompare(y.name); | |
| 122 if (tmp == 0) | |
| 123 return x.tid - y.tid; | |
| 124 return tmp; | |
| 125 } else if(x.name) { | |
| 126 return -1; | |
| 127 } else if(y.name){ | |
| 128 return 1; | |
| 129 } else { | |
| 130 return x.tid - y.tid; | |
| 131 } | |
| 132 } else { | |
| 133 return x.parent.pid - y.parent.pid; | |
| 134 } | |
| 135 }; | |
| 136 | |
| 98 | 137 |
| 99 /** | 138 /** |
| 100 * The TimelineProcess represents a single process in the | 139 * The TimelineProcess represents a single process in the |
| 101 * trace. Right now, we keep this around purely for bookkeeping | 140 * trace. Right now, we keep this around purely for bookkeeping |
| 102 * reasons. | 141 * reasons. |
| 103 * @constructor | 142 * @constructor |
| 104 */ | 143 */ |
| 105 function TimelineProcess(pid) { | 144 function TimelineProcess(pid) { |
| 106 this.pid = pid; | 145 this.pid = pid; |
| 107 this.threads = {}; | 146 this.threads = {}; |
| 108 }; | 147 }; |
| 109 | 148 |
| 110 TimelineProcess.prototype = { | 149 TimelineProcess.prototype = { |
| 111 getThread: function(tid) { | 150 getThread: function(tid) { |
| 112 if (!this.threads[tid]) | 151 if (!this.threads[tid]) |
| 113 this.threads[tid] = new TimelineThread(this, tid); | 152 this.threads[tid] = new TimelineThread(this, tid); |
| 114 return this.threads[tid]; | 153 return this.threads[tid]; |
| 115 } | 154 } |
| 116 }; | 155 }; |
| 117 | 156 |
| 118 /** | 157 /** |
| 119 * Builds a model from an array of TraceEvent objects. | 158 * Builds a model from an array of TraceEvent objects. |
| 120 * @param {Array} events An array of TraceEvents created by | 159 * @param {Array} events An array of TraceEvents created by |
| 121 * TraceEvent.ToJSON(). | 160 * TraceEvent.ToJSON(). |
| 122 * @constructor | 161 * @constructor |
| 123 */ | 162 */ |
| 124 function TimelineModel(events) { | 163 function TimelineModel(events) { |
| 125 this.processes = {}; | 164 this.processes = {}; |
| 165 this.importErrors = []; | |
| 126 | 166 |
| 127 if (events) | 167 if (events) |
| 128 this.importEvents(events); | 168 this.importEvents(events); |
| 129 } | 169 } |
| 130 | 170 |
| 131 TimelineModel.prototype = { | 171 TimelineModel.prototype = { |
| 132 __proto__: cr.EventTarget.prototype, | 172 __proto__: cr.EventTarget.prototype, |
| 133 | 173 |
| 134 getProcess: function(pid) { | 174 getProcess: function(pid) { |
| 135 if (!this.processes[pid]) | 175 if (!this.processes[pid]) |
| 136 this.processes[pid] = new TimelineProcess(pid); | 176 this.processes[pid] = new TimelineProcess(pid); |
| 137 return this.processes[pid]; | 177 return this.processes[pid]; |
| 138 }, | 178 }, |
| 139 | 179 |
| 140 /** | 180 /** |
| 141 * The import takes an array of json-ified TraceEvents and adds them into | 181 * The import takes an array of json-ified TraceEvents and adds them into |
| 142 * the TimelineModel as processes, threads, and slices. | 182 * the TimelineModel as processes, threads, and slices. |
| 143 */ | 183 */ |
| 144 importEvents: function(events) { | 184 importEvents: function(events) { |
| 145 // A ptid is a pid and tid joined together x:y fashion, eg 1024:130 | 185 // 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. | 186 // The ptid is a unique key for a thread in the trace. |
| 147 | 187 this.importErrors = []; |
| 148 | 188 |
| 149 // Threadstate | 189 // Threadstate |
| 150 const numColorIds = 30; | 190 const numColorIds = 30; |
| 151 function ThreadState(tid) { | 191 function ThreadState(tid) { |
| 152 this.openSlices = []; | 192 this.openSlices = []; |
| 153 this.openNonNestedSlices = {}; | 193 this.openNonNestedSlices = {}; |
| 154 } | 194 } |
| 155 var threadStateByPTID = {}; | 195 var threadStateByPTID = {}; |
| 156 | 196 |
| 157 var nameToColorMap = {}; | 197 var nameToColorMap = {}; |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 181 var slice = | 221 var slice = |
| 182 { index: eI, | 222 { index: eI, |
| 183 slice: new TimelineSlice(event.name, colorId, event.ts, | 223 slice: new TimelineSlice(event.name, colorId, event.ts, |
| 184 event.args) }; | 224 event.args) }; |
| 185 if (event.args['ui-nest'] === '0') { | 225 if (event.args['ui-nest'] === '0') { |
| 186 var sliceID = event.name; | 226 var sliceID = event.name; |
| 187 for (var x in event.args) { | 227 for (var x in event.args) { |
| 188 sliceID += ';' + event.args[x]; | 228 sliceID += ';' + event.args[x]; |
| 189 } | 229 } |
| 190 if (state.openNonNestedSlices[sliceID]) | 230 if (state.openNonNestedSlices[sliceID]) |
| 191 console.log('Event ' + sliceID + ' already open.'); | 231 this.importErrors.push('Event ' + sliceID + ' already open.'); |
| 192 state.openNonNestedSlices[sliceID] = slice; | 232 state.openNonNestedSlices[sliceID] = slice; |
| 193 } else | 233 } else |
| 194 state.openSlices.push(slice); | 234 state.openSlices.push(slice); |
| 195 } else if (event.ph == 'E') { | 235 } else if (event.ph == 'E') { |
| 196 if (event.args['ui-nest'] === '0') { | 236 if (event.args['ui-nest'] === '0') { |
| 197 var sliceID = event.name; | 237 var sliceID = event.name; |
| 198 for (var x in event.args) { | 238 for (var x in event.args) { |
| 199 sliceID += ';' + event.args[x]; | 239 sliceID += ';' + event.args[x]; |
| 200 } | 240 } |
| 201 var slice = state.openNonNestedSlices[sliceID]; | 241 var slice = state.openNonNestedSlices[sliceID]; |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 222 | 262 |
| 223 // Add the slice to the subSlices array of its parent. | 263 // Add the slice to the subSlices array of its parent. |
| 224 if (state.openSlices.length) { | 264 if (state.openSlices.length) { |
| 225 var parentSlice = state.openSlices[state.openSlices.length - 1]; | 265 var parentSlice = state.openSlices[state.openSlices.length - 1]; |
| 226 parentSlice.slice.subSlices.push(slice); | 266 parentSlice.slice.subSlices.push(slice); |
| 227 } | 267 } |
| 228 } | 268 } |
| 229 } else if (event.ph == 'I') { | 269 } else if (event.ph == 'I') { |
| 230 // TODO(nduca): Implement parsing of immediate events. | 270 // TODO(nduca): Implement parsing of immediate events. |
| 231 console.log('Parsing of I-type events not implemented.'); | 271 console.log('Parsing of I-type events not implemented.'); |
| 272 } else if (event.ph == 'M') { | |
| 273 if (event.name == 'thread_name') { | |
| 274 var thread = this.getProcess(event.pid).getThread(event.tid); | |
| 275 thread.name = event.args.name; | |
| 276 } else { | |
| 277 this.importErrors.push('Unrecognized metadata name: ' + event.name); | |
| 278 } | |
| 232 } else { | 279 } else { |
| 233 throw new Error('Unrecognized event phase: ' + event.ph + | 280 this.importErrors.push('Unrecognized event phase: ' + event.ph + |
| 234 '(' + event.name + ')'); | 281 '(' + event.name + ')'); |
| 235 } | 282 } |
| 236 } | 283 } |
| 237 | 284 this.pruneEmptyThreads(); |
| 238 this.updateBounds(); | 285 this.updateBounds(); |
| 239 | 286 |
| 240 // Add end events for any events that are still on the stack. These | 287 // Add end events for any events that are still on the stack. These |
| 241 // are events that were still open when trace was ended, and can often | 288 // are events that were still open when trace was ended, and can often |
| 242 // indicate deadlock behavior. | 289 // indicate deadlock behavior. |
| 243 for (var ptid in threadStateByPTID) { | 290 for (var ptid in threadStateByPTID) { |
| 244 var state = threadStateByPTID[ptid]; | 291 var state = threadStateByPTID[ptid]; |
| 245 while (state.openSlices.length > 0) { | 292 while (state.openSlices.length > 0) { |
| 246 var slice = state.openSlices.pop(); | 293 var slice = state.openSlices.pop(); |
| 247 slice.slice.duration = this.maxTimestamp - slice.slice.start; | 294 slice.slice.duration = this.maxTimestamp - slice.slice.start; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 261 } | 308 } |
| 262 } | 309 } |
| 263 | 310 |
| 264 this.shiftWorldToMicroseconds(); | 311 this.shiftWorldToMicroseconds(); |
| 265 | 312 |
| 266 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; | 313 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; |
| 267 this.minTimestamp = this.minTimestamp - boost; | 314 this.minTimestamp = this.minTimestamp - boost; |
| 268 this.maxTimestamp = this.maxTimestamp + boost; | 315 this.maxTimestamp = this.maxTimestamp + boost; |
| 269 }, | 316 }, |
| 270 | 317 |
| 318 /** | |
| 319 * Removes threads from the model that have no subrows. | |
| 320 */ | |
| 321 pruneEmptyThreads: function() { | |
| 322 for (var pid in this.processes) { | |
| 323 var process = this.processes[pid]; | |
| 324 var prunedThreads = []; | |
| 325 for (var tid in process.threads) { | |
| 326 var thread = process.threads[tid]; | |
| 327 if (thread.subRows[0].length || thread.nonNestedSubRows.legnth) | |
| 328 prunedThreads.push(thread); | |
| 329 } | |
| 330 process.threads = prunedThreads; | |
| 331 } | |
| 332 }, | |
| 333 | |
| 271 updateBounds: function() { | 334 updateBounds: function() { |
| 272 var wmin = Infinity; | 335 var wmin = Infinity; |
| 273 var wmax = -wmin; | 336 var wmax = -wmin; |
| 274 var threads = this.getAllThreads(); | 337 var threads = this.getAllThreads(); |
| 275 for (var tI = 0; tI < threads.length; tI++) { | 338 for (var tI = 0; tI < threads.length; tI++) { |
| 276 var thread = threads[tI]; | 339 var thread = threads[tI]; |
| 277 thread.updateBounds(); | 340 thread.updateBounds(); |
| 278 if (thread.minTimestamp && thread.maxTimestamp) { | 341 if (thread.minTimestamp != undefined && |
| 342 thread.maxTimestamp != undefined) { | |
| 279 wmin = Math.min(wmin, thread.minTimestamp); | 343 wmin = Math.min(wmin, thread.minTimestamp); |
| 280 wmax = Math.max(wmax, thread.maxTimestamp); | 344 wmax = Math.max(wmax, thread.maxTimestamp); |
| 281 } | 345 } |
| 282 } | 346 } |
| 283 this.minTimestamp = wmin; | 347 this.minTimestamp = wmin; |
| 284 this.maxTimestamp = wmax; | 348 this.maxTimestamp = wmax; |
| 285 }, | 349 }, |
| 286 | 350 |
| 287 shiftWorldToMicroseconds: function() { | 351 shiftWorldToMicroseconds: function() { |
| 288 var timeBase = this.minTimestamp; | 352 var timeBase = this.minTimestamp; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 320 | 384 |
| 321 }; | 385 }; |
| 322 | 386 |
| 323 return { | 387 return { |
| 324 TimelineSlice: TimelineSlice, | 388 TimelineSlice: TimelineSlice, |
| 325 TimelineThread: TimelineThread, | 389 TimelineThread: TimelineThread, |
| 326 TimelineProcess: TimelineProcess, | 390 TimelineProcess: TimelineProcess, |
| 327 TimelineModel: TimelineModel | 391 TimelineModel: TimelineModel |
| 328 }; | 392 }; |
| 329 }); | 393 }); |
| OLD | NEW |