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 |