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 |