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