LCOV - code coverage report
Current view: top level - src/profiler - tick-sample.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 61 75 81.3 %
Date: 2019-04-18 Functions: 5 6 83.3 %

          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/asan.h"
       9             : #include "src/counters.h"
      10             : #include "src/frames-inl.h"
      11             : #include "src/heap/heap-inl.h"  // For MemoryAllocator::code_range.
      12             : #include "src/msan.h"
      13             : #include "src/simulator.h"
      14             : #include "src/vm-state-inl.h"
      15             : 
      16             : namespace v8 {
      17             : namespace {
      18             : 
      19             : bool IsSamePage(i::Address ptr1, i::Address ptr2) {
      20             :   const uint32_t kPageSize = 4096;
      21             :   i::Address mask = ~static_cast<i::Address>(kPageSize - 1);
      22        1334 :   return (ptr1 & mask) == (ptr2 & mask);
      23             : }
      24             : 
      25             : // Check if the code at specified address could potentially be a
      26             : // frame setup code.
      27         445 : bool IsNoFrameRegion(i::Address address) {
      28             :   struct Pattern {
      29             :     int bytes_count;
      30             :     i::byte bytes[8];
      31             :     int offsets[4];
      32             :   };
      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         445 :   i::byte* pc = reinterpret_cast<i::byte*>(address);
      58        3111 :   for (Pattern* pattern = patterns; pattern->bytes_count; ++pattern) {
      59        4001 :     for (int* offset_ptr = pattern->offsets; *offset_ptr != -1; ++offset_ptr) {
      60             :       int offset = *offset_ptr;
      61        4002 :       if (!offset || IsSamePage(address, address - offset)) {
      62             :         MSAN_MEMORY_IS_INITIALIZED(pc - offset, pattern->bytes_count);
      63        2665 :         if (!memcmp(pc - offset, pattern->bytes, pattern->bytes_count))
      64             :           return true;
      65             :       } else {
      66             :         // It is not safe to examine bytes on another page as it might not be
      67             :         // allocated thus causing a SEGFAULT.
      68             :         // Check the pattern part that's on the same page and
      69             :         // pessimistically assume it could be the entire pattern match.
      70             :         MSAN_MEMORY_IS_INITIALIZED(pc, pattern->bytes_count - offset);
      71           3 :         if (!memcmp(pc, pattern->bytes + offset, pattern->bytes_count - offset))
      72             :           return true;
      73             :       }
      74             :     }
      75             :   }
      76             :   return false;
      77             : }
      78             : 
      79             : }  // namespace
      80             : 
      81             : namespace internal {
      82             : namespace {
      83             : 
      84             : #if defined(USE_SIMULATOR)
      85             : class SimulatorHelper {
      86             :  public:
      87             :   // Returns true if register values were successfully retrieved
      88             :   // from the simulator, otherwise returns false.
      89             :   static bool FillRegisters(Isolate* isolate, v8::RegisterState* state);
      90             : };
      91             : 
      92             : bool SimulatorHelper::FillRegisters(Isolate* isolate,
      93             :                                     v8::RegisterState* state) {
      94             :   Simulator* simulator = isolate->thread_local_top()->simulator_;
      95             :   // Check if there is active simulator.
      96             :   if (simulator == nullptr) return false;
      97             : #if V8_TARGET_ARCH_ARM
      98             :   if (!simulator->has_bad_pc()) {
      99             :     state->pc = reinterpret_cast<void*>(simulator->get_pc());
     100             :   }
     101             :   state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
     102             :   state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::r11));
     103             : #elif V8_TARGET_ARCH_ARM64
     104             :   state->pc = reinterpret_cast<void*>(simulator->pc());
     105             :   state->sp = reinterpret_cast<void*>(simulator->sp());
     106             :   state->fp = reinterpret_cast<void*>(simulator->fp());
     107             : #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
     108             :   if (!simulator->has_bad_pc()) {
     109             :     state->pc = reinterpret_cast<void*>(simulator->get_pc());
     110             :   }
     111             :   state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
     112             :   state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::fp));
     113             : #elif V8_TARGET_ARCH_PPC
     114             :   if (!simulator->has_bad_pc()) {
     115             :     state->pc = reinterpret_cast<void*>(simulator->get_pc());
     116             :   }
     117             :   state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
     118             :   state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::fp));
     119             : #elif V8_TARGET_ARCH_S390
     120             :   if (!simulator->has_bad_pc()) {
     121             :     state->pc = reinterpret_cast<void*>(simulator->get_pc());
     122             :   }
     123             :   state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
     124             :   state->fp = reinterpret_cast<void*>(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       65405 : 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       65405 :   this->update_stats = update_stats;
     156             :   SampleInfo info;
     157       65405 :   RegisterState regs = reg_state;
     158       65405 :   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           1 :     pc = nullptr;
     163           1 :     return;
     164             :   }
     165             : 
     166       65404 :   state = info.vm_state;
     167       65404 :   pc = regs.pc;
     168       65404 :   frames_count = static_cast<unsigned>(info.frames_count);
     169       65404 :   has_external_callback = info.external_callback_entry != nullptr;
     170       65404 :   if (has_external_callback) {
     171       16876 :     external_callback_entry = info.external_callback_entry;
     172       48528 :   } else if (frames_count) {
     173             :     // sp register may point at an arbitrary place in memory, make
     174             :     // sure sanitizers don't complain about it.
     175             :     ASAN_UNPOISON_MEMORY_REGION(regs.sp, sizeof(void*));
     176             :     MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(void*));
     177             :     // Sample potential return address value for frameless invocation of
     178             :     // stubs (we'll figure out later, if this value makes sense).
     179             : 
     180             :     // TODO(petermarshall): This read causes guard page violations on Windows.
     181             :     // Either fix this mechanism for frameless stubs or remove it.
     182             :     // tos =
     183             :     // i::ReadUnalignedValue<void*>(reinterpret_cast<i::Address>(regs.sp));
     184       43864 :     tos = nullptr;
     185             :   } else {
     186        4664 :     tos = nullptr;
     187             :   }
     188             : }
     189             : 
     190       65420 : bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
     191             :                                 RecordCEntryFrame record_c_entry_frame,
     192             :                                 void** frames, size_t frames_limit,
     193             :                                 v8::SampleInfo* sample_info,
     194             :                                 bool use_simulator_reg_state) {
     195             :   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
     196       65420 :   sample_info->frames_count = 0;
     197       65420 :   sample_info->vm_state = isolate->current_vm_state();
     198       65420 :   sample_info->external_callback_entry = nullptr;
     199       65420 :   if (sample_info->vm_state == GC) return true;
     200             : 
     201             :   i::Address js_entry_sp = isolate->js_entry_sp();
     202       62921 :   if (js_entry_sp == 0) return true;  // Not executing JS now.
     203             : 
     204             : #if defined(USE_SIMULATOR)
     205             :   if (use_simulator_reg_state) {
     206             :     if (!i::SimulatorHelper::FillRegisters(isolate, regs)) return false;
     207             :   }
     208             : #else
     209             :   USE(use_simulator_reg_state);
     210             : #endif
     211             :   DCHECK(regs->sp);
     212             : 
     213             :   // Check whether we interrupted setup/teardown of a stack frame in JS code.
     214             :   // Avoid this check for C++ code, as that would trigger false positives.
     215      184112 :   if (regs->pc &&
     216       61354 :       isolate->heap()->memory_allocator()->code_range().contains(
     217       61824 :           reinterpret_cast<i::Address>(regs->pc)) &&
     218         445 :       IsNoFrameRegion(reinterpret_cast<i::Address>(regs->pc))) {
     219             :     // The frame is not setup, so it'd be hard to iterate the stack. Bailout.
     220             :     return false;
     221             :   }
     222             : 
     223             :   i::ExternalCallbackScope* scope = isolate->external_callback_scope();
     224             :   i::Address handler = i::Isolate::handler(isolate->thread_local_top());
     225             :   // If there is a handler on top of the external callback scope then
     226             :   // we have already entrered JavaScript again and the external callback
     227             :   // is not the top function.
     228       78336 :   if (scope && scope->scope_address() < handler) {
     229             :     i::Address* external_callback_entry_ptr =
     230             :         scope->callback_entrypoint_address();
     231             :     sample_info->external_callback_entry =
     232             :         external_callback_entry_ptr == nullptr
     233             :             ? nullptr
     234       16891 :             : reinterpret_cast<void*>(*external_callback_entry_ptr);
     235             :   }
     236             : 
     237       61378 :   i::SafeStackFrameIterator it(isolate, reinterpret_cast<i::Address>(regs->fp),
     238       61378 :                                reinterpret_cast<i::Address>(regs->sp),
     239      122756 :                                js_entry_sp);
     240       61378 :   if (it.done()) return true;
     241             : 
     242             :   size_t i = 0;
     243      121510 :   if (record_c_entry_frame == kIncludeCEntryFrame &&
     244       42695 :       (it.top_frame_type() == internal::StackFrame::EXIT ||
     245             :        it.top_frame_type() == internal::StackFrame::BUILTIN_EXIT)) {
     246       18166 :     frames[i++] = reinterpret_cast<void*>(isolate->c_function());
     247             :   }
     248             :   i::RuntimeCallTimer* timer =
     249             :       isolate->counters()->runtime_call_stats()->current_timer();
     250      430695 :   for (; !it.done() && i < frames_limit; it.Advance()) {
     251      184970 :     while (timer && reinterpret_cast<i::Address>(timer) < it.frame()->fp() &&
     252             :            i < frames_limit) {
     253           0 :       frames[i++] = reinterpret_cast<void*>(timer->counter());
     254             :       timer = timer->parent();
     255             :     }
     256      184970 :     if (i == frames_limit) break;
     257      184970 :     if (it.frame()->is_interpreted()) {
     258             :       // For interpreted frames use the bytecode array pointer as the pc.
     259             :       i::InterpretedFrame* frame =
     260             :           static_cast<i::InterpretedFrame*>(it.frame());
     261             :       // Since the sampler can interrupt execution at any point the
     262             :       // bytecode_array might be garbage, so don't actually dereference it. We
     263             :       // avoid the frame->GetXXX functions since they call BytecodeArray::cast,
     264             :       // which has a heap access in its DCHECK.
     265             :       i::Address bytecode_array = i::Memory<i::Address>(
     266      342340 :           frame->fp() + i::InterpreterFrameConstants::kBytecodeArrayFromFp);
     267             :       i::Address bytecode_offset = i::Memory<i::Address>(
     268      342340 :           frame->fp() + i::InterpreterFrameConstants::kBytecodeOffsetFromFp);
     269             : 
     270             :       // If the bytecode array is a heap object and the bytecode offset is a
     271             :       // Smi, use those, otherwise fall back to using the frame's pc.
     272      171170 :       if (HAS_HEAP_OBJECT_TAG(bytecode_array) && HAS_SMI_TAG(bytecode_offset)) {
     273      171170 :         frames[i++] = reinterpret_cast<void*>(
     274      171170 :             bytecode_array + i::Internals::SmiValue(bytecode_offset));
     275      171170 :         continue;
     276             :       }
     277             :     }
     278       27600 :     frames[i++] = reinterpret_cast<void*>(it.frame()->pc());
     279             :   }
     280       60755 :   sample_info->frames_count = i;
     281       60755 :   return true;
     282             : }
     283             : 
     284             : namespace internal {
     285             : 
     286       65390 : void TickSample::Init(Isolate* isolate, const v8::RegisterState& state,
     287             :                       RecordCEntryFrame record_c_entry_frame, bool update_stats,
     288             :                       bool use_simulator_reg_state) {
     289       65390 :   v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state,
     290             :                        record_c_entry_frame, update_stats,
     291       65390 :                        use_simulator_reg_state);
     292       65390 :   if (pc == nullptr) return;
     293       64625 :   timestamp = base::TimeTicks::HighResolutionNow();
     294             : }
     295             : 
     296           0 : void TickSample::print() const {
     297           0 :   PrintF("TickSample: at %p\n", this);
     298           0 :   PrintF(" - state: %s\n", StateToString(state));
     299           0 :   PrintF(" - pc: %p\n", pc);
     300           0 :   PrintF(" - stack: (%u frames)\n", frames_count);
     301           0 :   for (unsigned i = 0; i < frames_count; i++) {
     302           0 :     PrintF("    %p\n", stack[i]);
     303             :   }
     304           0 :   PrintF(" - has_external_callback: %d\n", has_external_callback);
     305           0 :   PrintF(" - %s: %p\n",
     306           0 :          has_external_callback ? "external_callback_entry" : "tos", tos);
     307           0 :   PrintF(" - update_stats: %d\n", update_stats);
     308           0 :   PrintF("\n");
     309           0 : }
     310             : 
     311             : }  // namespace internal
     312      122036 : }  // namespace v8

Generated by: LCOV version 1.10