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

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: . 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
« no previous file with comments | « no previous file | chrome/browser/resources/tracing/timeline_model_test.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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) {
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
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
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
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 });
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/resources/tracing/timeline_model_test.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698