Home | History | Annotate | Download | only in profiler
      1 // Copyright 2013 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/profiler/tick-sample.h"
      6 
      7 #include "include/v8-profiler.h"
      8 #include "src/counters.h"
      9 #include "src/frames-inl.h"
     10 #include "src/msan.h"
     11 #include "src/simulator.h"
     12 #include "src/vm-state-inl.h"
     13 
     14 namespace v8 {
     15 namespace {
     16 
     17 bool IsSamePage(i::byte* ptr1, i::byte* ptr2) {
     18   const uint32_t kPageSize = 4096;
     19   uintptr_t mask = ~static_cast<uintptr_t>(kPageSize - 1);
     20   return (reinterpret_cast<uintptr_t>(ptr1) & mask) ==
     21          (reinterpret_cast<uintptr_t>(ptr2) & mask);
     22 }
     23 
     24 // Check if the code at specified address could potentially be a
     25 // frame setup code.
     26 bool IsNoFrameRegion(i::Address address) {
     27   struct Pattern {
     28     int bytes_count;
     29     i::byte bytes[8];
     30     int offsets[4];
     31   };
     32   i::byte* pc = reinterpret_cast<i::byte*>(address);
     33   static Pattern patterns[] = {
     34 #if V8_HOST_ARCH_IA32
     35     // push %ebp
     36     // mov %esp,%ebp
     37     {3, {0x55, 0x89, 0xe5}, {0, 1, -1}},
     38     // pop %ebp
     39     // ret N
     40     {2, {0x5d, 0xc2}, {0, 1, -1}},
     41     // pop %ebp
     42     // ret
     43     {2, {0x5d, 0xc3}, {0, 1, -1}},
     44 #elif V8_HOST_ARCH_X64
     45     // pushq %rbp
     46     // movq %rsp,%rbp
     47     {4, {0x55, 0x48, 0x89, 0xe5}, {0, 1, -1}},
     48     // popq %rbp
     49     // ret N
     50     {2, {0x5d, 0xc2}, {0, 1, -1}},
     51     // popq %rbp
     52     // ret
     53     {2, {0x5d, 0xc3}, {0, 1, -1}},
     54 #endif
     55     {0, {}, {}}
     56   };
     57   for (Pattern* pattern = patterns; pattern->bytes_count; ++pattern) {
     58     for (int* offset_ptr = pattern->offsets; *offset_ptr != -1; ++offset_ptr) {
     59       int offset = *offset_ptr;
     60       if (!offset || IsSamePage(pc, pc - offset)) {
     61         MSAN_MEMORY_IS_INITIALIZED(pc - offset, pattern->bytes_count);
     62         if (!memcmp(pc - offset, pattern->bytes, pattern->bytes_count))
     63           return true;
     64       } else {
     65         // It is not safe to examine bytes on another page as it might not be
     66         // allocated thus causing a SEGFAULT.
     67         // Check the pattern part that's on the same page and
     68         // pessimistically assume it could be the entire pattern match.
     69         MSAN_MEMORY_IS_INITIALIZED(pc, pattern->bytes_count - offset);
     70         if (!memcmp(pc, pattern->bytes + offset, pattern->bytes_count - offset))
     71           return true;
     72       }
     73     }
     74   }
     75   return false;
     76 }
     77 
     78 }  // namespace
     79 
     80 namespace internal {
     81 namespace {
     82 
     83 #if defined(USE_SIMULATOR)
     84 class SimulatorHelper {
     85  public:
     86   // Returns true if register values were successfully retrieved
     87   // from the simulator, otherwise returns false.
     88   static bool FillRegisters(Isolate* isolate, v8::RegisterState* state);
     89 };
     90 
     91 bool SimulatorHelper::FillRegisters(Isolate* isolate,
     92                                     v8::RegisterState* state) {
     93   Simulator* simulator = isolate->thread_local_top()->simulator_;
     94   // Check if there is active simulator.
     95   if (simulator == NULL) return false;
     96 #if V8_TARGET_ARCH_ARM
     97   if (!simulator->has_bad_pc()) {
     98     state->pc = reinterpret_cast<Address>(simulator->get_pc());
     99   }
    100   state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
    101   state->fp =
    102       reinterpret_cast<Address>(simulator->get_register(Simulator::r11));
    103 #elif V8_TARGET_ARCH_ARM64
    104   state->pc = reinterpret_cast<Address>(simulator->pc());
    105   state->sp = reinterpret_cast<Address>(simulator->sp());
    106   state->fp = reinterpret_cast<Address>(simulator->fp());
    107 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
    108   if (!simulator->has_bad_pc()) {
    109     state->pc = reinterpret_cast<Address>(simulator->get_pc());
    110   }
    111   state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
    112   state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
    113 #elif V8_TARGET_ARCH_PPC
    114   if (!simulator->has_bad_pc()) {
    115     state->pc = reinterpret_cast<Address>(simulator->get_pc());
    116   }
    117   state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
    118   state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
    119 #elif V8_TARGET_ARCH_S390
    120   if (!simulator->has_bad_pc()) {
    121     state->pc = reinterpret_cast<Address>(simulator->get_pc());
    122   }
    123   state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
    124   state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
    125 #endif
    126   if (state->sp == 0 || state->fp == 0) {
    127     // It possible that the simulator is interrupted while it is updating
    128     // the sp or fp register. ARM64 simulator does this in two steps:
    129     // first setting it to zero and then setting it to the new value.
    130     // Bailout if sp/fp doesn't contain the new value.
    131     //
    132     // FIXME: The above doesn't really solve the issue.
    133     // If a 64-bit target is executed on a 32-bit host even the final
    134     // write is non-atomic, so it might obtain a half of the result.
    135     // Moreover as long as the register set code uses memcpy (as of now),
    136     // it is not guaranteed to be atomic even when both host and target
    137     // are of same bitness.
    138     return false;
    139   }
    140   return true;
    141 }
    142 #endif  // USE_SIMULATOR
    143 
    144 }  // namespace
    145 }  // namespace internal
    146 
    147 //
    148 // StackTracer implementation
    149 //
    150 DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate,
    151                                    const RegisterState& reg_state,
    152                                    RecordCEntryFrame record_c_entry_frame,
    153                                    bool update_stats,
    154                                    bool use_simulator_reg_state) {
    155   this->update_stats = update_stats;
    156   SampleInfo info;
    157   RegisterState regs = reg_state;
    158   if (!GetStackSample(v8_isolate, &regs, record_c_entry_frame, stack,
    159                       kMaxFramesCount, &info, use_simulator_reg_state)) {
    160     // It is executing JS but failed to collect a stack trace.
    161     // Mark the sample as spoiled.
    162     pc = nullptr;
    163     return;
    164   }
    165 
    166   state = info.vm_state;
    167   pc = regs.pc;
    168   frames_count = static_cast<unsigned>(info.frames_count);
    169   has_external_callback = info.external_callback_entry != nullptr;
    170   if (has_external_callback) {
    171     external_callback_entry = info.external_callback_entry;
    172   } else if (frames_count) {
    173     // sp register may point at an arbitrary place in memory, make
    174     // sure MSAN doesn't complain about it.
    175     MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(void*));
    176     // Sample potential return address value for frameless invocation of
    177     // stubs (we'll figure out later, if this value makes sense).
    178     tos = i::Memory::Address_at(reinterpret_cast<i::Address>(regs.sp));
    179   } else {
    180     tos = nullptr;
    181   }
    182 }
    183 
    184 bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
    185                                 RecordCEntryFrame record_c_entry_frame,
    186                                 void** frames, size_t frames_limit,
    187                                 v8::SampleInfo* sample_info,
    188                                 bool use_simulator_reg_state) {
    189   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
    190   sample_info->frames_count = 0;
    191   sample_info->vm_state = isolate->current_vm_state();
    192   sample_info->external_callback_entry = nullptr;
    193   if (sample_info->vm_state == GC) return true;
    194 
    195   i::Address js_entry_sp = isolate->js_entry_sp();
    196   if (js_entry_sp == nullptr) return true;  // Not executing JS now.
    197 
    198 #if defined(USE_SIMULATOR)
    199   if (use_simulator_reg_state) {
    200     if (!i::SimulatorHelper::FillRegisters(isolate, regs)) return false;
    201   }
    202 #else
    203   USE(use_simulator_reg_state);
    204 #endif
    205   DCHECK(regs->sp);
    206 
    207   if (regs->pc && IsNoFrameRegion(static_cast<i::Address>(regs->pc))) {
    208     // The frame is not setup, so it'd be hard to iterate the stack. Bailout.
    209     return false;
    210   }
    211 
    212   i::ExternalCallbackScope* scope = isolate->external_callback_scope();
    213   i::Address handler = i::Isolate::handler(isolate->thread_local_top());
    214   // If there is a handler on top of the external callback scope then
    215   // we have already entrered JavaScript again and the external callback
    216   // is not the top function.
    217   if (scope && scope->scope_address() < handler) {
    218     i::Address* external_callback_entry_ptr =
    219         scope->callback_entrypoint_address();
    220     sample_info->external_callback_entry =
    221         external_callback_entry_ptr == nullptr ? nullptr
    222                                                : *external_callback_entry_ptr;
    223   }
    224 
    225   i::SafeStackFrameIterator it(isolate, reinterpret_cast<i::Address>(regs->fp),
    226                                reinterpret_cast<i::Address>(regs->sp),
    227                                js_entry_sp);
    228 
    229   // If at this point iterator does not see any frames,
    230   // is usually means something is wrong with the FP,
    231   // e.g. it is used as a general purpose register in the function.
    232   // Bailout.
    233   if (it.done()) return false;
    234 
    235   size_t i = 0;
    236   if (record_c_entry_frame == kIncludeCEntryFrame &&
    237       (it.top_frame_type() == internal::StackFrame::EXIT ||
    238        it.top_frame_type() == internal::StackFrame::BUILTIN_EXIT)) {
    239     frames[i++] = isolate->c_function();
    240   }
    241   i::RuntimeCallTimer* timer =
    242       isolate->counters()->runtime_call_stats()->current_timer();
    243   for (; !it.done() && i < frames_limit; it.Advance()) {
    244     while (timer && reinterpret_cast<i::Address>(timer) < it.frame()->fp() &&
    245            i < frames_limit) {
    246       frames[i++] = reinterpret_cast<i::Address>(timer->counter());
    247       timer = timer->parent();
    248     }
    249     if (i == frames_limit) break;
    250     if (!it.frame()->is_interpreted()) {
    251       frames[i++] = it.frame()->pc();
    252       continue;
    253     }
    254     // For interpreted frames use the bytecode array pointer as the pc.
    255     i::InterpretedFrame* frame = static_cast<i::InterpretedFrame*>(it.frame());
    256     // Since the sampler can interrupt execution at any point the
    257     // bytecode_array might be garbage, so don't dereference it.
    258     i::Address bytecode_array =
    259         reinterpret_cast<i::Address>(frame->GetBytecodeArray()) -
    260         i::kHeapObjectTag;
    261     frames[i++] = bytecode_array + i::BytecodeArray::kHeaderSize +
    262                   frame->GetBytecodeOffset();
    263   }
    264   sample_info->frames_count = i;
    265   return true;
    266 }
    267 
    268 namespace internal {
    269 
    270 void TickSample::Init(Isolate* isolate, const v8::RegisterState& state,
    271                       RecordCEntryFrame record_c_entry_frame, bool update_stats,
    272                       bool use_simulator_reg_state) {
    273   v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state,
    274                        record_c_entry_frame, update_stats,
    275                        use_simulator_reg_state);
    276   if (pc == nullptr) return;
    277   timestamp = base::TimeTicks::HighResolutionNow();
    278 }
    279 
    280 }  // namespace internal
    281 }  // namespace v8
    282