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

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

Issue 7495031: trace_event support for thread names (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Handle thread id repeats, fix locking, clarify name lifetime. Created 9 years, 4 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 | 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 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 20 matching lines...) Expand all
178 var colorId = getColor(event.name); 223 var colorId = getColor(event.name);
179 var slice = 224 var slice =
180 { index: eI, 225 { index: eI,
181 slice: new TimelineSlice(event.name, colorId, event.ts, 226 slice: new TimelineSlice(event.name, colorId, event.ts,
182 event.args) }; 227 event.args) };
183 if (event.args['ui-nest'] === '0') { 228 if (event.args['ui-nest'] === '0') {
184 var sliceID = event.name; 229 var sliceID = event.name;
185 for (var x in event.args) 230 for (var x in event.args)
186 sliceID += ';' + event.args[x]; 231 sliceID += ';' + event.args[x];
187 if (state.openNonNestedSlices[sliceID]) 232 if (state.openNonNestedSlices[sliceID])
188 console.log('Event ' + sliceID + ' already open.'); 233 this.importErrors.push('Event ' + sliceID + ' already open.');
189 state.openNonNestedSlices[sliceID] = slice; 234 state.openNonNestedSlices[sliceID] = slice;
190 } else { 235 } else {
191 state.openSlices.push(slice); 236 state.openSlices.push(slice);
192 } 237 }
193 } 238 }
194 239
195 /** 240 /**
196 * Helper to process an 'end' event (e.g. close a slice). 241 * Helper to process an 'end' event (e.g. close a slice).
197 * @param {ThreadState} state Thread state (holds slices). 242 * @param {ThreadState} state Thread state (holds slices).
198 * @param {Object} event The current trace event. 243 * @param {Object} event The current trace event.
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 288
244 if (event.ph == 'B') { 289 if (event.ph == 'B') {
245 processBegin(state, event); 290 processBegin(state, event);
246 } else if (event.ph == 'E') { 291 } else if (event.ph == 'E') {
247 processEnd(state, event); 292 processEnd(state, event);
248 } else if (event.ph == 'I') { 293 } else if (event.ph == 'I') {
249 // Treat an Instant event as a duration 0 slice. 294 // Treat an Instant event as a duration 0 slice.
250 // TimelineSliceTrack's redraw() knows how to handle this. 295 // TimelineSliceTrack's redraw() knows how to handle this.
251 processBegin(state, event); 296 processBegin(state, event);
252 processEnd(state, event); 297 processEnd(state, event);
298 } else if (event.ph == 'M') {
299 if (event.name == 'thread_name') {
300 var thread = this.getProcess(event.pid).getThread(event.tid);
301 thread.name = event.args.name;
302 } else {
303 this.importErrors.push('Unrecognized metadata name: ' + event.name);
304 }
253 } else { 305 } else {
254 throw new Error('Unrecognized event phase: ' + event.ph + 306 this.importErrors.push('Unrecognized event phase: ' + event.ph +
255 '(' + event.name + ')'); 307 '(' + event.name + ')');
256 } 308 }
257 } 309 }
258 310 this.pruneEmptyThreads();
259 this.updateBounds(); 311 this.updateBounds();
260 312
261 // Add end events for any events that are still on the stack. These 313 // Add end events for any events that are still on the stack. These
262 // are events that were still open when trace was ended, and can often 314 // are events that were still open when trace was ended, and can often
263 // indicate deadlock behavior. 315 // indicate deadlock behavior.
264 for (var ptid in threadStateByPTID) { 316 for (var ptid in threadStateByPTID) {
265 var state = threadStateByPTID[ptid]; 317 var state = threadStateByPTID[ptid];
266 while (state.openSlices.length > 0) { 318 while (state.openSlices.length > 0) {
267 var slice = state.openSlices.pop(); 319 var slice = state.openSlices.pop();
268 slice.slice.duration = this.maxTimestamp - slice.slice.start; 320 slice.slice.duration = this.maxTimestamp - slice.slice.start;
(...skipping 13 matching lines...) Expand all
282 } 334 }
283 } 335 }
284 336
285 this.shiftWorldToMicroseconds(); 337 this.shiftWorldToMicroseconds();
286 338
287 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15; 339 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15;
288 this.minTimestamp = this.minTimestamp - boost; 340 this.minTimestamp = this.minTimestamp - boost;
289 this.maxTimestamp = this.maxTimestamp + boost; 341 this.maxTimestamp = this.maxTimestamp + boost;
290 }, 342 },
291 343
344 /**
345 * Removes threads from the model that have no subrows.
346 */
347 pruneEmptyThreads: function() {
348 for (var pid in this.processes) {
349 var process = this.processes[pid];
350 var prunedThreads = [];
351 for (var tid in process.threads) {
352 var thread = process.threads[tid];
353 if (thread.subRows[0].length || thread.nonNestedSubRows.legnth)
354 prunedThreads.push(thread);
355 }
356 process.threads = prunedThreads;
357 }
358 },
359
292 updateBounds: function() { 360 updateBounds: function() {
293 var wmin = Infinity; 361 var wmin = Infinity;
294 var wmax = -wmin; 362 var wmax = -wmin;
295 var threads = this.getAllThreads(); 363 var threads = this.getAllThreads();
296 for (var tI = 0; tI < threads.length; tI++) { 364 for (var tI = 0; tI < threads.length; tI++) {
297 var thread = threads[tI]; 365 var thread = threads[tI];
298 thread.updateBounds(); 366 thread.updateBounds();
299 if (thread.minTimestamp && thread.maxTimestamp) { 367 if (thread.minTimestamp != undefined &&
368 thread.maxTimestamp != undefined) {
300 wmin = Math.min(wmin, thread.minTimestamp); 369 wmin = Math.min(wmin, thread.minTimestamp);
301 wmax = Math.max(wmax, thread.maxTimestamp); 370 wmax = Math.max(wmax, thread.maxTimestamp);
302 } 371 }
303 } 372 }
304 this.minTimestamp = wmin; 373 this.minTimestamp = wmin;
305 this.maxTimestamp = wmax; 374 this.maxTimestamp = wmax;
306 }, 375 },
307 376
308 shiftWorldToMicroseconds: function() { 377 shiftWorldToMicroseconds: function() {
309 var timeBase = this.minTimestamp; 378 var timeBase = this.minTimestamp;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 410
342 }; 411 };
343 412
344 return { 413 return {
345 TimelineSlice: TimelineSlice, 414 TimelineSlice: TimelineSlice,
346 TimelineThread: TimelineThread, 415 TimelineThread: TimelineThread,
347 TimelineProcess: TimelineProcess, 416 TimelineProcess: TimelineProcess,
348 TimelineModel: TimelineModel 417 TimelineModel: TimelineModel
349 }; 418 };
350 }); 419 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698