LCOV - code coverage report
Current view: top level - src/profiler - tick-sample.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 61 76 80.3 %
Date: 2019-01-20 Functions: 6 7 85.7 %

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

Generated by: LCOV version 1.10