2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
31 #include "InspectorProfilerAgent.h"
33 #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)
36 #include "InspectorAgent.h"
37 #include "InspectorConsoleAgent.h"
38 #include "InspectorFrontend.h"
39 #include "InspectorValues.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>
50 #include "JSDOMWindow.h"
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";
59 PassOwnPtr<InspectorProfilerAgent> InspectorProfilerAgent::create(InspectorAgent* inspectorAgent)
61 OwnPtr<InspectorProfilerAgent> agent = adoptPtr(new InspectorProfilerAgent(inspectorAgent));
62 return agent.release();
65 InspectorProfilerAgent::InspectorProfilerAgent(InspectorAgent* inspectorAgent)
66 : m_inspectorAgent(inspectorAgent)
69 , m_recordingUserInitiatedProfile(false)
70 , m_currentUserInitiatedProfileNumber(-1)
71 , m_nextUserInitiatedProfileNumber(1)
72 , m_nextUserInitiatedHeapSnapshotNumber(1)
76 InspectorProfilerAgent::~InspectorProfilerAgent()
80 void InspectorProfilerAgent::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
82 RefPtr<ScriptProfile> profile = prpProfile;
83 m_profiles.add(profile->uid(), profile);
85 m_frontend->addProfileHeader(createProfileHeader(*profile));
86 addProfileFinishedMessageToConsole(profile, lineNumber, sourceURL);
89 void InspectorProfilerAgent::addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
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);
99 void InspectorProfilerAgent::addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, const String& sourceURL)
103 String message = makeString("Profile \"webkit-profile://", CPUProfileType, '/', encodeWithURLEscapeSequences(title), "#0\" started.");
104 m_inspectorAgent->consoleAgent()->addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceURL);
107 PassRefPtr<InspectorObject> InspectorProfilerAgent::createProfileHeader(const ScriptProfile& profile)
109 RefPtr<InspectorObject> header = InspectorObject::create();
110 header->setString("title", profile.title());
111 header->setNumber("uid", profile.uid());
112 header->setString("typeId", String(CPUProfileType));
116 PassRefPtr<InspectorObject> InspectorProfilerAgent::createSnapshotHeader(const ScriptHeapSnapshot& snapshot)
118 RefPtr<InspectorObject> header = InspectorObject::create();
119 header->setString("title", snapshot.title());
120 header->setNumber("uid", snapshot.uid());
121 header->setString("typeId", String(HeapProfileType));
125 void InspectorProfilerAgent::disable()
130 ScriptDebugServer::shared().recompileAllJSFunctionsSoon();
132 m_frontend->profilerWasDisabled();
135 void InspectorProfilerAgent::enable(bool skipRecompile)
141 ScriptDebugServer::shared().recompileAllJSFunctionsSoon();
143 m_frontend->profilerWasEnabled();
146 String InspectorProfilerAgent::getCurrentUserInitiatedProfileName(bool incrementProfileNumber)
148 if (incrementProfileNumber)
149 m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++;
151 return makeString(UserInitiatedProfileName, '.', String::number(m_currentUserInitiatedProfileNumber));
154 void InspectorProfilerAgent::getProfileHeaders(RefPtr<InspectorArray>* headers)
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));
166 class OutputStream : public ScriptHeapSnapshot::OutputStream {
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); }
173 InspectorFrontend* m_frontend;
179 void InspectorProfilerAgent::getProfile(const String& type, unsigned uid, RefPtr<InspectorObject>* profileObject)
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());
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);
193 OutputStream stream(m_frontend, uid);
194 snapshot->writeJSON(&stream);
200 void InspectorProfilerAgent::removeProfile(const String& type, unsigned uid)
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);
211 void InspectorProfilerAgent::resetState()
215 m_currentUserInitiatedProfileNumber = 1;
216 m_nextUserInitiatedProfileNumber = 1;
217 m_nextUserInitiatedHeapSnapshotNumber = 1;
218 resetFrontendProfiles();
221 void InspectorProfilerAgent::resetFrontendProfiles()
224 && m_profiles.begin() == m_profiles.end()
225 && m_snapshots.begin() == m_snapshots.end())
226 m_frontend->resetProfiles();
229 void InspectorProfilerAgent::startUserInitiatedProfiling()
231 if (m_recordingUserInitiatedProfile)
235 ScriptDebugServer::shared().recompileAllJSFunctions();
237 m_recordingUserInitiatedProfile = true;
238 String title = getCurrentUserInitiatedProfileName(true);
240 JSC::ExecState* scriptState = toJSDOMWindow(m_inspectorAgent->inspectedPage()->mainFrame(), debuggerWorld())->globalExec();
242 ScriptState* scriptState = 0;
244 ScriptProfiler::start(scriptState, title);
245 addStartProfilingMessageToConsole(title, 0, String());
246 toggleRecordButton(true);
249 void InspectorProfilerAgent::stopUserInitiatedProfiling(bool ignoreProfile)
251 if (!m_recordingUserInitiatedProfile)
253 m_recordingUserInitiatedProfile = false;
254 String title = getCurrentUserInitiatedProfileName();
256 JSC::ExecState* scriptState = toJSDOMWindow(m_inspectorAgent->inspectedPage()->mainFrame(), debuggerWorld())->globalExec();
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;
262 RefPtr<ScriptProfile> profile = ScriptProfiler::stop(scriptState, title);
265 addProfile(profile, 0, String());
267 addProfileFinishedMessageToConsole(profile, 0, String());
269 toggleRecordButton(false);
274 class HeapSnapshotProgress: public ScriptProfiler::HeapSnapshotProgress {
276 explicit HeapSnapshotProgress(InspectorFrontend* frontend)
277 : m_frontend(frontend) { }
278 void Start(int totalWork)
280 m_totalWork = totalWork;
282 void Worked(int workDone)
285 m_frontend->reportHeapSnapshotProgress(workDone, m_totalWork);
288 bool isCanceled() { return false; }
290 InspectorFrontend* m_frontend;
296 void InspectorProfilerAgent::takeHeapSnapshot(bool detailed)
298 String title = makeString(UserInitiatedProfileName, '.', String::number(m_nextUserInitiatedHeapSnapshotNumber));
299 ++m_nextUserInitiatedHeapSnapshotNumber;
301 HeapSnapshotProgress progress(m_frontend);
302 RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title, detailed ? &progress : 0);
304 m_snapshots.add(snapshot->uid(), snapshot);
306 m_frontend->addProfileHeader(createSnapshotHeader(*snapshot));
310 void InspectorProfilerAgent::toggleRecordButton(bool isProfiling)
313 m_frontend->setRecordingProfile(isProfiling);
316 } // namespace WebCore
318 #endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)