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

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

Issue 7555005: Moving the contents of chrome://gpu Profiling to chrome://tracing. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remove tabs 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
(Empty)
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
3 // found in the LICENSE file.
4
5
6 /**
7 * @fileoverview TimelineModel is a parsed representation of the
8 * TraceEvents obtained from base/trace_event in which the begin-end
9 * tokens are converted into a hierarchy of processes, threads,
10 * subrows, and slices.
11 *
12 * The building block of the model is a slice. A slice is roughly
13 * equivalent to function call executing on a specific thread. As a
14 * result, slices may have one or more subslices.
15 *
16 * A thread contains one or more subrows of slices. Row 0 corresponds to
17 * the "root" slices, e.g. the topmost slices. Row 1 contains slices that
18 * are nested 1 deep in the stack, and so on. We use these subrows to draw
19 * nesting tasks.
20 *
21 */
22 cr.define('gpu', function() {
23 /**
24 * A TimelineSlice represents an interval of time on a given thread
25 * associated with a specific trace event. For example,
26 * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
27 * TRACE_EVENT_END() at time=0.3ms
28 * Results in a single timeline slice from 0.1 with duration 0.2.
29 *
30 * All time units are stored in milliseconds.
31 * @constructor
32 */
33 function TimelineSlice(title, colorId, start, args) {
34 this.title = title;
35 this.start = start;
36 this.colorId = colorId;
37 this.args = args;
38 this.didNotFinish = false;
39 this.subSlices = [];
40 }
41
42 TimelineSlice.prototype = {
43 selected: false,
44
45 duration: undefined,
46
47 get end() {
48 return this.start + this.duration;
49 }
50 };
51
52 /**
53 * A TimelineThread stores all the trace events collected for a particular
54 * thread. We organize the slices on a thread by "subrows," where subrow 0
55 * has all the root slices, subrow 1 those nested 1 deep, and so on. There
56 * is also a set of non-nested subrows.
57 *
58 * @constructor
59 */
60 function TimelineThread(parent, tid) {
61 this.parent = parent;
62 this.tid = tid;
63 this.subRows = [[]];
64 this.nonNestedSubRows = [];
65 }
66
67 TimelineThread.prototype = {
68 getSubrow: function(i) {
69 while (i >= this.subRows.length)
70 this.subRows.push([]);
71 return this.subRows[i];
72 },
73
74 addNonNestedSlice: function(slice) {
75 for (var i = 0; i < this.nonNestedSubRows.length; i++) {
76 var currSubRow = this.nonNestedSubRows[i];
77 var lastSlice = currSubRow[currSubRow.length - 1];
78 if (slice.start >= lastSlice.start + lastSlice.duration) {
79 currSubRow.push(slice);
80 return;
81 }
82 }
83 this.nonNestedSubRows.push([slice]);
84 },
85
86 updateBounds: function() {
87 var slices = this.subRows[0];
88 if (slices.length != 0) {
89 this.minTimestamp = slices[0].start;
90 this.maxTimestamp = slices[slices.length - 1].end;
91 } else {
92 this.minTimestamp = undefined;
93 this.maxTimestamp = undefined;
94 }
95 }
96
97 };
98
99 /**
100 * The TimelineProcess represents a single process in the
101 * trace. Right now, we keep this around purely for bookkeeping
102 * reasons.
103 * @constructor
104 */
105 function TimelineProcess(pid) {
106 this.pid = pid;
107 this.threads = {};
108 };
109
110 TimelineProcess.prototype = {
111 getThread: function(tid) {
112 if (!this.threads[tid])
113 this.threads[tid] = new TimelineThread(this, tid);
114 return this.threads[tid];
115 }
116 };
117
118 /**
119 * Builds a model from an array of TraceEvent objects.
120 * @param {Array} events An array of TraceEvents created by
121 * TraceEvent.ToJSON().
122 * @constructor
123 */
124 function TimelineModel(events) {
125 this.processes = {};
126
127 if (events)
128 this.importEvents(events);
129 }
130
131 TimelineModel.prototype = {
132 __proto__: cr.EventTarget.prototype,
133
134 getProcess: function(pid) {
135 if (!this.processes[pid])
136 this.processes[pid] = new TimelineProcess(pid);
137 return this.processes[pid];
138 },
139
140 /**
141 * The import takes an array of json-ified TraceEvents and adds them into
142 * the TimelineModel as processes, threads, and slices.
143 */
144 importEvents: function(events) {
145 // 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.
147
148
149 // Threadstate
150 const numColorIds = 30;
151 function ThreadState(tid) {
152 this.openSlices = [];
153 this.openNonNestedSlices = {};
154 }
155 var threadStateByPTID = {};
156
157 var nameToColorMap = {};
158 function getColor(name) {
159 if (!(name in nameToColorMap)) {
160 // Compute a simplistic hashcode of the string so we get consistent
161 // coloring across traces.
162 var hash = 0;
163 for (var i = 0; i < name.length; ++i)
164 hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF;
165 nameToColorMap[name] = hash % numColorIds;
166 }
167 return nameToColorMap[name];
168 }
169
170 var self = this;
171
172 /**
173 * Helper to process a 'begin' event (e.g. initiate a slice).
174 * @param {ThreadState} state Thread state (holds slices).
175 * @param {Object} event The current trace event.
176 */
177 function processBegin(state, event) {
178 var colorId = getColor(event.name);
179 var slice =
180 { index: eI,
181 slice: new TimelineSlice(event.name, colorId, event.ts,
182 event.args) };
183 if (event.args['ui-nest'] === '0') {
184 var sliceID = event.name;
185 for (var x in event.args)
186 sliceID += ';' + event.args[x];
187 if (state.openNonNestedSlices[sliceID])
188 console.log('Event ' + sliceID + ' already open.');
189 state.openNonNestedSlices[sliceID] = slice;
190 } else {
191 state.openSlices.push(slice);
192 }
193 }
194
195 /**
196 * Helper to process an 'end' event (e.g. close a slice).
197 * @param {ThreadState} state Thread state (holds slices).
198 * @param {Object} event The current trace event.
199 */
200 function processEnd(state, event) {
201 if (event.args['ui-nest'] === '0') {
202 var sliceID = event.name;
203 for (var x in event.args)
204 sliceID += ';' + event.args[x];
205 var slice = state.openNonNestedSlices[sliceID];
206 if (!slice)
207 return;
208 slice.slice.duration = event.ts - slice.slice.start;
209
210 // Store the slice in a non-nested subrow.
211 var thread = self.getProcess(event.pid).getThread(event.tid);
212 thread.addNonNestedSlice(slice.slice);
213 delete state.openNonNestedSlices[name];
214 } else {
215 if (state.openSlices.length == 0) {
216 // Ignore E events that are unmatched.
217 return;
218 }
219 var slice = state.openSlices.pop().slice;
220 slice.duration = event.ts - slice.start;
221
222 // Store the slice on the correct subrow.
223 var thread = self.getProcess(event.pid).getThread(event.tid);
224 var subRowIndex = state.openSlices.length;
225 thread.getSubrow(subRowIndex).push(slice);
226
227 // Add the slice to the subSlices array of its parent.
228 if (state.openSlices.length) {
229 var parentSlice = state.openSlices[state.openSlices.length - 1];
230 parentSlice.slice.subSlices.push(slice);
231 }
232 }
233 }
234
235 // Walk through events
236 for (var eI = 0; eI < events.length; eI++) {
237 var event = events[eI];
238 var ptid = event.pid + ':' + event.tid;
239
240 if (!(ptid in threadStateByPTID))
241 threadStateByPTID[ptid] = new ThreadState();
242 var state = threadStateByPTID[ptid];
243
244 if (event.ph == 'B') {
245 processBegin(state, event);
246 } else if (event.ph == 'E') {
247 processEnd(state, event);
248 } else if (event.ph == 'I') {
249 // Treat an Instant event as a duration 0 slice.
250 // TimelineSliceTrack's redraw() knows how to handle this.
251 processBegin(state, event);
252 processEnd(state, event);
253 } else {
254 throw new Error('Unrecognized event phase: ' + event.ph +
255 '(' + event.name + ')');
256 }
257 }
258
259 this.updateBounds();
260
261 // 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
263 // indicate deadlock behavior.
264 for (var ptid in threadStateByPTID) {
265 var state = threadStateByPTID[ptid];
266 while (state.openSlices.length > 0) {
267 var slice = state.openSlices.pop();
268 slice.slice.duration = this.maxTimestamp - slice.slice.start;
269 slice.slice.didNotFinish = true;
270 var event = events[slice.index];
271
272 // Store the slice on the correct subrow.
273 var thread = this.getProcess(event.pid).getThread(event.tid);
274 var subRowIndex = state.openSlices.length;
275 thread.getSubrow(subRowIndex).push(slice.slice);
276
277 // Add the slice to the subSlices array of its parent.
278 if (state.openSlices.length) {
279 var parentSlice = state.openSlices[state.openSlices.length - 1];
280 parentSlice.slice.subSlices.push(slice.slice);
281 }
282 }
283 }
284
285 this.shiftWorldToMicroseconds();
286
287 var boost = (this.maxTimestamp - this.minTimestamp) * 0.15;
288 this.minTimestamp = this.minTimestamp - boost;
289 this.maxTimestamp = this.maxTimestamp + boost;
290 },
291
292 updateBounds: function() {
293 var wmin = Infinity;
294 var wmax = -wmin;
295 var threads = this.getAllThreads();
296 for (var tI = 0; tI < threads.length; tI++) {
297 var thread = threads[tI];
298 thread.updateBounds();
299 if (thread.minTimestamp && thread.maxTimestamp) {
300 wmin = Math.min(wmin, thread.minTimestamp);
301 wmax = Math.max(wmax, thread.maxTimestamp);
302 }
303 }
304 this.minTimestamp = wmin;
305 this.maxTimestamp = wmax;
306 },
307
308 shiftWorldToMicroseconds: function() {
309 var timeBase = this.minTimestamp;
310 var threads = this.getAllThreads();
311 for (var tI = 0; tI < threads.length; tI++) {
312 var thread = threads[tI];
313 var shiftSubRow = function(subRow) {
314 for (var tS = 0; tS < subRow.length; tS++) {
315 var slice = subRow[tS];
316 slice.start = (slice.start - timeBase) / 1000;
317 slice.duration /= 1000;
318 }
319 };
320 for (var tSR = 0; tSR < thread.subRows.length; tSR++) {
321 shiftSubRow(thread.subRows[tSR]);
322 }
323 for (var tSR = 0; tSR < thread.nonNestedSubRows.length; tSR++) {
324 shiftSubRow(thread.nonNestedSubRows[tSR]);
325 }
326 }
327
328 this.updateBounds();
329 },
330
331 getAllThreads: function() {
332 var threads = [];
333 for (var pid in this.processes) {
334 var process = this.processes[pid];
335 for (var tid in process.threads) {
336 threads.push(process.threads[tid]);
337 }
338 }
339 return threads;
340 }
341
342 };
343
344 return {
345 TimelineSlice: TimelineSlice,
346 TimelineThread: TimelineThread,
347 TimelineProcess: TimelineProcess,
348 TimelineModel: TimelineModel
349 };
350 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698