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, ®s, 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