1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/inspector/InspectorHeapProfilerAgent.h" 33 34 #include "bindings/v8/ScriptProfiler.h" 35 #include "bindings/v8/ScriptScope.h" 36 #include "core/inspector/InjectedScript.h" 37 #include "core/inspector/InjectedScriptHost.h" 38 #include "core/inspector/InspectorState.h" 39 #include "platform/Timer.h" 40 #include "wtf/CurrentTime.h" 41 42 namespace WebCore { 43 44 namespace HeapProfilerAgentState { 45 static const char heapProfilerEnabled[] = "heapProfilerEnabled"; 46 } 47 48 class InspectorHeapProfilerAgent::HeapStatsUpdateTask { 49 public: 50 HeapStatsUpdateTask(InspectorHeapProfilerAgent*); 51 void startTimer(); 52 void resetTimer() { m_timer.stop(); } 53 void onTimer(Timer<HeapStatsUpdateTask>*); 54 55 private: 56 InspectorHeapProfilerAgent* m_heapProfilerAgent; 57 Timer<HeapStatsUpdateTask> m_timer; 58 }; 59 60 PassOwnPtr<InspectorHeapProfilerAgent> InspectorHeapProfilerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager) 61 { 62 return adoptPtr(new InspectorHeapProfilerAgent(instrumentingAgents, inspectorState, injectedScriptManager)); 63 } 64 65 InspectorHeapProfilerAgent::InspectorHeapProfilerAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager) 66 : InspectorBaseAgent<InspectorHeapProfilerAgent>("HeapProfiler", instrumentingAgents, inspectorState) 67 , m_injectedScriptManager(injectedScriptManager) 68 , m_frontend(0) 69 , m_nextUserInitiatedHeapSnapshotNumber(1) 70 { 71 } 72 73 InspectorHeapProfilerAgent::~InspectorHeapProfilerAgent() 74 { 75 } 76 77 void InspectorHeapProfilerAgent::clearProfiles(ErrorString*) 78 { 79 m_snapshots.clear(); 80 m_nextUserInitiatedHeapSnapshotNumber = 1; 81 stopTrackingHeapObjectsInternal(); 82 resetFrontendProfiles(); 83 m_injectedScriptManager->injectedScriptHost()->clearInspectedObjects(); 84 } 85 86 void InspectorHeapProfilerAgent::resetFrontendProfiles() 87 { 88 if (!m_frontend) 89 return; 90 if (!m_state->getBoolean(HeapProfilerAgentState::heapProfilerEnabled)) 91 return; 92 if (m_snapshots.isEmpty()) 93 m_frontend->resetProfiles(); 94 } 95 96 void InspectorHeapProfilerAgent::setFrontend(InspectorFrontend* frontend) 97 { 98 m_frontend = frontend->heapprofiler(); 99 } 100 101 void InspectorHeapProfilerAgent::clearFrontend() 102 { 103 m_frontend = 0; 104 ErrorString error; 105 clearProfiles(&error); 106 disable(&error); 107 } 108 109 void InspectorHeapProfilerAgent::restore() 110 { 111 resetFrontendProfiles(); 112 } 113 114 void InspectorHeapProfilerAgent::collectGarbage(WebCore::ErrorString*) 115 { 116 ScriptProfiler::collectGarbage(); 117 } 118 119 PassRefPtr<TypeBuilder::HeapProfiler::ProfileHeader> InspectorHeapProfilerAgent::createSnapshotHeader(const ScriptHeapSnapshot& snapshot) 120 { 121 RefPtr<TypeBuilder::HeapProfiler::ProfileHeader> header = TypeBuilder::HeapProfiler::ProfileHeader::create() 122 .setUid(snapshot.uid()) 123 .setTitle(snapshot.title()); 124 header->setMaxJSObjectId(snapshot.maxSnapshotJSObjectId()); 125 return header.release(); 126 } 127 128 InspectorHeapProfilerAgent::HeapStatsUpdateTask::HeapStatsUpdateTask(InspectorHeapProfilerAgent* heapProfilerAgent) 129 : m_heapProfilerAgent(heapProfilerAgent) 130 , m_timer(this, &HeapStatsUpdateTask::onTimer) 131 { 132 } 133 134 void InspectorHeapProfilerAgent::HeapStatsUpdateTask::onTimer(Timer<HeapStatsUpdateTask>*) 135 { 136 // The timer is stopped on m_heapProfilerAgent destruction, 137 // so this method will never be called after m_heapProfilerAgent has been destroyed. 138 m_heapProfilerAgent->requestHeapStatsUpdate(); 139 } 140 141 void InspectorHeapProfilerAgent::HeapStatsUpdateTask::startTimer() 142 { 143 ASSERT(!m_timer.isActive()); 144 m_timer.startRepeating(0.05); 145 } 146 147 class InspectorHeapProfilerAgent::HeapStatsStream : public ScriptProfiler::OutputStream { 148 public: 149 HeapStatsStream(InspectorHeapProfilerAgent* heapProfilerAgent) 150 : m_heapProfilerAgent(heapProfilerAgent) 151 { 152 } 153 154 virtual void write(const uint32_t* chunk, const int size) OVERRIDE 155 { 156 ASSERT(chunk); 157 ASSERT(size > 0); 158 m_heapProfilerAgent->pushHeapStatsUpdate(chunk, size); 159 } 160 private: 161 InspectorHeapProfilerAgent* m_heapProfilerAgent; 162 }; 163 164 void InspectorHeapProfilerAgent::startTrackingHeapObjects(ErrorString*) 165 { 166 if (m_heapStatsUpdateTask) 167 return; 168 ScriptProfiler::startTrackingHeapObjects(); 169 m_heapStatsUpdateTask = adoptPtr(new HeapStatsUpdateTask(this)); 170 m_heapStatsUpdateTask->startTimer(); 171 } 172 173 void InspectorHeapProfilerAgent::requestHeapStatsUpdate() 174 { 175 if (!m_frontend) 176 return; 177 HeapStatsStream stream(this); 178 SnapshotObjectId lastSeenObjectId = ScriptProfiler::requestHeapStatsUpdate(&stream); 179 m_frontend->lastSeenObjectId(lastSeenObjectId, WTF::currentTimeMS()); 180 } 181 182 void InspectorHeapProfilerAgent::pushHeapStatsUpdate(const uint32_t* const data, const int size) 183 { 184 if (!m_frontend) 185 return; 186 RefPtr<TypeBuilder::Array<int> > statsDiff = TypeBuilder::Array<int>::create(); 187 for (int i = 0; i < size; ++i) 188 statsDiff->addItem(data[i]); 189 m_frontend->heapStatsUpdate(statsDiff.release()); 190 } 191 192 void InspectorHeapProfilerAgent::stopTrackingHeapObjects(ErrorString* error, const bool* reportProgress) 193 { 194 if (!m_heapStatsUpdateTask) { 195 *error = "Heap object tracking is not started."; 196 return; 197 } 198 requestHeapStatsUpdate(); 199 takeHeapSnapshot(error, reportProgress); 200 stopTrackingHeapObjectsInternal(); 201 } 202 203 void InspectorHeapProfilerAgent::stopTrackingHeapObjectsInternal() 204 { 205 if (!m_heapStatsUpdateTask) 206 return; 207 ScriptProfiler::stopTrackingHeapObjects(); 208 m_heapStatsUpdateTask->resetTimer(); 209 m_heapStatsUpdateTask.clear(); 210 } 211 212 void InspectorHeapProfilerAgent::enable(ErrorString*) 213 { 214 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true); 215 } 216 217 void InspectorHeapProfilerAgent::disable(ErrorString* error) 218 { 219 stopTrackingHeapObjectsInternal(); 220 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false); 221 } 222 223 void InspectorHeapProfilerAgent::getHeapSnapshot(ErrorString* errorString, int rawUid) 224 { 225 class OutputStream : public ScriptHeapSnapshot::OutputStream { 226 public: 227 OutputStream(InspectorFrontend::HeapProfiler* frontend, unsigned uid) 228 : m_frontend(frontend), m_uid(uid) { } 229 void Write(const String& chunk) { m_frontend->addHeapSnapshotChunk(m_uid, chunk); } 230 void Close() { } 231 private: 232 InspectorFrontend::HeapProfiler* m_frontend; 233 int m_uid; 234 }; 235 236 unsigned uid = static_cast<unsigned>(rawUid); 237 IdToHeapSnapshotMap::iterator it = m_snapshots.find(uid); 238 if (it == m_snapshots.end()) { 239 *errorString = "Profile wasn't found"; 240 return; 241 } 242 RefPtr<ScriptHeapSnapshot> snapshot = it->value; 243 if (m_frontend) { 244 OutputStream stream(m_frontend, uid); 245 snapshot->writeJSON(&stream); 246 } 247 } 248 249 void InspectorHeapProfilerAgent::removeProfile(ErrorString*, int rawUid) 250 { 251 unsigned uid = static_cast<unsigned>(rawUid); 252 if (m_snapshots.contains(uid)) 253 m_snapshots.remove(uid); 254 } 255 256 void InspectorHeapProfilerAgent::takeHeapSnapshot(ErrorString*, const bool* reportProgress) 257 { 258 class HeapSnapshotProgress: public ScriptProfiler::HeapSnapshotProgress { 259 public: 260 explicit HeapSnapshotProgress(InspectorFrontend::HeapProfiler* frontend) 261 : m_frontend(frontend) { } 262 void Start(int totalWork) 263 { 264 m_totalWork = totalWork; 265 } 266 void Worked(int workDone) 267 { 268 if (m_frontend) 269 m_frontend->reportHeapSnapshotProgress(workDone, m_totalWork); 270 } 271 void Done() { } 272 bool isCanceled() { return false; } 273 private: 274 InspectorFrontend::HeapProfiler* m_frontend; 275 int m_totalWork; 276 }; 277 278 String title = "Snapshot " + String::number(m_nextUserInitiatedHeapSnapshotNumber++); 279 HeapSnapshotProgress progress(reportProgress && *reportProgress ? m_frontend : 0); 280 RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title, &progress); 281 if (snapshot) { 282 m_snapshots.add(snapshot->uid(), snapshot); 283 if (m_frontend) 284 m_frontend->addProfileHeader(createSnapshotHeader(*snapshot)); 285 } 286 } 287 288 void InspectorHeapProfilerAgent::getObjectByHeapObjectId(ErrorString* error, const String& heapSnapshotObjectId, const String* objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result) 289 { 290 bool ok; 291 unsigned id = heapSnapshotObjectId.toUInt(&ok); 292 if (!ok) { 293 *error = "Invalid heap snapshot object id"; 294 return; 295 } 296 ScriptObject heapObject = ScriptProfiler::objectByHeapObjectId(id); 297 if (heapObject.hasNoValue()) { 298 *error = "Object is not available"; 299 return; 300 } 301 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(heapObject.scriptState()); 302 if (injectedScript.hasNoValue()) { 303 *error = "Object is not available. Inspected context is gone"; 304 return; 305 } 306 result = injectedScript.wrapObject(heapObject, objectGroup ? *objectGroup : ""); 307 if (!result) 308 *error = "Failed to wrap object"; 309 } 310 311 void InspectorHeapProfilerAgent::getHeapObjectId(ErrorString* errorString, const String& objectId, String* heapSnapshotObjectId) 312 { 313 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId); 314 if (injectedScript.hasNoValue()) { 315 *errorString = "Inspected context has gone"; 316 return; 317 } 318 ScriptValue value = injectedScript.findObjectById(objectId); 319 ScriptScope scope(injectedScript.scriptState()); 320 if (value.hasNoValue() || value.isUndefined()) { 321 *errorString = "Object with given id not found"; 322 return; 323 } 324 unsigned id = ScriptProfiler::getHeapObjectId(value); 325 *heapSnapshotObjectId = String::number(id); 326 } 327 328 } // namespace WebCore 329 330