UNPKG

strong-arc

Version:

A visual suite for the StrongLoop API Platform

319 lines (258 loc) 10.9 kB
/* * Copyright (C) 2009 280 North Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Bottom Up Profiling shows the entire callstack backwards: // The root node is a representation of each individual function called, and each child of that node represents // a reverse-callstack showing how many of those calls came from it. So, unlike top-down, the statistics in // each child still represent the root node. We have to be particularly careful of recursion with this mode // because a root node can represent itself AND an ancestor. /** * @constructor * @extends {WebInspector.ProfileDataGridNode} * @param {!ProfilerAgent.CPUProfileNode} profileNode * @param {!WebInspector.TopDownProfileDataGridTree} owningTree */ WebInspector.BottomUpProfileDataGridNode = function(profileNode, owningTree) { WebInspector.ProfileDataGridNode.call(this, profileNode, owningTree, this._willHaveChildren(profileNode)); this._remainingNodeInfos = []; } WebInspector.BottomUpProfileDataGridNode.prototype = { /** * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode */ _takePropertiesFromProfileDataGridNode: function(profileDataGridNode) { this.save(); this.selfTime = profileDataGridNode.selfTime; this.totalTime = profileDataGridNode.totalTime; }, /** * When focusing, we keep just the members of the callstack. * @param {!WebInspector.ProfileDataGridNode} child */ _keepOnlyChild: function(child) { this.save(); this.removeChildren(); this.appendChild(child); }, /** * @param {number} aCallUID */ _exclude: function(aCallUID) { if (this._remainingNodeInfos) this.populate(); this.save(); var children = this.children; var index = this.children.length; while (index--) children[index]._exclude(aCallUID); var child = this.childrenByCallUID[aCallUID]; if (child) this.merge(child, true); }, /** * @override */ restore: function() { WebInspector.ProfileDataGridNode.prototype.restore.call(this); if (!this.children.length) this.hasChildren = this._willHaveChildren(this.profileNode); }, /** * @override * @param {!WebInspector.ProfileDataGridNode} child * @param {boolean} shouldAbsorb */ merge: function(child, shouldAbsorb) { this.selfTime -= child.selfTime; WebInspector.ProfileDataGridNode.prototype.merge.call(this, child, shouldAbsorb); }, /** * @override */ populateChildren: function() { WebInspector.BottomUpProfileDataGridNode._sharedPopulate(this); }, _willHaveChildren: function(profileNode) { // In bottom up mode, our parents are our children since we display an inverted tree. // However, we don't want to show the very top parent since it is redundant. return !!(profileNode.parent && profileNode.parent.parent); }, __proto__: WebInspector.ProfileDataGridNode.prototype } /** * @param {!WebInspector.BottomUpProfileDataGridNode|!WebInspector.BottomUpProfileDataGridTree} container */ WebInspector.BottomUpProfileDataGridNode._sharedPopulate = function(container) { var remainingNodeInfos = container._remainingNodeInfos; var count = remainingNodeInfos.length; for (var index = 0; index < count; ++index) { var nodeInfo = remainingNodeInfos[index]; var ancestor = nodeInfo.ancestor; var focusNode = nodeInfo.focusNode; var child = container.findChild(ancestor); // If we already have this child, then merge the data together. if (child) { var totalTimeAccountedFor = nodeInfo.totalTimeAccountedFor; child.selfTime += focusNode.selfTime; if (!totalTimeAccountedFor) child.totalTime += focusNode.totalTime; } else { // If not, add it as a true ancestor. // In heavy mode, we take our visual identity from ancestor node... child = new WebInspector.BottomUpProfileDataGridNode(ancestor, container.tree); if (ancestor !== focusNode) { // But the actual statistics from the "root" node (bottom of the callstack). child.selfTime = focusNode.selfTime; child.totalTime = focusNode.totalTime; } container.appendChild(child); } var parent = ancestor.parent; if (parent && parent.parent) { nodeInfo.ancestor = parent; child._remainingNodeInfos.push(nodeInfo); } } for (var i = 0; i < container.children.length; ++i) container.children[i].buildData(); delete container._remainingNodeInfos; } /** * @constructor * @extends {WebInspector.ProfileDataGridTree} * @param {!WebInspector.CPUProfileView} profileView * @param {!ProfilerAgent.CPUProfileNode} rootProfileNode */ WebInspector.BottomUpProfileDataGridTree = function(profileView, rootProfileNode) { WebInspector.ProfileDataGridTree.call(this, profileView, rootProfileNode); // Iterate each node in pre-order. var profileNodeUIDs = 0; var profileNodeGroups = [[], [rootProfileNode]]; var visitedProfileNodesForCallUID = {}; this._remainingNodeInfos = []; for (var profileNodeGroupIndex = 0; profileNodeGroupIndex < profileNodeGroups.length; ++profileNodeGroupIndex) { var parentProfileNodes = profileNodeGroups[profileNodeGroupIndex]; var profileNodes = profileNodeGroups[++profileNodeGroupIndex]; var count = profileNodes.length; for (var index = 0; index < count; ++index) { var profileNode = profileNodes[index]; if (!profileNode.UID) profileNode.UID = ++profileNodeUIDs; if (profileNode.parent) { // The total time of this ancestor is accounted for if we're in any form of recursive cycle. var visitedNodes = visitedProfileNodesForCallUID[profileNode.callUID]; var totalTimeAccountedFor = false; if (!visitedNodes) { visitedNodes = {} visitedProfileNodesForCallUID[profileNode.callUID] = visitedNodes; } else { // The total time for this node has already been accounted for iff one of it's parents has already been visited. // We can do this check in this style because we are traversing the tree in pre-order. var parentCount = parentProfileNodes.length; for (var parentIndex = 0; parentIndex < parentCount; ++parentIndex) { if (visitedNodes[parentProfileNodes[parentIndex].UID]) { totalTimeAccountedFor = true; break; } } } visitedNodes[profileNode.UID] = true; this._remainingNodeInfos.push({ ancestor:profileNode, focusNode:profileNode, totalTimeAccountedFor:totalTimeAccountedFor }); } var children = profileNode.children; if (children.length) { profileNodeGroups.push(parentProfileNodes.concat([profileNode])) profileNodeGroups.push(children); } } } // Populate the top level nodes. WebInspector.ProfileDataGridNode.populate(this); return this; } WebInspector.BottomUpProfileDataGridTree.prototype = { /** * When focusing, we keep the entire callstack up to this ancestor. * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode */ focus: function(profileDataGridNode) { if (!profileDataGridNode) return; this.save(); var currentNode = profileDataGridNode; var focusNode = profileDataGridNode; while (currentNode.parent && (currentNode instanceof WebInspector.ProfileDataGridNode)) { currentNode._takePropertiesFromProfileDataGridNode(profileDataGridNode); focusNode = currentNode; currentNode = currentNode.parent; if (currentNode instanceof WebInspector.ProfileDataGridNode) currentNode._keepOnlyChild(focusNode); } this.children = [focusNode]; this.totalTime = profileDataGridNode.totalTime; }, /** * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode */ exclude: function(profileDataGridNode) { if (!profileDataGridNode) return; this.save(); var excludedCallUID = profileDataGridNode.callUID; var excludedTopLevelChild = this.childrenByCallUID[excludedCallUID]; // If we have a top level node that is excluded, get rid of it completely (not keeping children), // since bottom up data relies entirely on the root node. if (excludedTopLevelChild) this.children.remove(excludedTopLevelChild); var children = this.children; var count = children.length; for (var index = 0; index < count; ++index) children[index]._exclude(excludedCallUID); if (this.lastComparator) this.sort(this.lastComparator, true); }, buildData: function() { }, /** * @override */ populateChildren: function() { WebInspector.BottomUpProfileDataGridNode._sharedPopulate(this); }, __proto__: WebInspector.ProfileDataGridTree.prototype }