• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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