Home | History | Annotate | Download | only in inspector
      1 // Copyright 2016 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/inspector/v8-heap-profiler-agent-impl.h"
      6 
      7 #include "src/inspector/injected-script.h"
      8 #include "src/inspector/protocol/Protocol.h"
      9 #include "src/inspector/string-util.h"
     10 #include "src/inspector/v8-debugger.h"
     11 #include "src/inspector/v8-inspector-impl.h"
     12 #include "src/inspector/v8-inspector-session-impl.h"
     13 
     14 #include "include/v8-inspector.h"
     15 #include "include/v8-profiler.h"
     16 #include "include/v8-version.h"
     17 
     18 namespace v8_inspector {
     19 
     20 namespace {
     21 
     22 namespace HeapProfilerAgentState {
     23 static const char heapProfilerEnabled[] = "heapProfilerEnabled";
     24 static const char heapObjectsTrackingEnabled[] = "heapObjectsTrackingEnabled";
     25 static const char allocationTrackingEnabled[] = "allocationTrackingEnabled";
     26 static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled";
     27 static const char samplingHeapProfilerInterval[] =
     28     "samplingHeapProfilerInterval";
     29 }
     30 
     31 class HeapSnapshotProgress final : public v8::ActivityControl {
     32  public:
     33   explicit HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend)
     34       : m_frontend(frontend) {}
     35   ControlOption ReportProgressValue(int done, int total) override {
     36     m_frontend->reportHeapSnapshotProgress(done, total,
     37                                            protocol::Maybe<bool>());
     38     if (done >= total) {
     39       m_frontend->reportHeapSnapshotProgress(total, total, true);
     40     }
     41     m_frontend->flush();
     42     return kContinue;
     43   }
     44 
     45  private:
     46   protocol::HeapProfiler::Frontend* m_frontend;
     47 };
     48 
     49 class GlobalObjectNameResolver final
     50     : public v8::HeapProfiler::ObjectNameResolver {
     51  public:
     52   explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session)
     53       : m_offset(0), m_strings(10000), m_session(session) {}
     54 
     55   const char* GetName(v8::Local<v8::Object> object) override {
     56     InspectedContext* context = m_session->inspector()->getContext(
     57         m_session->contextGroupId(),
     58         V8Debugger::contextId(object->CreationContext()));
     59     if (!context) return "";
     60     String16 name = context->origin();
     61     size_t length = name.length();
     62     if (m_offset + length + 1 >= m_strings.size()) return "";
     63     for (size_t i = 0; i < length; ++i) {
     64       UChar ch = name[i];
     65       m_strings[m_offset + i] = ch > 0xff ? '?' : static_cast<char>(ch);
     66     }
     67     m_strings[m_offset + length] = '\0';
     68     char* result = &*m_strings.begin() + m_offset;
     69     m_offset += length + 1;
     70     return result;
     71   }
     72 
     73  private:
     74   size_t m_offset;
     75   std::vector<char> m_strings;
     76   V8InspectorSessionImpl* m_session;
     77 };
     78 
     79 class HeapSnapshotOutputStream final : public v8::OutputStream {
     80  public:
     81   explicit HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend* frontend)
     82       : m_frontend(frontend) {}
     83   void EndOfStream() override {}
     84   int GetChunkSize() override { return 102400; }
     85   WriteResult WriteAsciiChunk(char* data, int size) override {
     86     m_frontend->addHeapSnapshotChunk(String16(data, size));
     87     m_frontend->flush();
     88     return kContinue;
     89   }
     90 
     91  private:
     92   protocol::HeapProfiler::Frontend* m_frontend;
     93 };
     94 
     95 v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) {
     96   v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
     97   v8::Local<v8::Value> value = profiler->FindObjectById(id);
     98   if (value.IsEmpty() || !value->IsObject()) return v8::Local<v8::Object>();
     99   return value.As<v8::Object>();
    100 }
    101 
    102 class InspectableHeapObject final : public V8InspectorSession::Inspectable {
    103  public:
    104   explicit InspectableHeapObject(int heapObjectId)
    105       : m_heapObjectId(heapObjectId) {}
    106   v8::Local<v8::Value> get(v8::Local<v8::Context> context) override {
    107     return objectByHeapObjectId(context->GetIsolate(), m_heapObjectId);
    108   }
    109 
    110  private:
    111   int m_heapObjectId;
    112 };
    113 
    114 class HeapStatsStream final : public v8::OutputStream {
    115  public:
    116   explicit HeapStatsStream(protocol::HeapProfiler::Frontend* frontend)
    117       : m_frontend(frontend) {}
    118 
    119   void EndOfStream() override {}
    120 
    121   WriteResult WriteAsciiChunk(char* data, int size) override {
    122     DCHECK(false);
    123     return kAbort;
    124   }
    125 
    126   WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData,
    127                                   int count) override {
    128     DCHECK_GT(count, 0);
    129     std::unique_ptr<protocol::Array<int>> statsDiff =
    130         protocol::Array<int>::create();
    131     for (int i = 0; i < count; ++i) {
    132       statsDiff->addItem(updateData[i].index);
    133       statsDiff->addItem(updateData[i].count);
    134       statsDiff->addItem(updateData[i].size);
    135     }
    136     m_frontend->heapStatsUpdate(std::move(statsDiff));
    137     return kContinue;
    138   }
    139 
    140  private:
    141   protocol::HeapProfiler::Frontend* m_frontend;
    142 };
    143 
    144 }  // namespace
    145 
    146 V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(
    147     V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
    148     protocol::DictionaryValue* state)
    149     : m_session(session),
    150       m_isolate(session->inspector()->isolate()),
    151       m_frontend(frontendChannel),
    152       m_state(state),
    153       m_hasTimer(false) {}
    154 
    155 V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() {}
    156 
    157 void V8HeapProfilerAgentImpl::restore() {
    158   if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled,
    159                                false))
    160     m_frontend.resetProfiles();
    161   if (m_state->booleanProperty(
    162           HeapProfilerAgentState::heapObjectsTrackingEnabled, false))
    163     startTrackingHeapObjectsInternal(m_state->booleanProperty(
    164         HeapProfilerAgentState::allocationTrackingEnabled, false));
    165   if (m_state->booleanProperty(
    166           HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) {
    167     double samplingInterval = m_state->doubleProperty(
    168         HeapProfilerAgentState::samplingHeapProfilerInterval, -1);
    169     DCHECK_GE(samplingInterval, 0);
    170     startSampling(Maybe<double>(samplingInterval));
    171   }
    172 }
    173 
    174 Response V8HeapProfilerAgentImpl::collectGarbage() {
    175   m_isolate->LowMemoryNotification();
    176   return Response::OK();
    177 }
    178 
    179 Response V8HeapProfilerAgentImpl::startTrackingHeapObjects(
    180     Maybe<bool> trackAllocations) {
    181   m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true);
    182   bool allocationTrackingEnabled = trackAllocations.fromMaybe(false);
    183   m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled,
    184                       allocationTrackingEnabled);
    185   startTrackingHeapObjectsInternal(allocationTrackingEnabled);
    186   return Response::OK();
    187 }
    188 
    189 Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects(
    190     Maybe<bool> reportProgress) {
    191   requestHeapStatsUpdate();
    192   takeHeapSnapshot(std::move(reportProgress));
    193   stopTrackingHeapObjectsInternal();
    194   return Response::OK();
    195 }
    196 
    197 Response V8HeapProfilerAgentImpl::enable() {
    198   m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true);
    199   return Response::OK();
    200 }
    201 
    202 Response V8HeapProfilerAgentImpl::disable() {
    203   stopTrackingHeapObjectsInternal();
    204   if (m_state->booleanProperty(
    205           HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) {
    206     v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
    207     if (profiler) profiler->StopSamplingHeapProfiler();
    208   }
    209   m_isolate->GetHeapProfiler()->ClearObjectIds();
    210   m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false);
    211   return Response::OK();
    212 }
    213 
    214 Response V8HeapProfilerAgentImpl::takeHeapSnapshot(Maybe<bool> reportProgress) {
    215   v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
    216   if (!profiler) return Response::Error("Cannot access v8 heap profiler");
    217   std::unique_ptr<HeapSnapshotProgress> progress;
    218   if (reportProgress.fromMaybe(false))
    219     progress = wrapUnique(new HeapSnapshotProgress(&m_frontend));
    220 
    221   GlobalObjectNameResolver resolver(m_session);
    222   const v8::HeapSnapshot* snapshot =
    223       profiler->TakeHeapSnapshot(progress.get(), &resolver);
    224   if (!snapshot) return Response::Error("Failed to take heap snapshot");
    225   HeapSnapshotOutputStream stream(&m_frontend);
    226   snapshot->Serialize(&stream);
    227   const_cast<v8::HeapSnapshot*>(snapshot)->Delete();
    228   return Response::OK();
    229 }
    230 
    231 Response V8HeapProfilerAgentImpl::getObjectByHeapObjectId(
    232     const String16& heapSnapshotObjectId, Maybe<String16> objectGroup,
    233     std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
    234   bool ok;
    235   int id = heapSnapshotObjectId.toInteger(&ok);
    236   if (!ok) return Response::Error("Invalid heap snapshot object id");
    237 
    238   v8::HandleScope handles(m_isolate);
    239   v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
    240   if (heapObject.IsEmpty()) return Response::Error("Object is not available");
    241 
    242   if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
    243     return Response::Error("Object is not available");
    244 
    245   *result = m_session->wrapObject(heapObject->CreationContext(), heapObject,
    246                                   objectGroup.fromMaybe(""), false);
    247   if (!result) return Response::Error("Object is not available");
    248   return Response::OK();
    249 }
    250 
    251 Response V8HeapProfilerAgentImpl::addInspectedHeapObject(
    252     const String16& inspectedHeapObjectId) {
    253   bool ok;
    254   int id = inspectedHeapObjectId.toInteger(&ok);
    255   if (!ok) return Response::Error("Invalid heap snapshot object id");
    256 
    257   v8::HandleScope handles(m_isolate);
    258   v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
    259   if (heapObject.IsEmpty()) return Response::Error("Object is not available");
    260 
    261   if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
    262     return Response::Error("Object is not available");
    263   m_session->addInspectedObject(wrapUnique(new InspectableHeapObject(id)));
    264   return Response::OK();
    265 }
    266 
    267 Response V8HeapProfilerAgentImpl::getHeapObjectId(
    268     const String16& objectId, String16* heapSnapshotObjectId) {
    269   v8::HandleScope handles(m_isolate);
    270   v8::Local<v8::Value> value;
    271   v8::Local<v8::Context> context;
    272   Response response =
    273       m_session->unwrapObject(objectId, &value, &context, nullptr);
    274   if (!response.isSuccess()) return response;
    275   if (value->IsUndefined()) return Response::InternalError();
    276 
    277   v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value);
    278   *heapSnapshotObjectId = String16::fromInteger(static_cast<size_t>(id));
    279   return Response::OK();
    280 }
    281 
    282 void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() {
    283   HeapStatsStream stream(&m_frontend);
    284   v8::SnapshotObjectId lastSeenObjectId =
    285       m_isolate->GetHeapProfiler()->GetHeapStats(&stream);
    286   m_frontend.lastSeenObjectId(
    287       lastSeenObjectId, m_session->inspector()->client()->currentTimeMS());
    288 }
    289 
    290 // static
    291 void V8HeapProfilerAgentImpl::onTimer(void* data) {
    292   reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate();
    293 }
    294 
    295 void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal(
    296     bool trackAllocations) {
    297   m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations);
    298   if (!m_hasTimer) {
    299     m_hasTimer = true;
    300     m_session->inspector()->client()->startRepeatingTimer(
    301         0.05, &V8HeapProfilerAgentImpl::onTimer, reinterpret_cast<void*>(this));
    302   }
    303 }
    304 
    305 void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() {
    306   if (m_hasTimer) {
    307     m_session->inspector()->client()->cancelTimer(
    308         reinterpret_cast<void*>(this));
    309     m_hasTimer = false;
    310   }
    311   m_isolate->GetHeapProfiler()->StopTrackingHeapObjects();
    312   m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled,
    313                       false);
    314   m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false);
    315 }
    316 
    317 Response V8HeapProfilerAgentImpl::startSampling(
    318     Maybe<double> samplingInterval) {
    319   v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
    320   if (!profiler) return Response::Error("Cannot access v8 heap profiler");
    321   const unsigned defaultSamplingInterval = 1 << 15;
    322   double samplingIntervalValue =
    323       samplingInterval.fromMaybe(defaultSamplingInterval);
    324   m_state->setDouble(HeapProfilerAgentState::samplingHeapProfilerInterval,
    325                      samplingIntervalValue);
    326   m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
    327                       true);
    328   profiler->StartSamplingHeapProfiler(
    329       static_cast<uint64_t>(samplingIntervalValue), 128,
    330       v8::HeapProfiler::kSamplingForceGC);
    331   return Response::OK();
    332 }
    333 
    334 namespace {
    335 std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode>
    336 buildSampingHeapProfileNode(const v8::AllocationProfile::Node* node) {
    337   auto children = protocol::Array<
    338       protocol::HeapProfiler::SamplingHeapProfileNode>::create();
    339   for (const auto* child : node->children)
    340     children->addItem(buildSampingHeapProfileNode(child));
    341   size_t selfSize = 0;
    342   for (const auto& allocation : node->allocations)
    343     selfSize += allocation.size * allocation.count;
    344   std::unique_ptr<protocol::Runtime::CallFrame> callFrame =
    345       protocol::Runtime::CallFrame::create()
    346           .setFunctionName(toProtocolString(node->name))
    347           .setScriptId(String16::fromInteger(node->script_id))
    348           .setUrl(toProtocolString(node->script_name))
    349           .setLineNumber(node->line_number - 1)
    350           .setColumnNumber(node->column_number - 1)
    351           .build();
    352   std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result =
    353       protocol::HeapProfiler::SamplingHeapProfileNode::create()
    354           .setCallFrame(std::move(callFrame))
    355           .setSelfSize(selfSize)
    356           .setChildren(std::move(children))
    357           .build();
    358   return result;
    359 }
    360 }  // namespace
    361 
    362 Response V8HeapProfilerAgentImpl::stopSampling(
    363     std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
    364   v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
    365   if (!profiler) return Response::Error("Cannot access v8 heap profiler");
    366   v8::HandleScope scope(
    367       m_isolate);  // Allocation profile contains Local handles.
    368   std::unique_ptr<v8::AllocationProfile> v8Profile(
    369       profiler->GetAllocationProfile());
    370   profiler->StopSamplingHeapProfiler();
    371   m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
    372                       false);
    373   if (!v8Profile)
    374     return Response::Error("Cannot access v8 sampled heap profile.");
    375   v8::AllocationProfile::Node* root = v8Profile->GetRootNode();
    376   *profile = protocol::HeapProfiler::SamplingHeapProfile::create()
    377                  .setHead(buildSampingHeapProfileNode(root))
    378                  .build();
    379   return Response::OK();
    380 }
    381 
    382 }  // namespace v8_inspector
    383