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