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