Index: Source/devtools/front_end/timeline/TimelineModel.js |
diff --git a/Source/devtools/front_end/timeline/TimelineModel.js b/Source/devtools/front_end/timeline/TimelineModel.js |
index 327abf5a35b97ebf31136c855f0b3507c7794237..16b07e5a5e1c73aaff4a812d5e7fab2eaa4d4043 100644 |
--- a/Source/devtools/front_end/timeline/TimelineModel.js |
+++ b/Source/devtools/front_end/timeline/TimelineModel.js |
@@ -1455,6 +1455,163 @@ WebInspector.TimelineModel.ProfileTreeNode = function() |
} |
/** |
+ * @param {!Array<!WebInspector.TracingModel.Event>} events |
+ * @param {number} startTime |
+ * @param {number} endTime |
+ * @param {!Array<!WebInspector.TraceEventFilter>} filters |
+ * @param {function(!WebInspector.TracingModel.Event):string} eventIdCallback |
+ * @return {!WebInspector.TimelineModel.ProfileTreeNode} |
+ */ |
+WebInspector.TimelineModel.buildTopDownTree = function(events, startTime, endTime, filters, eventIdCallback) |
+{ |
+ // Temporarily deposit a big enough value that exceeds the max recording time. |
+ var /** @const */ initialTime = 1e7; |
+ var root = new WebInspector.TimelineModel.ProfileTreeNode(); |
+ root.totalTime = initialTime; |
+ root.selfTime = initialTime; |
+ root.name = WebInspector.UIString("Top-Down Chart"); |
+ var parent = root; |
+ |
+ /** |
+ * @param {!WebInspector.TracingModel.Event} e |
+ * @return {boolean} |
+ */ |
+ function filter(e) |
+ { |
+ if (!e.endTime && e.phase !== WebInspector.TracingModel.Phase.Instant) |
+ return false; |
+ if (e.endTime <= startTime || e.startTime >= endTime) |
+ return false; |
+ if (WebInspector.TracingModel.isAsyncPhase(e.phase)) |
+ return false; |
+ for (var filter of filters) { |
+ if (!filter.accept(e)) |
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.TracingModel.Event} e |
+ */ |
+ function onStartEvent(e) |
+ { |
+ if (!filter(e)) |
+ return; |
+ var time = Math.min(endTime, e.endTime) - Math.max(startTime, e.startTime); |
+ var id = eventIdCallback(e); |
+ if (!parent.children) |
+ parent.children = /** @type {!Map<string,!WebInspector.TimelineModel.ProfileTreeNode>} */ (new Map()); |
+ var node = parent.children.get(id); |
+ if (node) { |
+ node.selfTime += time; |
+ node.totalTime += time; |
+ } else { |
+ node = new WebInspector.TimelineModel.ProfileTreeNode(); |
+ node.totalTime = time; |
+ node.selfTime = time; |
+ node.parent = parent; |
+ node.name = eventName(e); |
+ node.id = id; |
+ node.event = e; |
+ parent.children.set(id, node); |
+ } |
+ parent.selfTime -= time; |
+ if (parent.selfTime < 0) { |
+ console.log("Error: Negative self of " + parent.selfTime, e); |
+ parent.selfTime = 0; |
+ } |
+ parent = node; |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.TracingModel.Event} e |
+ */ |
+ function onEndEvent(e) |
+ { |
+ if (!filter(e)) |
+ return; |
+ parent = parent.parent; |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.TracingModel.Event} e |
+ * @return {string} |
+ */ |
+ function eventName(e) |
+ { |
+ if (e.name === "JSFrame") |
+ return WebInspector.beautifyFunctionName(e.args.data.functionName); |
+ if (e.name === "EventDispatch") |
+ return WebInspector.UIString("Event%s", e.args.data ? " (" + e.args.data.type + ")" : ""); |
+ return e.name; |
+ } |
+ |
+ WebInspector.TimelineModel.forEachEvent(events, onStartEvent, onEndEvent); |
+ root.totalTime -= root.selfTime; |
+ root.selfTime = 0; |
+ return root; |
+} |
+ |
+/** |
+ * @param {!WebInspector.TimelineModel.ProfileTreeNode} topDownTree |
+ * @param {function(!WebInspector.TimelineModel.ProfileTreeNode):?WebInspector.TimelineModel.ProfileTreeNode=} groupingCallback |
+ * @return {!WebInspector.TimelineModel.ProfileTreeNode} |
+ */ |
+WebInspector.TimelineModel.buildBottomUpTree = function(topDownTree, groupingCallback) |
+{ |
+ var buRoot = new WebInspector.TimelineModel.ProfileTreeNode(); |
+ buRoot.totalTime = 0; |
+ buRoot.name = WebInspector.UIString("Bottom-Up Chart"); |
+ /** @type {!Map<string,!WebInspector.TimelineModel.ProfileTreeNode>} */ |
+ buRoot.children = new Map(); |
+ processNode(topDownTree); |
+ |
+ /** |
+ * @param {!WebInspector.TimelineModel.ProfileTreeNode} tdNode |
+ */ |
+ function processNode(tdNode) |
+ { |
+ if (tdNode.selfTime > 0) { |
+ var buParent = groupingCallback && groupingCallback(tdNode) || buRoot; |
+ appendNode(tdNode, buParent); |
+ } |
+ if (tdNode.children) |
+ tdNode.children.forEach(processNode); |
+ } |
+ |
+ /** |
+ * @param {!WebInspector.TimelineModel.ProfileTreeNode} tdNode |
+ * @param {!WebInspector.TimelineModel.ProfileTreeNode} buParent |
+ */ |
+ function appendNode(tdNode, buParent) |
+ { |
+ var time = tdNode.selfTime; |
+ buParent.totalTime += time; |
+ while (tdNode.parent) { |
+ if (!buParent.children) |
+ buParent.children = /** @type {!Map<string,!WebInspector.TimelineModel.ProfileTreeNode>} */ (new Map()); |
+ var id = tdNode.id; |
+ var buNode = buParent.children.get(id); |
+ if (!buNode) { |
+ buNode = new WebInspector.TimelineModel.ProfileTreeNode(); |
+ buNode.totalTime = time; |
+ buNode.name = tdNode.name; |
+ buNode.event = tdNode.event; |
+ buNode.id = id; |
+ buParent.children.set(id, buNode); |
+ } else { |
+ buNode.totalTime += time; |
+ } |
+ tdNode = tdNode.parent; |
+ buParent = buNode; |
+ } |
+ } |
+ |
+ return buRoot; |
+} |
+ |
+/** |
* @constructor |
* @param {!WebInspector.TracingModel.Event} event |
*/ |