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 |