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/core/v8/ScriptProfiler.h"
     35 #include "core/inspector/InjectedScript.h"
     36 #include "core/inspector/InjectedScriptHost.h"
     37 #include "core/inspector/InspectorState.h"
     38 #include "platform/Timer.h"
     39 #include "wtf/CurrentTime.h"
     40 
     41 namespace blink {
     42 
     43 typedef uint32_t SnapshotObjectId;
     44 
     45 namespace HeapProfilerAgentState {
     46 static const char heapProfilerEnabled[] = "heapProfilerEnabled";
     47 static const char heapObjectsTrackingEnabled[] = "heapObjectsTrackingEnabled";
     48 static const char allocationTrackingEnabled[] = "allocationTrackingEnabled";
     49 }
     50 
     51 class InspectorHeapProfilerAgent::HeapStatsUpdateTask FINAL : public NoBaseWillBeGarbageCollectedFinalized<InspectorHeapProfilerAgent::HeapStatsUpdateTask> {
     52 public:
     53     explicit HeapStatsUpdateTask(InspectorHeapProfilerAgent*);
     54     void startTimer();
     55     void resetTimer() { m_timer.stop(); }
     56     void onTimer(Timer<HeapStatsUpdateTask>*);
     57     void trace(Visitor*);
     58 
     59 private:
     60     RawPtrWillBeMember<InspectorHeapProfilerAgent> m_heapProfilerAgent;
     61     Timer<HeapStatsUpdateTask> m_timer;
     62 };
     63 
     64 PassOwnPtrWillBeRawPtr<InspectorHeapProfilerAgent> InspectorHeapProfilerAgent::create(InjectedScriptManager* injectedScriptManager)
     65 {
     66     return adoptPtrWillBeNoop(new InspectorHeapProfilerAgent(injectedScriptManager));
     67 }
     68 
     69 InspectorHeapProfilerAgent::InspectorHeapProfilerAgent(InjectedScriptManager* injectedScriptManager)
     70     : InspectorBaseAgent<InspectorHeapProfilerAgent>("HeapProfiler")
     71     , m_injectedScriptManager(injectedScriptManager)
     72     , m_frontend(0)
     73     , m_nextUserInitiatedHeapSnapshotNumber(1)
     74 {
     75 }
     76 
     77 InspectorHeapProfilerAgent::~InspectorHeapProfilerAgent()
     78 {
     79 }
     80 
     81 void InspectorHeapProfilerAgent::setFrontend(InspectorFrontend* frontend)
     82 {
     83     m_frontend = frontend->heapprofiler();
     84 }
     85 
     86 void InspectorHeapProfilerAgent::clearFrontend()
     87 {
     88     m_frontend = 0;
     89 
     90     m_nextUserInitiatedHeapSnapshotNumber = 1;
     91     stopTrackingHeapObjectsInternal();
     92     m_injectedScriptManager->injectedScriptHost()->clearInspectedObjects();
     93 
     94     ErrorString error;
     95     disable(&error);
     96 }
     97 
     98 void InspectorHeapProfilerAgent::restore()
     99 {
    100     if (m_state->getBoolean(HeapProfilerAgentState::heapProfilerEnabled))
    101         m_frontend->resetProfiles();
    102     if (m_state->getBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled))
    103         startTrackingHeapObjectsInternal(m_state->getBoolean(HeapProfilerAgentState::allocationTrackingEnabled));
    104 }
    105 
    106 void InspectorHeapProfilerAgent::collectGarbage(blink::ErrorString*)
    107 {
    108     ScriptProfiler::collectGarbage();
    109 }
    110 
    111 InspectorHeapProfilerAgent::HeapStatsUpdateTask::HeapStatsUpdateTask(InspectorHeapProfilerAgent* heapProfilerAgent)
    112     : m_heapProfilerAgent(heapProfilerAgent)
    113     , m_timer(this, &HeapStatsUpdateTask::onTimer)
    114 {
    115 }
    116 
    117 void InspectorHeapProfilerAgent::HeapStatsUpdateTask::onTimer(Timer<HeapStatsUpdateTask>*)
    118 {
    119     // The timer is stopped on m_heapProfilerAgent destruction,
    120     // so this method will never be called after m_heapProfilerAgent has been destroyed.
    121     m_heapProfilerAgent->requestHeapStatsUpdate();
    122 }
    123 
    124 void InspectorHeapProfilerAgent::HeapStatsUpdateTask::startTimer()
    125 {
    126     ASSERT(!m_timer.isActive());
    127     m_timer.startRepeating(0.05, FROM_HERE);
    128 }
    129 
    130 void InspectorHeapProfilerAgent::HeapStatsUpdateTask::trace(Visitor* visitor)
    131 {
    132     visitor->trace(m_heapProfilerAgent);
    133 }
    134 
    135 class InspectorHeapProfilerAgent::HeapStatsStream FINAL : public ScriptProfiler::OutputStream {
    136 public:
    137     HeapStatsStream(InspectorHeapProfilerAgent* heapProfilerAgent)
    138         : m_heapProfilerAgent(heapProfilerAgent)
    139     {
    140     }
    141 
    142     virtual void write(const uint32_t* chunk, const int size) OVERRIDE
    143     {
    144         ASSERT(chunk);
    145         ASSERT(size > 0);
    146         m_heapProfilerAgent->pushHeapStatsUpdate(chunk, size);
    147     }
    148 private:
    149     InspectorHeapProfilerAgent* m_heapProfilerAgent;
    150 };
    151 
    152 void InspectorHeapProfilerAgent::startTrackingHeapObjects(ErrorString*, const bool* trackAllocations)
    153 {
    154     m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true);
    155     bool allocationTrackingEnabled = asBool(trackAllocations);
    156     m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, allocationTrackingEnabled);
    157     startTrackingHeapObjectsInternal(allocationTrackingEnabled);
    158 }
    159 
    160 void InspectorHeapProfilerAgent::requestHeapStatsUpdate()
    161 {
    162     if (!m_frontend)
    163         return;
    164     HeapStatsStream stream(this);
    165     SnapshotObjectId lastSeenObjectId = ScriptProfiler::requestHeapStatsUpdate(&stream);
    166     m_frontend->lastSeenObjectId(lastSeenObjectId, WTF::currentTimeMS());
    167 }
    168 
    169 void InspectorHeapProfilerAgent::pushHeapStatsUpdate(const uint32_t* const data, const int size)
    170 {
    171     if (!m_frontend)
    172         return;
    173     RefPtr<TypeBuilder::Array<int> > statsDiff = TypeBuilder::Array<int>::create();
    174     for (int i = 0; i < size; ++i)
    175         statsDiff->addItem(data[i]);
    176     m_frontend->heapStatsUpdate(statsDiff.release());
    177 }
    178 
    179 void InspectorHeapProfilerAgent::stopTrackingHeapObjects(ErrorString* error, const bool* reportProgress)
    180 {
    181     if (!m_heapStatsUpdateTask) {
    182         *error = "Heap object tracking is not started.";
    183         return;
    184     }
    185     requestHeapStatsUpdate();
    186     takeHeapSnapshot(error, reportProgress);
    187     stopTrackingHeapObjectsInternal();
    188 }
    189 
    190 void InspectorHeapProfilerAgent::startTrackingHeapObjectsInternal(bool trackAllocations)
    191 {
    192     if (m_heapStatsUpdateTask)
    193         return;
    194     ScriptProfiler::startTrackingHeapObjects(trackAllocations);
    195     m_heapStatsUpdateTask = adoptPtrWillBeNoop(new HeapStatsUpdateTask(this));
    196     m_heapStatsUpdateTask->startTimer();
    197 }
    198 
    199 void InspectorHeapProfilerAgent::stopTrackingHeapObjectsInternal()
    200 {
    201     if (!m_heapStatsUpdateTask)
    202         return;
    203     ScriptProfiler::stopTrackingHeapObjects();
    204     m_heapStatsUpdateTask->resetTimer();
    205     m_heapStatsUpdateTask.clear();
    206     m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, false);
    207     m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false);
    208 }
    209 
    210 void InspectorHeapProfilerAgent::enable(ErrorString*)
    211 {
    212     m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true);
    213 }
    214 
    215 void InspectorHeapProfilerAgent::disable(ErrorString* error)
    216 {
    217     stopTrackingHeapObjectsInternal();
    218     ScriptProfiler::clearHeapObjectIds();
    219     m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false);
    220 }
    221 
    222 void InspectorHeapProfilerAgent::takeHeapSnapshot(ErrorString* errorString, const bool* reportProgress)
    223 {
    224     class HeapSnapshotProgress FINAL : public ScriptProfiler::HeapSnapshotProgress {
    225     public:
    226         explicit HeapSnapshotProgress(InspectorFrontend::HeapProfiler* frontend)
    227             : m_frontend(frontend) { }
    228         virtual void Start(int totalWork) OVERRIDE
    229         {
    230             m_totalWork = totalWork;
    231         }
    232         virtual void Worked(int workDone) OVERRIDE
    233         {
    234             if (m_frontend) {
    235                 m_frontend->reportHeapSnapshotProgress(workDone, m_totalWork, 0);
    236                 m_frontend->flush();
    237             }
    238         }
    239         virtual void Done() OVERRIDE
    240         {
    241             const bool finished = true;
    242             if (m_frontend) {
    243                 m_frontend->reportHeapSnapshotProgress(m_totalWork, m_totalWork, &finished);
    244                 m_frontend->flush();
    245             }
    246         }
    247         virtual bool isCanceled() OVERRIDE { return false; }
    248     private:
    249         InspectorFrontend::HeapProfiler* m_frontend;
    250         int m_totalWork;
    251     };
    252 
    253     String title = "Snapshot " + String::number(m_nextUserInitiatedHeapSnapshotNumber++);
    254     HeapSnapshotProgress progress(asBool(reportProgress) ? m_frontend : 0);
    255     RefPtr<ScriptHeapSnapshot> snapshot = ScriptProfiler::takeHeapSnapshot(title, &progress);
    256     if (!snapshot) {
    257         *errorString = "Failed to take heap snapshot";
    258         return;
    259     }
    260 
    261     class OutputStream : public ScriptHeapSnapshot::OutputStream {
    262     public:
    263         explicit OutputStream(InspectorFrontend::HeapProfiler* frontend)
    264             : m_frontend(frontend) { }
    265         void Write(const String& chunk)
    266         {
    267             m_frontend->addHeapSnapshotChunk(chunk);
    268             m_frontend->flush();
    269         }
    270         void Close() { }
    271     private:
    272         InspectorFrontend::HeapProfiler* m_frontend;
    273     };
    274 
    275     if (m_frontend) {
    276         OutputStream stream(m_frontend);
    277         snapshot->writeJSON(&stream);
    278     }
    279 }
    280 
    281 void InspectorHeapProfilerAgent::getObjectByHeapObjectId(ErrorString* error, const String& heapSnapshotObjectId, const String* objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result)
    282 {
    283     bool ok;
    284     unsigned id = heapSnapshotObjectId.toUInt(&ok);
    285     if (!ok) {
    286         *error = "Invalid heap snapshot object id";
    287         return;
    288     }
    289     ScriptValue heapObject = ScriptProfiler::objectByHeapObjectId(id);
    290     if (heapObject.isEmpty()) {
    291         *error = "Object is not available";
    292         return;
    293     }
    294     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(heapObject.scriptState());
    295     if (injectedScript.isEmpty()) {
    296         *error = "Object is not available. Inspected context is gone";
    297         return;
    298     }
    299     result = injectedScript.wrapObject(heapObject, objectGroup ? *objectGroup : "");
    300     if (!result)
    301         *error = "Failed to wrap object";
    302 }
    303 
    304 void InspectorHeapProfilerAgent::getHeapObjectId(ErrorString* errorString, const String& objectId, String* heapSnapshotObjectId)
    305 {
    306     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
    307     if (injectedScript.isEmpty()) {
    308         *errorString = "Inspected context has gone";
    309         return;
    310     }
    311     ScriptValue value = injectedScript.findObjectById(objectId);
    312     ScriptState::Scope scope(injectedScript.scriptState());
    313     if (value.isEmpty() || value.isUndefined()) {
    314         *errorString = "Object with given id not found";
    315         return;
    316     }
    317     unsigned id = ScriptProfiler::getHeapObjectId(value);
    318     *heapSnapshotObjectId = String::number(id);
    319 }
    320 
    321 void InspectorHeapProfilerAgent::trace(Visitor* visitor)
    322 {
    323     visitor->trace(m_injectedScriptManager);
    324     visitor->trace(m_heapStatsUpdateTask);
    325     InspectorBaseAgent::trace(visitor);
    326 }
    327 
    328 } // namespace blink
    329 
    330