OSDN Git Service

Merge WebKit at r78450: Initial merge by git.
[android-x86/external-webkit.git] / Source / 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) && ENABLE(INSPECTOR)
34
35 #include "Console.h"
36 #include "InspectorAgent.h"
37 #include "InspectorConsoleAgent.h"
38 #include "InspectorFrontend.h"
39 #include "InspectorValues.h"
40 #include "KURL.h"
41 #include "Page.h"
42 #include "ScriptDebugServer.h"
43 #include "ScriptHeapSnapshot.h"
44 #include "ScriptProfile.h"
45 #include "ScriptProfiler.h"
46 #include <wtf/OwnPtr.h>
47 #include <wtf/text/StringConcatenate.h>
48
49 #if USE(JSC)
50 #include "JSDOMWindow.h"
51 #endif
52
53 namespace WebCore {
54
55 static const char* const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
56 static const char* const CPUProfileType = "CPU";
57 static const char* const HeapProfileType = "HEAP";
58
59 PassOwnPtr<InspectorProfilerAgent> InspectorProfilerAgent::create(InspectorAgent* inspectorAgent)
60 {
61     OwnPtr<InspectorProfilerAgent> agent = adoptPtr(new InspectorProfilerAgent(inspectorAgent));
62     return agent.release();
63 }
64
65 InspectorProfilerAgent::InspectorProfilerAgent(InspectorAgent* inspectorAgent)
66     : m_inspectorAgent(inspectorAgent)
67     , m_frontend(0)
68     , m_enabled(false)
69     , m_recordingUserInitiatedProfile(false)
70     , m_currentUserInitiatedProfileNumber(-1)
71     , m_nextUserInitiatedProfileNumber(1)
72     , m_nextUserInitiatedHeapSnapshotNumber(1)
73 {
74 }
75
76 InspectorProfilerAgent::~InspectorProfilerAgent()
77 {
78 }
79
80 void InspectorProfilerAgent::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
81 {
82     RefPtr<ScriptProfile> profile = prpProfile;
83     m_profiles.add(profile->uid(), profile);
84     if (m_frontend)
85         m_frontend->addProfileHeader(createProfileHeader(*profile));
86     addProfileFinishedMessageToConsole(profile, lineNumber, sourceURL);
87 }
88
89 void InspectorProfilerAgent::addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
90 {
91     if (!m_frontend)
92         return;
93     RefPtr<ScriptProfile> profile = prpProfile;
94     String title = profile->title();
95     String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), '#', String::number(profile->uid()), "\" finished.");
96     m_inspectorAgent->consoleAgent()->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
97 }
98
99 void InspectorProfilerAgent::addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, const String& sourceURL)
100 {
101     if (!m_frontend)
102         return;
103     String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), "#0\" started.");
104     m_inspectorAgent->consoleAgent()->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
105 }
106
107 PassRefPtr<InspectorObject> InspectorProfilerAgent::createProfileHeader(const ScriptProfile& profile)
108 {
109     RefPtr<InspectorObject> header = InspectorObject::create();
110     header->setString("title", profile.title());
111     header->setNumber("uid", profile.uid());
112     header->setString("typeId", String(CPUProfileType));
113     return header;
114 }
115
116 PassRefPtr<InspectorObject> InspectorProfilerAgent::createSnapshotHeader(const ScriptHeapSnapshot& snapshot)
117 {
118     RefPtr<InspectorObject> header = InspectorObject::create();
119     header->setString("title", snapshot.title());
120     header->setNumber("uid", snapshot.uid());
121     header->setString("typeId", String(HeapProfileType));
122     return header;
123 }
124
125 void InspectorProfilerAgent::disable()
126 {
127     if (!m_enabled)
128         return;
129     m_enabled = false;
130     ScriptDebugServer::shared().recompileAllJSFunctionsSoon();
131     if (m_frontend)
132         m_frontend->profilerWasDisabled();
133 }
134
135 void InspectorProfilerAgent::enable(bool skipRecompile)
136 {
137     if (m_enabled)
138         return;
139     m_enabled = true;
140     if (!skipRecompile)
141         ScriptDebugServer::shared().recompileAllJSFunctionsSoon();
142     if (m_frontend)
143         m_frontend->profilerWasEnabled();
144 }
145
146 String InspectorProfilerAgent::getCurrentUserInitiatedProfileName(bool incrementProfileNumber)
147 {
148     if (incrementProfileNumber)
149         m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++;
150
151     return makeString(UserInitiatedProfileName, '.', String::number(m_currentUserInitiatedProfileNumber));
152 }
153
154 void InspectorProfilerAgent::getProfileHeaders(RefPtr<InspectorArray>* headers)
155 {
156     ProfilesMap::iterator profilesEnd = m_profiles.end();
157     for (ProfilesMap::iterator it = m_profiles.begin(); it != profilesEnd; ++it)
158         (*headers)->pushObject(createProfileHeader(*it->second));
159     HeapSnapshotsMap::iterator snapshotsEnd = m_snapshots.end();
160     for (HeapSnapshotsMap::iterator it = m_snapshots.begin(); it != snapshotsEnd; ++it)
161         (*headers)->pushObject(createSnapshotHeader(*it->second));
162 }
163
164 namespace {
165
166 class OutputStream : public ScriptHeapSnapshot::OutputStream {
167 public:
168     OutputStream(InspectorFrontend* frontend, unsigned long uid)
169         : m_frontend(frontend), m_uid(uid) { }
170     void Write(const String& chunk) { m_frontend->addHeapSnapshotChunk(m_uid, chunk); }
171     void Close() { m_frontend->finishHeapSnapshot(m_uid); }
172 private:
173     InspectorFrontend* m_frontend;
174     unsigned long m_uid;
175 };
176
177 } // namespace
178
179 void InspectorProfilerAgent::getProfile(const String& type, unsigned uid, RefPtr<InspectorObject>* profileObject)
180 {
181     if (type == CPUProfileType) {
182         ProfilesMap::iterator it = m_profiles.find(uid);
183         if (it != m_profiles.end()) {
184             *profileObject = createProfileHeader(*it->second);
185             (*profileObject)->setObject("head", it->second->buildInspectorObjectForHead());
186         }
187     } else if (type == HeapProfileType) {
188         HeapSnapshotsMap::iterator it = m_snapshots.find(uid);
189         if (it != m_snapshots.end()) {
190             RefPtr<ScriptHeapSnapshot> snapshot = it->second;
191             *profileObject = createSnapshotHeader(*snapshot);
192             if (m_frontend) {
193                 OutputStream stream(m_frontend, uid);
194                 snapshot->writeJSON(&stream);
195             }
196         }
197     }
198 }
199
200 void InspectorProfilerAgent::removeProfile(const String& type, unsigned uid)
201 {
202     if (type == CPUProfileType) {
203         if (m_profiles.contains(uid))
204             m_profiles.remove(uid);
205     } else if (type == HeapProfileType) {
206         if (m_snapshots.contains(uid))
207             m_snapshots.remove(uid);
208     }
209 }
210
211 void InspectorProfilerAgent::resetState()
212 {
213     m_profiles.clear();
214     m_snapshots.clear();
215     m_currentUserInitiatedProfileNumber = 1;
216     m_nextUserInitiatedProfileNumber = 1;
217     m_nextUserInitiatedHeapSnapshotNumber = 1;
218     resetFrontendProfiles();
219 }
220
221 void InspectorProfilerAgent::resetFrontendProfiles()
222 {
223     if (m_frontend
224         && m_profiles.begin() == m_profiles.end()
225         && m_snapshots.begin() == m_snapshots.end())
226         m_frontend->resetProfiles();
227 }
228
229 void InspectorProfilerAgent::startUserInitiatedProfiling()
230 {
231     if (m_recordingUserInitiatedProfile)
232         return;
233     if (!enabled()) {
234         enable(true);
235         ScriptDebugServer::shared().recompileAllJSFunctions();
236     }
237     m_recordingUserInitiatedProfile = true;
238     String title = getCurrentUserInitiatedProfileName(true);
239 #if USE(JSC)
240     JSC::ExecState* scriptState = toJSDOMWindow(m_inspectorAgent->inspectedPage()->mainFrame(), debuggerWorld())->globalExec();
241 #else
242     ScriptState* scriptState = 0;
243 #endif
244     ScriptProfiler::start(scriptState, title);
245     addStartProfilingMessageToConsole(title, 0, String());
246     toggleRecordButton(true);
247 }
248
249 void InspectorProfilerAgent::stopUserInitiatedProfiling(bool ignoreProfile)
250 {
251     if (!m_recordingUserInitiatedProfile)
252         return;
253     m_recordingUserInitiatedProfile = false;
254     String title = getCurrentUserInitiatedProfileName();
255 #if USE(JSC)
256     JSC::ExecState* scriptState = toJSDOMWindow(m_inspectorAgent->inspectedPage()->mainFrame(), debuggerWorld())->globalExec();
257 #else
258     // Use null script state to avoid filtering by context security token.
259     // All functions from all iframes should be visible from Inspector UI.
260     ScriptState* scriptState = 0;
261 #endif
262     RefPtr<ScriptProfile> profile = ScriptProfiler::stop(scriptState, title);
263     if (profile) {
264         if (!ignoreProfile)
265             addProfile(profile, 0, String());
266         else
267             addProfileFinishedMessageToConsole(profile, 0, String());
268     }
269     toggleRecordButton(false);
270 }
271
272 namespace {
273
274 class HeapSnapshotProgress: public ScriptProfiler::HeapSnapshotProgress {
275 public:
276     explicit HeapSnapshotProgress(InspectorFrontend* frontend)
277         : m_frontend(frontend) { }
278     void Start(int totalWork)
279     {
280         m_totalWork = totalWork;
281     }
282     void Worked(int workDone)
283     {
284         if (m_frontend)
285             m_frontend->reportHeapSnapshotProgress(workDone, m_totalWork);
286     }
287     void Done() { }
288     bool isCanceled() { return false; }
289 private:
290     InspectorFrontend* m_frontend;
291     int m_totalWork;
292 };
293
294 };
295
296 void InspectorProfilerAgent::takeHeapSnapshot(bool detailed)
297 {
298     String title = makeString(UserInitiatedProfileName, '.', String::number(m_nextUserInitiatedHeapSnapshotNumber));
299     ++m_nextUserInitiatedHeapSnapshotNumber;
300
301     HeapSnapshotProgress progress(m_frontend);
302     RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title, detailed ? &progress : 0);
303     if (snapshot) {
304         m_snapshots.add(snapshot->uid(), snapshot);
305         if (m_frontend)
306             m_frontend->addProfileHeader(createSnapshotHeader(*snapshot));
307     }
308 }
309
310 void InspectorProfilerAgent::toggleRecordButton(bool isProfiling)
311 {
312     if (m_frontend)
313         m_frontend->setRecordingProfile(isProfiling);
314 }
315
316 } // namespace WebCore
317
318 #endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)