Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(811)

Side by Side Diff: chrome/browser/resources/tracing/timeline_model.js

Issue 8342069: Add unit tests for about:tracing TimelineModel. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Just use jstest for now. Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698