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.BottomUpProfileDataGridTree = function(/*ProfileView*/ aProfileView
, /*ProfileNode*/ aProfileNode) |
| 33 { |
| 34 WebInspector.ProfileDataGridTree.call(this, aProfileView, aProfileNode); |
| 35 |
| 36 // Iterate each node in pre-order. |
| 37 var profileNodeUIDs = 0; |
| 38 var profileNodeGroups = [[], [aProfileNode]]; |
| 39 var visitedProfileNodesForCallUID = {}; |
| 40 |
| 41 this._remainingNodeInfos = []; |
| 42 |
| 43 for (var profileNodeGroupIndex = 0; profileNodeGroupIndex < profileNodeGroup
s.length; ++profileNodeGroupIndex) { |
| 44 var parentProfileNodes = profileNodeGroups[profileNodeGroupIndex]; |
| 45 var profileNodes = profileNodeGroups[++profileNodeGroupIndex]; |
| 46 var count = profileNodes.length; |
| 47 |
| 48 for (var index = 0; index < count; ++index) { |
| 49 var profileNode = profileNodes[index]; |
| 50 |
| 51 if (!profileNode.UID) |
| 52 profileNode.UID = ++profileNodeUIDs; |
| 53 |
| 54 if (profileNode.head && profileNode !== profileNode.head) { |
| 55 // The total time of this ancestor is accounted for if we're in
any form of recursive cycle. |
| 56 var visitedNodes = visitedProfileNodesForCallUID[profileNode.cal
lUID]; |
| 57 var totalTimeAccountedFor = false; |
| 58 |
| 59 if (!visitedNodes) { |
| 60 visitedNodes = {} |
| 61 visitedProfileNodesForCallUID[profileNode.callUID] = visited
Nodes; |
| 62 } else { |
| 63 // The total time for this node has already been accounted f
or iff one of it's parents has already been visited. |
| 64 // We can do this check in this style because we are travers
ing the tree in pre-order. |
| 65 var parentCount = parentProfileNodes.length; |
| 66 for (var parentIndex = 0; parentIndex < parentCount; ++paren
tIndex) { |
| 67 if (visitedNodes[parentProfileNodes[parentIndex].UID]) { |
| 68 totalTimeAccountedFor = true; |
| 69 break; |
| 70 } |
| 71 } |
| 72 } |
| 73 |
| 74 visitedNodes[profileNode.UID] = true; |
| 75 |
| 76 this._remainingNodeInfos.push({ ancestor:profileNode, focusNode:
profileNode, totalTimeAccountedFor:totalTimeAccountedFor }); |
| 77 } |
| 78 |
| 79 var children = profileNode.children; |
| 80 if (children.length) { |
| 81 profileNodeGroups.push(parentProfileNodes.concat([profileNode])) |
| 82 profileNodeGroups.push(children); |
| 83 } |
| 84 } |
| 85 } |
| 86 |
| 87 // Populate the top level nodes. |
| 88 WebInspector.BottomUpProfileDataGridNode.prototype._populate.call(this); |
| 89 |
| 90 return this; |
| 91 } |
| 92 |
| 93 WebInspector.BottomUpProfileDataGridTree.prototype = { |
| 94 // When focusing, we keep the entire callstack up to this ancestor. |
| 95 focus: function(/*ProfileDataGridNode*/ profileDataGridNode) |
| 96 { |
| 97 if (!profileDataGridNode) |
| 98 return; |
| 99 |
| 100 this._save(); |
| 101 |
| 102 var currentNode = profileDataGridNode; |
| 103 var focusNode = profileDataGridNode; |
| 104 |
| 105 while (currentNode.parent && (currentNode instanceof WebInspector.Profil
eDataGridNode)) { |
| 106 currentNode._takePropertiesFromProfileDataGridNode(profileDataGridNo
de); |
| 107 |
| 108 focusNode = currentNode; |
| 109 currentNode = currentNode.parent; |
| 110 |
| 111 if (currentNode instanceof WebInspector.ProfileDataGridNode) |
| 112 currentNode._keepOnlyChild(focusNode); |
| 113 } |
| 114 |
| 115 this.children = [focusNode]; |
| 116 this.totalTime = profileDataGridNode.totalTime; |
| 117 }, |
| 118 |
| 119 exclude: function(/*ProfileDataGridNode*/ profileDataGridNode) |
| 120 { |
| 121 if (!profileDataGridNode) |
| 122 return; |
| 123 |
| 124 this._save(); |
| 125 |
| 126 var excludedCallUID = profileDataGridNode.callUID; |
| 127 var excludedTopLevelChild = this.childrenByCallUID[excludedCallUID]; |
| 128 |
| 129 // If we have a top level node that is excluded, get rid of it completel
y (not keeping children), |
| 130 // since bottom up data relies entirely on the root node. |
| 131 if (excludedTopLevelChild) |
| 132 this.children.remove(excludedTopLevelChild); |
| 133 |
| 134 var children = this.children; |
| 135 var count = children.length; |
| 136 |
| 137 for (var index = 0; index < count; ++index) |
| 138 children[index]._exclude(excludedCallUID); |
| 139 |
| 140 if (this.lastComparator) |
| 141 this.sort(this.lastComparator, true); |
| 142 } |
| 143 } |
| 144 |
| 145 WebInspector.BottomUpProfileDataGridTree.prototype.__proto__ = WebInspector.Prof
ileDataGridTree.prototype; |
| 146 |
| 147 WebInspector.BottomUpProfileDataGridNode = function(/*ProfileView*/ profileView,
/*ProfileNode*/ profileNode, /*BottomUpProfileDataGridTree*/ owningTree) |
| 148 { |
| 149 // In bottom up mode, our parents are our children since we display an inver
ted tree. |
| 150 // However, we don't want to show the very top parent since it is redundant. |
| 151 var hasChildren = !!(profileNode.parent && profileNode.parent.parent); |
| 152 |
| 153 WebInspector.ProfileDataGridNode.call(this, profileView, profileNode, owning
Tree, hasChildren); |
| 154 |
| 155 this._remainingNodeInfos = []; |
| 156 } |
| 157 |
| 158 WebInspector.BottomUpProfileDataGridNode.prototype = { |
| 159 _takePropertiesFromProfileDataGridNode: function(/*ProfileDataGridNode*/ pro
fileDataGridNode) |
| 160 { |
| 161 this._save(); |
| 162 |
| 163 this.selfTime = profileDataGridNode.selfTime; |
| 164 this.totalTime = profileDataGridNode.totalTime; |
| 165 this.numberOfCalls = profileDataGridNode.numberOfCalls; |
| 166 }, |
| 167 |
| 168 // When focusing, we keep just the members of the callstack. |
| 169 _keepOnlyChild: function(/*ProfileDataGridNode*/ child) |
| 170 { |
| 171 this._save(); |
| 172 |
| 173 this.removeChildren(); |
| 174 this.appendChild(child); |
| 175 }, |
| 176 |
| 177 _exclude: function(aCallUID) |
| 178 { |
| 179 if (this._remainingNodeInfos) |
| 180 this._populate(); |
| 181 |
| 182 this._save(); |
| 183 |
| 184 var children = this.children; |
| 185 var index = this.children.length; |
| 186 |
| 187 while (index--) |
| 188 children[index]._exclude(aCallUID); |
| 189 |
| 190 var child = this.childrenByCallUID[aCallUID]; |
| 191 |
| 192 if (child) |
| 193 this._merge(child, true); |
| 194 }, |
| 195 |
| 196 _merge: function(/*ProfileDataGridNode*/ child, /*Boolean*/ shouldAbsorb) |
| 197 { |
| 198 this.selfTime -= child.selfTime; |
| 199 |
| 200 WebInspector.ProfileDataGridNode.prototype._merge.call(this, child, shou
ldAbsorb); |
| 201 }, |
| 202 |
| 203 _populate: function(event) |
| 204 { |
| 205 var remainingNodeInfos = this._remainingNodeInfos; |
| 206 var count = remainingNodeInfos.length; |
| 207 |
| 208 for (var index = 0; index < count; ++index) { |
| 209 var nodeInfo = remainingNodeInfos[index]; |
| 210 var ancestor = nodeInfo.ancestor; |
| 211 var focusNode = nodeInfo.focusNode; |
| 212 var child = this.findChild(ancestor); |
| 213 |
| 214 // If we already have this child, then merge the data together. |
| 215 if (child) { |
| 216 var totalTimeAccountedFor = nodeInfo.totalTimeAccountedFor; |
| 217 |
| 218 child.selfTime += focusNode.selfTime; |
| 219 child.numberOfCalls += focusNode.numberOfCalls; |
| 220 |
| 221 if (!totalTimeAccountedFor) |
| 222 child.totalTime += focusNode.totalTime; |
| 223 } else { |
| 224 // If not, add it as a true ancestor. |
| 225 // In heavy mode, we take our visual identity from ancestor node
... |
| 226 var child = new WebInspector.BottomUpProfileDataGridNode(this.pr
ofileView, ancestor, this.tree); |
| 227 |
| 228 if (ancestor !== focusNode) { |
| 229 // but the actual statistics from the "root" node (bottom of
the callstack). |
| 230 child.selfTime = focusNode.selfTime; |
| 231 child.totalTime = focusNode.totalTime; |
| 232 child.numberOfCalls = focusNode.numberOfCalls; |
| 233 } |
| 234 |
| 235 this.appendChild(child); |
| 236 } |
| 237 |
| 238 var parent = ancestor.parent; |
| 239 if (parent && parent.parent) { |
| 240 nodeInfo.ancestor = parent; |
| 241 child._remainingNodeInfos.push(nodeInfo); |
| 242 } |
| 243 } |
| 244 |
| 245 delete this._remainingNodeInfos; |
| 246 |
| 247 if (this.removeEventListener) |
| 248 this.removeEventListener("populate", this._populate, this); |
| 249 } |
| 250 } |
| 251 |
| 252 WebInspector.BottomUpProfileDataGridNode.prototype.__proto__ = WebInspector.Prof
ileDataGridNode.prototype; |
OLD | NEW |