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
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.
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.
30 * Creates a profile object for processing profiling-related events
31 * and calculating function execution times.
36 this.codeMap_ = new CodeMap();
37 this.topDownTree_ = new CallTree();
38 this.bottomUpTree_ = new CallTree();
43 * Returns whether a function with the specified name must be skipped.
44 * Should be overriden by subclasses.
46 * @param {string} name Function name.
48 Profile.prototype.skipThisFunction = function(name) {
54 * Enum for profiler operations that involve looking up existing
67 * Enum for code state regarding its dynamic optimization.
79 * Called whenever the specified operation has failed finding a function
80 * containing the specified address. Should be overriden by subclasses.
81 * See the Profile.Operation enum for the list of
82 * possible operations.
84 * @param {number} operation Operation.
85 * @param {number} addr Address of the unknown code.
86 * @param {number} opt_stackPos If an unknown address is encountered
87 * during stack strace processing, specifies a position of the frame
88 * containing the address.
90 Profile.prototype.handleUnknownCode = function(
91 operation, addr, opt_stackPos) {
96 * Registers a library.
98 * @param {string} name Code entry name.
99 * @param {number} startAddr Starting address.
100 * @param {number} endAddr Ending address.
102 Profile.prototype.addLibrary = function(
103 name, startAddr, endAddr) {
104 var entry = new CodeMap.CodeEntry(
105 endAddr - startAddr, name);
106 this.codeMap_.addLibrary(startAddr, entry);
112 * Registers statically compiled code entry.
114 * @param {string} name Code entry name.
115 * @param {number} startAddr Starting address.
116 * @param {number} endAddr Ending address.
118 Profile.prototype.addStaticCode = function(
119 name, startAddr, endAddr) {
120 var entry = new CodeMap.CodeEntry(
121 endAddr - startAddr, name);
122 this.codeMap_.addStaticCode(startAddr, entry);
128 * Registers dynamic (JIT-compiled) code entry.
130 * @param {string} type Code entry type.
131 * @param {string} name Code entry name.
132 * @param {number} start Starting address.
133 * @param {number} size Code entry size.
135 Profile.prototype.addCode = function(
136 type, name, start, size) {
137 var entry = new Profile.DynamicCodeEntry(size, type, name);
138 this.codeMap_.addCode(start, entry);
144 * Registers dynamic (JIT-compiled) code entry.
146 * @param {string} type Code entry type.
147 * @param {string} name Code entry name.
148 * @param {number} start Starting address.
149 * @param {number} size Code entry size.
150 * @param {number} funcAddr Shared function object address.
151 * @param {Profile.CodeState} state Optimization state.
153 Profile.prototype.addFuncCode = function(
154 type, name, start, size, funcAddr, state) {
155 // As code and functions are in the same address space,
156 // it is safe to put them in a single code map.
157 var func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr);
159 func = new Profile.FunctionEntry(name);
160 this.codeMap_.addCode(funcAddr, func);
161 } else if (func.name !== name) {
162 // Function object has been overwritten with a new one.
165 var entry = this.codeMap_.findDynamicEntryByStartAddress(start);
167 if (entry.size === size && entry.func === func) {
168 // Entry state has changed.
172 entry = new Profile.DynamicFuncCodeEntry(size, type, func, state);
173 this.codeMap_.addCode(start, entry);
180 * Reports about moving of a dynamic code entry.
182 * @param {number} from Current code entry address.
183 * @param {number} to New code entry address.
185 Profile.prototype.moveCode = function(from, to) {
187 this.codeMap_.moveCode(from, to);
189 this.handleUnknownCode(Profile.Operation.MOVE, from);
195 * Reports about deletion of a dynamic code entry.
197 * @param {number} start Starting address.
199 Profile.prototype.deleteCode = function(start) {
201 this.codeMap_.deleteCode(start);
203 this.handleUnknownCode(Profile.Operation.DELETE, start);
209 * Reports about moving of a dynamic code entry.
211 * @param {number} from Current code entry address.
212 * @param {number} to New code entry address.
214 Profile.prototype.moveFunc = function(from, to) {
215 if (this.codeMap_.findDynamicEntryByStartAddress(from)) {
216 this.codeMap_.moveCode(from, to);
222 * Retrieves a code entry by an address.
224 * @param {number} addr Entry address.
226 Profile.prototype.findEntry = function(addr) {
227 return this.codeMap_.findEntry(addr);
232 * Records a tick event. Stack must contain a sequence of
233 * addresses starting with the program counter value.
235 * @param {Array<number>} stack Stack sample.
237 Profile.prototype.recordTick = function(stack) {
238 var processedStack = this.resolveAndFilterFuncs_(stack);
239 this.bottomUpTree_.addPath(processedStack);
240 processedStack.reverse();
241 this.topDownTree_.addPath(processedStack);
246 * Translates addresses into function names and filters unneeded
249 * @param {Array<number>} stack Stack sample.
251 Profile.prototype.resolveAndFilterFuncs_ = function(stack) {
253 for (var i = 0; i < stack.length; ++i) {
254 var entry = this.codeMap_.findEntry(stack[i]);
256 var name = entry.getName();
257 if (!this.skipThisFunction(name)) {
261 this.handleUnknownCode(
262 Profile.Operation.TICK, stack[i], i);
270 * Performs a BF traversal of the top down call graph.
272 * @param {function(CallTree.Node)} f Visitor function.
274 Profile.prototype.traverseTopDownTree = function(f) {
275 this.topDownTree_.traverse(f);
280 * Performs a BF traversal of the bottom up call graph.
282 * @param {function(CallTree.Node)} f Visitor function.
284 Profile.prototype.traverseBottomUpTree = function(f) {
285 this.bottomUpTree_.traverse(f);
290 * Calculates a top down profile for a node with the specified label.
291 * If no name specified, returns the whole top down calls tree.
293 * @param {string} opt_label Node label.
295 Profile.prototype.getTopDownProfile = function(opt_label) {
296 return this.getTreeProfile_(this.topDownTree_, opt_label);
301 * Calculates a bottom up profile for a node with the specified label.
302 * If no name specified, returns the whole bottom up calls tree.
304 * @param {string} opt_label Node label.
306 Profile.prototype.getBottomUpProfile = function(opt_label) {
307 return this.getTreeProfile_(this.bottomUpTree_, opt_label);
312 * Helper function for calculating a tree profile.
314 * @param {Profile.CallTree} tree Call tree.
315 * @param {string} opt_label Node label.
317 Profile.prototype.getTreeProfile_ = function(tree, opt_label) {
319 tree.computeTotalWeights();
322 var subTree = tree.cloneSubtree(opt_label);
323 subTree.computeTotalWeights();
330 * Calculates a flat profile of callees starting from a node with
331 * the specified label. If no name specified, starts from the root.
333 * @param {string} opt_label Starting node label.
335 Profile.prototype.getFlatProfile = function(opt_label) {
336 var counters = new CallTree();
337 var rootLabel = opt_label || CallTree.ROOT_NODE_LABEL;
339 precs[rootLabel] = 0;
340 var root = counters.findOrAddChild(rootLabel);
342 this.topDownTree_.computeTotalWeights();
343 this.topDownTree_.traverseInDepth(
344 function onEnter(node) {
345 if (!(node.label in precs)) {
346 precs[node.label] = 0;
348 var nodeLabelIsRootLabel = node.label == rootLabel;
349 if (nodeLabelIsRootLabel || precs[rootLabel] > 0) {
350 if (precs[rootLabel] == 0) {
351 root.selfWeight += node.selfWeight;
352 root.totalWeight += node.totalWeight;
354 var rec = root.findOrAddChild(node.label);
355 rec.selfWeight += node.selfWeight;
356 if (nodeLabelIsRootLabel || precs[node.label] == 0) {
357 rec.totalWeight += node.totalWeight;
363 function onExit(node) {
364 if (node.label == rootLabel || precs[rootLabel] > 0) {
371 // If we have created a flat profile for the whole program, we don't
372 // need an explicit root in it. Thus, replace the counters tree
373 // root with the node corresponding to the whole program.
374 counters.root_ = root;
376 // Propagate weights so percents can be calculated correctly.
377 counters.getRoot().selfWeight = root.selfWeight;
378 counters.getRoot().totalWeight = root.totalWeight;
385 * Cleans up function entries that are not referenced by code entries.
387 Profile.prototype.cleanUpFuncEntries = function() {
388 var referencedFuncEntries = [];
389 var entries = this.codeMap_.getAllDynamicEntriesWithAddresses();
390 for (var i = 0, l = entries.length; i < l; ++i) {
391 if (entries[i][1].constructor === Profile.FunctionEntry) {
392 entries[i][1].used = false;
395 for (var i = 0, l = entries.length; i < l; ++i) {
396 if ("func" in entries[i][1]) {
397 entries[i][1].func.used = true;
400 for (var i = 0, l = entries.length; i < l; ++i) {
401 if (entries[i][1].constructor === Profile.FunctionEntry &&
402 !entries[i][1].used) {
403 this.codeMap_.deleteCode(entries[i][0]);
410 * Creates a dynamic code entry.
412 * @param {number} size Code size.
413 * @param {string} type Code type.
414 * @param {string} name Function name.
417 Profile.DynamicCodeEntry = function(size, type, name) {
418 CodeMap.CodeEntry.call(this, size, name);
426 Profile.DynamicCodeEntry.prototype.getName = function() {
427 return this.type + ': ' + this.name;
432 * Returns raw node name (without type decoration).
434 Profile.DynamicCodeEntry.prototype.getRawName = function() {
439 Profile.DynamicCodeEntry.prototype.isJSFunction = function() {
444 Profile.DynamicCodeEntry.prototype.toString = function() {
445 return this.getName() + ': ' + this.size.toString(16);
450 * Creates a dynamic code entry.
452 * @param {number} size Code size.
453 * @param {string} type Code type.
454 * @param {Profile.FunctionEntry} func Shared function entry.
455 * @param {Profile.CodeState} state Code optimization state.
458 Profile.DynamicFuncCodeEntry = function(size, type, func, state) {
459 CodeMap.CodeEntry.call(this, size);
465 Profile.DynamicFuncCodeEntry.STATE_PREFIX = ["", "~", "*"];
470 Profile.DynamicFuncCodeEntry.prototype.getName = function() {
471 var name = this.func.getName();
472 return this.type + ': ' + Profile.DynamicFuncCodeEntry.STATE_PREFIX[this.state] + name;
477 * Returns raw node name (without type decoration).
479 Profile.DynamicFuncCodeEntry.prototype.getRawName = function() {
480 return this.func.getName();
484 Profile.DynamicFuncCodeEntry.prototype.isJSFunction = function() {
489 Profile.DynamicFuncCodeEntry.prototype.toString = function() {
490 return this.getName() + ': ' + this.size.toString(16);
495 * Creates a shared function object entry.
497 * @param {string} name Function name.
500 Profile.FunctionEntry = function(name) {
501 CodeMap.CodeEntry.call(this, 0, name);
508 Profile.FunctionEntry.prototype.getName = function() {
509 var name = this.name;
510 if (name.length == 0) {
511 name = '<anonymous>';
512 } else if (name.charAt(0) == ' ') {
513 // An anonymous function with location: " aaa.js:10".
514 name = '<anonymous>' + name;
519 Profile.FunctionEntry.prototype.toString = CodeMap.CodeEntry.prototype.toString;
522 * Constructs a call graph.
526 function CallTree() {
527 this.root_ = new CallTree.Node(
528 CallTree.ROOT_NODE_LABEL);
533 * The label of the root node.
535 CallTree.ROOT_NODE_LABEL = '';
541 CallTree.prototype.totalsComputed_ = false;
545 * Returns the tree root.
547 CallTree.prototype.getRoot = function() {
553 * Adds the specified call path, constructing nodes as necessary.
555 * @param {Array<string>} path Call path.
557 CallTree.prototype.addPath = function(path) {
558 if (path.length == 0) {
561 var curr = this.root_;
562 for (var i = 0; i < path.length; ++i) {
563 curr = curr.findOrAddChild(path[i]);
566 this.totalsComputed_ = false;
571 * Finds an immediate child of the specified parent with the specified
572 * label, creates a child node if necessary. If a parent node isn't
573 * specified, uses tree root.
575 * @param {string} label Child node label.
577 CallTree.prototype.findOrAddChild = function(label) {
578 return this.root_.findOrAddChild(label);
583 * Creates a subtree by cloning and merging all subtrees rooted at nodes
584 * with a given label. E.g. cloning the following call tree on label 'A'
585 * will give the following result:
589 * <root> == clone on 'A' ==> <root>--<A>
593 * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the
596 * @param {string} label The label of the new root node.
598 CallTree.prototype.cloneSubtree = function(label) {
599 var subTree = new CallTree();
600 this.traverse(function(node, parent) {
601 if (!parent && node.label != label) {
604 var child = (parent ? parent : subTree).findOrAddChild(node.label);
605 child.selfWeight += node.selfWeight;
613 * Computes total weights in the call graph.
615 CallTree.prototype.computeTotalWeights = function() {
616 if (this.totalsComputed_) {
619 this.root_.computeTotalWeight();
620 this.totalsComputed_ = true;
625 * Traverses the call graph in preorder. This function can be used for
626 * building optionally modified tree clones. This is the boilerplate code
629 * callTree.traverse(function(node, parentClone) {
630 * var nodeClone = cloneNode(node);
632 * parentClone.addChild(nodeClone);
636 * @param {function(CallTree.Node, *)} f Visitor function.
637 * The second parameter is the result of calling 'f' on the parent node.
639 CallTree.prototype.traverse = function(f) {
640 var pairsToProcess = new ConsArray();
641 pairsToProcess.concat([{node: this.root_, param: null}]);
642 while (!pairsToProcess.atEnd()) {
643 var pair = pairsToProcess.next();
644 var node = pair.node;
645 var newParam = f(node, pair.param);
646 var morePairsToProcess = [];
647 node.forEachChild(function (child) {
648 morePairsToProcess.push({node: child, param: newParam}); });
649 pairsToProcess.concat(morePairsToProcess);
655 * Performs an indepth call graph traversal.
657 * @param {function(CallTree.Node)} enter A function called
658 * prior to visiting node's children.
659 * @param {function(CallTree.Node)} exit A function called
660 * after visiting node's children.
662 CallTree.prototype.traverseInDepth = function(enter, exit) {
663 function traverse(node) {
665 node.forEachChild(traverse);
668 traverse(this.root_);
673 * Constructs a call graph node.
675 * @param {string} label Node label.
676 * @param {CallTree.Node} opt_parent Node parent.
678 CallTree.Node = function(label, opt_parent) {
680 this.parent = opt_parent;
686 * Node self weight (how many times this node was the last node in
690 CallTree.Node.prototype.selfWeight = 0;
694 * Node total weight (includes weights of all children).
697 CallTree.Node.prototype.totalWeight = 0;
703 * @param {string} label Child node label.
705 CallTree.Node.prototype.addChild = function(label) {
706 var child = new CallTree.Node(label, this);
707 this.children[label] = child;
713 * Computes node's total weight.
715 CallTree.Node.prototype.computeTotalWeight =
717 var totalWeight = this.selfWeight;
718 this.forEachChild(function(child) {
719 totalWeight += child.computeTotalWeight(); });
720 return this.totalWeight = totalWeight;
725 * Returns all node's children as an array.
727 CallTree.Node.prototype.exportChildren = function() {
729 this.forEachChild(function (node) { result.push(node); });
735 * Finds an immediate child with the specified label.
737 * @param {string} label Child node label.
739 CallTree.Node.prototype.findChild = function(label) {
740 return this.children[label] || null;
745 * Finds an immediate child with the specified label, creates a child
748 * @param {string} label Child node label.
750 CallTree.Node.prototype.findOrAddChild = function(label) {
751 return this.findChild(label) || this.addChild(label);
756 * Calls the specified function for every child.
758 * @param {function(CallTree.Node)} f Visitor function.
760 CallTree.Node.prototype.forEachChild = function(f) {
761 for (var c in this.children) {
768 * Walks up from the current node up to the call tree root.
770 * @param {function(CallTree.Node)} f Visitor function.
772 CallTree.Node.prototype.walkUpToRoot = function(f) {
773 for (var curr = this; curr != null; curr = curr.parent) {
780 * Tries to find a node with the specified path.
782 * @param {Array<string>} labels The path.
783 * @param {function(CallTree.Node)} opt_f Visitor function.
785 CallTree.Node.prototype.descendToChild = function(
787 for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) {
788 var child = curr.findChild(labels[pos]);