2010-11-02 Mikhail Naganov <mnaganov@chromium.org>
[WebKit-https.git] / WebCore / inspector / InspectorProfilerAgent.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2010 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "InspectorProfilerAgent.h"
32
33 #if ENABLE(JAVASCRIPT_DEBUGGER)
34
35 #include "Console.h"
36 #include "InspectorController.h"
37 #include "InspectorFrontend.h"
38 #include "InspectorValues.h"
39 #include "KURL.h"
40 #include "Page.h"
41 #include "ScriptDebugServer.h"
42 #include "ScriptHeapSnapshot.h"
43 #include "ScriptProfile.h"
44 #include "ScriptProfiler.h"
45 #include <wtf/OwnPtr.h>
46 #include <wtf/text/StringConcatenate.h>
47
48 #if USE(JSC)
49 #include "JSDOMWindow.h"
50 #endif
51
52 namespace WebCore {
53
54 static const char* const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
55 static const char* const CPUProfileType = "CPU";
56 static const char* const HeapProfileType = "HEAP";
57
58 PassOwnPtr<InspectorProfilerAgent> InspectorProfilerAgent::create(InspectorController* inspectorController)
59 {
60     OwnPtr<InspectorProfilerAgent> agent = adoptPtr(new InspectorProfilerAgent(inspectorController));
61     return agent.release();
62 }
63
64 InspectorProfilerAgent::InspectorProfilerAgent(InspectorController* inspectorController)
65     : m_inspectorController(inspectorController)
66     , m_frontend(0)
67     , m_enabled(ScriptProfiler::isProfilerAlwaysEnabled())
68     , m_recordingUserInitiatedProfile(false)
69     , m_currentUserInitiatedProfileNumber(-1)
70     , m_nextUserInitiatedProfileNumber(1)
71     , m_nextUserInitiatedHeapSnapshotNumber(1)
72 {
73 }
74
75 InspectorProfilerAgent::~InspectorProfilerAgent()
76 {
77 }
78
79 void InspectorProfilerAgent::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
80 {
81     RefPtr<ScriptProfile> profile = prpProfile;
82     m_profiles.add(profile->uid(), profile);
83     if (m_frontend)
84         m_frontend->addProfileHeader(createProfileHeader(*profile));
85     addProfileFinishedMessageToConsole(profile, lineNumber, sourceURL);
86 }
87
88 void InspectorProfilerAgent::addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
89 {
90     RefPtr<ScriptProfile> profile = prpProfile;
91     String title = profile->title();
92     String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), '#', String::number(profile->uid()), "\" finished.");
93     m_inspectorController->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
94 }
95
96 void InspectorProfilerAgent::addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, const String& sourceURL)
97 {
98     String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), "#0\" started.");
99     m_inspectorController->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
100 }
101
102 PassRefPtr<InspectorObject> InspectorProfilerAgent::createProfileHeader(const ScriptProfile& profile)
103 {
104     RefPtr<InspectorObject> header = InspectorObject::create();
105     header->setString("title", profile.title());
106     header->setNumber("uid", profile.uid());
107     header->setString("typeId", String(CPUProfileType));
108     return header;
109 }
110
111 PassRefPtr<InspectorObject> InspectorProfilerAgent::createSnapshotHeader(const ScriptHeapSnapshot& snapshot)
112 {
113     RefPtr<InspectorObject> header = InspectorObject::create();
114     header->setString("title", snapshot.title());
115     header->setNumber("uid", snapshot.uid());
116     header->setString("typeId", String(HeapProfileType));
117     return header;
118 }
119
120 void InspectorProfilerAgent::disable()
121 {
122     if (!m_enabled)
123         return;
124     m_enabled = false;
125     ScriptDebugServer::shared().recompileAllJSFunctionsSoon();
126     if (m_frontend)
127         m_frontend->profilerWasDisabled();
128 }
129
130 void InspectorProfilerAgent::enable(bool skipRecompile)
131 {
132     if (m_enabled)
133         return;
134     m_enabled = true;
135     if (!skipRecompile)
136         ScriptDebugServer::shared().recompileAllJSFunctionsSoon();
137     if (m_frontend)
138         m_frontend->profilerWasEnabled();
139 }
140
141 String InspectorProfilerAgent::getCurrentUserInitiatedProfileName(bool incrementProfileNumber)
142 {
143     if (incrementProfileNumber)
144         m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++;
145
146     return makeString(UserInitiatedProfileName, '.', String::number(m_currentUserInitiatedProfileNumber));
147 }
148
149 void InspectorProfilerAgent::getProfileHeaders(RefPtr<InspectorArray>* headers)
150 {
151     ProfilesMap::iterator profilesEnd = m_profiles.end();
152     for (ProfilesMap::iterator it = m_profiles.begin(); it != profilesEnd; ++it)
153         (*headers)->pushObject(createProfileHeader(*it->second));
154     HeapSnapshotsMap::iterator snapshotsEnd = m_snapshots.end();
155     for (HeapSnapshotsMap::iterator it = m_snapshots.begin(); it != snapshotsEnd; ++it)
156         (*headers)->pushObject(createSnapshotHeader(*it->second));
157 }
158
159 void InspectorProfilerAgent::getProfile(const String& type, unsigned uid, RefPtr<InspectorObject>* profileObject)
160 {
161     if (type == CPUProfileType) {
162         ProfilesMap::iterator it = m_profiles.find(uid);
163         if (it != m_profiles.end()) {
164             *profileObject = createProfileHeader(*it->second);
165             (*profileObject)->setObject("head", it->second->buildInspectorObjectForHead());
166         }
167     } else if (type == HeapProfileType) {
168         HeapSnapshotsMap::iterator it = m_snapshots.find(uid);
169         if (it != m_snapshots.end()) {
170             *profileObject = createSnapshotHeader(*it->second);
171             (*profileObject)->setObject("head", it->second->buildInspectorObjectForHead());
172         }
173     }
174 }
175
176 void InspectorProfilerAgent::removeProfile(const String& type, unsigned uid)
177 {
178     if (type == CPUProfileType) {
179         if (m_profiles.contains(uid))
180             m_profiles.remove(uid);
181     } else if (type == HeapProfileType) {
182         if (m_snapshots.contains(uid))
183             m_snapshots.remove(uid);
184     }
185 }
186
187 void InspectorProfilerAgent::resetState()
188 {
189     m_profiles.clear();
190     m_snapshots.clear();
191     m_currentUserInitiatedProfileNumber = 1;
192     m_nextUserInitiatedProfileNumber = 1;
193     m_nextUserInitiatedHeapSnapshotNumber = 1;
194     if (m_frontend)
195         m_frontend->resetProfilesPanel();
196 }
197
198 void InspectorProfilerAgent::startUserInitiatedProfiling()
199 {
200     if (!enabled()) {
201         enable(false);
202         ScriptDebugServer::shared().recompileAllJSFunctions();
203     }
204     m_recordingUserInitiatedProfile = true;
205     String title = getCurrentUserInitiatedProfileName(true);
206 #if USE(JSC)
207     JSC::ExecState* scriptState = toJSDOMWindow(m_inspectorController->inspectedPage()->mainFrame(), debuggerWorld())->globalExec();
208 #else
209     ScriptState* scriptState = 0;
210 #endif
211     ScriptProfiler::start(scriptState, title);
212     addStartProfilingMessageToConsole(title, 0, String());
213     toggleRecordButton(true);
214 }
215
216 void InspectorProfilerAgent::stopUserInitiatedProfiling()
217 {
218     m_recordingUserInitiatedProfile = false;
219     String title = getCurrentUserInitiatedProfileName();
220 #if USE(JSC)
221     JSC::ExecState* scriptState = toJSDOMWindow(m_inspectorController->inspectedPage()->mainFrame(), debuggerWorld())->globalExec();
222 #else
223     // Use null script state to avoid filtering by context security token.
224     // All functions from all iframes should be visible from Inspector UI.
225     ScriptState* scriptState = 0;
226 #endif
227     RefPtr<ScriptProfile> profile = ScriptProfiler::stop(scriptState, title);
228     if (profile)
229         addProfile(profile, 0, String());
230     toggleRecordButton(false);
231 }
232
233 void InspectorProfilerAgent::takeHeapSnapshot()
234 {
235     String title = makeString(UserInitiatedProfileName, '.', String::number(m_nextUserInitiatedHeapSnapshotNumber));
236     ++m_nextUserInitiatedHeapSnapshotNumber;
237
238     RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title);
239     if (snapshot) {
240         m_snapshots.add(snapshot->uid(), snapshot);
241         if (m_frontend)
242             m_frontend->addProfileHeader(createSnapshotHeader(*snapshot));
243     }
244 }
245
246 void InspectorProfilerAgent::toggleRecordButton(bool isProfiling)
247 {
248     if (m_frontend)
249         m_frontend->setRecordingProfile(isProfiling);
250 }
251
252 } // namespace WebCore
253
254 #endif // ENABLE(JAVASCRIPT_DEBUGGER)