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