2008-05-21 Alp Toker <alp@nuanti.com>
[WebKit.git] / JavaScriptCore / profiler / ProfileNode.cpp
1 /*
2  * Copyright (C) 2008 Apple 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "ProfileNode.h"
31
32 #include "Profiler.h"
33 #include "DateMath.h"
34
35 #include <stdio.h>
36
37 namespace KJS {
38
39 static const char* NonJSExecution = "(non-JavaScript)";
40
41 ProfileNode::ProfileNode(const CallIdentifier& callIdentifier)
42     : m_callIdentifier(callIdentifier)
43     , m_totalTime (0.0)
44     , m_selfTime (0.0)
45     , m_totalPercent (0.0)
46     , m_selfPercent (0.0)
47     , m_numberOfCalls(0)
48     , m_visible(true)
49 {
50     m_startTime = getCurrentUTCTimeWithMicroseconds();
51 }
52
53 void ProfileNode::willExecute()
54 {
55     if (!m_startTime)
56         m_startTime = getCurrentUTCTimeWithMicroseconds();
57 }
58
59 // We start at the end of stackNames and work our way forwards since the names are in 
60 // the reverse order of how the ProfileNode tree is created (e.g. the leaf node is at
61 // index 0 and the top of the stack is at index stackNames.size() - 1)
62 void ProfileNode::didExecute(const Vector<CallIdentifier>& stackNames, unsigned int stackIndex)
63 {
64     for (size_t i = 0; i < m_children.size(); ++i) {
65         if (m_children[i]->callIdentifier() == stackNames[stackIndex]) {
66             if (stackIndex)   // We are not at the bottom of the stack yet
67                 m_children[i]->didExecute(stackNames, stackIndex - 1);
68             else    // This is the child we were looking for
69                 m_children[i]->endAndRecordCall();
70             return;
71         }
72     }
73 }
74
75 void ProfileNode::addChild(PassRefPtr<ProfileNode> prpChild)
76 {
77     ASSERT(prpChild);
78
79     RefPtr<ProfileNode> child = prpChild;
80     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) {
81         if ((*currentChild)->callIdentifier() == child->callIdentifier())
82             return;
83     }
84
85     m_children.append(child.release());
86 }
87
88 ProfileNode* ProfileNode::findChild(const CallIdentifier& functionName)
89 {
90     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) {
91         if ((*currentChild)->callIdentifier() == functionName)
92             return (*currentChild).get();
93     }
94
95     return 0;
96 }
97
98 void ProfileNode::stopProfiling(double totalProfileTime, bool headProfileNode)
99 {
100     if (m_startTime)
101         endAndRecordCall();
102
103     ASSERT(m_selfTime == 0.0);
104
105     if (headProfileNode)
106         totalProfileTime = m_totalTime;
107
108     // Calculate Self time and the percentages once we stop profiling.
109     StackIterator endOfChildren = m_children.end();
110     for (StackIterator currentChild = m_children.begin(); currentChild != endOfChildren; ++currentChild) {
111         (*currentChild)->stopProfiling(totalProfileTime);
112         m_selfTime += (*currentChild)->totalTime();
113     }
114
115     ASSERT(m_selfTime <= m_totalTime);
116     m_selfTime = m_totalTime - m_selfTime;
117
118     if (headProfileNode && m_selfTime) {
119         RefPtr<ProfileNode> idleNode = ProfileNode::create(CallIdentifier(NonJSExecution, 0, 0));
120
121         idleNode->setTotalTime(m_selfTime);
122         idleNode->setSelfTime(m_selfTime);
123         idleNode->setNumberOfCalls(0);
124         idleNode->calculatePercentages(totalProfileTime);
125
126         addChild(idleNode.release());
127         m_selfTime = 0.0;
128     }
129
130     calculatePercentages(totalProfileTime);
131 }
132
133 void ProfileNode::setTreeVisible(bool visible)
134 {
135     m_visible = visible;
136
137     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
138         (*currentChild)->setTreeVisible(visible);    
139 }
140
141 // Sorting methods
142
143 static inline bool totalTimeDescendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b)
144 {
145     return a->totalTime() > b->totalTime();
146 }
147
148 void ProfileNode::sortTotalTimeDescending()
149 {
150     std::sort(m_children.begin(), m_children.end(), totalTimeDescendingComparator);
151
152     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
153         (*currentChild)->sortTotalTimeDescending();
154 }
155
156 static inline bool totalTimeAscendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b)
157 {
158     return a->totalTime() < b->totalTime();
159 }
160
161 void ProfileNode::sortTotalTimeAscending()
162 {
163     std::sort(m_children.begin(), m_children.end(), totalTimeAscendingComparator);
164
165     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
166         (*currentChild)->sortTotalTimeAscending();
167 }
168
169 static inline bool selfTimeDescendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b)
170 {
171     return a->selfTime() > b->selfTime();
172 }
173
174 void ProfileNode::sortSelfTimeDescending()
175 {
176     std::sort(m_children.begin(), m_children.end(), selfTimeDescendingComparator);
177
178     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
179         (*currentChild)->sortSelfTimeDescending();
180 }
181
182 static inline bool selfTimeAscendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b)
183 {
184     return a->selfTime() < b->selfTime();
185 }
186
187 void ProfileNode::sortSelfTimeAscending()
188 {
189     std::sort(m_children.begin(), m_children.end(), selfTimeAscendingComparator);
190
191     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
192         (*currentChild)->sortSelfTimeAscending();
193 }
194
195 static inline bool callsDescendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b)
196 {
197     return a->numberOfCalls() > b->numberOfCalls();
198 }
199
200 void ProfileNode::sortCallsDescending()
201 {
202     std::sort(m_children.begin(), m_children.end(), callsDescendingComparator);
203
204     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
205         (*currentChild)->sortCallsDescending();
206 }
207
208 static inline bool callsAscendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b)
209 {
210     return a->numberOfCalls() < b->numberOfCalls();
211 }
212
213 void ProfileNode::sortCallsAscending()
214 {
215     std::sort(m_children.begin(), m_children.end(), callsAscendingComparator);
216
217     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
218         (*currentChild)->sortCallsAscending();
219 }
220
221 static inline bool functionNameDescendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b)
222 {
223     return a->functionName() > b->functionName();
224 }
225
226 void ProfileNode::sortFunctionNameDescending()
227 {
228     std::sort(m_children.begin(), m_children.end(), functionNameDescendingComparator);
229
230     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
231         (*currentChild)->sortFunctionNameDescending();
232 }
233
234 static inline bool functionNameAscendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b)
235 {
236     return a->functionName() < b->functionName();
237 }
238
239 void ProfileNode::sortFunctionNameAscending()
240 {
241     std::sort(m_children.begin(), m_children.end(), functionNameAscendingComparator);
242
243     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
244         (*currentChild)->sortFunctionNameAscending();
245 }
246
247 bool ProfileNode::focus(const CallIdentifier& callIdentifier)
248 {
249     if (m_callIdentifier == callIdentifier) {
250         m_visible = true;
251
252         for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
253             (*currentChild)->setTreeVisible(true);
254     } else {
255         m_visible = false;
256         for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
257             m_visible = (*currentChild)->focus(callIdentifier) || m_visible;
258     }
259     
260     return m_visible;
261 }
262
263 void ProfileNode::restoreAll()
264 {
265     m_visible = true;
266     
267     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
268         (*currentChild)->restoreAll();
269 }
270
271 void ProfileNode::endAndRecordCall()
272 {
273     m_totalTime += getCurrentUTCTimeWithMicroseconds() - m_startTime;
274     m_startTime = 0.0;
275
276     ++m_numberOfCalls;
277 }
278
279 void ProfileNode::calculatePercentages(double totalProfileTime)
280 {
281     m_totalPercent = (m_totalTime / totalProfileTime) * 100.0;
282     m_selfPercent = (m_selfTime / totalProfileTime) * 100.0;
283 }
284
285 #ifndef NDEBUG
286 void ProfileNode::debugPrintData(int indentLevel) const
287 {
288     // Print function names
289     for (int i = 0; i < indentLevel; ++i)
290         printf("  ");
291
292     printf("%d SelfTime %.3fms/%.3f%% TotalTime %.3fms/%.3f%% Full Name %s Visible %s\n", m_numberOfCalls, m_selfTime, selfPercent(), m_totalTime, totalPercent(), functionName().UTF8String().c_str(), (m_visible ? "True" : "False"));
293
294     ++indentLevel;
295
296     // Print children's names and information
297     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
298         (*currentChild)->debugPrintData(indentLevel);
299 }
300
301 // print the profiled data in a format that matches the tool sample's output.
302 double ProfileNode::debugPrintDataSampleStyle(int indentLevel, FunctionCallHashCount& countedFunctions) const
303 {
304     printf("    ");
305
306     // Print function names
307     const char* name = functionName().UTF8String().c_str();
308     double sampleCount = m_totalTime * 1000;
309     if (indentLevel) {
310         for (int i = 0; i < indentLevel; ++i)
311             printf("  ");
312
313          countedFunctions.add(functionName().rep());
314
315         printf("%.0f %s\n", sampleCount ? sampleCount : 1, name);
316     } else
317         printf("%s\n", name);
318
319     ++indentLevel;
320
321     // Print children's names and information
322     double sumOfChildrensCount = 0.0;
323     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
324         sumOfChildrensCount += (*currentChild)->debugPrintDataSampleStyle(indentLevel, countedFunctions);
325
326     sumOfChildrensCount *= 1000;    //
327     // Print remainder of samples to match sample's output
328     if (sumOfChildrensCount < sampleCount) {
329         printf("    ");
330         while (indentLevel--)
331             printf("  ");
332
333         printf("%.0f %s\n", sampleCount - sumOfChildrensCount, functionName().UTF8String().c_str());
334     }
335
336     return m_totalTime;
337 }
338 #endif
339
340 }   // namespace KJS