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
42namespace WebCore {
43
44namespace HeapProfilerAgentState {
45static const char heapProfilerEnabled[] = "heapProfilerEnabled";
46}
47
48class InspectorHeapProfilerAgent::HeapStatsUpdateTask {
49public:
50    HeapStatsUpdateTask(InspectorHeapProfilerAgent*);
51    void startTimer();
52    void resetTimer() { m_timer.stop(); }
53    void onTimer(Timer<HeapStatsUpdateTask>*);
54
55private:
56    InspectorHeapProfilerAgent* m_heapProfilerAgent;
57    Timer<HeapStatsUpdateTask> m_timer;
58};
59
60PassOwnPtr<InspectorHeapProfilerAgent> InspectorHeapProfilerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager)
61{
62    return adoptPtr(new InspectorHeapProfilerAgent(instrumentingAgents, inspectorState, injectedScriptManager));
63}
64
65InspectorHeapProfilerAgent::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
73InspectorHeapProfilerAgent::~InspectorHeapProfilerAgent()
74{
75}
76
77void InspectorHeapProfilerAgent::clearProfiles(ErrorString*)
78{
79    m_snapshots.clear();
80    m_nextUserInitiatedHeapSnapshotNumber = 1;
81    stopTrackingHeapObjectsInternal();
82    resetFrontendProfiles();
83    m_injectedScriptManager->injectedScriptHost()->clearInspectedObjects();
84}
85
86void 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
96void InspectorHeapProfilerAgent::setFrontend(InspectorFrontend* frontend)
97{
98    m_frontend = frontend->heapprofiler();
99}
100
101void InspectorHeapProfilerAgent::clearFrontend()
102{
103    m_frontend = 0;
104    ErrorString error;
105    clearProfiles(&error);
106    disable(&error);
107}
108
109void InspectorHeapProfilerAgent::restore()
110{
111    resetFrontendProfiles();
112}
113
114void InspectorHeapProfilerAgent::collectGarbage(WebCore::ErrorString*)
115{
116    ScriptProfiler::collectGarbage();
117}
118
119PassRefPtr<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
128InspectorHeapProfilerAgent::HeapStatsUpdateTask::HeapStatsUpdateTask(InspectorHeapProfilerAgent* heapProfilerAgent)
129    : m_heapProfilerAgent(heapProfilerAgent)
130    , m_timer(this, &HeapStatsUpdateTask::onTimer)
131{
132}
133
134void 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
141void InspectorHeapProfilerAgent::HeapStatsUpdateTask::startTimer()
142{
143    ASSERT(!m_timer.isActive());
144    m_timer.startRepeating(0.05);
145}
146
147class InspectorHeapProfilerAgent::HeapStatsStream : public ScriptProfiler::OutputStream {
148public:
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    }
160private:
161    InspectorHeapProfilerAgent* m_heapProfilerAgent;
162};
163
164void 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
173void 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
182void 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
192void 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
203void InspectorHeapProfilerAgent::stopTrackingHeapObjectsInternal()
204{
205    if (!m_heapStatsUpdateTask)
206        return;
207    ScriptProfiler::stopTrackingHeapObjects();
208    m_heapStatsUpdateTask->resetTimer();
209    m_heapStatsUpdateTask.clear();
210}
211
212void InspectorHeapProfilerAgent::enable(ErrorString*)
213{
214    m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true);
215}
216
217void InspectorHeapProfilerAgent::disable(ErrorString* error)
218{
219    stopTrackingHeapObjectsInternal();
220    m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false);
221}
222
223void 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
249void 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
256void 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
288void 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
311void 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