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 static const char preciseCoverageStarted[] = "preciseCoverageStarted";
     26 }
     27 
     28 namespace {
     29 
     30 std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>>
     31 buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) {
     32   unsigned lineCount = node->GetHitLineCount();
     33   if (!lineCount) return nullptr;
     34   auto array = protocol::Array<protocol::Profiler::PositionTickInfo>::create();
     35   std::vector<v8::CpuProfileNode::LineTick> entries(lineCount);
     36   if (node->GetLineTicks(&entries[0], lineCount)) {
     37     for (unsigned i = 0; i < lineCount; i++) {
     38       std::unique_ptr<protocol::Profiler::PositionTickInfo> line =
     39           protocol::Profiler::PositionTickInfo::create()
     40               .setLine(entries[i].line)
     41               .setTicks(entries[i].hit_count)
     42               .build();
     43       array->addItem(std::move(line));
     44     }
     45   }
     46   return array;
     47 }
     48 
     49 std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor(
     50     v8::Isolate* isolate, const v8::CpuProfileNode* node) {
     51   v8::HandleScope handleScope(isolate);
     52   auto callFrame =
     53       protocol::Runtime::CallFrame::create()
     54           .setFunctionName(toProtocolString(node->GetFunctionName()))
     55           .setScriptId(String16::fromInteger(node->GetScriptId()))
     56           .setUrl(toProtocolString(node->GetScriptResourceName()))
     57           .setLineNumber(node->GetLineNumber() - 1)
     58           .setColumnNumber(node->GetColumnNumber() - 1)
     59           .build();
     60   auto result = protocol::Profiler::ProfileNode::create()
     61                     .setCallFrame(std::move(callFrame))
     62                     .setHitCount(node->GetHitCount())
     63                     .setId(node->GetNodeId())
     64                     .build();
     65 
     66   const int childrenCount = node->GetChildrenCount();
     67   if (childrenCount) {
     68     auto children = protocol::Array<int>::create();
     69     for (int i = 0; i < childrenCount; i++)
     70       children->addItem(node->GetChild(i)->GetNodeId());
     71     result->setChildren(std::move(children));
     72   }
     73 
     74   const char* deoptReason = node->GetBailoutReason();
     75   if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason"))
     76     result->setDeoptReason(deoptReason);
     77 
     78   auto positionTicks = buildInspectorObjectForPositionTicks(node);
     79   if (positionTicks) result->setPositionTicks(std::move(positionTicks));
     80 
     81   return result;
     82 }
     83 
     84 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(
     85     v8::CpuProfile* v8profile) {
     86   auto array = protocol::Array<int>::create();
     87   int count = v8profile->GetSamplesCount();
     88   for (int i = 0; i < count; i++)
     89     array->addItem(v8profile->GetSample(i)->GetNodeId());
     90   return array;
     91 }
     92 
     93 std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps(
     94     v8::CpuProfile* v8profile) {
     95   auto array = protocol::Array<int>::create();
     96   int count = v8profile->GetSamplesCount();
     97   uint64_t lastTime = v8profile->GetStartTime();
     98   for (int i = 0; i < count; i++) {
     99     uint64_t ts = v8profile->GetSampleTimestamp(i);
    100     array->addItem(static_cast<int>(ts - lastTime));
    101     lastTime = ts;
    102   }
    103   return array;
    104 }
    105 
    106 void flattenNodesTree(v8::Isolate* isolate, const v8::CpuProfileNode* node,
    107                       protocol::Array<protocol::Profiler::ProfileNode>* list) {
    108   list->addItem(buildInspectorObjectFor(isolate, node));
    109   const int childrenCount = node->GetChildrenCount();
    110   for (int i = 0; i < childrenCount; i++)
    111     flattenNodesTree(isolate, node->GetChild(i), list);
    112 }
    113 
    114 std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
    115     v8::Isolate* isolate, v8::CpuProfile* v8profile) {
    116   auto nodes = protocol::Array<protocol::Profiler::ProfileNode>::create();
    117   flattenNodesTree(isolate, v8profile->GetTopDownRoot(), nodes.get());
    118   return protocol::Profiler::Profile::create()
    119       .setNodes(std::move(nodes))
    120       .setStartTime(static_cast<double>(v8profile->GetStartTime()))
    121       .setEndTime(static_cast<double>(v8profile->GetEndTime()))
    122       .setSamples(buildInspectorObjectForSamples(v8profile))
    123       .setTimeDeltas(buildInspectorObjectForTimestamps(v8profile))
    124       .build();
    125 }
    126 
    127 std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
    128     V8InspectorImpl* inspector) {
    129   std::unique_ptr<V8StackTraceImpl> callStack =
    130       inspector->debugger()->captureStackTrace(false /* fullStack */);
    131   auto location = protocol::Debugger::Location::create()
    132                       .setScriptId(toString16(callStack->topScriptId()))
    133                       .setLineNumber(callStack->topLineNumber())
    134                       .build();
    135   location->setColumnNumber(callStack->topColumnNumber());
    136   return location;
    137 }
    138 
    139 volatile int s_lastProfileId = 0;
    140 
    141 }  // namespace
    142 
    143 class V8ProfilerAgentImpl::ProfileDescriptor {
    144  public:
    145   ProfileDescriptor(const String16& id, const String16& title)
    146       : m_id(id), m_title(title) {}
    147   String16 m_id;
    148   String16 m_title;
    149 };
    150 
    151 V8ProfilerAgentImpl::V8ProfilerAgentImpl(
    152     V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
    153     protocol::DictionaryValue* state)
    154     : m_session(session),
    155       m_isolate(m_session->inspector()->isolate()),
    156       m_state(state),
    157       m_frontend(frontendChannel) {}
    158 
    159 V8ProfilerAgentImpl::~V8ProfilerAgentImpl() {
    160   if (m_profiler) m_profiler->Dispose();
    161 }
    162 
    163 void V8ProfilerAgentImpl::consoleProfile(const String16& title) {
    164   if (!m_enabled) return;
    165   String16 id = nextProfileId();
    166   m_startedProfiles.push_back(ProfileDescriptor(id, title));
    167   startProfiling(id);
    168   m_frontend.consoleProfileStarted(
    169       id, currentDebugLocation(m_session->inspector()), title);
    170 }
    171 
    172 void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
    173   if (!m_enabled) return;
    174   String16 id;
    175   String16 resolvedTitle;
    176   // Take last started profile if no title was passed.
    177   if (title.isEmpty()) {
    178     if (m_startedProfiles.empty()) return;
    179     id = m_startedProfiles.back().m_id;
    180     resolvedTitle = m_startedProfiles.back().m_title;
    181     m_startedProfiles.pop_back();
    182   } else {
    183     for (size_t i = 0; i < m_startedProfiles.size(); i++) {
    184       if (m_startedProfiles[i].m_title == title) {
    185         resolvedTitle = title;
    186         id = m_startedProfiles[i].m_id;
    187         m_startedProfiles.erase(m_startedProfiles.begin() + i);
    188         break;
    189       }
    190     }
    191     if (id.isEmpty()) return;
    192   }
    193   std::unique_ptr<protocol::Profiler::Profile> profile =
    194       stopProfiling(id, true);
    195   if (!profile) return;
    196   std::unique_ptr<protocol::Debugger::Location> location =
    197       currentDebugLocation(m_session->inspector());
    198   m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile),
    199                                     resolvedTitle);
    200 }
    201 
    202 Response V8ProfilerAgentImpl::enable() {
    203   if (m_enabled) return Response::OK();
    204   m_enabled = true;
    205   m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
    206   return Response::OK();
    207 }
    208 
    209 Response V8ProfilerAgentImpl::disable() {
    210   if (!m_enabled) return Response::OK();
    211   for (size_t i = m_startedProfiles.size(); i > 0; --i)
    212     stopProfiling(m_startedProfiles[i - 1].m_id, false);
    213   m_startedProfiles.clear();
    214   stop(nullptr);
    215   stopPreciseCoverage();
    216   DCHECK(!m_profiler);
    217   m_enabled = false;
    218   m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
    219   return Response::OK();
    220 }
    221 
    222 Response V8ProfilerAgentImpl::setSamplingInterval(int interval) {
    223   if (m_profiler) {
    224     return Response::Error("Cannot change sampling interval when profiling.");
    225   }
    226   m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
    227   return Response::OK();
    228 }
    229 
    230 void V8ProfilerAgentImpl::restore() {
    231   DCHECK(!m_enabled);
    232   if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false))
    233     return;
    234   m_enabled = true;
    235   DCHECK(!m_profiler);
    236   if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling,
    237                                false)) {
    238     start();
    239   }
    240   if (m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
    241                                false)) {
    242     startPreciseCoverage();
    243   }
    244 }
    245 
    246 Response V8ProfilerAgentImpl::start() {
    247   if (m_recordingCPUProfile) return Response::OK();
    248   if (!m_enabled) return Response::Error("Profiler is not enabled");
    249   m_recordingCPUProfile = true;
    250   m_frontendInitiatedProfileId = nextProfileId();
    251   startProfiling(m_frontendInitiatedProfileId);
    252   m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
    253   return Response::OK();
    254 }
    255 
    256 Response V8ProfilerAgentImpl::stop(
    257     std::unique_ptr<protocol::Profiler::Profile>* profile) {
    258   if (!m_recordingCPUProfile) {
    259     return Response::Error("No recording profiles found");
    260   }
    261   m_recordingCPUProfile = false;
    262   std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
    263       stopProfiling(m_frontendInitiatedProfileId, !!profile);
    264   if (profile) {
    265     *profile = std::move(cpuProfile);
    266     if (!profile->get()) return Response::Error("Profile is not found");
    267   }
    268   m_frontendInitiatedProfileId = String16();
    269   m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
    270   return Response::OK();
    271 }
    272 
    273 Response V8ProfilerAgentImpl::startPreciseCoverage() {
    274   if (!m_enabled) return Response::Error("Profiler is not enabled");
    275   m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, true);
    276   v8::debug::Coverage::TogglePrecise(m_isolate, true);
    277   return Response::OK();
    278 }
    279 
    280 Response V8ProfilerAgentImpl::stopPreciseCoverage() {
    281   if (!m_enabled) return Response::Error("Profiler is not enabled");
    282   m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, false);
    283   v8::debug::Coverage::TogglePrecise(m_isolate, false);
    284   return Response::OK();
    285 }
    286 
    287 namespace {
    288 Response takeCoverage(
    289     v8::Isolate* isolate, bool reset_count,
    290     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
    291         out_result) {
    292   std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>> result =
    293       protocol::Array<protocol::Profiler::ScriptCoverage>::create();
    294   v8::HandleScope handle_scope(isolate);
    295   v8::debug::Coverage coverage =
    296       v8::debug::Coverage::Collect(isolate, reset_count);
    297   for (size_t i = 0; i < coverage.ScriptCount(); i++) {
    298     v8::debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
    299     v8::Local<v8::debug::Script> script = script_data.GetScript();
    300     std::unique_ptr<protocol::Array<protocol::Profiler::FunctionCoverage>>
    301         functions =
    302             protocol::Array<protocol::Profiler::FunctionCoverage>::create();
    303     for (size_t j = 0; j < script_data.FunctionCount(); j++) {
    304       v8::debug::Coverage::FunctionData function_data =
    305           script_data.GetFunctionData(j);
    306       std::unique_ptr<protocol::Array<protocol::Profiler::CoverageRange>>
    307           ranges = protocol::Array<protocol::Profiler::CoverageRange>::create();
    308       // At this point we only have per-function coverage data, so there is
    309       // only one range per function.
    310       ranges->addItem(
    311           protocol::Profiler::CoverageRange::create()
    312               .setStartLineNumber(function_data.Start().GetLineNumber())
    313               .setStartColumnNumber(function_data.Start().GetColumnNumber())
    314               .setEndLineNumber(function_data.End().GetLineNumber())
    315               .setEndColumnNumber(function_data.End().GetColumnNumber())
    316               .setCount(function_data.Count())
    317               .build());
    318       functions->addItem(
    319           protocol::Profiler::FunctionCoverage::create()
    320               .setFunctionName(toProtocolString(
    321                   function_data.Name().FromMaybe(v8::Local<v8::String>())))
    322               .setRanges(std::move(ranges))
    323               .build());
    324     }
    325     String16 url;
    326     v8::Local<v8::String> name;
    327     if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name)) {
    328       url = toProtocolString(name);
    329     }
    330     result->addItem(protocol::Profiler::ScriptCoverage::create()
    331                         .setScriptId(String16::fromInteger(script->Id()))
    332                         .setUrl(url)
    333                         .setFunctions(std::move(functions))
    334                         .build());
    335   }
    336   *out_result = std::move(result);
    337   return Response::OK();
    338 }
    339 }  // anonymous namespace
    340 
    341 Response V8ProfilerAgentImpl::takePreciseCoverage(
    342     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
    343         out_result) {
    344   if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
    345                                 false)) {
    346     return Response::Error("Precise coverage has not been started.");
    347   }
    348   return takeCoverage(m_isolate, true, out_result);
    349 }
    350 
    351 Response V8ProfilerAgentImpl::getBestEffortCoverage(
    352     std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
    353         out_result) {
    354   return takeCoverage(m_isolate, false, out_result);
    355 }
    356 
    357 String16 V8ProfilerAgentImpl::nextProfileId() {
    358   return String16::fromInteger(
    359       v8::base::NoBarrier_AtomicIncrement(&s_lastProfileId, 1));
    360 }
    361 
    362 void V8ProfilerAgentImpl::startProfiling(const String16& title) {
    363   v8::HandleScope handleScope(m_isolate);
    364   if (!m_startedProfilesCount) {
    365     DCHECK(!m_profiler);
    366     m_profiler = v8::CpuProfiler::New(m_isolate);
    367     m_profiler->SetIdle(m_idle);
    368     int interval =
    369         m_state->integerProperty(ProfilerAgentState::samplingInterval, 0);
    370     if (interval) m_profiler->SetSamplingInterval(interval);
    371   }
    372   ++m_startedProfilesCount;
    373   m_profiler->StartProfiling(toV8String(m_isolate, title), true);
    374 }
    375 
    376 std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling(
    377     const String16& title, bool serialize) {
    378   v8::HandleScope handleScope(m_isolate);
    379   v8::CpuProfile* profile =
    380       m_profiler->StopProfiling(toV8String(m_isolate, title));
    381   std::unique_ptr<protocol::Profiler::Profile> result;
    382   if (profile) {
    383     if (serialize) result = createCPUProfile(m_isolate, profile);
    384     profile->Delete();
    385   }
    386   --m_startedProfilesCount;
    387   if (!m_startedProfilesCount) {
    388     m_profiler->Dispose();
    389     m_profiler = nullptr;
    390   }
    391   return result;
    392 }
    393 
    394 bool V8ProfilerAgentImpl::idleStarted() {
    395   m_idle = true;
    396   if (m_profiler) m_profiler->SetIdle(m_idle);
    397   return m_profiler;
    398 }
    399 
    400 bool V8ProfilerAgentImpl::idleFinished() {
    401   m_idle = false;
    402   if (m_profiler) m_profiler->SetIdle(m_idle);
    403   return m_profiler;
    404 }
    405 
    406 }  // namespace v8_inspector
    407