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 "core/inspector/InjectedScript.h" 36 #include "core/inspector/InjectedScriptHost.h" 37 #include "core/inspector/InspectorState.h" 38 #include "core/platform/Timer.h" 39 #include "wtf/CurrentTime.h" 40 41 namespace WebCore { 42 43 namespace HeapProfilerAgentState { 44 static const char profileHeadersRequested[] = "profileHeadersRequested"; 45 } 46 47 static const char* const userInitiatedProfileNameHeap = "org.webkit.profiles.user-initiated"; 48 49 class InspectorHeapProfilerAgent::HeapStatsUpdateTask { 50 public: 51 HeapStatsUpdateTask(InspectorHeapProfilerAgent*); 52 void startTimer(); 53 void resetTimer() { m_timer.stop(); } 54 void onTimer(Timer<HeapStatsUpdateTask>*); 55 56 private: 57 InspectorHeapProfilerAgent* m_heapProfilerAgent; 58 Timer<HeapStatsUpdateTask> m_timer; 59 }; 60 61 PassOwnPtr<InspectorHeapProfilerAgent> InspectorHeapProfilerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager) 62 { 63 return adoptPtr(new InspectorHeapProfilerAgent(instrumentingAgents, inspectorState, injectedScriptManager)); 64 } 65 66 InspectorHeapProfilerAgent::InspectorHeapProfilerAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager) 67 : InspectorBaseAgent<InspectorHeapProfilerAgent>("HeapProfiler", instrumentingAgents, inspectorState) 68 , m_injectedScriptManager(injectedScriptManager) 69 , m_frontend(0) 70 , m_nextUserInitiatedHeapSnapshotNumber(1) 71 { 72 } 73 74 InspectorHeapProfilerAgent::~InspectorHeapProfilerAgent() 75 { 76 } 77 78 void InspectorHeapProfilerAgent::clearProfiles(ErrorString*) 79 { 80 m_snapshots.clear(); 81 m_nextUserInitiatedHeapSnapshotNumber = 1; 82 resetFrontendProfiles(); 83 m_injectedScriptManager->injectedScriptHost()->clearInspectedObjects(); 84 } 85 86 void InspectorHeapProfilerAgent::resetFrontendProfiles() 87 { 88 stopTrackingHeapObjects(0); 89 if (!m_frontend) 90 return; 91 if (!m_state->getBoolean(HeapProfilerAgentState::profileHeadersRequested)) 92 return; 93 if (m_snapshots.isEmpty()) 94 m_frontend->resetProfiles(); 95 } 96 97 void InspectorHeapProfilerAgent::setFrontend(InspectorFrontend* frontend) 98 { 99 m_frontend = frontend->heapprofiler(); 100 } 101 102 void InspectorHeapProfilerAgent::clearFrontend() 103 { 104 stopTrackingHeapObjects(0); 105 m_state->setBoolean(HeapProfilerAgentState::profileHeadersRequested, false); 106 m_frontend = 0; 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*) 193 { 194 if (!m_heapStatsUpdateTask) 195 return; 196 ScriptProfiler::stopTrackingHeapObjects(); 197 m_heapStatsUpdateTask->resetTimer(); 198 m_heapStatsUpdateTask.clear(); 199 } 200 201 void InspectorHeapProfilerAgent::getProfileHeaders(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::HeapProfiler::ProfileHeader> >& headers) 202 { 203 m_state->setBoolean(HeapProfilerAgentState::profileHeadersRequested, true); 204 headers = TypeBuilder::Array<TypeBuilder::HeapProfiler::ProfileHeader>::create(); 205 206 IdToHeapSnapshotMap::iterator snapshotsEnd = m_snapshots.end(); 207 for (IdToHeapSnapshotMap::iterator it = m_snapshots.begin(); it != snapshotsEnd; ++it) 208 headers->addItem(createSnapshotHeader(*it->value)); 209 } 210 211 void InspectorHeapProfilerAgent::getHeapSnapshot(ErrorString* errorString, int rawUid) 212 { 213 class OutputStream : public ScriptHeapSnapshot::OutputStream { 214 public: 215 OutputStream(InspectorFrontend::HeapProfiler* frontend, unsigned uid) 216 : m_frontend(frontend), m_uid(uid) { } 217 void Write(const String& chunk) { m_frontend->addHeapSnapshotChunk(m_uid, chunk); } 218 void Close() { m_frontend->finishHeapSnapshot(m_uid); } 219 private: 220 InspectorFrontend::HeapProfiler* m_frontend; 221 int m_uid; 222 }; 223 224 unsigned uid = static_cast<unsigned>(rawUid); 225 IdToHeapSnapshotMap::iterator it = m_snapshots.find(uid); 226 if (it == m_snapshots.end()) { 227 *errorString = "Profile wasn't found"; 228 return; 229 } 230 RefPtr<ScriptHeapSnapshot> snapshot = it->value; 231 if (m_frontend) { 232 OutputStream stream(m_frontend, uid); 233 snapshot->writeJSON(&stream); 234 } 235 } 236 237 void InspectorHeapProfilerAgent::removeProfile(ErrorString*, int rawUid) 238 { 239 unsigned uid = static_cast<unsigned>(rawUid); 240 if (m_snapshots.contains(uid)) 241 m_snapshots.remove(uid); 242 } 243 244 void InspectorHeapProfilerAgent::takeHeapSnapshot(ErrorString*, const bool* reportProgress) 245 { 246 class HeapSnapshotProgress: public ScriptProfiler::HeapSnapshotProgress { 247 public: 248 explicit HeapSnapshotProgress(InspectorFrontend::HeapProfiler* frontend) 249 : m_frontend(frontend) { } 250 void Start(int totalWork) 251 { 252 m_totalWork = totalWork; 253 } 254 void Worked(int workDone) 255 { 256 if (m_frontend) 257 m_frontend->reportHeapSnapshotProgress(workDone, m_totalWork); 258 } 259 void Done() { } 260 bool isCanceled() { return false; } 261 private: 262 InspectorFrontend::HeapProfiler* m_frontend; 263 int m_totalWork; 264 }; 265 266 String title = String(userInitiatedProfileNameHeap) + "." + String::number(m_nextUserInitiatedHeapSnapshotNumber); 267 ++m_nextUserInitiatedHeapSnapshotNumber; 268 269 HeapSnapshotProgress progress(reportProgress && *reportProgress ? m_frontend : 0); 270 RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title, &progress); 271 if (snapshot) { 272 m_snapshots.add(snapshot->uid(), snapshot); 273 if (m_frontend) 274 m_frontend->addProfileHeader(createSnapshotHeader(*snapshot)); 275 } 276 } 277 278 void InspectorHeapProfilerAgent::getObjectByHeapObjectId(ErrorString* error, const String& heapSnapshotObjectId, const String* objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result) 279 { 280 bool ok; 281 unsigned id = heapSnapshotObjectId.toUInt(&ok); 282 if (!ok) { 283 *error = "Invalid heap snapshot object id"; 284 return; 285 } 286 ScriptObject heapObject = ScriptProfiler::objectByHeapObjectId(id); 287 if (heapObject.hasNoValue()) { 288 *error = "Object is not available"; 289 return; 290 } 291 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(heapObject.scriptState()); 292 if (injectedScript.hasNoValue()) { 293 *error = "Object is not available. Inspected context is gone"; 294 return; 295 } 296 result = injectedScript.wrapObject(heapObject, objectGroup ? *objectGroup : ""); 297 if (!result) 298 *error = "Failed to wrap object"; 299 } 300 301 void InspectorHeapProfilerAgent::getHeapObjectId(ErrorString* errorString, const String& objectId, String* heapSnapshotObjectId) 302 { 303 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId); 304 if (injectedScript.hasNoValue()) { 305 *errorString = "Inspected context has gone"; 306 return; 307 } 308 ScriptValue value = injectedScript.findObjectById(objectId); 309 if (value.hasNoValue() || value.isUndefined()) { 310 *errorString = "Object with given id not found"; 311 return; 312 } 313 unsigned id = ScriptProfiler::getHeapObjectId(value); 314 *heapSnapshotObjectId = String::number(id); 315 } 316 317 } // namespace WebCore 318 319