| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2009 280 North Inc. All Rights Reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions | |
| 6 * are met: | |
| 7 * 1. Redistributions of source code must retain the above copyright | |
| 8 * notice, this list of conditions and the following disclaimer. | |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | |
| 10 * notice, this list of conditions and the following disclaimer in the | |
| 11 * documentation and/or other materials provided with the distribution. | |
| 12 * | |
| 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
| 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
| 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 24 */ | |
| 25 | |
| 26 // Bottom Up Profiling shows the entire callstack backwards: | |
| 27 // The root node is a representation of each individual function called, and eac
h child of that node represents | |
| 28 // a reverse-callstack showing how many of those calls came from it. So, unlike
top-down, the statistics in | |
| 29 // each child still represent the root node. We have to be particularly careful
of recursion with this mode | |
| 30 // because a root node can represent itself AND an ancestor. | |
| 31 | |
| 32 WebInspector.BottomUpProfileDataGridNode = function(/*ProfileView*/ profileView,
/*ProfileNode*/ profileNode, /*BottomUpProfileDataGridTree*/ owningTree) | |
| 33 { | |
| 34 // In bottom up mode, our parents are our children since we display an inver
ted tree. | |
| 35 // However, we don't want to show the very top parent since it is redundant. | |
| 36 var hasChildren = !!(profileNode.parent && profileNode.parent.parent); | |
| 37 | |
| 38 WebInspector.ProfileDataGridNode.call(this, profileView, profileNode, owning
Tree, hasChildren); | |
| 39 | |
| 40 this._remainingNodeInfos = []; | |
| 41 } | |
| 42 | |
| 43 WebInspector.BottomUpProfileDataGridNode.prototype = { | |
| 44 _takePropertiesFromProfileDataGridNode: function(/*ProfileDataGridNode*/ pro
fileDataGridNode) | |
| 45 { | |
| 46 this._save(); | |
| 47 | |
| 48 this.selfTime = profileDataGridNode.selfTime; | |
| 49 this.totalTime = profileDataGridNode.totalTime; | |
| 50 this.numberOfCalls = profileDataGridNode.numberOfCalls; | |
| 51 }, | |
| 52 | |
| 53 // When focusing, we keep just the members of the callstack. | |
| 54 _keepOnlyChild: function(/*ProfileDataGridNode*/ child) | |
| 55 { | |
| 56 this._save(); | |
| 57 | |
| 58 this.removeChildren(); | |
| 59 this.appendChild(child); | |
| 60 }, | |
| 61 | |
| 62 _exclude: function(aCallUID) | |
| 63 { | |
| 64 if (this._remainingNodeInfos) | |
| 65 this._populate(); | |
| 66 | |
| 67 this._save(); | |
| 68 | |
| 69 var children = this.children; | |
| 70 var index = this.children.length; | |
| 71 | |
| 72 while (index--) | |
| 73 children[index]._exclude(aCallUID); | |
| 74 | |
| 75 var child = this.childrenByCallUID[aCallUID]; | |
| 76 | |
| 77 if (child) | |
| 78 this._merge(child, true); | |
| 79 }, | |
| 80 | |
| 81 _merge: function(/*ProfileDataGridNode*/ child, /*Boolean*/ shouldAbsorb) | |
| 82 { | |
| 83 this.selfTime -= child.selfTime; | |
| 84 | |
| 85 WebInspector.ProfileDataGridNode.prototype._merge.call(this, child, shou
ldAbsorb); | |
| 86 }, | |
| 87 | |
| 88 _sharedPopulate: function() | |
| 89 { | |
| 90 var remainingNodeInfos = this._remainingNodeInfos; | |
| 91 var count = remainingNodeInfos.length; | |
| 92 | |
| 93 for (var index = 0; index < count; ++index) { | |
| 94 var nodeInfo = remainingNodeInfos[index]; | |
| 95 var ancestor = nodeInfo.ancestor; | |
| 96 var focusNode = nodeInfo.focusNode; | |
| 97 var child = this.findChild(ancestor); | |
| 98 | |
| 99 // If we already have this child, then merge the data together. | |
| 100 if (child) { | |
| 101 var totalTimeAccountedFor = nodeInfo.totalTimeAccountedFor; | |
| 102 | |
| 103 child.selfTime += focusNode.selfTime; | |
| 104 child.numberOfCalls += focusNode.numberOfCalls; | |
| 105 | |
| 106 if (!totalTimeAccountedFor) | |
| 107 child.totalTime += focusNode.totalTime; | |
| 108 } else { | |
| 109 // If not, add it as a true ancestor. | |
| 110 // In heavy mode, we take our visual identity from ancestor node
... | |
| 111 var child = new WebInspector.BottomUpProfileDataGridNode(this.pr
ofileView, ancestor, this.tree); | |
| 112 | |
| 113 if (ancestor !== focusNode) { | |
| 114 // but the actual statistics from the "root" node (bottom of
the callstack). | |
| 115 child.selfTime = focusNode.selfTime; | |
| 116 child.totalTime = focusNode.totalTime; | |
| 117 child.numberOfCalls = focusNode.numberOfCalls; | |
| 118 } | |
| 119 | |
| 120 this.appendChild(child); | |
| 121 } | |
| 122 | |
| 123 var parent = ancestor.parent; | |
| 124 if (parent && parent.parent) { | |
| 125 nodeInfo.ancestor = parent; | |
| 126 child._remainingNodeInfos.push(nodeInfo); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 delete this._remainingNodeInfos; | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 WebInspector.BottomUpProfileDataGridNode.prototype.__proto__ = WebInspector.Prof
ileDataGridNode.prototype; | |
| 135 | |
| 136 WebInspector.BottomUpProfileDataGridTree = function(/*ProfileView*/ aProfileView
, /*ProfileNode*/ aProfileNode) | |
| 137 { | |
| 138 WebInspector.ProfileDataGridTree.call(this, aProfileView, aProfileNode); | |
| 139 | |
| 140 // Iterate each node in pre-order. | |
| 141 var profileNodeUIDs = 0; | |
| 142 var profileNodeGroups = [[], [aProfileNode]]; | |
| 143 var visitedProfileNodesForCallUID = {}; | |
| 144 | |
| 145 this._remainingNodeInfos = []; | |
| 146 | |
| 147 for (var profileNodeGroupIndex = 0; profileNodeGroupIndex < profileNodeGroup
s.length; ++profileNodeGroupIndex) { | |
| 148 var parentProfileNodes = profileNodeGroups[profileNodeGroupIndex]; | |
| 149 var profileNodes = profileNodeGroups[++profileNodeGroupIndex]; | |
| 150 var count = profileNodes.length; | |
| 151 | |
| 152 for (var index = 0; index < count; ++index) { | |
| 153 var profileNode = profileNodes[index]; | |
| 154 | |
| 155 if (!profileNode.UID) | |
| 156 profileNode.UID = ++profileNodeUIDs; | |
| 157 | |
| 158 if (profileNode.head && profileNode !== profileNode.head) { | |
| 159 // The total time of this ancestor is accounted for if we're in
any form of recursive cycle. | |
| 160 var visitedNodes = visitedProfileNodesForCallUID[profileNode.cal
lUID]; | |
| 161 var totalTimeAccountedFor = false; | |
| 162 | |
| 163 if (!visitedNodes) { | |
| 164 visitedNodes = {} | |
| 165 visitedProfileNodesForCallUID[profileNode.callUID] = visited
Nodes; | |
| 166 } else { | |
| 167 // The total time for this node has already been accounted f
or iff one of it's parents has already been visited. | |
| 168 // We can do this check in this style because we are travers
ing the tree in pre-order. | |
| 169 var parentCount = parentProfileNodes.length; | |
| 170 for (var parentIndex = 0; parentIndex < parentCount; ++paren
tIndex) { | |
| 171 if (visitedNodes[parentProfileNodes[parentIndex].UID]) { | |
| 172 totalTimeAccountedFor = true; | |
| 173 break; | |
| 174 } | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 visitedNodes[profileNode.UID] = true; | |
| 179 | |
| 180 this._remainingNodeInfos.push({ ancestor:profileNode, focusNode:
profileNode, totalTimeAccountedFor:totalTimeAccountedFor }); | |
| 181 } | |
| 182 | |
| 183 var children = profileNode.children; | |
| 184 if (children.length) { | |
| 185 profileNodeGroups.push(parentProfileNodes.concat([profileNode])) | |
| 186 profileNodeGroups.push(children); | |
| 187 } | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 // Populate the top level nodes. | |
| 192 WebInspector.BottomUpProfileDataGridNode.prototype._populate.call(this); | |
| 193 | |
| 194 return this; | |
| 195 } | |
| 196 | |
| 197 WebInspector.BottomUpProfileDataGridTree.prototype = { | |
| 198 // When focusing, we keep the entire callstack up to this ancestor. | |
| 199 focus: function(/*ProfileDataGridNode*/ profileDataGridNode) | |
| 200 { | |
| 201 if (!profileDataGridNode) | |
| 202 return; | |
| 203 | |
| 204 this._save(); | |
| 205 | |
| 206 var currentNode = profileDataGridNode; | |
| 207 var focusNode = profileDataGridNode; | |
| 208 | |
| 209 while (currentNode.parent && (currentNode instanceof WebInspector.Profil
eDataGridNode)) { | |
| 210 currentNode._takePropertiesFromProfileDataGridNode(profileDataGridNo
de); | |
| 211 | |
| 212 focusNode = currentNode; | |
| 213 currentNode = currentNode.parent; | |
| 214 | |
| 215 if (currentNode instanceof WebInspector.ProfileDataGridNode) | |
| 216 currentNode._keepOnlyChild(focusNode); | |
| 217 } | |
| 218 | |
| 219 this.children = [focusNode]; | |
| 220 this.totalTime = profileDataGridNode.totalTime; | |
| 221 }, | |
| 222 | |
| 223 exclude: function(/*ProfileDataGridNode*/ profileDataGridNode) | |
| 224 { | |
| 225 if (!profileDataGridNode) | |
| 226 return; | |
| 227 | |
| 228 this._save(); | |
| 229 | |
| 230 var excludedCallUID = profileDataGridNode.callUID; | |
| 231 var excludedTopLevelChild = this.childrenByCallUID[excludedCallUID]; | |
| 232 | |
| 233 // If we have a top level node that is excluded, get rid of it completel
y (not keeping children), | |
| 234 // since bottom up data relies entirely on the root node. | |
| 235 if (excludedTopLevelChild) | |
| 236 this.children.remove(excludedTopLevelChild); | |
| 237 | |
| 238 var children = this.children; | |
| 239 var count = children.length; | |
| 240 | |
| 241 for (var index = 0; index < count; ++index) | |
| 242 children[index]._exclude(excludedCallUID); | |
| 243 | |
| 244 if (this.lastComparator) | |
| 245 this.sort(this.lastComparator, true); | |
| 246 }, | |
| 247 | |
| 248 _sharedPopulate: WebInspector.BottomUpProfileDataGridNode.prototype._sharedP
opulate | |
| 249 } | |
| 250 | |
| 251 WebInspector.BottomUpProfileDataGridTree.prototype.__proto__ = WebInspector.Prof
ileDataGridTree.prototype; | |
| 252 | |
| OLD | NEW |