Home | History | Annotate | Download | only in inspector
      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