Enhancing profiling data processing code with functionality needed for the Dev Tools Profiler.
Details: - added properties / functions in view objects needed for WebKit's ProfileView; - added ability to count profiles for specific functions. The tickprocessor functionality does not affected. Review URL: http://codereview.chromium.org/99181 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1823 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
9156ed9e04
commit
178a656429
@ -124,6 +124,10 @@ ProfileTestDriver.prototype.execute = function() {
|
|||||||
this.leave();
|
this.leave();
|
||||||
this.leave();
|
this.leave();
|
||||||
this.leave();
|
this.leave();
|
||||||
|
this.enter('lib2-f1');
|
||||||
|
this.enter('lib1-f1');
|
||||||
|
this.leave();
|
||||||
|
this.leave();
|
||||||
this.stay();
|
this.stay();
|
||||||
this.leave();
|
this.leave();
|
||||||
};
|
};
|
||||||
@ -149,14 +153,14 @@ function Inherits(childCtor, parentCtor) {
|
|||||||
Driver.prototype.enter = function(func) {
|
Driver.prototype.enter = function(func) {
|
||||||
this.namesTopDown.push(func);
|
this.namesTopDown.push(func);
|
||||||
this.namesBottomUp.unshift(func);
|
this.namesBottomUp.unshift(func);
|
||||||
assertNoPathExists(this.profile.getTopDownTreeRoot(), this.namesTopDown,
|
assertNoPathExists(this.profile.getTopDownProfile().getRoot(), this.namesTopDown,
|
||||||
'pre enter/topDown');
|
'pre enter/topDown');
|
||||||
assertNoPathExists(this.profile.getBottomUpTreeRoot(), this.namesBottomUp,
|
assertNoPathExists(this.profile.getBottomUpProfile().getRoot(), this.namesBottomUp,
|
||||||
'pre enter/bottomUp');
|
'pre enter/bottomUp');
|
||||||
Driver.superClass_.enter.call(this, func);
|
Driver.superClass_.enter.call(this, func);
|
||||||
assertPathExists(this.profile.getTopDownTreeRoot(), this.namesTopDown,
|
assertPathExists(this.profile.getTopDownProfile().getRoot(), this.namesTopDown,
|
||||||
'post enter/topDown');
|
'post enter/topDown');
|
||||||
assertPathExists(this.profile.getBottomUpTreeRoot(), this.namesBottomUp,
|
assertPathExists(this.profile.getBottomUpProfile().getRoot(), this.namesBottomUp,
|
||||||
'post enter/bottomUp');
|
'post enter/bottomUp');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -196,8 +200,8 @@ function assertNodeWeights(root, path, selfTicks, totalTicks) {
|
|||||||
testDriver.execute();
|
testDriver.execute();
|
||||||
|
|
||||||
var pathWeights = [
|
var pathWeights = [
|
||||||
[['lib1-f1'], 1, 14],
|
[['lib1-f1'], 1, 16],
|
||||||
[['lib1-f1', 'lib1-f2'], 2, 13],
|
[['lib1-f1', 'lib1-f2'], 2, 15],
|
||||||
[['lib1-f1', 'lib1-f2', 'T: F1'], 2, 11],
|
[['lib1-f1', 'lib1-f2', 'T: F1'], 2, 11],
|
||||||
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F2'], 1, 1],
|
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F2'], 1, 1],
|
||||||
[['lib1-f1', 'lib1-f2', 'T: F1', 'lib2-f1'], 2, 3],
|
[['lib1-f1', 'lib1-f2', 'T: F1', 'lib2-f1'], 2, 3],
|
||||||
@ -205,10 +209,12 @@ function assertNodeWeights(root, path, selfTicks, totalTicks) {
|
|||||||
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3'], 1, 5],
|
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3'], 1, 5],
|
||||||
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3'], 1, 4],
|
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3'], 1, 4],
|
||||||
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F3'], 1, 1],
|
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F3'], 1, 1],
|
||||||
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F2'], 2, 2]
|
[['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F2'], 2, 2],
|
||||||
|
[['lib1-f1', 'lib1-f2', 'lib2-f1'], 1, 2],
|
||||||
|
[['lib1-f1', 'lib1-f2', 'lib2-f1', 'lib1-f1'], 1, 1]
|
||||||
];
|
];
|
||||||
|
|
||||||
var root = testDriver.profile.getTopDownTreeRoot();
|
var root = testDriver.profile.getTopDownProfile().getRoot();
|
||||||
for (var i = 0; i < pathWeights.length; ++i) {
|
for (var i = 0; i < pathWeights.length; ++i) {
|
||||||
var data = pathWeights[i];
|
var data = pathWeights[i];
|
||||||
assertNodeWeights(root, data[0], data[1], data[2]);
|
assertNodeWeights(root, data[0], data[1], data[2]);
|
||||||
@ -221,6 +227,7 @@ function assertNodeWeights(root, path, selfTicks, totalTicks) {
|
|||||||
ProfileTestDriver.call(this);
|
ProfileTestDriver.call(this);
|
||||||
this.namesTopDown = [''];
|
this.namesTopDown = [''];
|
||||||
this.counters = {};
|
this.counters = {};
|
||||||
|
this.root = null;
|
||||||
};
|
};
|
||||||
Inherits(Driver, ProfileTestDriver);
|
Inherits(Driver, ProfileTestDriver);
|
||||||
|
|
||||||
@ -262,16 +269,26 @@ function assertNodeWeights(root, path, selfTicks, totalTicks) {
|
|||||||
this.namesTopDown.pop();
|
this.namesTopDown.pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Driver.prototype.extractRoot = function() {
|
||||||
|
assertTrue('' in this.counters);
|
||||||
|
this.root = this.counters[''];
|
||||||
|
delete this.counters[''];
|
||||||
|
};
|
||||||
|
|
||||||
var testDriver = new Driver();
|
var testDriver = new Driver();
|
||||||
testDriver.execute();
|
testDriver.execute();
|
||||||
|
testDriver.extractRoot();
|
||||||
|
|
||||||
var counted = 0;
|
var counted = 0;
|
||||||
for (var c in testDriver.counters) {
|
for (var c in testDriver.counters) {
|
||||||
counted++;
|
counted++;
|
||||||
}
|
}
|
||||||
|
|
||||||
var flatProfile =
|
var flatProfileRoot = testDriver.profile.getFlatProfile().getRoot();
|
||||||
testDriver.profile.getFlatProfile().getRoot().exportChildren();
|
assertEquals(testDriver.root.self, flatProfileRoot.selfWeight);
|
||||||
|
assertEquals(testDriver.root.total, flatProfileRoot.totalWeight);
|
||||||
|
|
||||||
|
var flatProfile = flatProfileRoot.exportChildren();
|
||||||
assertEquals(counted, flatProfile.length, 'counted vs. flatProfile');
|
assertEquals(counted, flatProfile.length, 'counted vs. flatProfile');
|
||||||
for (var i = 0; i < flatProfile.length; ++i) {
|
for (var i = 0; i < flatProfile.length; ++i) {
|
||||||
var rec = flatProfile[i];
|
var rec = flatProfile[i];
|
||||||
@ -282,3 +299,50 @@ function assertNodeWeights(root, path, selfTicks, totalTicks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
(function testFunctionCalleesProfileTicks() {
|
||||||
|
var testDriver = new ProfileTestDriver();
|
||||||
|
testDriver.execute();
|
||||||
|
|
||||||
|
var pathWeights = [
|
||||||
|
[['lib2-f1'], 3, 5],
|
||||||
|
[['lib2-f1', 'lib2-f1'], 1, 1],
|
||||||
|
[['lib2-f1', 'lib1-f1'], 1, 1]
|
||||||
|
];
|
||||||
|
|
||||||
|
var profile = testDriver.profile.getTopDownProfile('lib2-f1');
|
||||||
|
var root = profile.getRoot();
|
||||||
|
for (var i = 0; i < pathWeights.length; ++i) {
|
||||||
|
var data = pathWeights[i];
|
||||||
|
assertNodeWeights(root, data[0], data[1], data[2]);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
(function testFunctionFlatProfileTicks() {
|
||||||
|
var testDriver = new ProfileTestDriver();
|
||||||
|
testDriver.execute();
|
||||||
|
|
||||||
|
var flatWeights = {
|
||||||
|
'lib2-f1': [1, 1],
|
||||||
|
'lib1-f1': [1, 1]
|
||||||
|
};
|
||||||
|
|
||||||
|
var flatProfileRoot =
|
||||||
|
testDriver.profile.getFlatProfile('lib2-f1').findOrAddChild('lib2-f1');
|
||||||
|
assertEquals(3, flatProfileRoot.selfWeight);
|
||||||
|
assertEquals(5, flatProfileRoot.totalWeight);
|
||||||
|
|
||||||
|
var flatProfile = flatProfileRoot.exportChildren();
|
||||||
|
assertEquals(2, flatProfile.length, 'counted vs. flatProfile');
|
||||||
|
for (var i = 0; i < flatProfile.length; ++i) {
|
||||||
|
var rec = flatProfile[i];
|
||||||
|
assertTrue(rec.label in flatWeights, 'uncounted: ' + rec.label);
|
||||||
|
var reference = flatWeights[rec.label];
|
||||||
|
assertEquals(reference[0], rec.selfWeight, 'self of ' + rec.label);
|
||||||
|
assertEquals(reference[1], rec.totalWeight, 'total of ' + rec.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
181
tools/profile.js
181
tools/profile.js
@ -185,25 +185,7 @@ devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the root of the top down call graph.
|
* Performs a BF traversal 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.
|
|
||||||
*
|
*
|
||||||
* @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
|
* @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.
|
* @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) {
|
devtools.profiler.Profile.prototype.getTopDownProfile = function(opt_label) {
|
||||||
if (!opt_root) {
|
return this.getTreeProfile_(this.topDownTree_, opt_label);
|
||||||
this.topDownTree_.computeTotalWeights();
|
};
|
||||||
return this.topDownTree_;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for calculating a tree profile.
|
||||||
|
*
|
||||||
|
* @param {devtools.profiler.Profile.CallTree} tree Call tree.
|
||||||
|
* @param {string} opt_label Node label.
|
||||||
|
*/
|
||||||
|
devtools.profiler.Profile.prototype.getTreeProfile_ = function(tree, opt_label) {
|
||||||
|
if (!opt_label) {
|
||||||
|
tree.computeTotalWeights();
|
||||||
|
return tree;
|
||||||
} else {
|
} else {
|
||||||
throw Error('not implemented');
|
var subTree = tree.cloneSubtree(opt_label);
|
||||||
|
subTree.computeTotalWeights();
|
||||||
|
return subTree;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates a bottom up profile 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.getBottomUpProfile = function(opt_root) {
|
devtools.profiler.Profile.prototype.getFlatProfile = function(opt_label) {
|
||||||
if (!opt_root) {
|
|
||||||
this.bottomUpTree_.computeTotalWeights();
|
|
||||||
return this.bottomUpTree_;
|
|
||||||
} else {
|
|
||||||
throw Error('not implemented');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates a flat profile of callees starting from the specified node.
|
|
||||||
*
|
|
||||||
* @param {devtools.profiler.CallTree.Node} opt_root Starting node.
|
|
||||||
*/
|
|
||||||
devtools.profiler.Profile.prototype.getFlatProfile = function(opt_root) {
|
|
||||||
var counters = new devtools.profiler.CallTree();
|
var counters = new devtools.profiler.CallTree();
|
||||||
|
var rootLabel = opt_label || devtools.profiler.CallTree.ROOT_NODE_LABEL;
|
||||||
var precs = {};
|
var precs = {};
|
||||||
|
precs[rootLabel] = 0;
|
||||||
|
var root = counters.findOrAddChild(rootLabel);
|
||||||
|
|
||||||
this.topDownTree_.computeTotalWeights();
|
this.topDownTree_.computeTotalWeights();
|
||||||
this.topDownTree_.traverseInDepth(
|
this.topDownTree_.traverseInDepth(
|
||||||
function onEnter(node) {
|
function onEnter(node) {
|
||||||
if (!(node.label in precs)) {
|
if (!(node.label in precs)) {
|
||||||
precs[node.label] = 0;
|
precs[node.label] = 0;
|
||||||
}
|
}
|
||||||
var rec = counters.findOrAddChild(node.label);
|
var nodeLabelIsRootLabel = node.label == rootLabel;
|
||||||
rec.selfWeight += node.selfWeight;
|
if (nodeLabelIsRootLabel || precs[rootLabel] > 0) {
|
||||||
if (precs[node.label] == 0) {
|
if (precs[rootLabel] == 0) {
|
||||||
rec.totalWeight += node.totalWeight;
|
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) {
|
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;
|
return counters;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -316,10 +334,17 @@ devtools.profiler.Profile.DynamicCodeEntry.prototype.getName = function() {
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
devtools.profiler.CallTree = function() {
|
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
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -359,10 +384,38 @@ devtools.profiler.CallTree.prototype.addPath = function(path) {
|
|||||||
*
|
*
|
||||||
* @param {string} label Child node label.
|
* @param {string} label Child node label.
|
||||||
*/
|
*/
|
||||||
devtools.profiler.CallTree.prototype.findOrAddChild = function(
|
devtools.profiler.CallTree.prototype.findOrAddChild = function(label) {
|
||||||
label, opt_parent) {
|
return this.root_.findOrAddChild(label);
|
||||||
var parent = opt_parent || this.root_;
|
};
|
||||||
return parent.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.
|
* @param {function(devtools.profiler.CallTree.Node, *)} f Visitor function.
|
||||||
* The second parameter is the result of calling 'f' on the parent node.
|
* 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();
|
var pairsToProcess = new ConsArray();
|
||||||
pairsToProcess.concat([{node: opt_start || this.root_, param: null}]);
|
pairsToProcess.concat([{node: this.root_, param: null}]);
|
||||||
while (!pairsToProcess.atEnd()) {
|
while (!pairsToProcess.atEnd()) {
|
||||||
var pair = pairsToProcess.next();
|
var pair = pairsToProcess.next();
|
||||||
var node = pair.node;
|
var node = pair.node;
|
||||||
@ -416,16 +468,14 @@ devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) {
|
|||||||
* prior to visiting node's children.
|
* prior to visiting node's children.
|
||||||
* @param {function(devtools.profiler.CallTree.Node)} exit A function called
|
* @param {function(devtools.profiler.CallTree.Node)} exit A function called
|
||||||
* after visiting node's children.
|
* after visiting node's children.
|
||||||
* @param {devtools.profiler.CallTree.Node} opt_start Starting node.
|
|
||||||
*/
|
*/
|
||||||
devtools.profiler.CallTree.prototype.traverseInDepth = function(
|
devtools.profiler.CallTree.prototype.traverseInDepth = function(enter, exit) {
|
||||||
enter, exit, opt_start) {
|
|
||||||
function traverse(node) {
|
function traverse(node) {
|
||||||
enter(node);
|
enter(node);
|
||||||
node.forEachChild(traverse);
|
node.forEachChild(traverse);
|
||||||
exit(node);
|
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.
|
* @param {string} label Child node label.
|
||||||
*/
|
*/
|
||||||
devtools.profiler.CallTree.Node.prototype.findOrAddChild = function(
|
devtools.profiler.CallTree.Node.prototype.findOrAddChild = function(label) {
|
||||||
label) {
|
|
||||||
return this.findChild(label) || this.addChild(label);
|
return this.findChild(label) || this.addChild(label);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,6 +77,26 @@ devtools.profiler.ViewBuilder.prototype.buildView = function(
|
|||||||
*/
|
*/
|
||||||
devtools.profiler.ProfileView = function(head) {
|
devtools.profiler.ProfileView = function(head) {
|
||||||
this.head = head;
|
this.head = head;
|
||||||
|
this.title = '';
|
||||||
|
this.uid = '';
|
||||||
|
this.heavyProfile = null;
|
||||||
|
this.treeProfile = null;
|
||||||
|
this.flatProfile = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates references between profiles. This is needed for WebKit
|
||||||
|
* ProfileView.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.prototype.updateProfilesRefs = function() {
|
||||||
|
var profileNames = ["treeProfile", "heavyProfile", "flatProfile"];
|
||||||
|
for (var i = 0; i < profileNames.length; ++i) {
|
||||||
|
var destProfile = this[profileNames[i]];
|
||||||
|
for (var j = 0; j < profileNames.length; ++j) {
|
||||||
|
destProfile[profileNames[j]] = this[profileNames[j]];
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -94,6 +114,73 @@ devtools.profiler.ProfileView.prototype.sort = function(sortFunc) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the profile view by self time, ascending.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.prototype.sortSelfTimeAscending = function() {
|
||||||
|
this.sort(function (node1, node2) {
|
||||||
|
return node1.selfTime - node2.selfTime; });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the profile view by self time, descending.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.prototype.sortSelfTimeDescending = function() {
|
||||||
|
this.sort(function (node1, node2) {
|
||||||
|
return node2.selfTime - node1.selfTime; });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the profile view by total time, ascending.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.prototype.sortTotalTimeAscending = function() {
|
||||||
|
this.sort(function (node1, node2) {
|
||||||
|
return node1.totalTime - node2.totalTime; });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the profile view by total time, descending.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.prototype.sortTotalTimeDescending = function() {
|
||||||
|
this.sort(function (node1, node2) {
|
||||||
|
return node2.totalTime - node1.totalTime; });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String comparator compatible with Array.sort requirements.
|
||||||
|
*
|
||||||
|
* @param {string} s1 First string.
|
||||||
|
* @param {string} s2 Second string.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.compareStrings = function(s1, s2) {
|
||||||
|
return s1 < s2 ? -1 : (s1 > s2 ? 1 : 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the profile view by function name, ascending.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.prototype.sortFunctionNameAscending = function() {
|
||||||
|
this.sort(function (node1, node2) {
|
||||||
|
return devtools.profiler.ProfileView.compareStrings(
|
||||||
|
node1.functionName, node2.functionName); });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the profile view by function name, descending.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.prototype.sortFunctionNameDescending = function() {
|
||||||
|
this.sort(function (node1, node2) {
|
||||||
|
return devtools.profiler.ProfileView.compareStrings(
|
||||||
|
node2.functionName, node1.functionName); });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Traverses profile view nodes in preorder.
|
* Traverses profile view nodes in preorder.
|
||||||
*
|
*
|
||||||
@ -125,12 +212,63 @@ devtools.profiler.ProfileView.prototype.traverse = function(f) {
|
|||||||
*/
|
*/
|
||||||
devtools.profiler.ProfileView.Node = function(
|
devtools.profiler.ProfileView.Node = function(
|
||||||
internalFuncName, totalTime, selfTime, head) {
|
internalFuncName, totalTime, selfTime, head) {
|
||||||
|
this.callIdentifier = 0;
|
||||||
this.internalFuncName = internalFuncName;
|
this.internalFuncName = internalFuncName;
|
||||||
|
this.initFuncInfo();
|
||||||
this.totalTime = totalTime;
|
this.totalTime = totalTime;
|
||||||
this.selfTime = selfTime;
|
this.selfTime = selfTime;
|
||||||
this.head = head;
|
this.head = head;
|
||||||
this.parent = null;
|
this.parent = null;
|
||||||
this.children = [];
|
this.children = [];
|
||||||
|
this.visible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegEx for stripping V8's prefixes of compiled functions.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.Node.FUNC_NAME_STRIP_RE =
|
||||||
|
/^(?:LazyCompile|Function): (.*)$/;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegEx for extracting script source URL and line number.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.Node.FUNC_NAME_PARSE_RE = /^([^ ]+) (.*):(\d+)$/;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegEx for removing protocol name from URL.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.Node.URL_PARSE_RE = /^(?:http:\/)?.*\/([^/]+)$/;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inits 'functionName', 'url', and 'lineNumber' fields using 'internalFuncName'
|
||||||
|
* field.
|
||||||
|
*/
|
||||||
|
devtools.profiler.ProfileView.Node.prototype.initFuncInfo = function() {
|
||||||
|
var nodeAlias = devtools.profiler.ProfileView.Node;
|
||||||
|
this.functionName = this.internalFuncName;
|
||||||
|
|
||||||
|
var strippedName = nodeAlias.FUNC_NAME_STRIP_RE.exec(this.functionName);
|
||||||
|
if (strippedName) {
|
||||||
|
this.functionName = strippedName[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedName = nodeAlias.FUNC_NAME_PARSE_RE.exec(this.functionName);
|
||||||
|
if (parsedName) {
|
||||||
|
this.url = parsedName[2];
|
||||||
|
var parsedUrl = nodeAlias.URL_PARSE_RE.exec(this.url);
|
||||||
|
if (parsedUrl) {
|
||||||
|
this.url = parsedUrl[1];
|
||||||
|
}
|
||||||
|
this.functionName = parsedName[1];
|
||||||
|
this.lineNumber = parsedName[3];
|
||||||
|
} else {
|
||||||
|
this.url = '';
|
||||||
|
this.lineNumber = 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +28,8 @@
|
|||||||
|
|
||||||
// A namespace stub. It will become more clear how to declare it properly
|
// A namespace stub. It will become more clear how to declare it properly
|
||||||
// during integration of this script into Dev Tools.
|
// during integration of this script into Dev Tools.
|
||||||
goog = { structs: {} };
|
var goog = goog || {};
|
||||||
|
goog.structs = goog.structs || {};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -273,6 +273,10 @@ TickProcessor.prototype.printStatistics = function() {
|
|||||||
this.printCounter(this.ticks_.unaccounted, this.ticks_.total);
|
this.printCounter(this.ticks_.unaccounted, this.ticks_.total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable initialization of 'funcName', 'url', 'lineNumber' as
|
||||||
|
// we don't use it and it just wastes time.
|
||||||
|
devtools.profiler.ProfileView.Node.prototype.initFuncInfo = function() {};
|
||||||
|
|
||||||
var flatProfile = this.profile_.getFlatProfile();
|
var flatProfile = this.profile_.getFlatProfile();
|
||||||
var flatView = this.viewBuilder_.buildView(flatProfile);
|
var flatView = this.viewBuilder_.buildView(flatProfile);
|
||||||
// Sort by self time, desc, then by name, desc.
|
// Sort by self time, desc, then by name, desc.
|
||||||
@ -361,8 +365,7 @@ TickProcessor.prototype.processProfile = function(
|
|||||||
profile, filterP, func) {
|
profile, filterP, func) {
|
||||||
for (var i = 0, n = profile.length; i < n; ++i) {
|
for (var i = 0, n = profile.length; i < n; ++i) {
|
||||||
var rec = profile[i];
|
var rec = profile[i];
|
||||||
// An empty record corresponds to a tree root.
|
if (!filterP(rec.internalFuncName)) {
|
||||||
if (!rec.internalFuncName || !filterP(rec.internalFuncName)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
func(rec);
|
func(rec);
|
||||||
|
Loading…
Reference in New Issue
Block a user