LCOV - code coverage report
Current view: top level - test/cctest - test-sampler-api.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 65 73 89.0 %
Date: 2019-04-17 Functions: 12 12 100.0 %

          Line data    Source code
       1             : // Copyright 2014 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             : // Tests the sampling API in include/v8.h
       6             : 
       7             : #include <map>
       8             : #include <string>
       9             : #include "include/v8.h"
      10             : #include "src/flags.h"
      11             : #include "src/simulator.h"
      12             : #include "test/cctest/cctest.h"
      13             : 
      14             : namespace {
      15             : 
      16             : class Sample {
      17             :  public:
      18             :   enum { kFramesLimit = 255 };
      19             : 
      20             :   Sample() = default;
      21             : 
      22             :   typedef const void* const* const_iterator;
      23             :   const_iterator begin() const { return data_.start(); }
      24             :   const_iterator end() const { return &data_[data_.length()]; }
      25             : 
      26             :   int size() const { return data_.length(); }
      27             :   v8::internal::Vector<void*>& data() { return data_; }
      28             : 
      29             :  private:
      30             :   v8::internal::EmbeddedVector<void*, kFramesLimit> data_;
      31             : };
      32             : 
      33             : 
      34             : #if defined(USE_SIMULATOR)
      35             : class SimulatorHelper {
      36             :  public:
      37             :   inline bool Init(v8::Isolate* isolate) {
      38             :     simulator_ = reinterpret_cast<v8::internal::Isolate*>(isolate)
      39             :                      ->thread_local_top()
      40             :                      ->simulator_;
      41             :     // Check if there is active simulator.
      42             :     return simulator_ != nullptr;
      43             :   }
      44             : 
      45             :   inline void FillRegisters(v8::RegisterState* state) {
      46             : #if V8_TARGET_ARCH_ARM
      47             :     state->pc = reinterpret_cast<void*>(simulator_->get_pc());
      48             :     state->sp = reinterpret_cast<void*>(
      49             :         simulator_->get_register(v8::internal::Simulator::sp));
      50             :     state->fp = reinterpret_cast<void*>(
      51             :         simulator_->get_register(v8::internal::Simulator::r11));
      52             : #elif V8_TARGET_ARCH_ARM64
      53             :     if (simulator_->sp() == 0 || simulator_->fp() == 0) {
      54             :       // It's possible that the simulator is interrupted while it is updating
      55             :       // the sp or fp register. ARM64 simulator does this in two steps:
      56             :       // first setting it to zero and then setting it to a new value.
      57             :       // Bailout if sp/fp doesn't contain the new value.
      58             :       return;
      59             :     }
      60             :     state->pc = reinterpret_cast<void*>(simulator_->pc());
      61             :     state->sp = reinterpret_cast<void*>(simulator_->sp());
      62             :     state->fp = reinterpret_cast<void*>(simulator_->fp());
      63             : #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
      64             :     state->pc = reinterpret_cast<void*>(simulator_->get_pc());
      65             :     state->sp = reinterpret_cast<void*>(
      66             :         simulator_->get_register(v8::internal::Simulator::sp));
      67             :     state->fp = reinterpret_cast<void*>(
      68             :         simulator_->get_register(v8::internal::Simulator::fp));
      69             : #elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
      70             :     state->pc = reinterpret_cast<void*>(simulator_->get_pc());
      71             :     state->sp = reinterpret_cast<void*>(
      72             :         simulator_->get_register(v8::internal::Simulator::sp));
      73             :     state->fp = reinterpret_cast<void*>(
      74             :         simulator_->get_register(v8::internal::Simulator::fp));
      75             : #elif V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
      76             :     state->pc = reinterpret_cast<void*>(simulator_->get_pc());
      77             :     state->sp = reinterpret_cast<void*>(
      78             :         simulator_->get_register(v8::internal::Simulator::sp));
      79             :     state->fp = reinterpret_cast<void*>(
      80             :         simulator_->get_register(v8::internal::Simulator::fp));
      81             : #endif
      82             :   }
      83             : 
      84             :  private:
      85             :   v8::internal::Simulator* simulator_;
      86             : };
      87             : #endif  // USE_SIMULATOR
      88             : 
      89             : 
      90             : class SamplingTestHelper {
      91             :  public:
      92         200 :   struct CodeEventEntry {
      93             :     std::string name;
      94             :     const void* code_start;
      95             :     size_t code_len;
      96             :   };
      97             :   typedef std::map<const void*, CodeEventEntry> CodeEntries;
      98             : 
      99          15 :   explicit SamplingTestHelper(const std::string& test_function)
     100          15 :       : sample_is_taken_(false), isolate_(CcTest::isolate()) {
     101          15 :     CHECK(!instance_);
     102          15 :     instance_ = this;
     103          30 :     v8::HandleScope scope(isolate_);
     104          15 :     v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);
     105          45 :     global->Set(v8_str("CollectSample"),
     106          15 :                 v8::FunctionTemplate::New(isolate_, CollectSample));
     107          30 :     LocalContext env(isolate_, nullptr, global);
     108          15 :     isolate_->SetJitCodeEventHandler(v8::kJitCodeEventDefault,
     109          15 :                                      JitCodeEventHandler);
     110          15 :     CompileRun(v8_str(test_function.c_str()));
     111          15 :   }
     112             : 
     113          30 :   ~SamplingTestHelper() {
     114          15 :     isolate_->SetJitCodeEventHandler(v8::kJitCodeEventDefault, nullptr);
     115          15 :     instance_ = nullptr;
     116          15 :   }
     117             : 
     118             :   Sample& sample() { return sample_; }
     119             : 
     120          10 :   const CodeEventEntry* FindEventEntry(const void* address) {
     121             :     CodeEntries::const_iterator it = code_entries_.upper_bound(address);
     122          10 :     if (it == code_entries_.begin()) return nullptr;
     123          10 :     const CodeEventEntry& entry = (--it)->second;
     124             :     const void* code_end =
     125          10 :         static_cast<const uint8_t*>(entry.code_start) + entry.code_len;
     126          10 :     return address < code_end ? &entry : nullptr;
     127             :   }
     128             : 
     129             :  private:
     130          15 :   static void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) {
     131          15 :     instance_->DoCollectSample();
     132          15 :   }
     133             : 
     134         473 :   static void JitCodeEventHandler(const v8::JitCodeEvent* event) {
     135         473 :     instance_->DoJitCodeEventHandler(event);
     136         473 :   }
     137             : 
     138             :   // The JavaScript calls this function when on full stack depth.
     139          15 :   void DoCollectSample() {
     140             :     v8::RegisterState state;
     141             : #if defined(USE_SIMULATOR)
     142             :     SimulatorHelper simulator_helper;
     143             :     if (!simulator_helper.Init(isolate_)) return;
     144             :     simulator_helper.FillRegisters(&state);
     145             : #else
     146             :     state.pc = nullptr;
     147          15 :     state.fp = &state;
     148          15 :     state.sp = &state;
     149             : #endif
     150             :     v8::SampleInfo info;
     151          15 :     isolate_->GetStackSample(state, sample_.data().start(),
     152          15 :                              static_cast<size_t>(sample_.size()), &info);
     153          15 :     size_t frames_count = info.frames_count;
     154          15 :     CHECK_LE(frames_count, static_cast<size_t>(sample_.size()));
     155          15 :     sample_.data().Truncate(static_cast<int>(frames_count));
     156          15 :     sample_is_taken_ = true;
     157          15 :   }
     158             : 
     159         473 :   void DoJitCodeEventHandler(const v8::JitCodeEvent* event) {
     160         473 :     if (sample_is_taken_) return;
     161         473 :     switch (event->type) {
     162             :       case v8::JitCodeEvent::CODE_ADDED: {
     163             :         CodeEventEntry entry;
     164          80 :         entry.name = std::string(event->name.str, event->name.len);
     165          40 :         entry.code_start = event->code_start;
     166          40 :         entry.code_len = event->code_len;
     167          40 :         code_entries_.insert(std::make_pair(entry.code_start, entry));
     168             :         break;
     169             :       }
     170             :       case v8::JitCodeEvent::CODE_MOVED: {
     171           0 :         CodeEntries::iterator it = code_entries_.find(event->code_start);
     172           0 :         CHECK(it != code_entries_.end());
     173             :         code_entries_.erase(it);
     174             :         CodeEventEntry entry;
     175           0 :         entry.name = std::string(event->name.str, event->name.len);
     176           0 :         entry.code_start = event->new_code_start;
     177           0 :         entry.code_len = event->code_len;
     178           0 :         code_entries_.insert(std::make_pair(entry.code_start, entry));
     179             :         break;
     180             :       }
     181             :       case v8::JitCodeEvent::CODE_REMOVED:
     182           0 :         code_entries_.erase(event->code_start);
     183           0 :         break;
     184             :       default:
     185             :         break;
     186             :     }
     187             :   }
     188             : 
     189             :   Sample sample_;
     190             :   bool sample_is_taken_;
     191             :   v8::Isolate* isolate_;
     192             :   CodeEntries code_entries_;
     193             : 
     194             :   static SamplingTestHelper* instance_;
     195             : };
     196             : 
     197             : SamplingTestHelper* SamplingTestHelper::instance_;
     198             : 
     199             : }  // namespace
     200             : 
     201             : 
     202             : // A JavaScript function which takes stack depth
     203             : // (minimum value 2) as an argument.
     204             : // When at the bottom of the recursion,
     205             : // the JavaScript code calls into C++ test code,
     206             : // waiting for the sampler to take a sample.
     207             : static const char* test_function =
     208             :     "function func(depth) {"
     209             :     "  if (depth == 2) CollectSample();"
     210             :     "  else return func(depth - 1);"
     211             :     "}";
     212             : 
     213             : 
     214       26644 : TEST(StackDepthIsConsistent) {
     215          20 :   SamplingTestHelper helper(std::string(test_function) + "func(8);");
     216           5 :   CHECK_EQ(8, helper.sample().size());
     217           5 : }
     218             : 
     219             : 
     220       26644 : TEST(StackDepthDoesNotExceedMaxValue) {
     221          20 :   SamplingTestHelper helper(std::string(test_function) + "func(300);");
     222           5 :   CHECK_EQ(Sample::kFramesLimit, helper.sample().size());
     223           5 : }
     224             : 
     225             : 
     226             : // The captured sample should have three pc values.
     227             : // They should fall in the range where the compiled code resides.
     228             : // The expected stack is:
     229             : // bottom of stack [{anon script}, outer, inner] top of stack
     230             : //                              ^      ^       ^
     231             : // sample.stack indices         2      1       0
     232       26644 : TEST(StackFramesConsistent) {
     233           5 :   i::FLAG_allow_natives_syntax = true;
     234             :   const char* test_script =
     235             :       "function test_sampler_api_inner() {"
     236             :       "  CollectSample();"
     237             :       "  return 0;"
     238             :       "}"
     239             :       "function test_sampler_api_outer() {"
     240             :       "  return test_sampler_api_inner();"
     241             :       "}"
     242             :       "%NeverOptimizeFunction(test_sampler_api_inner);"
     243             :       "%NeverOptimizeFunction(test_sampler_api_outer);"
     244             :       "test_sampler_api_outer();";
     245             : 
     246          15 :   SamplingTestHelper helper(test_script);
     247             :   Sample& sample = helper.sample();
     248           5 :   CHECK_EQ(3, sample.size());
     249             : 
     250             :   const SamplingTestHelper::CodeEventEntry* entry;
     251           5 :   entry = helper.FindEventEntry(sample.begin()[0]);
     252           5 :   CHECK(entry);
     253          10 :   CHECK(std::string::npos != entry->name.find("test_sampler_api_inner"));
     254             : 
     255           5 :   entry = helper.FindEventEntry(sample.begin()[1]);
     256           5 :   CHECK(entry);
     257          10 :   CHECK(std::string::npos != entry->name.find("test_sampler_api_outer"));
     258       79922 : }

Generated by: LCOV version 1.10