48d09d786a6b1650cbdfe974afe7bd304393435c
[WebKit-https.git] / JavaScriptCore / profiler / Profiler.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 "Profiler.h"
31
32 #include "FunctionCallProfile.h"
33 #include <kjs/ExecState.h>
34 #include <kjs/function.h>
35
36 namespace KJS {
37
38 static Profiler* sharedProfiler = 0;
39 static const char* Script = "[SCRIPT] ";
40
41 Profiler* Profiler::profiler()
42 {
43     if (!sharedProfiler)
44         sharedProfiler = new Profiler;
45     return sharedProfiler;
46 }
47
48 void Profiler::startProfiling()
49 {
50     if (m_profiling)
51         return;
52
53     // FIXME: When multi-threading is supported this will be a vector and calls
54     // into the profiler will need to know which thread it is executing on.
55     m_callTree.set(new FunctionCallProfile("Thread_1"));
56     m_profiling = true;
57 }
58
59 void Profiler::stopProfiling()
60 {
61     m_profiling = false;
62 }
63
64 void Profiler::willExecute(ExecState* exec, JSObject* calledFunction)
65 {
66     ASSERT(m_profiling);
67
68     Vector<UString> callStackNames;
69     getStackNames(callStackNames, exec, calledFunction);
70     insertStackNamesInTree(callStackNames);
71 }
72
73 void Profiler::willExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber)
74 {
75     ASSERT(m_profiling);
76
77     Vector<UString> callStackNames;
78     getStackNames(callStackNames, exec, sourceURL, startingLineNumber);
79     insertStackNamesInTree(callStackNames);
80 }
81
82 void Profiler::didExecute(ExecState* exec, JSObject* calledFunction)
83 {
84     ASSERT(m_profiling);
85
86     Vector<UString> callStackNames;
87     getStackNames(callStackNames, exec, calledFunction);
88     m_callTree->didExecute(callStackNames, 0);
89 }
90
91 void Profiler::didExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber)
92 {
93     ASSERT(m_profiling);
94
95     Vector<UString> callStackNames;
96     getStackNames(callStackNames, exec, sourceURL, startingLineNumber);
97     m_callTree->didExecute(callStackNames, 0);
98 }
99
100 void Profiler::insertStackNamesInTree(const Vector<UString>& callStackNames)
101 {
102     FunctionCallProfile* callTreeInsertionPoint = 0;
103     FunctionCallProfile* foundNameInTree = m_callTree.get();
104     NameIterator callStackLocation = callStackNames.begin();
105
106     while (callStackLocation != callStackNames.end() && foundNameInTree) {
107         callTreeInsertionPoint = foundNameInTree;
108         foundNameInTree = callTreeInsertionPoint->findChild(*callStackLocation);
109         ++callStackLocation;
110     }
111
112     if (!foundNameInTree) {   // Insert remains of the stack into the call tree.
113         --callStackLocation;
114         for (FunctionCallProfile* next; callStackLocation != callStackNames.end(); ++callStackLocation) {
115             next = new FunctionCallProfile(*callStackLocation);
116             callTreeInsertionPoint->addChild(next);
117             callTreeInsertionPoint = next;
118         }
119     } else    // We are calling a function that is already in the call tree.
120         foundNameInTree->willExecute();
121 }
122
123 void Profiler::getStackNames(Vector<UString>& names, ExecState* exec) const
124 {
125     UString currentName;
126     for (ExecState* currentState = exec; currentState; currentState = currentState->callingExecState()) {
127         if (FunctionImp* functionImp = exec->function())
128             names.prepend(getFunctionName(functionImp));
129         else if (ScopeNode* scopeNode = exec->scopeNode())
130             names.prepend(Script + scopeNode->sourceURL() + ": " + UString::from(scopeNode->lineNo() + 1));   // FIXME: Why is the line number always off by one?
131     }
132 }
133
134 void Profiler::getStackNames(Vector<UString>& names, ExecState* exec, JSObject* calledFunction) const
135 {
136     getStackNames(names, exec);
137
138     if (calledFunction->inherits(&FunctionImp::info))
139         names.append(getFunctionName(static_cast<FunctionImp*>(calledFunction)));
140     else if (calledFunction->inherits(&InternalFunctionImp::info))
141         names.append(static_cast<InternalFunctionImp*>(calledFunction)->functionName().ustring());
142 }
143
144
145 void Profiler::getStackNames(Vector<UString>& names, ExecState* exec, const UString& sourceURL, int startingLineNumber) const
146 {
147     getStackNames(names, exec);
148     names.append(Script + sourceURL + ": " + UString::from(startingLineNumber + 1));
149 }
150
151 UString Profiler::getFunctionName(FunctionImp* functionImp) const
152 {
153     UString name = functionImp->functionName().ustring();
154     int lineNumber = functionImp->body->lineNo();
155     UString URL = functionImp->body->sourceURL();
156
157     return (name.isEmpty() ? "[anonymous function]" : name) + " " + URL + ": " + UString::from(lineNumber);
158 }
159
160 void Profiler::printDataSampleStyle() const
161 {
162     printf("Call graph:\n");
163     m_callTree->printDataSampleStyle(0);
164
165     // FIXME: Since no one seems to understand this part of sample's output I will implement it when I have a better idea of what it's meant to be doing.
166     printf("\nTotal number in stack (recursive counted multiple, when >=5):\n");
167     printf("\nSort by top of stack, same collapsed (when >= 5):\n");
168 }
169
170 void Profiler::debugLog(UString message)
171 {
172     printf("Profiler Log: %s\n", message.UTF8String().c_str());
173 }
174
175 }   // namespace KJS