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 "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