Index: tools/profile.js |
diff --git a/tools/profile.js b/tools/profile.js |
index 70e30846da9ebef5dc135c234d8a9defee5fbadb..614c63557697b5a73858d38d0af8026e4e7b78ea 100644 |
--- a/tools/profile.js |
+++ b/tools/profile.js |
@@ -185,25 +185,7 @@ devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) { |
/** |
- * Returns the root of the top down call graph. |
- */ |
-devtools.profiler.Profile.prototype.getTopDownTreeRoot = function() { |
- this.topDownTree_.computeTotalWeights(); |
- return this.topDownTree_.getRoot(); |
-}; |
- |
- |
-/** |
- * Returns the root of the bottom up call graph. |
- */ |
-devtools.profiler.Profile.prototype.getBottomUpTreeRoot = function() { |
- this.bottomUpTree_.computeTotalWeights(); |
- return this.bottomUpTree_.getRoot(); |
-}; |
- |
- |
-/** |
- * Traverses the top down call graph in preorder. |
+ * Performs a BF traversal of the top down call graph. |
* |
* @param {function(devtools.profiler.CallTree.Node)} f Visitor function. |
*/ |
@@ -213,7 +195,7 @@ devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) { |
/** |
- * Traverses the bottom up call graph in preorder. |
+ * Performs a BF traversal of the bottom up call graph. |
* |
* @param {function(devtools.profiler.CallTree.Node)} f Visitor function. |
*/ |
@@ -223,60 +205,96 @@ devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) { |
/** |
- * Calculates a top down profile starting from the specified node. |
+ * Calculates a top down profile for a node with the specified label. |
+ * If no name specified, returns the whole top down calls tree. |
* |
- * @param {devtools.profiler.CallTree.Node} opt_root Starting node. |
+ * @param {string} opt_label Node label. |
*/ |
-devtools.profiler.Profile.prototype.getTopDownProfile = function(opt_root) { |
- if (!opt_root) { |
- this.topDownTree_.computeTotalWeights(); |
- return this.topDownTree_; |
- } else { |
- throw Error('not implemented'); |
- } |
+devtools.profiler.Profile.prototype.getTopDownProfile = function(opt_label) { |
+ return this.getTreeProfile_(this.topDownTree_, opt_label); |
+}; |
+ |
+ |
+/** |
+ * Calculates a bottom up profile for a node with the specified label. |
+ * If no name specified, returns the whole bottom up calls tree. |
+ * |
+ * @param {string} opt_label Node label. |
+ */ |
+devtools.profiler.Profile.prototype.getBottomUpProfile = function(opt_label) { |
+ return this.getTreeProfile_(this.bottomUpTree_, opt_label); |
}; |
/** |
- * Calculates a bottom up profile starting from the specified node. |
+ * Helper function for calculating a tree profile. |
* |
- * @param {devtools.profiler.CallTree.Node} opt_root Starting node. |
+ * @param {devtools.profiler.Profile.CallTree} tree Call tree. |
+ * @param {string} opt_label Node label. |
*/ |
-devtools.profiler.Profile.prototype.getBottomUpProfile = function(opt_root) { |
- if (!opt_root) { |
- this.bottomUpTree_.computeTotalWeights(); |
- return this.bottomUpTree_; |
+devtools.profiler.Profile.prototype.getTreeProfile_ = function(tree, opt_label) { |
+ if (!opt_label) { |
+ tree.computeTotalWeights(); |
+ return tree; |
} else { |
- throw Error('not implemented'); |
+ var subTree = tree.cloneSubtree(opt_label); |
+ subTree.computeTotalWeights(); |
+ return subTree; |
} |
}; |
/** |
- * Calculates a flat profile of callees starting from the specified node. |
+ * Calculates a flat profile of callees starting from a node with |
+ * the specified label. If no name specified, starts from the root. |
* |
- * @param {devtools.profiler.CallTree.Node} opt_root Starting node. |
+ * @param {string} opt_label Starting node label. |
*/ |
-devtools.profiler.Profile.prototype.getFlatProfile = function(opt_root) { |
+devtools.profiler.Profile.prototype.getFlatProfile = function(opt_label) { |
var counters = new devtools.profiler.CallTree(); |
+ var rootLabel = opt_label || devtools.profiler.CallTree.ROOT_NODE_LABEL; |
var precs = {}; |
+ precs[rootLabel] = 0; |
+ var root = counters.findOrAddChild(rootLabel); |
+ |
this.topDownTree_.computeTotalWeights(); |
this.topDownTree_.traverseInDepth( |
function onEnter(node) { |
if (!(node.label in precs)) { |
precs[node.label] = 0; |
} |
- var rec = counters.findOrAddChild(node.label); |
- rec.selfWeight += node.selfWeight; |
- if (precs[node.label] == 0) { |
- rec.totalWeight += node.totalWeight; |
+ var nodeLabelIsRootLabel = node.label == rootLabel; |
+ if (nodeLabelIsRootLabel || precs[rootLabel] > 0) { |
+ if (precs[rootLabel] == 0) { |
+ root.selfWeight += node.selfWeight; |
+ root.totalWeight += node.totalWeight; |
+ } else { |
+ var rec = root.findOrAddChild(node.label); |
+ rec.selfWeight += node.selfWeight; |
+ if (nodeLabelIsRootLabel || precs[node.label] == 0) { |
+ rec.totalWeight += node.totalWeight; |
+ } |
+ } |
+ precs[node.label]++; |
} |
- precs[node.label]++; |
}, |
function onExit(node) { |
- precs[node.label]--; |
+ if (node.label == rootLabel || precs[rootLabel] > 0) { |
+ precs[node.label]--; |
+ } |
}, |
- opt_root); |
+ null); |
+ |
+ if (!opt_label) { |
+ // If we have created a flat profile for the whole program, we don't |
+ // need an explicit root in it. Thus, replace the counters tree |
+ // root with the node corresponding to the whole program. |
+ counters.root_ = root; |
+ } else { |
+ // Propagate weights so percents can be calculated correctly. |
+ counters.getRoot().selfWeight = root.selfWeight; |
+ counters.getRoot().totalWeight = root.totalWeight; |
+ } |
return counters; |
}; |
@@ -316,11 +334,18 @@ devtools.profiler.Profile.DynamicCodeEntry.prototype.getName = function() { |
* @constructor |
*/ |
devtools.profiler.CallTree = function() { |
- this.root_ = new devtools.profiler.CallTree.Node(''); |
+ this.root_ = new devtools.profiler.CallTree.Node( |
+ devtools.profiler.CallTree.ROOT_NODE_LABEL); |
}; |
/** |
+ * The label of the root node. |
+ */ |
+devtools.profiler.CallTree.ROOT_NODE_LABEL = ''; |
+ |
+ |
+/** |
* @private |
*/ |
devtools.profiler.CallTree.prototype.totalsComputed_ = false; |
@@ -359,10 +384,38 @@ devtools.profiler.CallTree.prototype.addPath = function(path) { |
* |
* @param {string} label Child node label. |
*/ |
-devtools.profiler.CallTree.prototype.findOrAddChild = function( |
- label, opt_parent) { |
- var parent = opt_parent || this.root_; |
- return parent.findOrAddChild(label); |
+devtools.profiler.CallTree.prototype.findOrAddChild = function(label) { |
+ return this.root_.findOrAddChild(label); |
+}; |
+ |
+ |
+/** |
+ * Creates a subtree by cloning and merging all subtrees rooted at nodes |
+ * with a given label. E.g. cloning the following call tree on label 'A' |
+ * will give the following result: |
+ * |
+ * <A>--<B> <B> |
+ * / / |
+ * <root> == clone on 'A' ==> <root>--<A> |
+ * \ \ |
+ * <C>--<A>--<D> <D> |
+ * |
+ * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the |
+ * source call tree. |
+ * |
+ * @param {string} label The label of the new root node. |
+ */ |
+devtools.profiler.CallTree.prototype.cloneSubtree = function(label) { |
+ var subTree = new devtools.profiler.CallTree(); |
+ this.traverse(function(node, parent) { |
+ if (!parent && node.label != label) { |
+ return null; |
+ } |
+ var child = (parent ? parent : subTree).findOrAddChild(node.label); |
+ child.selfWeight += node.selfWeight; |
+ return child; |
+ }); |
+ return subTree; |
}; |
@@ -392,11 +445,10 @@ devtools.profiler.CallTree.prototype.computeTotalWeights = function() { |
* |
* @param {function(devtools.profiler.CallTree.Node, *)} f Visitor function. |
* The second parameter is the result of calling 'f' on the parent node. |
- * @param {devtools.profiler.CallTree.Node} opt_start Starting node. |
*/ |
-devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) { |
+devtools.profiler.CallTree.prototype.traverse = function(f) { |
var pairsToProcess = new ConsArray(); |
- pairsToProcess.concat([{node: opt_start || this.root_, param: null}]); |
+ pairsToProcess.concat([{node: this.root_, param: null}]); |
while (!pairsToProcess.atEnd()) { |
var pair = pairsToProcess.next(); |
var node = pair.node; |
@@ -416,16 +468,14 @@ devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) { |
* prior to visiting node's children. |
* @param {function(devtools.profiler.CallTree.Node)} exit A function called |
* after visiting node's children. |
- * @param {devtools.profiler.CallTree.Node} opt_start Starting node. |
*/ |
-devtools.profiler.CallTree.prototype.traverseInDepth = function( |
- enter, exit, opt_start) { |
+devtools.profiler.CallTree.prototype.traverseInDepth = function(enter, exit) { |
function traverse(node) { |
enter(node); |
node.forEachChild(traverse); |
exit(node); |
} |
- traverse(opt_start || this.root_); |
+ traverse(this.root_); |
}; |
@@ -507,8 +557,7 @@ devtools.profiler.CallTree.Node.prototype.findChild = function(label) { |
* |
* @param {string} label Child node label. |
*/ |
-devtools.profiler.CallTree.Node.prototype.findOrAddChild = function( |
- label) { |
+devtools.profiler.CallTree.Node.prototype.findOrAddChild = function(label) { |
return this.findChild(label) || this.addChild(label); |
}; |