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();
resetTimer()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
create(InstrumentingAgents * instrumentingAgents,InspectorCompositeState * inspectorState,InjectedScriptManager * injectedScriptManager)60 PassOwnPtr<InspectorHeapProfilerAgent> InspectorHeapProfilerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager)
61 {
62 return adoptPtr(new InspectorHeapProfilerAgent(instrumentingAgents, inspectorState, injectedScriptManager));
63 }
64
InspectorHeapProfilerAgent(InstrumentingAgents * instrumentingAgents,InspectorCompositeState * inspectorState,InjectedScriptManager * injectedScriptManager)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
~InspectorHeapProfilerAgent()73 InspectorHeapProfilerAgent::~InspectorHeapProfilerAgent()
74 {
75 }
76
clearProfiles(ErrorString *)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
resetFrontendProfiles()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
setFrontend(InspectorFrontend * frontend)96 void InspectorHeapProfilerAgent::setFrontend(InspectorFrontend* frontend)
97 {
98 m_frontend = frontend->heapprofiler();
99 }
100
clearFrontend()101 void InspectorHeapProfilerAgent::clearFrontend()
102 {
103 m_frontend = 0;
104 ErrorString error;
105 clearProfiles(&error);
106 disable(&error);
107 }
108
restore()109 void InspectorHeapProfilerAgent::restore()
110 {
111 resetFrontendProfiles();
112 }
113
collectGarbage(WebCore::ErrorString *)114 void InspectorHeapProfilerAgent::collectGarbage(WebCore::ErrorString*)
115 {
116 ScriptProfiler::collectGarbage();
117 }
118
createSnapshotHeader(const ScriptHeapSnapshot & snapshot)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
HeapStatsUpdateTask(InspectorHeapProfilerAgent * heapProfilerAgent)128 InspectorHeapProfilerAgent::HeapStatsUpdateTask::HeapStatsUpdateTask(InspectorHeapProfilerAgent* heapProfilerAgent)
129 : m_heapProfilerAgent(heapProfilerAgent)
130 , m_timer(this, &HeapStatsUpdateTask::onTimer)
131 {
132 }
133
onTimer(Timer<HeapStatsUpdateTask> *)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
startTimer()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:
HeapStatsStream(InspectorHeapProfilerAgent * heapProfilerAgent)149 HeapStatsStream(InspectorHeapProfilerAgent* heapProfilerAgent)
150 : m_heapProfilerAgent(heapProfilerAgent)
151 {
152 }
153
write(const uint32_t * chunk,const int size)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
startTrackingHeapObjects(ErrorString *)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
requestHeapStatsUpdate()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
pushHeapStatsUpdate(const uint32_t * const data,const int size)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
stopTrackingHeapObjects(ErrorString * error,const bool * reportProgress)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
stopTrackingHeapObjectsInternal()203 void InspectorHeapProfilerAgent::stopTrackingHeapObjectsInternal()
204 {
205 if (!m_heapStatsUpdateTask)
206 return;
207 ScriptProfiler::stopTrackingHeapObjects();
208 m_heapStatsUpdateTask->resetTimer();
209 m_heapStatsUpdateTask.clear();
210 }
211
enable(ErrorString *)212 void InspectorHeapProfilerAgent::enable(ErrorString*)
213 {
214 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true);
215 }
216
disable(ErrorString * error)217 void InspectorHeapProfilerAgent::disable(ErrorString* error)
218 {
219 stopTrackingHeapObjectsInternal();
220 m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false);
221 }
222
getHeapSnapshot(ErrorString * errorString,int rawUid)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
removeProfile(ErrorString *,int rawUid)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
takeHeapSnapshot(ErrorString *,const bool * reportProgress)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
getObjectByHeapObjectId(ErrorString * error,const String & heapSnapshotObjectId,const String * objectGroup,RefPtr<TypeBuilder::Runtime::RemoteObject> & result)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
getHeapObjectId(ErrorString * errorString,const String & objectId,String * heapSnapshotObjectId)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