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