Use member function pointers for the Profile::forEach function.
[WebKit-https.git] / JavaScriptCore / profiler / Profile.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  * 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 #include "config.h"
27 #include "Profile.h"
28
29 #include "ProfileNode.h"
30 #include "JSGlobalObject.h"
31 #include "ExecState.h"
32 #include "JSFunction.h"
33
34 #include <stdio.h>
35
36 namespace KJS {
37
38 static const char* NonJSExecution = "(idle)";
39
40 PassRefPtr<Profile> Profile::create(const UString& title, ExecState* originatingGlobalExec, unsigned pageGroupIdentifier, ProfilerClient* client)
41 {
42     return adoptRef(new Profile(title, originatingGlobalExec, pageGroupIdentifier, client));
43 }
44
45 Profile::Profile(const UString& title, ExecState* originatingGlobalExec, unsigned pageGroupIdentifier, ProfilerClient* client)
46     : m_title(title)
47     , m_originatingGlobalExec(originatingGlobalExec)
48     , m_pageGroupIdentifier(pageGroupIdentifier)
49     , m_client(client)
50     , m_stoppedProfiling(false)
51 {
52     // FIXME: When multi-threading is supported this will be a vector and calls
53     // into the profiler will need to know which thread it is executing on.
54     m_head = ProfileNode::create(CallIdentifier("Thread_1", 0, 0), 0, 0);
55     m_currentNode = m_head;
56 }
57
58 void Profile::stopProfiling()
59 {
60     forEach(&ProfileNode::stopProfiling);
61     removeProfileStart();
62     removeProfileEnd();
63
64     // Already at the head, set m_currentNode to prevent
65     // didExecute from recording more nodes.
66     if (m_currentNode == m_head)
67         m_currentNode = 0;
68
69     m_stoppedProfiling = true;
70 }
71
72 bool Profile::didFinishAllExecution()
73 {
74     if (!m_stoppedProfiling)
75         return false;
76
77     if (double headSelfTime = m_head->selfTime()) {
78         RefPtr<ProfileNode> idleNode = ProfileNode::create(CallIdentifier(NonJSExecution, 0, 0), m_head.get(), m_head.get());
79
80         idleNode->setTotalTime(headSelfTime);
81         idleNode->setSelfTime(headSelfTime);
82         idleNode->setVisible(true);
83
84         m_head->setSelfTime(0.0);
85         m_head->addChild(idleNode.release());
86     }
87
88     m_currentNode = 0;
89     m_originatingGlobalExec = 0;
90     return true;
91 }
92
93 // The console.profile that started this profile will be the first child.
94 void Profile::removeProfileStart() {
95     ProfileNode* currentNode = 0;
96     for (ProfileNode* next = m_head.get(); next; next = next->firstChild())
97         currentNode = next;
98
99     if (currentNode->callIdentifier().name != "profile")
100         return;
101
102     for (ProfileNode* currentParent = currentNode->parent(); currentParent; currentParent = currentParent->parent())
103         currentParent->setTotalTime(currentParent->totalTime() - currentNode->totalTime());
104
105     currentNode->parent()->removeChild(0);
106 }
107
108 // The console.profileEnd that stopped this profile will be the last child.
109 void Profile::removeProfileEnd() {
110     ProfileNode* currentNode = 0;
111     for (ProfileNode* next = m_head.get(); next; next = next->lastChild())
112         currentNode = next;
113
114     if (currentNode->callIdentifier().name != "profileEnd")
115         return;
116
117     for (ProfileNode* currentParent = currentNode->parent(); currentParent; currentParent = currentParent->parent())
118         currentParent->setTotalTime(currentParent->totalTime() - currentNode->totalTime());
119
120     currentNode->parent()->removeChild(currentNode->parent()->children().size() - 1);
121 }
122
123 void Profile::willExecute(const CallIdentifier& callIdentifier)
124 {
125     if (m_stoppedProfiling)
126         return;
127
128     ASSERT(m_currentNode);
129     m_currentNode = m_currentNode->willExecute(callIdentifier);
130 }
131
132 void Profile::didExecute(const CallIdentifier& callIdentifier)
133 {
134     if (!m_currentNode)
135         return;
136
137     if (m_currentNode == m_head) {
138         m_currentNode = ProfileNode::create(callIdentifier, m_head.get(), m_head.get());
139         m_currentNode->setStartTime(m_head->startTime());
140         m_currentNode->didExecute();
141         m_head->insertNode(m_currentNode.get());
142
143         if (m_stoppedProfiling) {
144             m_currentNode->setTotalTime(m_currentNode->totalTime() + m_head->selfTime());
145             m_head->setSelfTime(0.0);
146             m_currentNode->stopProfiling();
147             m_currentNode = 0;
148         } else
149             m_currentNode = m_head;
150
151         return;
152     }
153
154     if (m_stoppedProfiling) {
155         m_currentNode = m_currentNode->parent();
156         return;
157     }
158
159     m_currentNode = m_currentNode->didExecute();
160 }
161
162 void Profile::forEach(void (ProfileNode::*function)())
163 {
164     ProfileNode* currentNode = m_head->firstChild();
165     for (ProfileNode* nextNode = currentNode; nextNode; nextNode = nextNode->firstChild())
166         currentNode = nextNode;
167
168     ProfileNode* endNode = m_head->traverseNextNodePostOrder();
169     while (currentNode && currentNode != endNode) {
170         (currentNode->*function)();
171         currentNode = currentNode->traverseNextNodePostOrder();
172     } 
173 }
174
175 void Profile::focus(const ProfileNode* profileNode)
176 {
177     if (!profileNode || !m_head)
178         return;
179
180     bool processChildren;
181     const CallIdentifier& callIdentifier = profileNode->callIdentifier();
182     for (ProfileNode* currentNode = m_head.get(); currentNode; currentNode = currentNode->traverseNextNodePreOrder(processChildren))
183         processChildren = currentNode->focus(callIdentifier);
184
185     // Set the visible time of all nodes so that the %s display correctly.
186     forEach(&ProfileNode::calculateVisibleTotalTime);
187 }
188
189 void Profile::exclude(const ProfileNode* profileNode)
190 {
191     if (!profileNode || !m_head)
192         return;
193
194     const CallIdentifier& callIdentifier = profileNode->callIdentifier();
195
196     for (ProfileNode* currentNode = m_head.get(); currentNode; currentNode = currentNode->traverseNextNodePreOrder())
197         currentNode->exclude(callIdentifier);
198
199     // Set the visible time of the head so the %s display correctly.
200     m_head->setVisibleTotalTime(m_head->totalTime() - m_head->selfTime());
201     m_head->setVisibleSelfTime(0.0);
202 }
203
204 void Profile::restoreAll()
205 {
206     forEach(&ProfileNode::restore);
207 }
208
209 #ifndef NDEBUG
210 void Profile::debugPrintData() const
211 {
212     printf("Call graph:\n");
213     m_head->debugPrintData(0);
214 }
215
216 typedef pair<UString::Rep*, unsigned> NameCountPair;
217
218 static inline bool functionNameCountPairComparator(const NameCountPair& a, const NameCountPair& b)
219 {
220     return a.second > b.second;
221 }
222
223 void Profile::debugPrintDataSampleStyle() const
224 {
225     typedef Vector<NameCountPair> NameCountPairVector;
226
227     FunctionCallHashCount countedFunctions;
228     printf("Call graph:\n");
229     m_head->debugPrintDataSampleStyle(0, countedFunctions);
230
231     printf("\nTotal number in stack:\n");
232     NameCountPairVector sortedFunctions(countedFunctions.size());
233     copyToVector(countedFunctions, sortedFunctions);
234
235     std::sort(sortedFunctions.begin(), sortedFunctions.end(), functionNameCountPairComparator);
236     for (NameCountPairVector::iterator it = sortedFunctions.begin(); it != sortedFunctions.end(); ++it)
237         printf("        %-12d%s\n", (*it).second, UString((*it).first).UTF8String().c_str());
238
239     printf("\nSort by top of stack, same collapsed (when >= 5):\n");
240 }
241 #endif
242
243 }   // namespace KJS