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 return (ptr1 & mask) == (ptr2 & mask); 21 } 22 23 // Check if the code at specified address could potentially be a 24 // frame setup code. 25 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 i::byte* pc = reinterpret_cast<i::byte*>(address); 56 for (Pattern* pattern = patterns; pattern->bytes_count; ++pattern) { 57 for (int* offset_ptr = pattern->offsets; *offset_ptr != -1; ++offset_ptr) { 58 int offset = *offset_ptr; 59 if (!offset || IsSamePage(address, address - offset)) { 60 MSAN_MEMORY_IS_INITIALIZED(pc - offset, pattern->bytes_count); 61 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 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 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 this->update_stats = update_stats; 154 SampleInfo info; 155 RegisterState regs = reg_state; 156 if (!GetStackSample(v8_isolate, ®s, record_c_entry_frame, stack, 157 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 pc = nullptr; 161 return; 162 } 163 164 state = info.vm_state; 165 pc = regs.pc; 166 frames_count = static_cast<unsigned>(info.frames_count); 167 has_external_callback = info.external_callback_entry != nullptr; 168 if (has_external_callback) { 169 external_callback_entry = info.external_callback_entry; 170 } 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 i::Memory<i::Address>(reinterpret_cast<i::Address>(regs.sp))); 178 } else { 179 tos = nullptr; 180 } 181 } 182 183 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 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); 189 sample_info->frames_count = 0; 190 sample_info->vm_state = isolate->current_vm_state(); 191 sample_info->external_callback_entry = nullptr; 192 if (sample_info->vm_state == GC) return true; 193 194 i::Address js_entry_sp = isolate->js_entry_sp(); 195 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 if (regs->pc && 209 isolate->heap()->memory_allocator()->code_range()->contains( 210 reinterpret_cast<i::Address>(regs->pc)) && 211 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 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 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 : 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 js_entry_sp); 233 if (it.done()) return true; 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++] = reinterpret_cast<void*>(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<void*>(timer->counter()); 247 timer = timer->parent(); 248 } 249 if (i == frames_limit) break; 250 if (it.frame()->is_interpreted()) { 251 // For interpreted frames use the bytecode array pointer as the pc. 252 i::InterpretedFrame* frame = 253 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::Object* bytecode_array = i::Memory<i::Object*>( 259 frame->fp() + i::InterpreterFrameConstants::kBytecodeArrayFromFp); 260 i::Object* bytecode_offset = i::Memory<i::Object*>( 261 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 if (HAS_HEAP_OBJECT_TAG(bytecode_array) && HAS_SMI_TAG(bytecode_offset)) { 266 frames[i++] = reinterpret_cast<void*>( 267 reinterpret_cast<i::Address>(bytecode_array) + 268 i::Internals::SmiValue(bytecode_offset)); 269 continue; 270 } 271 } 272 frames[i++] = reinterpret_cast<void*>(it.frame()->pc()); 273 } 274 sample_info->frames_count = i; 275 return true; 276 } 277 278 namespace internal { 279 280 void TickSample::Init(Isolate* isolate, const v8::RegisterState& state, 281 RecordCEntryFrame record_c_entry_frame, bool update_stats, 282 bool use_simulator_reg_state) { 283 v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state, 284 record_c_entry_frame, update_stats, 285 use_simulator_reg_state); 286 if (pc == nullptr) return; 287 timestamp = base::TimeTicks::HighResolutionNow(); 288 } 289 290 } // namespace internal 291 } // namespace v8 292