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 23 matching lines...) Expand all Loading... |
181 var slice = | 226 var slice = |
182 { index: eI, | 227 { index: eI, |
183 slice: new TimelineSlice(event.name, colorId, event.ts, | 228 slice: new TimelineSlice(event.name, colorId, event.ts, |
184 event.args) }; | 229 event.args) }; |
185 if (event.args['ui-nest'] === '0') { | 230 if (event.args['ui-nest'] === '0') { |
186 var sliceID = event.name; | 231 var sliceID = event.name; |
187 for (var x in event.args) { | 232 for (var x in event.args) { |
188 sliceID += ';' + event.args[x]; | 233 sliceID += ';' + event.args[x]; |
189 } | 234 } |
190 if (state.openNonNestedSlices[sliceID]) | 235 if (state.openNonNestedSlices[sliceID]) |
191 console.log('Event ' + sliceID + ' already open.'); | 236 this.importErrors.push('Event ' + sliceID + ' already open.'); |
192 state.openNonNestedSlices[sliceID] = slice; | 237 state.openNonNestedSlices[sliceID] = slice; |
193 } else | 238 } else |
194 state.openSlices.push(slice); | 239 state.openSlices.push(slice); |
195 } else if (event.ph == 'E') { | 240 } else if (event.ph == 'E') { |
196 if (event.args['ui-nest'] === '0') { | 241 if (event.args['ui-nest'] === '0') { |
197 var sliceID = event.name; | 242 var sliceID = event.name; |
198 for (var x in event.args) { | 243 for (var x in event.args) { |
199 sliceID += ';' + event.args[x]; | 244 sliceID += ';' + event.args[x]; |
200 } | 245 } |
201 var slice = state.openNonNestedSlices[sliceID]; | 246 var slice = state.openNonNestedSlices[sliceID]; |
(...skipping 20 matching lines...) Expand all Loading... |
222 | 267 |
223 // Add the slice to the subSlices array of its parent. | 268 // Add the slice to the subSlices array of its parent. |
224 if (state.openSlices.length) { | 269 if (state.openSlices.length) { |
225 var parentSlice = state.openSlices[state.openSlices.length - 1]; | 270 var parentSlice = state.openSlices[state.openSlices.length - 1]; |
226 parentSlice.slice.subSlices.push(slice); | 271 parentSlice.slice.subSlices.push(slice); |
227 } | 272 } |
228 } | 273 } |
229 } else if (event.ph == 'I') { | 274 } else if (event.ph == 'I') { |
230 // TODO(nduca): Implement parsing of immediate events. | 275 // TODO(nduca): Implement parsing of immediate events. |
231 console.log('Parsing of I-type events not implemented.'); | 276 console.log('Parsing of I-type events not implemented.'); |
| 277 } else if (event.ph == 'M') { |
| 278 if (event.name == 'thread_name') { |
| 279 var thread = this.getProcess(event.pid).getThread(event.tid); |
| 280 thread.name = event.args.name; |
| 281 } else { |
| 282 this.importErrors.push('Unrecognized metadata name: ' + event.name); |
| 283 } |
232 } else { | 284 } else { |
233 throw new Error('Unrecognized event phase: ' + event.ph + | 285 this.importErrors.push('Unrecognized event phase: ' + event.ph + |
234 '(' + event.name + ')'); | 286 '(' + event.name + ')'); |
235 } | 287 } |
236 } | 288 } |
237 | 289 this.pruneEmptyThreads(); |
238 this.updateBounds(); | 290 this.updateBounds(); |
239 | 291 |
240 // Add end events for any events that are still on the stack. These | 292 // 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 | 293 // are events that were still open when trace was ended, and can often |
242 // indicate deadlock behavior. | 294 // indicate deadlock behavior. |
243 for (var ptid in threadStateByPTID) { | 295 for (var ptid in threadStateByPTID) { |
244 var state = threadStateByPTID[ptid]; | 296 var state = threadStateByPTID[ptid]; |
245 while (state.openSlices.length > 0) { | 297 while (state.openSlices.length > 0) { |
246 var slice = state.openSlices.pop(); | 298 var slice = state.openSlices.pop(); |
247 slice.slice.duration = this.maxTimestamp - slice.slice.start; | 299 slice.slice.duration = this.maxTimestamp - slice.slice.start; |
(...skipping 13 matching lines...) Expand all Loading... |
261 } | 313 } |
262 } | 314 } |
263 | 315 |
264 this.shiftWorldToMicroseconds(); | 316 this.shiftWorldToMicroseconds(); |
265 | 317 |
266 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; | 318 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; |
267 this.minTimestamp = this.minTimestamp - boost; | 319 this.minTimestamp = this.minTimestamp - boost; |
268 this.maxTimestamp = this.maxTimestamp + boost; | 320 this.maxTimestamp = this.maxTimestamp + boost; |
269 }, | 321 }, |
270 | 322 |
| 323 /** |
| 324 * Removes threads from the model that have no subrows. |
| 325 */ |
| 326 pruneEmptyThreads: function() { |
| 327 for (var pid in this.processes) { |
| 328 var process = this.processes[pid]; |
| 329 var prunedThreads = []; |
| 330 for (var tid in process.threads) { |
| 331 var thread = process.threads[tid]; |
| 332 if (thread.subRows[0].length || thread.nonNestedSubRows.legnth) |
| 333 prunedThreads.push(thread); |
| 334 } |
| 335 process.threads = prunedThreads; |
| 336 } |
| 337 }, |
| 338 |
271 updateBounds: function() { | 339 updateBounds: function() { |
272 var wmin = Infinity; | 340 var wmin = Infinity; |
273 var wmax = -wmin; | 341 var wmax = -wmin; |
274 var threads = this.getAllThreads(); | 342 var threads = this.getAllThreads(); |
275 for (var tI = 0; tI < threads.length; tI++) { | 343 for (var tI = 0; tI < threads.length; tI++) { |
276 var thread = threads[tI]; | 344 var thread = threads[tI]; |
277 thread.updateBounds(); | 345 thread.updateBounds(); |
278 if (thread.minTimestamp && thread.maxTimestamp) { | 346 if (thread.minTimestamp != undefined && |
| 347 thread.maxTimestamp != undefined) { |
279 wmin = Math.min(wmin, thread.minTimestamp); | 348 wmin = Math.min(wmin, thread.minTimestamp); |
280 wmax = Math.max(wmax, thread.maxTimestamp); | 349 wmax = Math.max(wmax, thread.maxTimestamp); |
281 } | 350 } |
282 } | 351 } |
283 this.minTimestamp = wmin; | 352 this.minTimestamp = wmin; |
284 this.maxTimestamp = wmax; | 353 this.maxTimestamp = wmax; |
285 }, | 354 }, |
286 | 355 |
287 shiftWorldToMicroseconds: function() { | 356 shiftWorldToMicroseconds: function() { |
288 var timeBase = this.minTimestamp; | 357 var timeBase = this.minTimestamp; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
320 | 389 |
321 }; | 390 }; |
322 | 391 |
323 return { | 392 return { |
324 TimelineSlice: TimelineSlice, | 393 TimelineSlice: TimelineSlice, |
325 TimelineThread: TimelineThread, | 394 TimelineThread: TimelineThread, |
326 TimelineProcess: TimelineProcess, | 395 TimelineProcess: TimelineProcess, |
327 TimelineModel: TimelineModel | 396 TimelineModel: TimelineModel |
328 }; | 397 }; |
329 }); | 398 }); |
OLD | NEW |