Web Inspector: Remove InspectorState
[WebKit-https.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
32 #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)
33
34 #include "InspectorProfilerAgent.h"
35
36 #include "Console.h"
37 #include "ConsoleAPITypes.h"
38 #include "ConsoleTypes.h"
39 #include "InjectedScript.h"
40 #include "InjectedScriptHost.h"
41 #include "InspectorConsoleAgent.h"
42 #include "InspectorFrontend.h"
43 #include "InspectorValues.h"
44 #include "InstrumentingAgents.h"
45 #include "URL.h"
46 #include "Page.h"
47 #include "PageScriptDebugServer.h"
48 #include "ScriptHeapSnapshot.h"
49 #include "ScriptObject.h"
50 #include "ScriptProfile.h"
51 #include "ScriptProfiler.h"
52 #include "WorkerScriptDebugServer.h"
53 #include <wtf/CurrentTime.h>
54 #include <wtf/OwnPtr.h>
55 #include <wtf/text/StringConcatenate.h>
56
57 namespace WebCore {
58
59 static const char* const UserInitiatedProfileName = "org.webkit.profiles.user-initiated";
60 static const char* const CPUProfileType = "CPU";
61 static const char* const HeapProfileType = "HEAP";
62
63
64 class PageProfilerAgent : public InspectorProfilerAgent {
65 public:
66     PageProfilerAgent(InstrumentingAgents* instrumentingAgents, InspectorConsoleAgent* consoleAgent, Page* inspectedPage, InjectedScriptManager* injectedScriptManager)
67         : InspectorProfilerAgent(instrumentingAgents, consoleAgent, injectedScriptManager), m_inspectedPage(inspectedPage) { }
68     virtual ~PageProfilerAgent() { }
69
70 private:
71     virtual void recompileScript()
72     {
73         PageScriptDebugServer::shared().recompileAllJSFunctionsSoon();
74     }
75
76     virtual void startProfiling(const String& title)
77     {
78         ScriptProfiler::startForPage(m_inspectedPage, title);
79     }
80
81     virtual PassRefPtr<ScriptProfile> stopProfiling(const String& title)
82     {
83         return ScriptProfiler::stopForPage(m_inspectedPage, title);
84     }
85
86     Page* m_inspectedPage;
87 };
88
89 PassOwnPtr<InspectorProfilerAgent> InspectorProfilerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorConsoleAgent* consoleAgent, Page* inspectedPage, InjectedScriptManager* injectedScriptManager)
90 {
91     return adoptPtr(new PageProfilerAgent(instrumentingAgents, consoleAgent, inspectedPage, injectedScriptManager));
92 }
93
94 #if ENABLE(WORKERS)
95 class WorkerProfilerAgent : public InspectorProfilerAgent {
96 public:
97     WorkerProfilerAgent(InstrumentingAgents* instrumentingAgents, InspectorConsoleAgent* consoleAgent, WorkerGlobalScope* workerGlobalScope, InjectedScriptManager* injectedScriptManager)
98         : InspectorProfilerAgent(instrumentingAgents, consoleAgent, injectedScriptManager), m_workerGlobalScope(workerGlobalScope) { }
99     virtual ~WorkerProfilerAgent() { }
100
101 private:
102     virtual void recompileScript() { }
103
104     virtual void startProfiling(const String& title)
105     {
106         ScriptProfiler::startForWorkerGlobalScope(m_workerGlobalScope, title);
107     }
108
109     virtual PassRefPtr<ScriptProfile> stopProfiling(const String& title)
110     {
111         return ScriptProfiler::stopForWorkerGlobalScope(m_workerGlobalScope, title);
112     }
113
114     WorkerGlobalScope* m_workerGlobalScope;
115 };
116
117 PassOwnPtr<InspectorProfilerAgent> InspectorProfilerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorConsoleAgent* consoleAgent, WorkerGlobalScope* workerGlobalScope, InjectedScriptManager* injectedScriptManager)
118 {
119     return adoptPtr(new WorkerProfilerAgent(instrumentingAgents, consoleAgent, workerGlobalScope, injectedScriptManager));
120 }
121 #endif
122
123 InspectorProfilerAgent::InspectorProfilerAgent(InstrumentingAgents* instrumentingAgents, InspectorConsoleAgent* consoleAgent, InjectedScriptManager* injectedScriptManager)
124     : InspectorBaseAgent<InspectorProfilerAgent>("Profiler", instrumentingAgents)
125     , m_consoleAgent(consoleAgent)
126     , m_injectedScriptManager(injectedScriptManager)
127     , m_frontend(0)
128     , m_enabled(false)
129     , m_profileHeadersRequested(false)
130     , m_recordingCPUProfile(false)
131     , m_currentUserInitiatedProfileNumber(-1)
132     , m_nextUserInitiatedProfileNumber(1)
133     , m_nextUserInitiatedHeapSnapshotNumber(1)
134     , m_profileNameIdleTimeMap(ScriptProfiler::currentProfileNameIdleTimeMap())
135 {
136     m_instrumentingAgents->setInspectorProfilerAgent(this);
137 }
138
139 InspectorProfilerAgent::~InspectorProfilerAgent()
140 {
141     m_instrumentingAgents->setInspectorProfilerAgent(0);
142 }
143
144 void InspectorProfilerAgent::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, unsigned columnNumber, const String& sourceURL)
145 {
146     RefPtr<ScriptProfile> profile = prpProfile;
147     m_profiles.add(profile->uid(), profile);
148     if (m_frontend && m_profileHeadersRequested)
149         m_frontend->addProfileHeader(createProfileHeader(*profile));
150     addProfileFinishedMessageToConsole(profile, lineNumber, columnNumber, sourceURL);
151 }
152
153 void InspectorProfilerAgent::addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, unsigned columnNumber, const String& sourceURL)
154 {
155     if (!m_frontend)
156         return;
157     RefPtr<ScriptProfile> profile = prpProfile;
158     String message = makeString(profile->title(), '#', String::number(profile->uid()));
159     m_consoleAgent->addMessageToConsole(ConsoleAPIMessageSource, ProfileEndMessageType, DebugMessageLevel, message, sourceURL, lineNumber, columnNumber);
160 }
161
162 void InspectorProfilerAgent::addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, unsigned columnNumber, const String& sourceURL)
163 {
164     if (!m_frontend)
165         return;
166     m_consoleAgent->addMessageToConsole(ConsoleAPIMessageSource, ProfileMessageType, DebugMessageLevel, title, sourceURL, lineNumber, columnNumber);
167 }
168
169 void InspectorProfilerAgent::collectGarbage(WebCore::ErrorString*)
170 {
171     ScriptProfiler::collectGarbage();
172 }
173
174 PassRefPtr<TypeBuilder::Profiler::ProfileHeader> InspectorProfilerAgent::createProfileHeader(const ScriptProfile& profile)
175 {
176     return TypeBuilder::Profiler::ProfileHeader::create()
177         .setTypeId(TypeBuilder::Profiler::ProfileHeader::TypeId::CPU)
178         .setUid(profile.uid())
179         .setTitle(profile.title())
180         .release();
181 }
182
183 PassRefPtr<TypeBuilder::Profiler::ProfileHeader> InspectorProfilerAgent::createSnapshotHeader(const ScriptHeapSnapshot& snapshot)
184 {
185     RefPtr<TypeBuilder::Profiler::ProfileHeader> header = TypeBuilder::Profiler::ProfileHeader::create()
186         .setTypeId(TypeBuilder::Profiler::ProfileHeader::TypeId::HEAP)
187         .setUid(snapshot.uid())
188         .setTitle(snapshot.title());
189     header->setMaxJSObjectId(snapshot.maxSnapshotJSObjectId());
190     return header.release();
191 }
192
193 void InspectorProfilerAgent::causesRecompilation(ErrorString*, bool* result)
194 {
195     *result = ScriptProfiler::causesRecompilation();
196 }
197
198 void InspectorProfilerAgent::isSampling(ErrorString*, bool* result)
199 {
200     *result = ScriptProfiler::isSampling();
201 }
202
203 void InspectorProfilerAgent::hasHeapProfiler(ErrorString*, bool* result)
204 {
205     *result = ScriptProfiler::hasHeapProfiler();
206 }
207
208 void InspectorProfilerAgent::enable(ErrorString*)
209 {
210     if (enabled())
211         return;
212     enable(false);
213 }
214
215 void InspectorProfilerAgent::disable(ErrorString*)
216 {
217     disable();
218 }
219
220 void InspectorProfilerAgent::disable()
221 {
222     if (!m_enabled)
223         return;
224     m_enabled = false;
225     m_profileHeadersRequested = false;
226     recompileScript();
227 }
228
229 void InspectorProfilerAgent::enable(bool skipRecompile)
230 {
231     if (m_enabled)
232         return;
233     m_enabled = true;
234     if (!skipRecompile)
235         recompileScript();
236 }
237
238 String InspectorProfilerAgent::getCurrentUserInitiatedProfileName(bool incrementProfileNumber)
239 {
240     if (incrementProfileNumber)
241         m_currentUserInitiatedProfileNumber = m_nextUserInitiatedProfileNumber++;
242
243     return makeString(UserInitiatedProfileName, '.', String::number(m_currentUserInitiatedProfileNumber));
244 }
245
246 void InspectorProfilerAgent::getProfileHeaders(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::Profiler::ProfileHeader>>& headers)
247 {
248     m_profileHeadersRequested = true;
249     headers = TypeBuilder::Array<TypeBuilder::Profiler::ProfileHeader>::create();
250
251     ProfilesMap::iterator profilesEnd = m_profiles.end();
252     for (ProfilesMap::iterator it = m_profiles.begin(); it != profilesEnd; ++it)
253         headers->addItem(createProfileHeader(*it->value));
254     HeapSnapshotsMap::iterator snapshotsEnd = m_snapshots.end();
255     for (HeapSnapshotsMap::iterator it = m_snapshots.begin(); it != snapshotsEnd; ++it)
256         headers->addItem(createSnapshotHeader(*it->value));
257 }
258
259 namespace {
260
261 class OutputStream : public ScriptHeapSnapshot::OutputStream {
262 public:
263     OutputStream(InspectorFrontend::Profiler* frontend, unsigned uid)
264         : m_frontend(frontend), m_uid(uid) { }
265     void Write(const String& chunk) { m_frontend->addHeapSnapshotChunk(m_uid, chunk); }
266     void Close() { m_frontend->finishHeapSnapshot(m_uid); }
267 private:
268     InspectorFrontend::Profiler* m_frontend;
269     int m_uid;
270 };
271
272 } // namespace
273
274 void InspectorProfilerAgent::getCPUProfile(ErrorString* errorString, int rawUid, RefPtr<TypeBuilder::Profiler::CPUProfile>& profileObject)
275 {
276     unsigned uid = static_cast<unsigned>(rawUid);
277     ProfilesMap::iterator it = m_profiles.find(uid);
278     if (it == m_profiles.end()) {
279         *errorString = "Profile wasn't found";
280         return;
281     }
282     profileObject = TypeBuilder::Profiler::CPUProfile::create();
283     profileObject->setHead(it->value->buildInspectorObjectForHead());
284     profileObject->setIdleTime(it->value->idleTime());
285 }
286
287 void InspectorProfilerAgent::getHeapSnapshot(ErrorString* errorString, int rawUid)
288 {
289     unsigned uid = static_cast<unsigned>(rawUid);
290     HeapSnapshotsMap::iterator it = m_snapshots.find(uid);
291     if (it == m_snapshots.end()) {
292         *errorString = "Profile wasn't found";
293         return;
294     }
295     RefPtr<ScriptHeapSnapshot> snapshot = it->value;
296     if (m_frontend) {
297         OutputStream stream(m_frontend, uid);
298         snapshot->writeJSON(&stream);
299     }
300 }
301
302 void InspectorProfilerAgent::removeProfile(ErrorString*, const String& type, int rawUid)
303 {
304     unsigned uid = static_cast<unsigned>(rawUid);
305     if (type == CPUProfileType)
306         m_profiles.remove(uid);
307     else if (type == HeapProfileType)
308         m_snapshots.remove(uid);
309 }
310
311 void InspectorProfilerAgent::resetState()
312 {
313     stop();
314     m_profiles.clear();
315     m_snapshots.clear();
316     m_currentUserInitiatedProfileNumber = 1;
317     m_nextUserInitiatedProfileNumber = 1;
318     m_nextUserInitiatedHeapSnapshotNumber = 1;
319     resetFrontendProfiles();
320     m_injectedScriptManager->injectedScriptHost()->clearInspectedObjects();
321 }
322
323 void InspectorProfilerAgent::resetFrontendProfiles()
324 {
325     if (!m_frontend)
326         return;
327     if (!m_profileHeadersRequested)
328         return;
329     if (m_profiles.isEmpty() && m_snapshots.isEmpty())
330         m_frontend->resetProfiles();
331 }
332
333 void InspectorProfilerAgent::setFrontend(InspectorFrontend* frontend)
334 {
335     m_frontend = frontend->profiler();
336 }
337
338 void InspectorProfilerAgent::clearFrontend()
339 {
340     m_frontend = 0;
341     stop();
342     ErrorString error;
343     disable(&error);
344 }
345
346 void InspectorProfilerAgent::start(ErrorString*)
347 {
348     if (m_recordingCPUProfile)
349         return;
350     if (!enabled()) {
351         enable(true);
352         PageScriptDebugServer::shared().recompileAllJSFunctions(0);
353     }
354     m_recordingCPUProfile = true;
355     String title = getCurrentUserInitiatedProfileName(true);
356     startProfiling(title);
357     addStartProfilingMessageToConsole(title, 0, 0, String());
358     toggleRecordButton(true);
359 }
360
361 void InspectorProfilerAgent::stop(ErrorString*)
362 {
363     if (!m_recordingCPUProfile)
364         return;
365     m_recordingCPUProfile = false;
366     String title = getCurrentUserInitiatedProfileName();
367     RefPtr<ScriptProfile> profile = stopProfiling(title);
368     if (profile)
369         addProfile(profile, 0, 0, String());
370     toggleRecordButton(false);
371 }
372
373 namespace {
374
375 class HeapSnapshotProgress: public ScriptProfiler::HeapSnapshotProgress {
376 public:
377     explicit HeapSnapshotProgress(InspectorFrontend::Profiler* frontend)
378         : m_frontend(frontend) { }
379     void Start(int totalWork)
380     {
381         m_totalWork = totalWork;
382     }
383     void Worked(int workDone)
384     {
385         if (m_frontend)
386             m_frontend->reportHeapSnapshotProgress(workDone, m_totalWork);
387     }
388     void Done() { }
389     bool isCanceled() { return false; }
390 private:
391     InspectorFrontend::Profiler* m_frontend;
392     int m_totalWork;
393 };
394
395 };
396
397 void InspectorProfilerAgent::takeHeapSnapshot(ErrorString*, const bool* reportProgress)
398 {
399     String title = makeString(UserInitiatedProfileName, '.', String::number(m_nextUserInitiatedHeapSnapshotNumber));
400     ++m_nextUserInitiatedHeapSnapshotNumber;
401
402     HeapSnapshotProgress progress(reportProgress && *reportProgress ? m_frontend : 0);
403     RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title, &progress);
404     if (snapshot) {
405         m_snapshots.add(snapshot->uid(), snapshot);
406         if (m_frontend)
407             m_frontend->addProfileHeader(createSnapshotHeader(*snapshot));
408     }
409 }
410
411 void InspectorProfilerAgent::toggleRecordButton(bool isProfiling)
412 {
413     if (m_frontend)
414         m_frontend->setRecordingProfile(isProfiling);
415 }
416
417 void InspectorProfilerAgent::getObjectByHeapObjectId(ErrorString* error, const String& heapSnapshotObjectId, const String* objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result)
418 {
419     bool ok;
420     unsigned id = heapSnapshotObjectId.toUInt(&ok);
421     if (!ok) {
422         *error = "Invalid heap snapshot object id";
423         return;
424     }
425     ScriptObject heapObject = ScriptProfiler::objectByHeapObjectId(id);
426     if (heapObject.hasNoValue()) {
427         *error = "Object is not available";
428         return;
429     }
430     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(heapObject.scriptState());
431     if (injectedScript.hasNoValue()) {
432         *error = "Object is not available. Inspected context is gone";
433         return;
434     }
435     result = injectedScript.wrapObject(heapObject, objectGroup ? *objectGroup : "");
436     if (!result)
437         *error = "Failed to wrap object";
438 }
439
440 void InspectorProfilerAgent::getHeapObjectId(ErrorString* errorString, const String& objectId, String* heapSnapshotObjectId)
441 {
442     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
443     if (injectedScript.hasNoValue()) {
444         *errorString = "Inspected context has gone";
445         return;
446     }
447     ScriptValue value = injectedScript.findObjectById(objectId);
448     if (value.hasNoValue() || value.isUndefined()) {
449         *errorString = "Object with given id not found";
450         return;
451     }
452     unsigned id = ScriptProfiler::getHeapObjectId(value);
453     *heapSnapshotObjectId = String::number(id);
454 }
455
456 } // namespace WebCore
457
458 #endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)