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