/src/3rdparty/webkit/Source/WebCore/inspector/InspectorProfilerAgent.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 381 lines · 304 code · 45 blank · 32 comment · 48 complexity · c044423fd602e2d6502976e790d4652d MD5 · raw file

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