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

Side by Side Diff: tools/profile.js

Issue 77014: Implemented Profile object that processes profiling events and calculates profiling data. (Closed)
Patch Set: addressed Soeren's comments Created 11 years, 8 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 | « tools/codemap.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2009 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29 // Initlialize namespaces
30 var devtools = devtools || {};
31 devtools.profiler = devtools.profiler || {};
32
33
34 /**
35 * Creates a profile object for processing profiling-related events
36 * and calculating function execution times.
37 *
38 * @constructor
39 */
40 devtools.profiler.Profile = function() {
41 this.codeMap_ = new devtools.profiler.CodeMap();
42 this.topDownTree_ = new devtools.profiler.CallTree();
43 this.bottomUpTree_ = new devtools.profiler.CallTree();
44 };
45
46
47 /**
48 * Returns whether a function with the specified name must be skipped.
49 * Should be overriden by subclasses.
50 *
51 * @param {string} name Function name.
52 */
53 devtools.profiler.Profile.prototype.skipThisFunction = function(name) {
54 return false;
55 };
56
57
58 /**
59 * Called whenever the specified operation has failed finding a function
60 * containing the specified address. Should be overriden by subclasses.
61 * Operation is one of the following: 'move', 'delete', 'tick'.
62 *
63 * @param {string} operation Operation name.
64 * @param {number} addr Address of the unknown code.
65 */
66 devtools.profiler.Profile.prototype.handleUnknownCode = function(
67 operation, addr) {
68 };
69
70
71 /**
72 * Registers static (library) code entry.
73 *
74 * @param {string} name Code entry name.
75 * @param {number} startAddr Starting address.
76 * @param {number} endAddr Ending address.
77 */
78 devtools.profiler.Profile.prototype.addStaticCode = function(
79 name, startAddr, endAddr) {
80 this.codeMap_.addStaticCode(startAddr,
81 new devtools.profiler.CodeMap.CodeEntry(endAddr - startAddr, name));
82 };
83
84
85 /**
86 * Registers dynamic (JIT-compiled) code entry.
87 *
88 * @param {string} type Code entry type.
89 * @param {string} name Code entry name.
90 * @param {number} start Starting address.
91 * @param {number} size Code entry size.
92 */
93 devtools.profiler.Profile.prototype.addCode = function(
94 type, name, start, size) {
95 this.codeMap_.addCode(start,
96 new devtools.profiler.Profile.DynamicCodeEntry(size, type, name));
97 };
98
99
100 /**
101 * Reports about moving of a dynamic code entry.
102 *
103 * @param {number} from Current code entry address.
104 * @param {number} to New code entry address.
105 */
106 devtools.profiler.Profile.prototype.moveCode = function(from, to) {
107 try {
108 this.codeMap_.moveCode(from, to);
109 } catch (e) {
110 this.handleUnknownCode('move', from);
111 }
112 };
113
114
115 /**
116 * Reports about deletion of a dynamic code entry.
117 *
118 * @param {number} start Starting address.
119 */
120 devtools.profiler.Profile.prototype.deleteCode = function(start) {
121 try {
122 this.codeMap_.deleteCode(start);
123 } catch (e) {
124 this.handleUnknownCode('delete', start);
125 }
126 };
127
128
129 /**
130 * Records a tick event. Stack must contain a sequence of
131 * addresses starting with the program counter value.
132 *
133 * @param {Array<number>} stack Stack sample.
134 */
135 devtools.profiler.Profile.prototype.recordTick = function(stack) {
136 var processedStack = this.resolveAndFilterFuncs_(stack);
137 this.bottomUpTree_.addPath(processedStack);
138 processedStack.reverse();
139 this.topDownTree_.addPath(processedStack);
140 };
141
142
143 /**
144 * Translates addresses into function names and filters unneeded
145 * functions.
146 *
147 * @param {Array<number>} stack Stack sample.
148 */
149 devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) {
150 var result = [];
151 for (var i = 0; i < stack.length; ++i) {
152 var entry = this.codeMap_.findEntry(stack[i]);
153 if (entry) {
154 var name = entry.getName();
155 if (!this.skipThisFunction(name)) {
156 result.push(name);
157 }
158 } else {
159 this.handleUnknownCode('tick', stack[i]);
160 }
161 }
162 return result;
163 };
164
165
166 /**
167 * Returns the root of the top down call graph.
168 */
169 devtools.profiler.Profile.prototype.getTopDownTreeRoot = function() {
170 this.topDownTree_.computeTotalWeights();
171 return this.topDownTree_.root_;
172 };
173
174
175 /**
176 * Returns the root of the bottom up call graph.
177 */
178 devtools.profiler.Profile.prototype.getBottomUpTreeRoot = function() {
179 this.bottomUpTree_.computeTotalWeights();
180 return this.bottomUpTree_.root_;
181 };
182
183
184 /**
185 * Traverses the top down call graph in preorder.
186 *
187 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
188 */
189 devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) {
190 this.topDownTree_.traverse(f);
191 };
192
193
194 /**
195 * Traverses the bottom up call graph in preorder.
196 *
197 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
198 */
199 devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) {
200 this.bottomUpTree_.traverse(f);
201 };
202
203
204 /**
205 * Calculates a flat profile of callees starting from the specified node.
206 *
207 * @param {devtools.profiler.CallTree.Node} opt_root Starting node.
208 */
209 devtools.profiler.Profile.prototype.getFlatProfile = function(opt_root) {
210 var counters = new devtools.profiler.CallTree.Node('');
211 var precs = {};
212 this.topDownTree_.computeTotalWeights();
213 this.topDownTree_.traverseInDepth(
214 function onEnter(node) {
215 if (!(node.label in precs)) {
216 precs[node.label] = 0;
217 }
218 var rec = counters.findOrAddChild(node.label);
219 rec.selfWeight += node.selfWeight;
220 if (precs[node.label] == 0) {
221 rec.totalWeight += node.totalWeight;
222 }
223 precs[node.label]++;
224 },
225 function onExit(node) {
226 precs[node.label]--;
227 },
228 opt_root);
229 return counters.exportChildren();
230 };
231
232
233 /**
234 * Creates a dynamic code entry.
235 *
236 * @param {number} size Code size.
237 * @param {string} type Code type.
238 * @param {string} name Function name.
239 * @constructor
240 */
241 devtools.profiler.Profile.DynamicCodeEntry = function(size, type, name) {
242 devtools.profiler.CodeMap.CodeEntry.call(this, size, name);
243 this.type = type;
244 };
245
246
247 /**
248 * Returns node name.
249 */
250 devtools.profiler.Profile.DynamicCodeEntry.prototype.getName = function() {
251 var name = this.name;
252 if (name.length == 0) {
253 name = '<anonymous>';
254 } else if (name.charAt(0) == ' ') {
255 // An anonymous function with location: " aaa.js:10".
256 name = '<anonymous>' + name;
257 }
258 return this.type + ': ' + name;
259 };
260
261
262 /**
263 * Constructs a call graph.
264 *
265 * @constructor
266 */
267 devtools.profiler.CallTree = function() {
268 this.root_ = new devtools.profiler.CallTree.Node('');
269 };
270
271
272 /**
273 * @private
274 */
275 devtools.profiler.CallTree.prototype.totalsComputed_ = false;
276
277
278 /**
279 * Adds the specified call path, constructing nodes as necessary.
280 *
281 * @param {Array<string>} path Call path.
282 */
283 devtools.profiler.CallTree.prototype.addPath = function(path) {
284 if (path.length == 0) {
285 return;
286 }
287 var curr = this.root_;
288 for (var i = 0; i < path.length; ++i) {
289 curr = curr.findOrAddChild(path[i]);
290 }
291 curr.selfWeight++;
292 this.totalsComputed_ = false;
293 };
294
295
296 /**
297 * Computes total weights in the call graph.
298 */
299 devtools.profiler.CallTree.prototype.computeTotalWeights = function() {
300 if (this.totalsComputed_) {
301 return;
302 }
303 this.root_.computeTotalWeight();
304 this.totalsComputed_ = true;
305 };
306
307
308 /**
309 * Traverses the call graph in preorder.
310 *
311 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
312 * @param {devtools.profiler.CallTree.Node} opt_start Starting node.
313 */
314 devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) {
315 var nodesToVisit = [opt_start || this.root_];
316 while (nodesToVisit.length > 0) {
317 var node = nodesToVisit.shift();
318 f(node);
319 nodesToVisit = nodesToVisit.concat(node.exportChildren());
320 }
321 };
322
323
324 /**
325 * Performs an indepth call graph traversal.
326 *
327 * @param {function(devtools.profiler.CallTree.Node)} enter A function called
328 * prior to visiting node's children.
329 * @param {function(devtools.profiler.CallTree.Node)} exit A function called
330 * after visiting node's children.
331 * @param {devtools.profiler.CallTree.Node} opt_start Starting node.
332 */
333 devtools.profiler.CallTree.prototype.traverseInDepth = function(
334 enter, exit, opt_start) {
335 function traverse(node) {
336 enter(node);
337 node.forEachChild(traverse);
338 exit(node);
339 }
340 traverse(opt_start || this.root_);
341 };
342
343
344 /**
345 * Constructs a call graph node.
346 *
347 * @param {string} label Node label.
348 * @param {devtools.profiler.CallTree.Node} opt_parent Node parent.
349 */
350 devtools.profiler.CallTree.Node = function(label, opt_parent) {
351 this.label = label;
352 this.parent = opt_parent;
353 this.children = {};
354 };
355
356
357 /**
358 * Node self weight (how many times this node was the last node in
359 * a call path).
360 * @type {number}
361 */
362 devtools.profiler.CallTree.Node.prototype.selfWeight = 0;
363
364
365 /**
366 * Node total weight (includes weights of all children).
367 * @type {number}
368 */
369 devtools.profiler.CallTree.Node.prototype.totalWeight = 0;
370
371
372 /**
373 * Adds a child node.
374 *
375 * @param {string} label Child node label.
376 */
377 devtools.profiler.CallTree.Node.prototype.addChild = function(label) {
378 var child = new devtools.profiler.CallTree.Node(label, this);
379 this.children[label] = child;
380 return child;
381 };
382
383
384 /**
385 * Computes node's total weight.
386 */
387 devtools.profiler.CallTree.Node.prototype.computeTotalWeight =
388 function() {
389 var totalWeight = this.selfWeight;
390 this.forEachChild(function(child) {
391 totalWeight += child.computeTotalWeight(); });
392 return this.totalWeight = totalWeight;
393 };
394
395
396 /**
397 * Returns all node's children as an array.
398 */
399 devtools.profiler.CallTree.Node.prototype.exportChildren = function() {
400 var result = [];
401 this.forEachChild(function (node) { result.push(node); });
402 return result;
403 };
404
405
406 /**
407 * Finds an immediate child with the specified label.
408 *
409 * @param {string} label Child node label.
410 */
411 devtools.profiler.CallTree.Node.prototype.findChild = function(label) {
412 return this.children[label] || null;
413 };
414
415
416 /**
417 * Finds an immediate child with the specified label, creates a child
418 * node if necessary.
419 *
420 * @param {string} label Child node label.
421 */
422 devtools.profiler.CallTree.Node.prototype.findOrAddChild = function(
423 label) {
424 return this.findChild(label) || this.addChild(label);
425 };
426
427
428 /**
429 * Calls the specified function for every child.
430 *
431 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
432 */
433 devtools.profiler.CallTree.Node.prototype.forEachChild = function(f) {
434 for (var c in this.children) {
435 f(this.children[c]);
436 }
437 };
438
439
440 /**
441 * Walks up from the current node up to the call tree root.
442 *
443 * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
444 */
445 devtools.profiler.CallTree.Node.prototype.walkUpToRoot = function(f) {
446 for (var curr = this; curr != null; curr = curr.parent) {
447 f(curr);
448 }
449 };
450
451
452 /**
453 * Tries to find a node with the specified path.
454 *
455 * @param {Array<string>} labels The path.
456 * @param {function(devtools.profiler.CallTree.Node)} opt_f Visitor function.
457 */
458 devtools.profiler.CallTree.Node.prototype.descendToChild = function(
459 labels, opt_f) {
460 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) {
461 var child = curr.findChild(labels[pos]);
462 if (opt_f) {
463 opt_f(child, pos);
464 }
465 curr = child;
466 }
467 return curr;
468 };
OLDNEW
« no previous file with comments | « tools/codemap.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698