| 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);
 | 
|  };
 | 
|  
 | 
| 
 |