Home | History | Annotate | Download | only in inspector
      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 "core/inspector/InspectorProfilerAgent.h"
     32 
     33 #include "bindings/v8/ScriptCallStackFactory.h"
     34 #include "bindings/v8/ScriptProfiler.h"
     35 #include "core/inspector/InjectedScript.h"
     36 #include "core/inspector/InjectedScriptHost.h"
     37 #include "core/inspector/InspectorOverlay.h"
     38 #include "core/inspector/InspectorState.h"
     39 #include "core/inspector/InstrumentingAgents.h"
     40 #include "core/inspector/ScriptCallStack.h"
     41 #include "core/inspector/ScriptProfile.h"
     42 #include "core/frame/ConsoleTypes.h"
     43 #include "wtf/CurrentTime.h"
     44 #include "wtf/text/StringConcatenate.h"
     45 
     46 namespace WebCore {
     47 
     48 namespace ProfilerAgentState {
     49 static const char samplingInterval[] = "samplingInterval";
     50 static const char userInitiatedProfiling[] = "userInitiatedProfiling";
     51 static const char profilerEnabled[] = "profilerEnabled";
     52 }
     53 
     54 static PassRefPtr<TypeBuilder::Profiler::CPUProfile> createCPUProfile(const ScriptProfile& scriptProfile)
     55 {
     56     RefPtr<TypeBuilder::Profiler::CPUProfile> profile = TypeBuilder::Profiler::CPUProfile::create()
     57         .setHead(scriptProfile.buildInspectorObjectForHead())
     58         .setStartTime(scriptProfile.startTime())
     59         .setEndTime(scriptProfile.endTime());
     60     profile->setSamples(scriptProfile.buildInspectorObjectForSamples());
     61     return profile.release();
     62 }
     63 
     64 static PassRefPtr<TypeBuilder::Debugger::Location> currentDebugLocation()
     65 {
     66     RefPtr<ScriptCallStack> callStack(createScriptCallStackForConsole(1));
     67     const ScriptCallFrame& lastCaller = callStack->at(0);
     68     RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create()
     69         .setScriptId(lastCaller.scriptId())
     70         .setLineNumber(lastCaller.lineNumber());
     71     location->setColumnNumber(lastCaller.columnNumber());
     72     return location.release();
     73 }
     74 
     75 class InspectorProfilerAgent::ProfileDescriptor {
     76 public:
     77     ProfileDescriptor(const String& id, const String& title)
     78         : m_id(id)
     79         , m_title(title) { }
     80     String m_id;
     81     String m_title;
     82 };
     83 
     84 PassOwnPtr<InspectorProfilerAgent> InspectorProfilerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager, InspectorOverlay* overlay)
     85 {
     86     return adoptPtr(new InspectorProfilerAgent(instrumentingAgents, inspectorState, injectedScriptManager, overlay));
     87 }
     88 
     89 InspectorProfilerAgent::InspectorProfilerAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager, InspectorOverlay* overlay)
     90     : InspectorBaseAgent<InspectorProfilerAgent>("Profiler", instrumentingAgents, inspectorState)
     91     , m_injectedScriptManager(injectedScriptManager)
     92     , m_frontend(0)
     93     , m_recordingCPUProfile(false)
     94     , m_nextProfileId(1)
     95     , m_profileNameIdleTimeMap(ScriptProfiler::currentProfileNameIdleTimeMap())
     96     , m_idleStartTime(0.0)
     97     , m_overlay(overlay)
     98 {
     99 }
    100 
    101 InspectorProfilerAgent::~InspectorProfilerAgent()
    102 {
    103 }
    104 
    105 void InspectorProfilerAgent::consoleProfile(const String& title, ScriptState* state)
    106 {
    107     ASSERT(m_frontend && enabled());
    108     String id = String::number(m_nextProfileId++);
    109     m_startedProfiles.append(ProfileDescriptor(id, title));
    110     ScriptProfiler::start(id);
    111     m_frontend->consoleProfile(id, currentDebugLocation(), title.isNull() ? 0 : &title);
    112 }
    113 
    114 void InspectorProfilerAgent::consoleProfileEnd(const String& title)
    115 {
    116     ASSERT(m_frontend && enabled());
    117     String id;
    118     String resolvedTitle;
    119     // Take last started profile if no title was passed.
    120     if (title.isNull()) {
    121         if (m_startedProfiles.isEmpty())
    122             return;
    123         id = m_startedProfiles.last().m_id;
    124         resolvedTitle = m_startedProfiles.last().m_title;
    125         m_startedProfiles.removeLast();
    126     } else {
    127         for (size_t i = 0; i < m_startedProfiles.size(); i++) {
    128             if (m_startedProfiles[i].m_title == title) {
    129                 resolvedTitle = title;
    130                 id = m_startedProfiles[i].m_id;
    131                 m_startedProfiles.remove(i);
    132                 break;
    133             }
    134         }
    135         if (id.isEmpty())
    136             return;
    137     }
    138     RefPtr<ScriptProfile> profile = ScriptProfiler::stop(id);
    139     if (!profile)
    140         return;
    141     RefPtr<TypeBuilder::Debugger::Location> location = currentDebugLocation();
    142     if (!m_keepAliveProfile)
    143         m_keepAliveProfile = profile;
    144     m_frontend->addProfileHeader(id, location, createCPUProfile(*profile), resolvedTitle.isNull() ? 0 : &resolvedTitle);
    145 }
    146 
    147 void InspectorProfilerAgent::enable(ErrorString*)
    148 {
    149     m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
    150     doEnable();
    151 }
    152 
    153 void InspectorProfilerAgent::doEnable()
    154 {
    155     m_instrumentingAgents->setInspectorProfilerAgent(this);
    156 }
    157 
    158 void InspectorProfilerAgent::disable(ErrorString*)
    159 {
    160     m_keepAliveProfile.clear();
    161     m_instrumentingAgents->setInspectorProfilerAgent(0);
    162     m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
    163 }
    164 
    165 bool InspectorProfilerAgent::enabled()
    166 {
    167     return m_state->getBoolean(ProfilerAgentState::profilerEnabled);
    168 }
    169 
    170 void InspectorProfilerAgent::setSamplingInterval(ErrorString* error, int interval)
    171 {
    172     if (m_recordingCPUProfile) {
    173         *error = "Cannot change sampling interval when profiling.";
    174         return;
    175     }
    176     m_state->setLong(ProfilerAgentState::samplingInterval, interval);
    177     ScriptProfiler::setSamplingInterval(interval);
    178 }
    179 
    180 void InspectorProfilerAgent::setFrontend(InspectorFrontend* frontend)
    181 {
    182     m_frontend = frontend->profiler();
    183 }
    184 
    185 void InspectorProfilerAgent::clearFrontend()
    186 {
    187     m_frontend = 0;
    188     stop(0, 0);
    189     m_injectedScriptManager->injectedScriptHost()->clearInspectedObjects();
    190     ErrorString error;
    191     disable(&error);
    192 }
    193 
    194 void InspectorProfilerAgent::restore()
    195 {
    196     if (m_state->getBoolean(ProfilerAgentState::profilerEnabled)) {
    197         doEnable();
    198         m_frontend->resetProfiles();
    199     }
    200     if (long interval = m_state->getLong(ProfilerAgentState::samplingInterval, 0))
    201         ScriptProfiler::setSamplingInterval(interval);
    202     if (m_state->getBoolean(ProfilerAgentState::userInitiatedProfiling))
    203         start();
    204 }
    205 
    206 void InspectorProfilerAgent::start(ErrorString* error)
    207 {
    208     if (m_recordingCPUProfile)
    209         return;
    210     if (!enabled()) {
    211         ErrorString error;
    212         enable(&error);
    213     }
    214     m_recordingCPUProfile = true;
    215     if (m_overlay)
    216         m_overlay->startedRecordingProfile();
    217     m_frontendInitiatedProfileId = String::number(m_nextProfileId++);
    218     ScriptProfiler::start(m_frontendInitiatedProfileId);
    219     m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
    220 }
    221 
    222 void InspectorProfilerAgent::stop(ErrorString* errorString, RefPtr<TypeBuilder::Profiler::CPUProfile>& profile)
    223 {
    224     stop(errorString, &profile);
    225 }
    226 
    227 void InspectorProfilerAgent::stop(ErrorString* errorString, RefPtr<TypeBuilder::Profiler::CPUProfile>* profile)
    228 {
    229     if (!m_recordingCPUProfile) {
    230         if (errorString)
    231             *errorString = "No recording profiles found";
    232         return;
    233     }
    234     m_recordingCPUProfile = false;
    235     if (m_overlay)
    236         m_overlay->finishedRecordingProfile();
    237     RefPtr<ScriptProfile> scriptProfile = ScriptProfiler::stop(m_frontendInitiatedProfileId);
    238     m_frontendInitiatedProfileId = String();
    239     if (scriptProfile && profile) {
    240         *profile = createCPUProfile(*scriptProfile);
    241         if (!m_keepAliveProfile)
    242             m_keepAliveProfile = scriptProfile;
    243     } else if (errorString) {
    244         *errorString = "Profile wasn't found";
    245     }
    246     m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
    247 }
    248 
    249 void InspectorProfilerAgent::idleFinished()
    250 {
    251     if (!m_profileNameIdleTimeMap || !m_profileNameIdleTimeMap->size())
    252         return;
    253     ScriptProfiler::setIdle(false);
    254     if (!m_idleStartTime)
    255         return;
    256 
    257     double idleTime = WTF::monotonicallyIncreasingTime() - m_idleStartTime;
    258     m_idleStartTime = 0.0;
    259     ProfileNameIdleTimeMap::iterator end = m_profileNameIdleTimeMap->end();
    260     for (ProfileNameIdleTimeMap::iterator it = m_profileNameIdleTimeMap->begin(); it != end; ++it)
    261         it->value += idleTime;
    262 }
    263 
    264 void InspectorProfilerAgent::idleStarted()
    265 {
    266     if (!m_profileNameIdleTimeMap || !m_profileNameIdleTimeMap->size())
    267         return;
    268     m_idleStartTime = WTF::monotonicallyIncreasingTime();
    269     ScriptProfiler::setIdle(true);
    270 }
    271 
    272 void InspectorProfilerAgent::willProcessTask()
    273 {
    274     idleFinished();
    275 }
    276 
    277 void InspectorProfilerAgent::didProcessTask()
    278 {
    279     idleStarted();
    280 }
    281 
    282 void InspectorProfilerAgent::willEnterNestedRunLoop()
    283 {
    284     idleStarted();
    285 }
    286 
    287 void InspectorProfilerAgent::didLeaveNestedRunLoop()
    288 {
    289     idleFinished();
    290 }
    291 
    292 } // namespace WebCore
    293 
    294