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