| OLD | NEW |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/profiler/tick-sample.h" | 5 #include "src/profiler/tick-sample.h" |
| 6 | 6 |
| 7 #include "include/v8-profiler.h" | 7 #include "include/v8-profiler.h" |
| 8 #include "src/frames-inl.h" | 8 #include "src/frames-inl.h" |
| 9 #include "src/msan.h" | 9 #include "src/msan.h" |
| 10 #include "src/simulator.h" | 10 #include "src/simulator.h" |
| 11 #include "src/vm-state-inl.h" | 11 #include "src/vm-state-inl.h" |
| 12 | 12 |
| 13 namespace v8 { | 13 namespace v8 { |
| 14 | |
| 15 namespace { | 14 namespace { |
| 16 | 15 |
| 17 bool IsSamePage(i::byte* ptr1, i::byte* ptr2) { | 16 bool IsSamePage(i::byte* ptr1, i::byte* ptr2) { |
| 18 const uint32_t kPageSize = 4096; | 17 const uint32_t kPageSize = 4096; |
| 19 uintptr_t mask = ~static_cast<uintptr_t>(kPageSize - 1); | 18 uintptr_t mask = ~static_cast<uintptr_t>(kPageSize - 1); |
| 20 return (reinterpret_cast<uintptr_t>(ptr1) & mask) == | 19 return (reinterpret_cast<uintptr_t>(ptr1) & mask) == |
| 21 (reinterpret_cast<uintptr_t>(ptr2) & mask); | 20 (reinterpret_cast<uintptr_t>(ptr2) & mask); |
| 22 } | 21 } |
| 23 | 22 |
| 24 // Check if the code at specified address could potentially be a | 23 // Check if the code at specified address could potentially be a |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 70 if (!memcmp(pc, pattern->bytes + offset, pattern->bytes_count - offset)) | 69 if (!memcmp(pc, pattern->bytes + offset, pattern->bytes_count - offset)) |
| 71 return true; | 70 return true; |
| 72 } | 71 } |
| 73 } | 72 } |
| 74 } | 73 } |
| 75 return false; | 74 return false; |
| 76 } | 75 } |
| 77 | 76 |
| 78 } // namespace | 77 } // namespace |
| 79 | 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 == NULL) return false; |
| 95 #if V8_TARGET_ARCH_ARM |
| 96 if (!simulator->has_bad_pc()) { |
| 97 state->pc = reinterpret_cast<Address>(simulator->get_pc()); |
| 98 } |
| 99 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); |
| 100 state->fp = |
| 101 reinterpret_cast<Address>(simulator->get_register(Simulator::r11)); |
| 102 #elif V8_TARGET_ARCH_ARM64 |
| 103 state->pc = reinterpret_cast<Address>(simulator->pc()); |
| 104 state->sp = reinterpret_cast<Address>(simulator->sp()); |
| 105 state->fp = reinterpret_cast<Address>(simulator->fp()); |
| 106 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 |
| 107 if (!simulator->has_bad_pc()) { |
| 108 state->pc = reinterpret_cast<Address>(simulator->get_pc()); |
| 109 } |
| 110 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); |
| 111 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); |
| 112 #elif V8_TARGET_ARCH_PPC |
| 113 if (!simulator->has_bad_pc()) { |
| 114 state->pc = reinterpret_cast<Address>(simulator->get_pc()); |
| 115 } |
| 116 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); |
| 117 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); |
| 118 #elif V8_TARGET_ARCH_S390 |
| 119 if (!simulator->has_bad_pc()) { |
| 120 state->pc = reinterpret_cast<Address>(simulator->get_pc()); |
| 121 } |
| 122 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); |
| 123 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); |
| 124 #endif |
| 125 if (state->sp == 0 || state->fp == 0) { |
| 126 // It possible that the simulator is interrupted while it is updating |
| 127 // the sp or fp register. ARM64 simulator does this in two steps: |
| 128 // first setting it to zero and then setting it to the new value. |
| 129 // Bailout if sp/fp doesn't contain the new value. |
| 130 // |
| 131 // FIXME: The above doesn't really solve the issue. |
| 132 // If a 64-bit target is executed on a 32-bit host even the final |
| 133 // write is non-atomic, so it might obtain a half of the result. |
| 134 // Moreover as long as the register set code uses memcpy (as of now), |
| 135 // it is not guaranteed to be atomic even when both host and target |
| 136 // are of same bitness. |
| 137 return false; |
| 138 } |
| 139 return true; |
| 140 } |
| 141 #endif // USE_SIMULATOR |
| 142 |
| 143 } // namespace |
| 144 } // namespace internal |
| 145 |
| 80 // | 146 // |
| 81 // StackTracer implementation | 147 // StackTracer implementation |
| 82 // | 148 // |
| 83 DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate, | 149 DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate, |
| 84 const RegisterState& regs, | 150 const RegisterState& reg_state, |
| 85 RecordCEntryFrame record_c_entry_frame, | 151 RecordCEntryFrame record_c_entry_frame, |
| 86 bool update_stats) { | 152 bool update_stats, |
| 153 bool use_simulator_reg_state) { |
| 87 this->update_stats = update_stats; | 154 this->update_stats = update_stats; |
| 88 | |
| 89 SampleInfo info; | 155 SampleInfo info; |
| 90 if (GetStackSample(v8_isolate, const_cast<RegisterState&>(regs), | 156 RegisterState regs = reg_state; |
| 91 record_c_entry_frame, reinterpret_cast<void**>(&stack[0]), | 157 if (!GetStackSample(v8_isolate, ®s, record_c_entry_frame, stack, |
| 92 kMaxFramesCount, &info)) { | 158 kMaxFramesCount, &info, use_simulator_reg_state)) { |
| 93 state = info.vm_state; | |
| 94 pc = regs.pc; | |
| 95 frames_count = static_cast<unsigned>(info.frames_count); | |
| 96 has_external_callback = info.external_callback_entry != nullptr; | |
| 97 if (has_external_callback) { | |
| 98 external_callback_entry = info.external_callback_entry; | |
| 99 } else if (frames_count) { | |
| 100 // sp register may point at an arbitrary place in memory, make | |
| 101 // sure MSAN doesn't complain about it. | |
| 102 MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(void*)); | |
| 103 // Sample potential return address value for frameless invocation of | |
| 104 // stubs (we'll figure out later, if this value makes sense). | |
| 105 tos = i::Memory::Address_at(reinterpret_cast<i::Address>(regs.sp)); | |
| 106 } else { | |
| 107 tos = nullptr; | |
| 108 } | |
| 109 } else { | |
| 110 // It is executing JS but failed to collect a stack trace. | 159 // It is executing JS but failed to collect a stack trace. |
| 111 // Mark the sample as spoiled. | 160 // Mark the sample as spoiled. |
| 112 pc = nullptr; | 161 pc = nullptr; |
| 162 return; |
| 163 } |
| 164 |
| 165 state = info.vm_state; |
| 166 pc = regs.pc; |
| 167 frames_count = static_cast<unsigned>(info.frames_count); |
| 168 has_external_callback = info.external_callback_entry != nullptr; |
| 169 if (has_external_callback) { |
| 170 external_callback_entry = info.external_callback_entry; |
| 171 } else if (frames_count) { |
| 172 // sp register may point at an arbitrary place in memory, make |
| 173 // sure MSAN doesn't complain about it. |
| 174 MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(void*)); |
| 175 // Sample potential return address value for frameless invocation of |
| 176 // stubs (we'll figure out later, if this value makes sense). |
| 177 tos = i::Memory::Address_at(reinterpret_cast<i::Address>(regs.sp)); |
| 178 } else { |
| 179 tos = nullptr; |
| 113 } | 180 } |
| 114 } | 181 } |
| 115 | 182 |
| 116 bool TickSample::GetStackSample(Isolate* v8_isolate, const RegisterState& regs, | 183 bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs, |
| 117 RecordCEntryFrame record_c_entry_frame, | 184 RecordCEntryFrame record_c_entry_frame, |
| 118 void** frames, size_t frames_limit, | 185 void** frames, size_t frames_limit, |
| 119 v8::SampleInfo* sample_info) { | 186 v8::SampleInfo* sample_info, |
| 187 bool use_simulator_reg_state) { |
| 120 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); | 188 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); |
| 121 sample_info->frames_count = 0; | 189 sample_info->frames_count = 0; |
| 122 sample_info->vm_state = isolate->current_vm_state(); | 190 sample_info->vm_state = isolate->current_vm_state(); |
| 123 sample_info->external_callback_entry = nullptr; | 191 sample_info->external_callback_entry = nullptr; |
| 124 if (sample_info->vm_state == GC) return true; | 192 if (sample_info->vm_state == GC) return true; |
| 125 | 193 |
| 126 i::Address js_entry_sp = isolate->js_entry_sp(); | 194 i::Address js_entry_sp = isolate->js_entry_sp(); |
| 127 if (js_entry_sp == nullptr) return true; // Not executing JS now. | 195 if (js_entry_sp == nullptr) return true; // Not executing JS now. |
| 128 DCHECK(regs.sp); | |
| 129 | 196 |
| 130 if (regs.pc && IsNoFrameRegion(static_cast<i::Address>(regs.pc))) { | 197 #if defined(USE_SIMULATOR) |
| 131 // Can't collect stack. | 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 if (regs->pc && IsNoFrameRegion(static_cast<i::Address>(regs->pc))) { |
| 207 // The frame is not setup, so it'd be hard to iterate the stack. Bailout. |
| 132 return false; | 208 return false; |
| 133 } | 209 } |
| 134 | 210 |
| 135 i::ExternalCallbackScope* scope = isolate->external_callback_scope(); | 211 i::ExternalCallbackScope* scope = isolate->external_callback_scope(); |
| 136 i::Address handler = i::Isolate::handler(isolate->thread_local_top()); | 212 i::Address handler = i::Isolate::handler(isolate->thread_local_top()); |
| 137 // If there is a handler on top of the external callback scope then | 213 // If there is a handler on top of the external callback scope then |
| 138 // we have already entrered JavaScript again and the external callback | 214 // we have already entrered JavaScript again and the external callback |
| 139 // is not the top function. | 215 // is not the top function. |
| 140 if (scope && scope->scope_address() < handler) { | 216 if (scope && scope->scope_address() < handler) { |
| 141 sample_info->external_callback_entry = | 217 sample_info->external_callback_entry = |
| 142 *scope->callback_entrypoint_address(); | 218 *scope->callback_entrypoint_address(); |
| 143 } | 219 } |
| 144 | 220 |
| 145 i::SafeStackFrameIterator it(isolate, reinterpret_cast<i::Address>(regs.fp), | 221 i::SafeStackFrameIterator it(isolate, reinterpret_cast<i::Address>(regs->fp), |
| 146 reinterpret_cast<i::Address>(regs.sp), | 222 reinterpret_cast<i::Address>(regs->sp), |
| 147 js_entry_sp); | 223 js_entry_sp); |
| 148 | 224 |
| 149 // If at this point iterator does not see any frames, | 225 // If at this point iterator does not see any frames, |
| 150 // is usually means something is wrong with the FP, | 226 // is usually means something is wrong with the FP, |
| 151 // e.g. it is used as a general purpose register in the function. | 227 // e.g. it is used as a general purpose register in the function. |
| 152 // Bailout. | 228 // Bailout. |
| 153 if (it.done()) return false; | 229 if (it.done()) return false; |
| 154 | 230 |
| 155 size_t i = 0; | 231 size_t i = 0; |
| 156 if (record_c_entry_frame == kIncludeCEntryFrame && | 232 if (record_c_entry_frame == kIncludeCEntryFrame && |
| (...skipping 16 matching lines...) Expand all Loading... |
| 173 frames[i++] = bytecode_array + i::BytecodeArray::kHeaderSize + | 249 frames[i++] = bytecode_array + i::BytecodeArray::kHeaderSize + |
| 174 frame->GetBytecodeOffset(); | 250 frame->GetBytecodeOffset(); |
| 175 } | 251 } |
| 176 sample_info->frames_count = i; | 252 sample_info->frames_count = i; |
| 177 return true; | 253 return true; |
| 178 } | 254 } |
| 179 | 255 |
| 180 namespace internal { | 256 namespace internal { |
| 181 | 257 |
| 182 void TickSample::Init(Isolate* isolate, const v8::RegisterState& state, | 258 void TickSample::Init(Isolate* isolate, const v8::RegisterState& state, |
| 183 RecordCEntryFrame record_c_entry_frame, | 259 RecordCEntryFrame record_c_entry_frame, bool update_stats, |
| 184 bool update_stats) { | 260 bool use_simulator_reg_state) { |
| 185 v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state, | 261 v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state, |
| 186 record_c_entry_frame, update_stats); | 262 record_c_entry_frame, update_stats, |
| 263 use_simulator_reg_state); |
| 187 if (pc == nullptr) return; | 264 if (pc == nullptr) return; |
| 188 timestamp = base::TimeTicks::HighResolutionNow(); | 265 timestamp = base::TimeTicks::HighResolutionNow(); |
| 189 } | 266 } |
| 190 | 267 |
| 191 #if defined(USE_SIMULATOR) | |
| 192 bool SimulatorHelper::FillRegisters(Isolate* isolate, | |
| 193 v8::RegisterState* state) { | |
| 194 Simulator* simulator = isolate->thread_local_top()->simulator_; | |
| 195 // Check if there is active simulator. | |
| 196 if (simulator == NULL) return false; | |
| 197 #if V8_TARGET_ARCH_ARM | |
| 198 if (!simulator->has_bad_pc()) { | |
| 199 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
| 200 } | |
| 201 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
| 202 state->fp = | |
| 203 reinterpret_cast<Address>(simulator->get_register(Simulator::r11)); | |
| 204 #elif V8_TARGET_ARCH_ARM64 | |
| 205 state->pc = reinterpret_cast<Address>(simulator->pc()); | |
| 206 state->sp = reinterpret_cast<Address>(simulator->sp()); | |
| 207 state->fp = reinterpret_cast<Address>(simulator->fp()); | |
| 208 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 | |
| 209 if (!simulator->has_bad_pc()) { | |
| 210 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
| 211 } | |
| 212 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
| 213 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); | |
| 214 #elif V8_TARGET_ARCH_PPC | |
| 215 if (!simulator->has_bad_pc()) { | |
| 216 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
| 217 } | |
| 218 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
| 219 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); | |
| 220 #elif V8_TARGET_ARCH_S390 | |
| 221 if (!simulator->has_bad_pc()) { | |
| 222 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
| 223 } | |
| 224 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
| 225 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); | |
| 226 #endif | |
| 227 if (state->sp == 0 || state->fp == 0) { | |
| 228 // It possible that the simulator is interrupted while it is updating | |
| 229 // the sp or fp register. ARM64 simulator does this in two steps: | |
| 230 // first setting it to zero and then setting it to the new value. | |
| 231 // Bailout if sp/fp doesn't contain the new value. | |
| 232 // | |
| 233 // FIXME: The above doesn't really solve the issue. | |
| 234 // If a 64-bit target is executed on a 32-bit host even the final | |
| 235 // write is non-atomic, so it might obtain a half of the result. | |
| 236 // Moreover as long as the register set code uses memcpy (as of now), | |
| 237 // it is not guaranteed to be atomic even when both host and target | |
| 238 // are of same bitness. | |
| 239 return false; | |
| 240 } | |
| 241 return true; | |
| 242 } | |
| 243 #endif // USE_SIMULATOR | |
| 244 | |
| 245 } // namespace internal | 268 } // namespace internal |
| 246 } // namespace v8 | 269 } // namespace v8 |
| OLD | NEW |