• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "InspectorConsoleAgent.h"
37 #include "InspectorFrontend.h"
38 #include "InspectorState.h"
39 #include "InspectorValues.h"
40 #include "InstrumentingAgents.h"
41 #include "KURL.h"
42 #include "Page.h"
43 #include "PageScriptDebugServer.h"
44 #include "ScriptController.h"
45 #include "ScriptHeapSnapshot.h"
46 #include "ScriptProfile.h"
47 #include "ScriptProfiler.h"
48 #include <wtf/OwnPtr.h>
49 #include <wtf/text/StringConcatenate.h>
50 
51 #if USE(JSC)
52 #include "JSDOMWindow.h"
53 #endif
54 
55 namespace WebCore {
56 
57 namespace ProfilerAgentState {
58 static const char userInitiatedProfiling[] = "userInitiatedProfiling";
59 static const char profilerEnabled[] = "profilerEnabled";
60 }
61 
62 static const char* const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
63 static const char* const CPUProfileType = "CPU";
64 static const char* const HeapProfileType = "HEAP";
65 
create(InstrumentingAgents * instrumentingAgents,InspectorConsoleAgent * consoleAgent,Page * inspectedPage,InspectorState * inspectorState)66 PassOwnPtr<InspectorProfilerAgent> InspectorProfilerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorConsoleAgent* consoleAgent, Page* inspectedPage, InspectorState* inspectorState)
67 {
68     return adoptPtr(new InspectorProfilerAgent(instrumentingAgents, consoleAgent, inspectedPage, inspectorState));
69 }
70 
InspectorProfilerAgent(InstrumentingAgents * instrumentingAgents,InspectorConsoleAgent * consoleAgent,Page * inspectedPage,InspectorState * inspectorState)71 InspectorProfilerAgent::InspectorProfilerAgent(InstrumentingAgents* instrumentingAgents, InspectorConsoleAgent* consoleAgent, Page* inspectedPage, InspectorState* inspectorState)
72     : m_instrumentingAgents(instrumentingAgents)
73     , m_consoleAgent(consoleAgent)
74     , m_inspectedPage(inspectedPage)
75     , m_inspectorState(inspectorState)
76     , m_frontend(0)
77     , m_enabled(false)
78     , m_recordingUserInitiatedProfile(false)
79     , m_currentUserInitiatedProfileNumber(-1)
80     , m_nextUserInitiatedProfileNumber(1)
81     , m_nextUserInitiatedHeapSnapshotNumber(1)
82 {
83     m_instrumentingAgents->setInspectorProfilerAgent(this);
84 }
85 
~InspectorProfilerAgent()86 InspectorProfilerAgent::~InspectorProfilerAgent()
87 {
88     m_instrumentingAgents->setInspectorProfilerAgent(0);
89 }
90 
addProfile(PassRefPtr<ScriptProfile> prpProfile,unsigned lineNumber,const String & sourceURL)91 void InspectorProfilerAgent::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
92 {
93     RefPtr<ScriptProfile> profile = prpProfile;
94     m_profiles.add(profile->uid(), profile);
95     if (m_frontend)
96         m_frontend->addProfileHeader(createProfileHeader(*profile));
97     addProfileFinishedMessageToConsole(profile, lineNumber, sourceURL);
98 }
99 
addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile> prpProfile,unsigned lineNumber,const String & sourceURL)100 void InspectorProfilerAgent::addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
101 {
102     if (!m_frontend)
103         return;
104     RefPtr<ScriptProfile> profile = prpProfile;
105     String title = profile->title();
106     String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), '#', String::number(profile->uid()), "\" finished.");
107     m_consoleAgent->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
108 }
109 
addStartProfilingMessageToConsole(const String & title,unsigned lineNumber,const String & sourceURL)110 void InspectorProfilerAgent::addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, const String& sourceURL)
111 {
112     if (!m_frontend)
113         return;
114     String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), "#0\" started.");
115     m_consoleAgent->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
116 }
117 
collectGarbage(WebCore::ErrorString *)118 void InspectorProfilerAgent::collectGarbage(WebCore::ErrorString*)
119 {
120     ScriptProfiler::collectGarbage();
121 }
122 
createProfileHeader(const ScriptProfile & profile)123 PassRefPtr<InspectorObject> InspectorProfilerAgent::createProfileHeader(const ScriptProfile& profile)
124 {
125     RefPtr<InspectorObject> header = InspectorObject::create();
126     header->setString("title", profile.title());
127     header->setNumber("uid", profile.uid());
128     header->setString("typeId", String(CPUProfileType));
129     return header;
130 }
131 
createSnapshotHeader(const ScriptHeapSnapshot & snapshot)132 PassRefPtr<InspectorObject> InspectorProfilerAgent::createSnapshotHeader(const ScriptHeapSnapshot& snapshot)
133 {
134     RefPtr<InspectorObject> header = InspectorObject::create();
135     header->setString("title", snapshot.title());
136     header->setNumber("uid", snapshot.uid());
137     header->setString("typeId", String(HeapProfileType));
138     return header;
139 }
140 
enable(ErrorString *)141 void InspectorProfilerAgent::enable(ErrorString*)
142 {
143     if (enabled())
144         return;
145     m_inspectorState->setBoolean(ProfilerAgentState::profilerEnabled, true);
146     enable(false);
147 }
148 
disable(ErrorString *)149 void InspectorProfilerAgent::disable(ErrorString*)
150 {
151     m_inspectorState->setBoolean(ProfilerAgentState::profilerEnabled, false);
152     disable();
153 }
154 
disable()155 void InspectorProfilerAgent::disable()
156 {
157     if (!m_enabled)
158         return;
159     m_enabled = false;
160     PageScriptDebugServer::shared().recompileAllJSFunctionsSoon();
161     if (m_frontend)
162         m_frontend->profilerWasDisabled();
163 }
164 
enable(bool skipRecompile)165 void InspectorProfilerAgent::enable(bool skipRecompile)
166 {
167     if (m_enabled)
168         return;
169     m_enabled = true;
170     if (!skipRecompile)
171         PageScriptDebugServer::shared().recompileAllJSFunctionsSoon();
172     if (m_frontend)
173         m_frontend->profilerWasEnabled();
174 }
175 
getCurrentUserInitiatedProfileName(bool incrementProfileNumber)176 String InspectorProfilerAgent::getCurrentUserInitiatedProfileName(bool incrementProfileNumber)
177 {
178     if (incrementProfileNumber)
179         m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++;
180 
181     return makeString(UserInitiatedProfileName, '.', String::number(m_currentUserInitiatedProfileNumber));
182 }
183 
getProfileHeaders(ErrorString *,RefPtr<InspectorArray> * headers)184 void InspectorProfilerAgent::getProfileHeaders(ErrorString*, RefPtr<InspectorArray>* headers)
185 {
186     ProfilesMap::iterator profilesEnd = m_profiles.end();
187     for (ProfilesMap::iterator it = m_profiles.begin(); it != profilesEnd; ++it)
188         (*headers)->pushObject(createProfileHeader(*it->second));
189     HeapSnapshotsMap::iterator snapshotsEnd = m_snapshots.end();
190     for (HeapSnapshotsMap::iterator it = m_snapshots.begin(); it != snapshotsEnd; ++it)
191         (*headers)->pushObject(createSnapshotHeader(*it->second));
192 }
193 
194 namespace {
195 
196 class OutputStream : public ScriptHeapSnapshot::OutputStream {
197 public:
OutputStream(InspectorFrontend::Profiler * frontend,unsigned uid)198     OutputStream(InspectorFrontend::Profiler* frontend, unsigned uid)
199         : m_frontend(frontend), m_uid(uid) { }
Write(const String & chunk)200     void Write(const String& chunk) { m_frontend->addHeapSnapshotChunk(m_uid, chunk); }
Close()201     void Close() { m_frontend->finishHeapSnapshot(m_uid); }
202 private:
203     InspectorFrontend::Profiler* m_frontend;
204     int m_uid;
205 };
206 
207 } // namespace
208 
getProfile(ErrorString *,const String & type,unsigned uid,RefPtr<InspectorObject> * profileObject)209 void InspectorProfilerAgent::getProfile(ErrorString*, const String& type, unsigned uid, RefPtr<InspectorObject>* profileObject)
210 {
211     if (type == CPUProfileType) {
212         ProfilesMap::iterator it = m_profiles.find(uid);
213         if (it != m_profiles.end()) {
214             *profileObject = createProfileHeader(*it->second);
215             (*profileObject)->setObject("head", it->second->buildInspectorObjectForHead());
216         }
217     } else if (type == HeapProfileType) {
218         HeapSnapshotsMap::iterator it = m_snapshots.find(uid);
219         if (it != m_snapshots.end()) {
220             RefPtr<ScriptHeapSnapshot> snapshot = it->second;
221             *profileObject = createSnapshotHeader(*snapshot);
222             if (m_frontend) {
223                 OutputStream stream(m_frontend, uid);
224                 snapshot->writeJSON(&stream);
225             }
226         }
227     }
228 }
229 
removeProfile(ErrorString *,const String & type,unsigned uid)230 void InspectorProfilerAgent::removeProfile(ErrorString*, const String& type, unsigned uid)
231 {
232     if (type == CPUProfileType) {
233         if (m_profiles.contains(uid))
234             m_profiles.remove(uid);
235     } else if (type == HeapProfileType) {
236         if (m_snapshots.contains(uid))
237             m_snapshots.remove(uid);
238     }
239 }
240 
resetState()241 void InspectorProfilerAgent::resetState()
242 {
243     stopUserInitiatedProfiling();
244     m_profiles.clear();
245     m_snapshots.clear();
246     m_currentUserInitiatedProfileNumber = 1;
247     m_nextUserInitiatedProfileNumber = 1;
248     m_nextUserInitiatedHeapSnapshotNumber = 1;
249     resetFrontendProfiles();
250 }
251 
resetFrontendProfiles()252 void InspectorProfilerAgent::resetFrontendProfiles()
253 {
254     if (m_frontend
255         && m_profiles.begin() == m_profiles.end()
256         && m_snapshots.begin() == m_snapshots.end())
257         m_frontend->resetProfiles();
258 }
259 
setFrontend(InspectorFrontend * frontend)260 void InspectorProfilerAgent::setFrontend(InspectorFrontend* frontend)
261 {
262     m_frontend = frontend->profiler();
263     restoreEnablement();
264 }
265 
clearFrontend()266 void InspectorProfilerAgent::clearFrontend()
267 {
268     m_frontend = 0;
269     stopUserInitiatedProfiling();
270 }
271 
restore()272 void InspectorProfilerAgent::restore()
273 {
274     // Need to restore enablement state here as in setFrontend m_inspectorState wasn't loaded yet.
275     restoreEnablement();
276 
277     // Revisit this.
278     resetFrontendProfiles();
279     if (m_inspectorState->getBoolean(ProfilerAgentState::userInitiatedProfiling))
280         startUserInitiatedProfiling();
281 }
282 
restoreEnablement()283 void InspectorProfilerAgent::restoreEnablement()
284 {
285     if (m_inspectorState->getBoolean(ProfilerAgentState::profilerEnabled)) {
286         ErrorString error;
287         enable(&error);
288     }
289 }
290 
startUserInitiatedProfiling()291 void InspectorProfilerAgent::startUserInitiatedProfiling()
292 {
293     if (m_recordingUserInitiatedProfile)
294         return;
295     if (!enabled()) {
296         enable(true);
297         PageScriptDebugServer::shared().recompileAllJSFunctions(0);
298     }
299     m_recordingUserInitiatedProfile = true;
300     String title = getCurrentUserInitiatedProfileName(true);
301 #if USE(JSC)
302     JSC::ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec();
303 #else
304     ScriptState* scriptState = 0;
305 #endif
306     ScriptProfiler::start(scriptState, title);
307     addStartProfilingMessageToConsole(title, 0, String());
308     toggleRecordButton(true);
309     m_inspectorState->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
310 }
311 
stopUserInitiatedProfiling(bool ignoreProfile)312 void InspectorProfilerAgent::stopUserInitiatedProfiling(bool ignoreProfile)
313 {
314     if (!m_recordingUserInitiatedProfile)
315         return;
316     m_recordingUserInitiatedProfile = false;
317     String title = getCurrentUserInitiatedProfileName();
318 #if USE(JSC)
319     JSC::ExecState* scriptState = toJSDOMWindow(m_inspectedPage->mainFrame(), debuggerWorld())->globalExec();
320 #else
321     // Use null script state to avoid filtering by context security token.
322     // All functions from all iframes should be visible from Inspector UI.
323     ScriptState* scriptState = 0;
324 #endif
325     RefPtr<ScriptProfile> profile = ScriptProfiler::stop(scriptState, title);
326     if (profile) {
327         if (!ignoreProfile)
328             addProfile(profile, 0, String());
329         else
330             addProfileFinishedMessageToConsole(profile, 0, String());
331     }
332     toggleRecordButton(false);
333     m_inspectorState->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
334 }
335 
336 namespace {
337 
338 class HeapSnapshotProgress: public ScriptProfiler::HeapSnapshotProgress {
339 public:
HeapSnapshotProgress(InspectorFrontend::Profiler * frontend)340     explicit HeapSnapshotProgress(InspectorFrontend::Profiler* frontend)
341         : m_frontend(frontend) { }
Start(int totalWork)342     void Start(int totalWork)
343     {
344         m_totalWork = totalWork;
345     }
Worked(int workDone)346     void Worked(int workDone)
347     {
348         if (m_frontend)
349             m_frontend->reportHeapSnapshotProgress(workDone, m_totalWork);
350     }
Done()351     void Done() { }
isCanceled()352     bool isCanceled() { return false; }
353 private:
354     InspectorFrontend::Profiler* m_frontend;
355     int m_totalWork;
356 };
357 
358 };
359 
takeHeapSnapshot(ErrorString *,bool detailed)360 void InspectorProfilerAgent::takeHeapSnapshot(ErrorString*, bool detailed)
361 {
362     String title = makeString(UserInitiatedProfileName, '.', String::number(m_nextUserInitiatedHeapSnapshotNumber));
363     ++m_nextUserInitiatedHeapSnapshotNumber;
364 
365     HeapSnapshotProgress progress(m_frontend);
366     RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title, detailed ? &progress : 0);
367     if (snapshot) {
368         m_snapshots.add(snapshot->uid(), snapshot);
369         if (m_frontend)
370             m_frontend->addProfileHeader(createSnapshotHeader(*snapshot));
371     }
372 }
373 
toggleRecordButton(bool isProfiling)374 void InspectorProfilerAgent::toggleRecordButton(bool isProfiling)
375 {
376     if (m_frontend)
377         m_frontend->setRecordingProfile(isProfiling);
378 }
379 
380 } // namespace WebCore
381 
382 #endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)
383