LCOV - code coverage report
Current view: top level - src/inspector - v8-heap-profiler-agent-impl.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 110 210 52.4 %
Date: 2019-04-19 Functions: 15 39 38.5 %

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

Generated by: LCOV version 1.10