Commit 3b17e107 authored by alph's avatar alph Committed by Commit bot

DevTools: Refactor Profiler domain interface

Flatten nodes into an array.

Review-Url: https://codereview.chromium.org/2244783004
Cr-Commit-Position: refs/heads/master@{#412613}
parent efc134da
......@@ -46,22 +46,15 @@ function test()
function checkInnerProfile(profile)
{
InspectorTest.log("SUCCESS: retrieved '42' profile");
var root = profile.head;
if (!findFunctionInProfile(root, "collectProfiles"))
if (!findFunctionInProfile(profile.nodes, "collectProfiles"))
return InspectorTest.fail("collectProfiles function not found in the profile: " + JSON.stringify(profile, null, 4));
InspectorTest.log("SUCCESS: found 'collectProfiles' function in the profile");
InspectorTest.completeTest();
}
function findFunctionInProfile(node, functionName)
function findFunctionInProfile(nodes, functionName)
{
if (node.callFrame.functionName === functionName)
return true;
var children = node.children;
for (var i = 0; i < children.length; ++i)
if (findFunctionInProfile(children[i], functionName))
return true;
return false;
return nodes.some(n => n.callFrame.functionName === functionName);
}
}
</script>
......
......@@ -6,15 +6,15 @@
function test()
{
var nodes = 1000;
function buildTree(count)
var nodesCount = 1000;
function buildTree(startId, count)
{
// Build a call tree of a chain form: foo1 -> foo2 -> foo3 -> ...
// This should give a O(n^2) nodes in bottom-up tree.
var node = null;
for (var i = count; i > 0; --i) {
var child = node;
node = {
var nodes = [];
for (var i = 1; i <= count; ++i) {
nodes.push({
"id": startId + i - 1,
"callFrame":
{
"functionName": "foo" + i,
......@@ -23,13 +23,10 @@ function test()
"lineNumber": i
},
"hitCount": 10,
"callUID": 10000 + i,
"children": []
};
if (child)
node.children.push(child);
"children": i < count ? [startId + i] : []
});
}
return node;
return nodes;
}
var profileAndExpectations = {
"title": "profile1",
......@@ -37,18 +34,21 @@ function test()
return WebInspector.targetManager.targets()[0];
},
"_profile": {
"head": {
"callFrame":
"nodes": [
{
"functionName": "(root)",
"scriptId": "0",
"url": "a.js",
"lineNumber": 0,
"id": 0,
"callFrame":
{
"functionName": "(root)",
"scriptId": "0",
"url": "a.js",
"lineNumber": 0,
},
"hitCount": 1,
"children": [1,2]
},
"hitCount": 1,
"callUID": 1000,
"children": [
{
"id": 1,
"callFrame":
{
"functionName": "(idle)",
......@@ -57,15 +57,12 @@ function test()
"lineNumber": 1
},
"hitCount": 2,
"callUID": 2,
"children": []
},
buildTree(nodes)
]
},
}
].concat(buildTree(2, nodesCount)),
"idleTime": 0.002,
"startTime": 0,
"endTime": nodes * 0.01 + 0.003
"endTime": nodesCount * 0.01 + 0.003
}
};
var view = new WebInspector.CPUProfileView(profileAndExpectations);
......
......@@ -12,18 +12,21 @@ function test()
return WebInspector.targetManager.targets()[0];
},
"_profile": {
"head": {
"callFrame":
"nodes": [
{
"functionName": "(root)",
"scriptId": "0",
"url": "a.js",
"lineNumber": 0
"id": 0,
"callFrame":
{
"functionName": "(root)",
"scriptId": "0",
"url": "a.js",
"lineNumber": 0
},
"hitCount": 350,
"children": [1, 2, 5]
},
"hitCount": 350,
"callUID": 1000,
"children": [
{
"id": 1,
"callFrame":
{
"functionName": "(idle)",
......@@ -32,10 +35,10 @@ function test()
"lineNumber": 1
},
"hitCount": 1000,
"callUID": 2,
"children": []
},
{
"id": 2,
"callFrame":
{
"functionName": "A",
......@@ -44,36 +47,34 @@ function test()
"lineNumber": 4642
},
"hitCount": 250,
"callUID": 1001,
"children": [
"children": [3]
},
{
"id": 3,
"callFrame":
{
"callFrame":
{
"functionName": "C",
"scriptId": "0",
"url": "a.js",
"lineNumber": 525
},
"hitCount": 100,
"callUID": 2000,
"children": [
{
"callFrame":
{
"functionName": "D",
"scriptId": "0",
"url": "a.js",
"lineNumber": 425
},
"hitCount": 20,
"callUID": 3000,
"children": []
}
]
}
]
"functionName": "C",
"scriptId": "0",
"url": "a.js",
"lineNumber": 525
},
"hitCount": 100,
"children": [4]
},
{
"id": 4,
"callFrame":
{
"functionName": "D",
"scriptId": "0",
"url": "a.js",
"lineNumber": 425
},
"hitCount": 20,
"children": []
},
{
"id": 5,
"callFrame":
{
"functionName": "B",
......@@ -82,37 +83,33 @@ function test()
"lineNumber": 4662
},
"hitCount": 150,
"callUID": 1002,
"children": [
"children": [6]
},
{
"id": 6,
"callFrame":
{
"functionName": "C",
"scriptId": "0",
"url": "a.js",
"lineNumber": 525
},
"hitCount": 100,
"children": [7]
},
{
"id": 7,
"callFrame":
{
"callFrame":
{
"functionName": "C",
"scriptId": "0",
"url": "a.js",
"lineNumber": 525
},
"hitCount": 100,
"callUID": 2000,
"children": [
{
"callFrame":
{
"functionName": "D",
"scriptId": "0",
"url": "a.js",
"lineNumber": 425
},
"hitCount": 30,
"callUID": 3000,
"children": []
}
]
}
]
"functionName": "D",
"scriptId": "0",
"url": "a.js",
"lineNumber": 425
},
"hitCount": 30,
"children": []
}
]
},
],
"startTime": 0,
"endTime": 1.000
}
......
......@@ -931,6 +931,7 @@ function test()
var profile = profileAndExpectations._profile;
var startTime = profile.startTime * 1000;
var endTime = profile.endTime * 1000;
profile.nodes = flattenNodes(profile.head);
profile.startTime /= 1000;
profile.endTime /= 1000;
var samplingInterval = (endTime - startTime) / (profile.samples.length - 1);
......@@ -949,6 +950,14 @@ function test()
console.log(Object.values(overviewPane._calculateDrawData(2)));
console.log(Object.values(overviewPane._calculateDrawData(1)));
InspectorTest.completeTest();
function flattenNodes(node)
{
var childrenIds = node.children.map(n => n.id);
var result = node.children.reduce((res, n) => res.concat(flattenNodes(n)), [node]);
node.children = childrenIds;
return result;
}
}
</script>
......
......@@ -128,6 +128,7 @@ function test()
"endTime": 100000 + 111.110 + 22.220 + 1.000,
"samples": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
};
profile.nodes = flattenNodes(profile.head);
var model = new WebInspector.CPUProfileDataModel(profile);
printTree("", model.profileHead);
function printTree(padding, node)
......@@ -137,6 +138,13 @@ function test()
}
InspectorTest.addResult(model.samples.join(", "));
InspectorTest.completeTest();
function flattenNodes(node)
{
var childrenIds = node.children.map(n => n.id);
var result = node.children.reduce((res, n) => res.concat(flattenNodes(n)), [node]);
node.children = childrenIds;
return result;
}
}
</script>
......
......@@ -9,43 +9,49 @@ function test()
var cpuProfile = {
startTime: 10,
endTime: 20,
head: {
callFrame: { functionName: "(root)" },
hitCount: 0,
children: [
{
callFrame: { functionName: "foo1" },
hitCount: 100,
positionTicks: [{line:1, ticks:10}, {line:2, ticks:20}, {line:3, ticks:30}, {line:4, ticks:40}],
children: []
},
{
callFrame: { functionName: "foo2" },
hitCount: 200,
positionTicks: [{line:100, ticks:1}, {line:102, ticks:190}],
children: []
},
{
callFrame: { functionName: "null" },
hitCount: 0,
positionTicks: [],
children: [
{
callFrame: { functionName: "bar" },
hitCount: 300,
positionTicks: [{line:55, ticks:22}],
children: []
},
{
callFrame: { functionName: "baz" },
hitCount: 400,
// no positionTicks for the node.
children: []
}
]
}
]
}
nodes: [
{
id: 0,
callFrame: { functionName: "(root)" },
hitCount: 0,
children: [1, 2]
},
{
id: 1,
callFrame: { functionName: "foo1" },
hitCount: 100,
positionTicks: [{line:1, ticks:10}, {line:2, ticks:20}, {line:3, ticks:30}, {line:4, ticks:40}],
children: []
},
{
id: 2,
callFrame: { functionName: "foo2" },
hitCount: 200,
positionTicks: [{line:100, ticks:1}, {line:102, ticks:190}],
children: [3]
},
{
id: 3,
callFrame: { functionName: "null" },
hitCount: 0,
positionTicks: [],
children: [4, 5]
},
{
id: 4,
callFrame: { functionName: "bar" },
hitCount: 300,
positionTicks: [{line:55, ticks:22}],
children: []
},
{
id: 5,
callFrame: { functionName: "baz" },
hitCount: 400,
// no positionTicks for the node.
children: []
}
]
};
InspectorTest.addSniffer(WebInspector.CodeMirrorTextEditor.prototype, "setGutterDecoration", decorationAdded, true);
......@@ -56,17 +62,11 @@ function test()
InspectorTest.addResult(`${line} ${type} ${element.textContent} ${element.style.backgroundColor}`);
}
function setUrls(url, node)
{
node.callFrame.url = url;
node.children.forEach(setUrls.bind(null, url));
}
function frameRevealed(frame)
{
var url = frame.uiSourceCode().url();
InspectorTest.addResult(InspectorTest.formatters.formatAsURL(url));
setUrls(url, cpuProfile.head);
cpuProfile.nodes.forEach(n => n.callFrame.url = url);
var lineProfile = new WebInspector.LineLevelProfile.instance();
lineProfile.appendCPUProfile(new WebInspector.CPUProfileDataModel(cpuProfile));
setTimeout(() => InspectorTest.completeTest(), 0);
......
......@@ -262,24 +262,28 @@ function test()
var cpuProfile = {
startTime: 420,
endTime: 430,
head: {
callFrame: { functionName: "(root)" },
id: 1,
children: [{
nodes: [
{
callFrame: { functionName: "(root)" },
id: 1,
children: [2]
},
{
callFrame: { functionName: "foo" },
id: 2,
children: [{
callFrame: { functionName: "bar" },
id: 3,
children: []
},
{
callFrame: { functionName: "baz" },
id: 4,
children: []
}]
}]
},
children: [3, 4]
},
{
callFrame: { functionName: "bar" },
id: 3,
children: []
},
{
callFrame: { functionName: "baz" },
id: 4,
children: []
}
],
timestamps: [421000, 422000, 423000, 424000, 425000, 426000, 427000, 428000, 429000],
samples: [2, 2, 3, 3, 3, 4, 4, 2, 2 ]
};
......
......@@ -42,7 +42,7 @@ WebInspector.CPUProfileDataModel = function(profile)
this.profileStartTime = profile.startTime * 1000;
this.profileEndTime = profile.endTime * 1000;
this.totalHitCount = 0;
this.profileHead = this._translateProfileTree(profile.head);
this.profileHead = this._translateProfileTree(profile.nodes);
WebInspector.ProfileTreeModel.call(this, this.profileHead);
this._extractMetaNodes();
if (this.samples) {
......@@ -54,19 +54,11 @@ WebInspector.CPUProfileDataModel = function(profile)
WebInspector.CPUProfileDataModel.prototype = {
/**
* @param {!ProfilerAgent.CPUProfileNode} root
* @param {!Array<!ProfilerAgent.CPUProfileNode>} nodes
* @return {!WebInspector.CPUProfileNode}
*/
_translateProfileTree: function(root)
_translateProfileTree: function(nodes)
{
/**
* @param {!ProfilerAgent.CPUProfileNode} node
* @return {number}
*/
function computeHitCountForSubtree(node)
{
return node.children.reduce((acc, node) => acc + computeHitCountForSubtree(node), node.hitCount);
}
/**
* @param {!ProfilerAgent.CPUProfileNode} node
* @return {boolean}
......@@ -77,14 +69,21 @@ WebInspector.CPUProfileDataModel.prototype = {
return !!node.callFrame.url && node.callFrame.url.startsWith("native ");
return !!node.url && node.url.startsWith("native ");
}
this.totalHitCount = computeHitCountForSubtree(root);
/** @type {!Map<number, !ProfilerAgent.CPUProfileNode>} */
var nodeByIdMap = new Map();
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
nodeByIdMap.set(node.id, node);
}
this.totalHitCount = nodes.reduce((acc, node) => acc + node.hitCount, 0);
var sampleTime = (this.profileEndTime - this.profileStartTime) / this.totalHitCount;
var keepNatives = !!WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get();
var root = nodes[0];
/** @type {!Map<number, number>} */
var idMap = new Map([[root.id, root.id]]);
var resultRoot = new WebInspector.CPUProfileNode(root, sampleTime);
var parentNodeStack = root.children.map(() => resultRoot);
var sourceNodeStack = root.children;
var sourceNodeStack = root.children.map(id => nodeByIdMap.get(id));
while (sourceNodeStack.length) {
var parentNode = parentNodeStack.pop();
var sourceNode = sourceNodeStack.pop();
......@@ -97,7 +96,7 @@ WebInspector.CPUProfileDataModel.prototype = {
}
idMap.set(sourceNode.id, parentNode.id);
parentNodeStack.push.apply(parentNodeStack, sourceNode.children.map(() => parentNode));
sourceNodeStack.push.apply(sourceNodeStack, sourceNode.children);
sourceNodeStack.push.apply(sourceNodeStack, sourceNode.children.map(id => nodeByIdMap.get(id)));
}
if (this.samples)
this.samples = this.samples.map(id => idMap.get(id));
......
......@@ -50,12 +50,10 @@ std::unique_ptr<protocol::Profiler::CPUProfileNode> buildInspectorObjectFor(v8::
{
v8::HandleScope handleScope(isolate);
std::unique_ptr<protocol::Array<protocol::Profiler::CPUProfileNode>> children = protocol::Array<protocol::Profiler::CPUProfileNode>::create();
std::unique_ptr<protocol::Array<int>> children = protocol::Array<int>::create();
const int childrenCount = node->GetChildrenCount();
for (int i = 0; i < childrenCount; i++) {
const v8::CpuProfileNode* child = node->GetChild(i);
children->addItem(buildInspectorObjectFor(isolate, child));
}
for (int i = 0; i < childrenCount; i++)
children->addItem(node->GetChild(i)->GetNodeId());
std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>> positionTicks = buildInspectorObjectForPositionTicks(node);
......@@ -94,10 +92,21 @@ std::unique_ptr<protocol::Array<double>> buildInspectorObjectForTimestamps(v8::C
return array;
}
void flattenNodesTree(v8::Isolate* isolate, const v8::CpuProfileNode* node, protocol::Array<protocol::Profiler::CPUProfileNode>* list)
{
list->addItem(buildInspectorObjectFor(isolate, node));
const int childrenCount = node->GetChildrenCount();
for (int i = 0; i < childrenCount; i++)
flattenNodesTree(isolate, node->GetChild(i), list);
}
std::unique_ptr<protocol::Profiler::CPUProfile> createCPUProfile(v8::Isolate* isolate, v8::CpuProfile* v8profile)
{
std::unique_ptr<protocol::Array<protocol::Profiler::CPUProfileNode>> nodes = protocol::Array<protocol::Profiler::CPUProfileNode>::create();
flattenNodesTree(isolate, v8profile->GetTopDownRoot(), nodes.get());
std::unique_ptr<protocol::Profiler::CPUProfile> profile = protocol::Profiler::CPUProfile::create()
.setHead(buildInspectorObjectFor(isolate, v8profile->GetTopDownRoot()))
.setNodes(std::move(nodes))
.setStartTime(static_cast<double>(v8profile->GetStartTime()) / 1000000)
.setEndTime(static_cast<double>(v8profile->GetEndTime()) / 1000000).build();
profile->setSamples(buildInspectorObjectForSamples(v8profile));
......
......@@ -777,12 +777,12 @@
"type": "object",
"description": "CPU Profile node. Holds callsite information, execution statistics and child nodes.",
"properties": [
{ "name": "id", "type": "integer", "description": "Unique id of the node." },
{ "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." },
{ "name": "hitCount", "type": "integer", "description": "Number of samples where this node was on top of the call stack." },
{ "name": "children", "type": "array", "items": { "$ref": "CPUProfileNode" }, "description": "Child nodes." },
{ "name": "deoptReason", "type": "string", "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."},
{ "name": "id", "type": "integer", "description": "Unique id of the node." },
{ "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "description": "An array of source position ticks." }
{ "name": "children", "type": "array", "items": { "type": "integer" }, "description": "Child node ids." },
{ "name": "deoptReason", "type": "string", "experimental": true, "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."},
{ "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "experimental": true, "description": "An array of source position ticks." }
]
},
{
......@@ -790,7 +790,7 @@
"type": "object",
"description": "Profile.",
"properties": [
{ "name": "head", "$ref": "CPUProfileNode" },
{ "name": "nodes", "type": "array", "items": { "$ref": "CPUProfileNode" }, "description": "The list of profile nodes. First item is the root node." },
{ "name": "startTime", "type": "number", "description": "Profiling start time in seconds." },
{ "name": "endTime", "type": "number", "description": "Profiling end time in seconds." },
{ "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." },
......@@ -800,6 +800,7 @@
{
"id": "PositionTickInfo",
"type": "object",
"experimental": true,
"description": "Specifies a number of samples attributed to a certain source position.",
"properties": [
{ "name": "line", "type": "integer", "description": "Source line number (1-based)." },
......@@ -837,7 +838,7 @@
"parameters": [
{ "name": "id", "type": "string" },
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." },
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argument to console.profile()." }
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." }
],
"description": "Sent when new profile recodring is started using console.profile() call."
},
......@@ -847,7 +848,7 @@
{ "name": "id", "type": "string" },
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." },
{ "name": "profile", "$ref": "CPUProfile" },
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argunet to console.profile()." }
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." }
]
}
]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment