Home | History | Annotate | Download | only in inspector
      1 // Copyright 2015 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-profiler-agent-impl.h"
      6 
      7 #include <vector>
      8 
      9 #include "src/base/atomicops.h"
     10 #include "src/inspector/protocol/Protocol.h"
     11 #include "src/inspector/string-util.h"
     12 #include "src/inspector/v8-debugger.h"
     13 #include "src/inspector/v8-inspector-impl.h"
     14 #include "src/inspector/v8-inspector-session-impl.h"
     15 #include "src/inspector/v8-stack-trace-impl.h"
     16 
     17 #include "include/v8-profiler.h"
     18 
     19 namespace v8_inspector {
     20 
     21 namespace ProfilerAgentState {
     22 static const char samplingInterval[] = "samplingInterval";
     23 static const char userInitiatedProfiling[] = "userInitiatedProfiling";
     24 static const char profilerEnabled[] = "profilerEnabled";
     25 }
     26 
     27 namespace {
     28 
     29 std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
     30 buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) {
     31   unsigned lineCount = node->GetHitLineCount();
     32   if (!lineCount) return nullptr;
     33   auto array = protocol::Array<protocol::Profiler::PositionTickInfo>::create();
     34   std::vector<v8::CpuProfileNode::LineTick> entries(lineCount);
     35   if (node->GetLineTicks(&entries[0], lineCount)) {
     36     for (unsigned i = 0; i < lineCount; i++) {
     37       std::unique_ptr<protocol::Profiler::PositionTickInfo> line =
     38           protocol::Profiler::PositionTickInfo::create()
     39               .setLine(entries[i].line)
     40               .setTicks(entries[i].hit_count)
     41               .build();
     42       array->addItem(std::move(line));
     43     }
     44   }
     45   return array;
     46 }
     47 
     48 std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
     49     v8::Isolate* isolate, const v8::CpuProfileNode* node) {
     50   v8::HandleScope handleScope(isolate);
     51   auto callFrame =
     52       protocol::Runtime::CallFrame::create()
     53           .setFunctionName(toProtocolString(node->GetFunctionName()))
     54           .setScriptId(String16::fromInteger(node->GetScriptId()))
     55           .setUrl(toProtocolString(node->GetScriptResourceName()))
     56           .setLineNumber(node->GetLineNumber() - 1)
     57           .setColumnNumber(node->GetColumnNumber() - 1)
     58           .build();
     59   auto result = protocol::Profiler::ProfileNode::create()
     60                     .setCallFrame(std::move(callFrame))
     61                     .setHitCount(node->GetHitCount())
     62                     .setId(node->GetNodeId())
     63                     .build();
     64 
     65   const int childrenCount = node->GetChildrenCount();
     66   if (childrenCount) {
     67     auto children = protocol::Array<int>::create();
     68     for (int i = 0; i < childrenCount; i++)
     69       children->addItem(node->GetChild(i)->GetNodeId());
     70     result->setChildren(std::move(children));
     71   }
     72 
     73   const char* deoptReason = node->GetBailoutReason();
     74   if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason"))
     75     result->setDeoptReason(deoptReason);
     76 
     77   auto positionTicks = buildInspectorObjectForPositionTicks(node);
     78   if (positionTicks) result->setPositionTicks(std::move(positionTicks));
     79 
     80   return result;
     81 }
     82 
     83 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
     84     v8::CpuProfile* v8profile) {
     85   auto array = protocol::Array<int>::create();
     86   int count = v8profile->GetSamplesCount();
     87   for (int i = 0; i < count; i++)
     88     array->addItem(v8profile->GetSample(i)->GetNodeId());
     89   return array;
     90 }
     91 
     92 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
     93     v8::CpuProfile* v8profile) {
     94   auto array = protocol::Array<int>::create();
     95   int count = v8profile->GetSamplesCount();
     96   uint64_t lastTime = v8profile->GetStartTime();
     97   for (int i = 0; i < count; i++) {
     98     uint64_t ts = v8profile->GetSampleTimestamp(i);
     99     array->addItem(static_cast<int>(ts - lastTime));
    100     lastTime = ts;
    101   }
    102   return array;
    103 }
    104 
    105 void flattenNodesTree(v8::Isolate* isolate, const v8::CpuProfileNode* node,
    106                       protocol::Array<protocol::Profiler::ProfileNode>* list) {
    107   list->addItem(buildInspectorObjectFor(isolate, node));
    108   const int childrenCount = node->GetChildrenCount();
    109   for (int i = 0; i < childrenCount; i++)
    110     flattenNodesTree(isolate, node->GetChild(i), list);
    111 }
    112 
    113 std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
    114     v8::Isolate* isolate, v8::CpuProfile* v8profile) {
    115   auto nodes = protocol::Array<protocol::Profiler::ProfileNode>::create();
    116   flattenNodesTree(isolate, v8profile->GetTopDownRoot(), nodes.get());
    117   return protocol::Profiler::Profile::create()
    118       .setNodes(std::move(nodes))
    119       .setStartTime(static_cast<double>(v8profile->GetStartTime()))
    120       .setEndTime(static_cast<double>(v8profile->GetEndTime()))
    121       .setSamples(buildInspectorObjectForSamples(v8profile))
    122       .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile))
    123       .build();
    124 }
    125 
    126 std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
    127     V8InspectorImpl* inspector) {
    128   std::unique_ptr<V8StackTraceImpl> callStack =
    129       inspector->debugger()->captureStackTrace(false /* fullStack */);
    130   auto location = protocol::Debugger::Location::create()
    131                       .setScriptId(toString16(callStack->topScriptId()))
    132                       .setLineNumber(callStack->topLineNumber())
    133                       .build();
    134   location->setColumnNumber(callStack->topColumnNumber());
    135   return location;
    136 }
    137 
    138 volatile int s_lastProfileId = 0;
    139 
    140 }  // namespace
    141 
    142 class V8ProfilerAgentImpl::ProfileDescriptor {
    143  public:
    144   ProfileDescriptor(const String16& id, const String16& title)
    145       : m_id(id), m_title(title) {}
    146   String16 m_id;
    147   String16 m_title;
    148 };
    149 
    150 V8ProfilerAgentImpl::V8ProfilerAgentImpl(
    151     V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
    152     protocol::DictionaryValue* state)
    153     : m_session(session),
    154       m_isolate(m_session->inspector()->isolate()),
    155       m_profiler(nullptr),
    156       m_state(state),
    157       m_frontend(frontendChannel),
    158       m_enabled(false),
    159       m_recordingCPUProfile(false) {}
    160 
    161 V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
    162   if (m_profiler) m_profiler->Dispose();
    163 }
    164 
    165 void V8ProfilerAgentImpl::consoleProfile(const String16& title) {
    166   if (!m_enabled) return;
    167   String16 id = nextProfileId();
    168   m_startedProfiles.push_back(ProfileDescriptor(id, title));
    169   startProfiling(id);
    170   m_frontend.consoleProfileStarted(
    171       id, currentDebugLocation(m_session->inspector()), title);
    172 }
    173 
    174 void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
    175   if (!m_enabled) return;
    176   String16 id;
    177   String16 resolvedTitle;
    178   // Take last started profile if no title was passed.
    179   if (title.isEmpty()) {
    180     if (m_startedProfiles.empty()) return;
    181     id = m_startedProfiles.back().m_id;
    182     resolvedTitle = m_startedProfiles.back().m_title;
    183     m_startedProfiles.pop_back();
    184   } else {
    185     for (size_t i = 0; i < m_startedProfiles.size(); i++) {
    186       if (m_startedProfiles[i].m_title == title) {
    187         resolvedTitle = title;
    188         id = m_startedProfiles[i].m_id;
    189         m_startedProfiles.erase(m_startedProfiles.begin() + i);
    190         break;
    191       }
    192     }
    193     if (id.isEmpty()) return;
    194   }
    195   std::unique_ptr<protocol::Profiler::Profile> profile =
    196       stopProfiling(id, true);
    197   if (!profile) return;
    198   std::unique_ptr<protocol::Debugger::Location> location =
    199       currentDebugLocation(m_session->inspector());
    200   m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile),
    201                                     resolvedTitle);
    202 }
    203 
    204 Response V8ProfilerAgentImpl::enable() {
    205   if (m_enabled) return Response::OK();
    206   m_enabled = true;
    207   DCHECK(!m_profiler);
    208   m_profiler = v8::CpuProfiler::New(m_isolate);
    209   m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
    210   return Response::OK();
    211 }
    212 
    213 Response V8ProfilerAgentImpl::disable() {
    214   if (!m_enabled) return Response::OK();
    215   for (size_t i = m_startedProfiles.size(); i > 0; --i)
    216     stopProfiling(m_startedProfiles[i - 1].m_id, false);
    217   m_startedProfiles.clear();
    218   stop(nullptr);
    219   m_profiler->Dispose();
    220   m_profiler = nullptr;
    221   m_enabled = false;
    222   m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
    223   return Response::OK();
    224 }
    225 
    226 Response V8ProfilerAgentImpl::setSamplingInterval(int interval) {
    227   if (m_recordingCPUProfile)
    228     return Response::Error("Cannot change sampling interval when profiling.");
    229   m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
    230   m_profiler->SetSamplingInterval(interval);
    231   return Response::OK();
    232 }
    233 
    234 void V8ProfilerAgentImpl::restore() {
    235   DCHECK(!m_enabled);
    236   if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false))
    237     return;
    238   m_enabled = true;
    239   DCHECK(!m_profiler);
    240   m_profiler = v8::CpuProfiler::New(m_isolate);
    241   int interval = 0;
    242   m_state->getInteger(ProfilerAgentState::samplingInterval, &interval);
    243   if (interval) m_profiler->SetSamplingInterval(interval);
    244   if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
    245                                false)) {
    246     start();
    247   }
    248 }
    249 
    250 Response V8ProfilerAgentImpl::start() {
    251   if (m_recordingCPUProfile) return Response::OK();
    252   if (!m_enabled) return Response::Error("Profiler is not enabled");
    253   m_recordingCPUProfile = true;
    254   m_frontendInitiatedProfileId = nextProfileId();
    255   startProfiling(m_frontendInitiatedProfileId);
    256   m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
    257   return Response::OK();
    258 }
    259 
    260 Response V8ProfilerAgentImpl::stop(
    261     std::unique_ptr<protocol::Profiler::Profile>* profile) {
    262   if (!m_recordingCPUProfile)
    263     return Response::Error("No recording profiles found");
    264   m_recordingCPUProfile = false;
    265   std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
    266       stopProfiling(m_frontendInitiatedProfileId, !!profile);
    267   if (profile) {
    268     *profile = std::move(cpuProfile);
    269     if (!profile->get()) return Response::Error("Profile is not found");
    270   }
    271   m_frontendInitiatedProfileId = String16();
    272   m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
    273   return Response::OK();
    274 }
    275 
    276 String16 V8ProfilerAgentImpl::nextProfileId() {
    277   return String16::fromInteger(
    278       v8::base::NoBarrier_AtomicIncrement(&s_lastProfileId, 1));
    279 }
    280 
    281 void V8ProfilerAgentImpl::startProfiling(const String16& title) {
    282   v8::HandleScope handleScope(m_isolate);
    283   m_profiler->StartProfiling(toV8String(m_isolate, title), true);
    284 }
    285 
    286 std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
    287     const String16& title, bool serialize) {
    288   v8::HandleScope handleScope(m_isolate);
    289   v8::CpuProfile* profile =
    290       m_profiler->StopProfiling(toV8String(m_isolate, title));
    291   if (!profile) return nullptr;
    292   std::unique_ptr<protocol::Profiler::Profile> result;
    293   if (serialize) result = createCPUProfile(m_isolate, profile);
    294   profile->Delete();
    295   return result;
    296 }
    297 
    298 bool V8ProfilerAgentImpl::isRecording() const {
    299   return m_recordingCPUProfile || !m_startedProfiles.empty();
    300 }
    301 
    302 bool V8ProfilerAgentImpl::idleStarted() {
    303   if (m_profiler) m_profiler->SetIdle(true);
    304   return m_profiler;
    305 }
    306 
    307 bool V8ProfilerAgentImpl::idleFinished() {
    308   if (m_profiler) m_profiler->SetIdle(false);
    309   return m_profiler;
    310 }
    311 
    312 void V8ProfilerAgentImpl::collectSample() {
    313   if (m_profiler) m_profiler->CollectSample();
    314 }
    315 
    316 }  // namespace v8_inspector
    317