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

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

Issue 8513009: Add TRACE_COUNTER support to about:tracing (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 1 month 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 | Annotate | Revision Log
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 12 matching lines...) Expand all
23 /** 23 /**
24 * A TimelineSlice represents an interval of time on a given thread 24 * A TimelineSlice represents an interval of time on a given thread
25 * associated with a specific trace event. For example, 25 * associated with a specific trace event. For example,
26 * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms 26 * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
27 * TRACE_EVENT_END() at time=0.3ms 27 * TRACE_EVENT_END() at time=0.3ms
28 * Results in a single timeline slice from 0.1 with duration 0.2. 28 * Results in a single timeline slice from 0.1 with duration 0.2.
29 * 29 *
30 * All time units are stored in milliseconds. 30 * All time units are stored in milliseconds.
31 * @constructor 31 * @constructor
32 */ 32 */
33 function TimelineSlice(title, colorId, start, args) { 33 function TimelineSlice(title, colorId, start, args, opt_duration) {
34 this.title = title; 34 this.title = title;
35 this.start = start; 35 this.start = start;
36 this.colorId = colorId; 36 this.colorId = colorId;
37 this.args = args; 37 this.args = args;
38 this.didNotFinish = false; 38 this.didNotFinish = false;
39 this.subSlices = []; 39 this.subSlices = [];
40 if (opt_duration !== undefined)
41 this.duration = opt_duration;
40 } 42 }
41 43
42 TimelineSlice.prototype = { 44 TimelineSlice.prototype = {
43 selected: false, 45 selected: false,
44 46
45 duration: undefined, 47 duration: undefined,
46 48
47 get end() { 49 get end() {
48 return this.start + this.duration; 50 return this.start + this.duration;
49 } 51 }
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 values.push(slices[0].start); 107 values.push(slices[0].start);
106 values.push(slices[slices.length - 1].end); 108 values.push(slices[slices.length - 1].end);
107 } 109 }
108 if (values.length) { 110 if (values.length) {
109 this.minTimestamp = Math.min.apply(Math, values); 111 this.minTimestamp = Math.min.apply(Math, values);
110 this.maxTimestamp = Math.max.apply(Math, values); 112 this.maxTimestamp = Math.max.apply(Math, values);
111 } else { 113 } else {
112 this.minTimestamp = undefined; 114 this.minTimestamp = undefined;
113 this.maxTimestamp = undefined; 115 this.maxTimestamp = undefined;
114 } 116 }
117 },
118
119 /**
120 * @return {String} A user-friendly name for this thread.
121 */
122 get userFriendlyName() {
123 var tname = this.name || this.tid;
124 return this.parent.pid + ': ' + tname;
125 },
126
127 /**
128 * @return {String} User friendly details about this thread.
129 */
130 get userFriendlyDetials() {
131 return 'pid: ' + this.parent.pid +
132 ', tid: ' + this.tid +
133 (this.name ? ', name: ' + this.name : '');
115 } 134 }
116 135
117 }; 136 };
118 137
119 /** 138 /**
120 * Comparison between threads that orders first by pid, 139 * Comparison between threads that orders first by pid,
121 * then by names, then by tid. 140 * then by names, then by tid.
122 */ 141 */
123 TimelineThread.compare = function(x, y) { 142 TimelineThread.compare = function(x, y) {
124 if (x.parent.pid != y.parent.pid) { 143 if (x.parent.pid != y.parent.pid) {
125 return x.parent.pid - y.parent.pid; 144 return TimelineProcess.compare(x.parent, y.parent.pid);
126 } 145 }
127 146
128 if (x.name && y.name) { 147 if (x.name && y.name) {
129 var tmp = x.name.localeCompare(y.name); 148 var tmp = x.name.localeCompare(y.name);
130 if (tmp == 0) 149 if (tmp == 0)
131 return x.tid - y.tid; 150 return x.tid - y.tid;
132 return tmp; 151 return tmp;
133 } else if (x.name) { 152 } else if (x.name) {
134 return -1; 153 return -1;
135 } else if (y.name) { 154 } else if (y.name) {
136 return 1; 155 return 1;
137 } else { 156 } else {
138 return x.tid - y.tid; 157 return x.tid - y.tid;
139 } 158 }
140 }; 159 };
141 160
161 /**
162 * Stores all the samples for a given counter.
163 * @constructor
164 */
165 function TimelineCounter(parent, name) {
166 this.parent = parent;
167 this.name = name;
168 this.seriesNames = [];
169 this.seriesColors = [];
170 this.timestamps = [];
171 this.samples = [];
172 }
173
174 TimelineCounter.prototype = {
175 __proto__: Object.prototype,
176
177 get numSeries() {
178 return this.seriesNames.length;
179 },
180
181 get numSamples() {
182 return this.timestamps.length;
183 },
184
185 /**
186 * Updates the bounds for this counter based on the samples it contains.
187 */
188 updateBounds: function() {
189 if (this.seriesNames.length != this.seriesColors.length)
190 throw 'seriesNames.length must match seriesColors.length';
191 if (this.numSeries * this.numSamples != this.samples.length)
192 throw 'samples.length must be a multiple of numSamples.';
193
194 this.totals = [];
195 if (this.samples.length == 0) {
196 this.minTimestamp = undefined;
197 this.maxTimestamp = undefined;
198 this.maxTotal = 0;
199 return;
200 }
201 this.minTimestamp = this.timestamps[0];
202 this.maxTimestamp = this.timestamps[this.timestamps.length - 1];
203
204 var N = this.numSeries;
James Hawkins 2011/11/16 17:41:38 s/N/n/
nduca 2011/11/16 19:03:56 Done.
205 var maxTotal = -Infinity;
206 for (var i = 0; i < this.timestamps.length; i++) {
207 var total = 0;
208 for (var j = 0; j < N; j++) {
209 total += this.samples[i * N + j];
210 this.totals.push(total);
211 }
212 if (total > maxTotal)
213 maxTotal = total;
214 }
215 this.maxTotal = maxTotal;
216 }
217
218 };
219
220 /**
221 * Comparison between counters that orders by pid, then name.
222 */
223 TimelineCounter.compare = function(x, y) {
224 if (x.parent.pid != y.parent.pid) {
225 return TimelineProcess.compare(x.parent, y.parent.pid);
226 }
227 var tmp = x.name.localeCompare(y.name);
228 if (tmp == 0)
229 return x.tid - y.tid;
230 return tmp;
231 };
142 232
143 /** 233 /**
144 * The TimelineProcess represents a single process in the 234 * The TimelineProcess represents a single process in the
145 * trace. Right now, we keep this around purely for bookkeeping 235 * trace. Right now, we keep this around purely for bookkeeping
146 * reasons. 236 * reasons.
147 * @constructor 237 * @constructor
148 */ 238 */
149 function TimelineProcess(pid) { 239 function TimelineProcess(pid) {
150 this.pid = pid; 240 this.pid = pid;
151 this.threads = {}; 241 this.threads = {};
242 this.counters = {};
152 }; 243 };
153 244
154 TimelineProcess.prototype = { 245 TimelineProcess.prototype = {
155 get numThreads() { 246 get numThreads() {
156 var n = 0; 247 var n = 0;
157 for (var p in this.threads) { 248 for (var p in this.threads) {
158 n++; 249 n++;
159 } 250 }
160 return n; 251 return n;
161 }, 252 },
162 253
163 getOrCreateThread: function(tid) { 254 getOrCreateThread: function(tid) {
164 if (!this.threads[tid]) 255 if (!this.threads[tid])
165 this.threads[tid] = new TimelineThread(this, tid); 256 this.threads[tid] = new TimelineThread(this, tid);
166 return this.threads[tid]; 257 return this.threads[tid];
258 },
259
260 getOrCreateCounter: function(name) {
James Hawkins 2011/11/16 17:41:38 Document this method.
nduca 2011/11/16 19:03:56 Done.
261 if (!this.counters[name])
262 this.counters[name] = new TimelineCounter(this, name);
263 return this.counters[name];
167 } 264 }
168 }; 265 };
169 266
170 /** 267 /**
268 * Comparison between processes that orders by pid.
269 */
270 TimelineProcess.compare = function(x, y) {
271 return x.pid - y.pid;
272 };
273
274 /**
171 * Computes a simplistic hashcode of the provide name. Used to chose colors 275 * Computes a simplistic hashcode of the provide name. Used to chose colors
172 * for slices. 276 * for slices.
173 * @param {string} name The string to hash. 277 * @param {string} name The string to hash.
174 */ 278 */
175 function getStringHash(name) { 279 function getStringHash(name) {
176 var hash = 0; 280 var hash = 0;
177 for (var i = 0; i < name.length; ++i) 281 for (var i = 0; i < name.length; ++i)
178 hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF; 282 hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF;
179 return hash; 283 return hash;
180 } 284 }
181 285
286 const numColorIds = 30;
James Hawkins 2011/11/16 17:41:38 Document this var.
nduca 2011/11/16 19:03:56 Done.
287 /**
288 * @return {Number} A color ID that is stably associated to the provided via
289 * the getStringHash method.
290 */
291 function getStringColorId(string) {
292 var hash = getStringHash(string);
293 return hash % numColorIds;
294 }
295
182 /** 296 /**
183 * Builds a model from an array of TraceEvent objects. 297 * Builds a model from an array of TraceEvent objects.
184 * @param {Array} events An array of TraceEvents created by 298 * @param {Array} events An array of TraceEvents created by
185 * TraceEvent.ToJSON(). 299 * TraceEvent.ToJSON().
186 * @constructor 300 * @constructor
187 */ 301 */
188 function TimelineModel(events) { 302 function TimelineModel(events) {
189 this.processes = {}; 303 this.processes = {};
190 this.importErrors = []; 304 this.importErrors = [];
191 305
(...skipping 19 matching lines...) Expand all
211 325
212 /** 326 /**
213 * The import takes an array of json-ified TraceEvents and adds them into 327 * The import takes an array of json-ified TraceEvents and adds them into
214 * the TimelineModel as processes, threads, and slices. 328 * the TimelineModel as processes, threads, and slices.
215 */ 329 */
216 importEvents: function(events) { 330 importEvents: function(events) {
217 // A ptid is a pid and tid joined together x:y fashion, eg 1024:130 331 // A ptid is a pid and tid joined together x:y fashion, eg 1024:130
218 // The ptid is a unique key for a thread in the trace. 332 // The ptid is a unique key for a thread in the trace.
219 this.importErrors = []; 333 this.importErrors = [];
220 334
221 // Threadstate 335 // Threadstate.
222 const numColorIds = 30;
223 function ThreadState(tid) { 336 function ThreadState(tid) {
224 this.openSlices = []; 337 this.openSlices = [];
225 this.openNonNestedSlices = {}; 338 this.openNonNestedSlices = {};
226 } 339 }
227 var threadStateByPTID = {}; 340 var threadStateByPTID = {};
228 341
229 var nameToColorMap = {}; 342 var nameToColorMap = {};
230 function getColor(name) { 343 function getColor(name) {
231 if (!(name in nameToColorMap)) { 344 if (!(name in nameToColorMap)) {
232 var hash = getStringHash(name); 345 nameToColorMap[name] = getStringColorId(name);
233 nameToColorMap[name] = hash % numColorIds;
234 } 346 }
235 return nameToColorMap[name]; 347 return nameToColorMap[name];
236 } 348 }
237 349
238 var self = this; 350 var self = this;
239 351
240 /** 352 /**
241 * Helper to process a 'begin' event (e.g. initiate a slice). 353 * Helper to process a 'begin' event (e.g. initiate a slice).
242 * @param {ThreadState} state Thread state (holds slices). 354 * @param {ThreadState} state Thread state (holds slices).
243 * @param {Object} event The current trace event. 355 * @param {Object} event The current trace event.
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 433
322 if (event.ph == 'B') { 434 if (event.ph == 'B') {
323 processBegin(state, event); 435 processBegin(state, event);
324 } else if (event.ph == 'E') { 436 } else if (event.ph == 'E') {
325 processEnd(state, event); 437 processEnd(state, event);
326 } else if (event.ph == 'I') { 438 } else if (event.ph == 'I') {
327 // Treat an Instant event as a duration 0 slice. 439 // Treat an Instant event as a duration 0 slice.
328 // TimelineSliceTrack's redraw() knows how to handle this. 440 // TimelineSliceTrack's redraw() knows how to handle this.
329 processBegin(state, event); 441 processBegin(state, event);
330 processEnd(state, event); 442 processEnd(state, event);
443 } else if (event.ph == 'C') {
444 var ctr = this.getOrCreateProcess(event.pid)
445 .getOrCreateCounter(event.name);
446 // Initialize the counter's series fields if needed.
447 if (ctr.numSeries == 0) {
448 for (var seriesName in event.args) {
449 ctr.seriesNames.push(seriesName);
450 ctr.seriesColors.push(getStringHash(ctr.name + '.' + seriesName));
451 }
452 if (ctr.numSeries == 0) {
453 this.importErrors.push('Expected counter ' + event.name +
454 ' to have at least one argument to use as a value.');
455 // Drop the counter.
456 delete ctr.parent.counters[ctr.name];
457 continue;
458 }
459 }
460
461 // Add the sample values.
462 ctr.timestamps.push(event.ts);
463 for (var i = 0; i < ctr.numSeries; i++) {
464 var seriesName = ctr.seriesNames[i];
465 if (event.args[seriesName] === undefined) {
466 ctr.samples.push(0);
467 continue;
468 }
469 ctr.samples.push(event.args[seriesName]);
470 }
471
331 } else if (event.ph == 'M') { 472 } else if (event.ph == 'M') {
332 if (event.name == 'thread_name') { 473 if (event.name == 'thread_name') {
333 var thread = this.getOrCreateProcess(event.pid) 474 var thread = this.getOrCreateProcess(event.pid)
334 .getOrCreateThread(event.tid); 475 .getOrCreateThread(event.tid);
335 thread.name = event.args.name; 476 thread.name = event.args.name;
336 } else { 477 } else {
337 this.importErrors.push('Unrecognized metadata name: ' + event.name); 478 this.importErrors.push('Unrecognized metadata name: ' + event.name);
338 } 479 }
339 } else { 480 } else {
340 this.importErrors.push('Unrecognized event phase: ' + event.ph + 481 this.importErrors.push('Unrecognized event phase: ' + event.ph +
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 var threads = this.getAllThreads(); 539 var threads = this.getAllThreads();
399 for (var tI = 0; tI < threads.length; tI++) { 540 for (var tI = 0; tI < threads.length; tI++) {
400 var thread = threads[tI]; 541 var thread = threads[tI];
401 thread.updateBounds(); 542 thread.updateBounds();
402 if (thread.minTimestamp != undefined && 543 if (thread.minTimestamp != undefined &&
403 thread.maxTimestamp != undefined) { 544 thread.maxTimestamp != undefined) {
404 wmin = Math.min(wmin, thread.minTimestamp); 545 wmin = Math.min(wmin, thread.minTimestamp);
405 wmax = Math.max(wmax, thread.maxTimestamp); 546 wmax = Math.max(wmax, thread.maxTimestamp);
406 } 547 }
407 } 548 }
549 var counters = this.getAllCounters();
550 for (var tI = 0; tI < counters.length; tI++) {
551 var counter = counters[tI];
552 counter.updateBounds();
553 if (counter.minTimestamp != undefined &&
554 counter.maxTimestamp != undefined) {
555 wmin = Math.min(wmin, counter.minTimestamp);
556 wmax = Math.max(wmax, counter.maxTimestamp);
557 }
558 }
408 this.minTimestamp = wmin; 559 this.minTimestamp = wmin;
409 this.maxTimestamp = wmax; 560 this.maxTimestamp = wmax;
410 }, 561 },
411 562
412 shiftWorldToMicroseconds: function() { 563 shiftWorldToMicroseconds: function() {
413 var timeBase = this.minTimestamp; 564 var timeBase = this.minTimestamp;
414 var threads = this.getAllThreads(); 565 var threads = this.getAllThreads();
415 for (var tI = 0; tI < threads.length; tI++) { 566 for (var tI = 0; tI < threads.length; tI++) {
416 var thread = threads[tI]; 567 var thread = threads[tI];
417 var shiftSubRow = function(subRow) { 568 var shiftSubRow = function(subRow) {
418 for (var tS = 0; tS < subRow.length; tS++) { 569 for (var tS = 0; tS < subRow.length; tS++) {
419 var slice = subRow[tS]; 570 var slice = subRow[tS];
420 slice.start = (slice.start - timeBase) / 1000; 571 slice.start = (slice.start - timeBase) / 1000;
421 slice.duration /= 1000; 572 slice.duration /= 1000;
422 if (slice.startInUserTime) 573 if (slice.startInUserTime)
423 slice.startInUserTime /= 1000; 574 slice.startInUserTime /= 1000;
424 if (slice.durationInUserTime) 575 if (slice.durationInUserTime)
425 slice.durationInUserTime /= 1000; 576 slice.durationInUserTime /= 1000;
426 } 577 }
427 }; 578 };
428 for (var tSR = 0; tSR < thread.subRows.length; tSR++) { 579 for (var tSR = 0; tSR < thread.subRows.length; tSR++) {
429 shiftSubRow(thread.subRows[tSR]); 580 shiftSubRow(thread.subRows[tSR]);
430 } 581 }
431 for (var tSR = 0; tSR < thread.nonNestedSubRows.length; tSR++) { 582 for (var tSR = 0; tSR < thread.nonNestedSubRows.length; tSR++) {
432 shiftSubRow(thread.nonNestedSubRows[tSR]); 583 shiftSubRow(thread.nonNestedSubRows[tSR]);
433 } 584 }
434 } 585 }
435 586 var counters = this.getAllCounters();
587 for (var tI = 0; tI < counters.length; tI++) {
588 var counter = counters[tI];
589 for (var sI = 0; sI < counter.timestamps.length; sI++)
590 counter.timestamps[sI] = (counter.timestamps[sI] - timeBase) / 1000;
591 }
436 this.updateBounds(); 592 this.updateBounds();
437 }, 593 },
438 594
439 getAllThreads: function() { 595 getAllThreads: function() {
440 var threads = []; 596 var threads = [];
441 for (var pid in this.processes) { 597 for (var pid in this.processes) {
442 var process = this.processes[pid]; 598 var process = this.processes[pid];
443 for (var tid in process.threads) { 599 for (var tid in process.threads) {
444 threads.push(process.threads[tid]); 600 threads.push(process.threads[tid]);
445 } 601 }
446 } 602 }
447 return threads; 603 return threads;
604 },
605
606 /**
607 * @return {Array} An array of all the counters in the model.
608 */
609 getAllCounters: function() {
610 var counters = [];
611 for (var pid in this.processes) {
612 var process = this.processes[pid];
613 for (var tid in process.counters) {
614 counters.push(process.counters[tid]);
615 }
616 }
617 return counters;
448 } 618 }
449 619
450 }; 620 };
451 621
452 return { 622 return {
453 getStringHash: getStringHash, 623 getStringHash: getStringHash,
624 getStringColorId: getStringColorId,
454 TimelineSlice: TimelineSlice, 625 TimelineSlice: TimelineSlice,
455 TimelineThread: TimelineThread, 626 TimelineThread: TimelineThread,
627 TimelineCounter: TimelineCounter,
456 TimelineProcess: TimelineProcess, 628 TimelineProcess: TimelineProcess,
457 TimelineModel: TimelineModel 629 TimelineModel: TimelineModel
458 }; 630 };
459 }); 631 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698