Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 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 "src/frames-inl.h" |
| 8 | #include "src/vm-state-inl.h" |
| 9 | |
| 10 | |
| 11 | namespace v8 { |
| 12 | namespace internal { |
| 13 | |
| 14 | namespace { |
| 15 | |
| 16 | bool IsSamePage(byte* ptr1, byte* ptr2) { |
| 17 | const uint32_t kPageSize = 4096; |
| 18 | uintptr_t mask = ~static_cast<uintptr_t>(kPageSize - 1); |
| 19 | return (reinterpret_cast<uintptr_t>(ptr1) & mask) == |
| 20 | (reinterpret_cast<uintptr_t>(ptr2) & mask); |
| 21 | } |
| 22 | |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 23 | // Check if the code at specified address could potentially be a |
| 24 | // frame setup code. |
| 25 | bool IsNoFrameRegion(Address address) { |
| 26 | struct Pattern { |
| 27 | int bytes_count; |
| 28 | byte bytes[8]; |
| 29 | int offsets[4]; |
| 30 | }; |
| 31 | byte* pc = reinterpret_cast<byte*>(address); |
| 32 | static Pattern patterns[] = { |
| 33 | #if V8_HOST_ARCH_IA32 |
| 34 | // push %ebp |
| 35 | // mov %esp,%ebp |
| 36 | {3, {0x55, 0x89, 0xe5}, {0, 1, -1}}, |
| 37 | // pop %ebp |
| 38 | // ret N |
| 39 | {2, {0x5d, 0xc2}, {0, 1, -1}}, |
| 40 | // pop %ebp |
| 41 | // ret |
| 42 | {2, {0x5d, 0xc3}, {0, 1, -1}}, |
| 43 | #elif V8_HOST_ARCH_X64 |
| 44 | // pushq %rbp |
| 45 | // movq %rsp,%rbp |
| 46 | {4, {0x55, 0x48, 0x89, 0xe5}, {0, 1, -1}}, |
| 47 | // popq %rbp |
| 48 | // ret N |
| 49 | {2, {0x5d, 0xc2}, {0, 1, -1}}, |
| 50 | // popq %rbp |
| 51 | // ret |
| 52 | {2, {0x5d, 0xc3}, {0, 1, -1}}, |
| 53 | #endif |
| 54 | {0, {}, {}} |
| 55 | }; |
| 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(pc, pc - 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 | |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 79 | // |
| 80 | // StackTracer implementation |
| 81 | // |
| 82 | DISABLE_ASAN void TickSample::Init(Isolate* isolate, |
| 83 | const v8::RegisterState& regs, |
| 84 | RecordCEntryFrame record_c_entry_frame, |
| 85 | bool update_stats) { |
| 86 | timestamp = base::TimeTicks::HighResolutionNow(); |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 87 | this->update_stats = update_stats; |
| 88 | |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 89 | SampleInfo info; |
| 90 | if (GetStackSample(isolate, regs, record_c_entry_frame, |
| 91 | reinterpret_cast<void**>(&stack[0]), kMaxFramesCount, |
| 92 | &info)) { |
| 93 | state = info.vm_state; |
| 94 | pc = static_cast<Address>(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 = |
| 99 | static_cast<Address>(info.external_callback_entry); |
| 100 | } else if (frames_count) { |
| 101 | // sp register may point at an arbitrary place in memory, make |
| 102 | // sure MSAN doesn't complain about it. |
| 103 | MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(Address)); |
| 104 | // Sample potential return address value for frameless invocation of |
| 105 | // stubs (we'll figure out later, if this value makes sense). |
| 106 | tos = Memory::Address_at(reinterpret_cast<Address>(regs.sp)); |
| 107 | } else { |
| 108 | tos = nullptr; |
| 109 | } |
| 110 | } else { |
| 111 | // It is executing JS but failed to collect a stack trace. |
| 112 | // Mark the sample as spoiled. |
| 113 | timestamp = base::TimeTicks(); |
| 114 | pc = nullptr; |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | bool TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs, |
| 119 | RecordCEntryFrame record_c_entry_frame, |
| 120 | void** frames, size_t frames_limit, |
| 121 | v8::SampleInfo* sample_info) { |
| 122 | sample_info->frames_count = 0; |
| 123 | sample_info->vm_state = isolate->current_vm_state(); |
| 124 | sample_info->external_callback_entry = nullptr; |
| 125 | if (sample_info->vm_state == GC) return true; |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 126 | |
| 127 | Address js_entry_sp = isolate->js_entry_sp(); |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 128 | if (js_entry_sp == 0) return true; // Not executing JS now. |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 129 | |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 130 | if (regs.pc && IsNoFrameRegion(static_cast<Address>(regs.pc))) { |
| 131 | // Can't collect stack. |
| 132 | return false; |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 133 | } |
| 134 | |
| 135 | ExternalCallbackScope* scope = isolate->external_callback_scope(); |
| 136 | Address handler = Isolate::handler(isolate->thread_local_top()); |
| 137 | // If there is a handler on top of the external callback scope then |
| 138 | // we have already entrered JavaScript again and the external callback |
| 139 | // is not the top function. |
| 140 | if (scope && scope->scope_address() < handler) { |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 141 | sample_info->external_callback_entry = |
| 142 | *scope->callback_entrypoint_address(); |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 143 | } |
| 144 | |
| 145 | SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp), |
| 146 | reinterpret_cast<Address>(regs.sp), js_entry_sp); |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 147 | size_t i = 0; |
| 148 | if (record_c_entry_frame == kIncludeCEntryFrame && !it.done() && |
| 149 | it.top_frame_type() == StackFrame::EXIT) { |
| 150 | frames[i++] = isolate->c_function(); |
| 151 | } |
| 152 | while (!it.done() && i < frames_limit) { |
| 153 | if (it.frame()->is_interpreted()) { |
| 154 | // For interpreted frames use the bytecode array pointer as the pc. |
| 155 | InterpretedFrame* frame = static_cast<InterpretedFrame*>(it.frame()); |
| 156 | // Since the sampler can interrupt execution at any point the |
| 157 | // bytecode_array might be garbage, so don't dereference it. |
| 158 | Address bytecode_array = |
| 159 | reinterpret_cast<Address>(frame->GetBytecodeArray()) - kHeapObjectTag; |
| 160 | frames[i++] = bytecode_array + BytecodeArray::kHeaderSize + |
| 161 | frame->GetBytecodeOffset(); |
| 162 | } else { |
| 163 | frames[i++] = it.frame()->pc(); |
| 164 | } |
| 165 | it.Advance(); |
| 166 | } |
| 167 | sample_info->frames_count = i; |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 168 | return true; |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 169 | } |
| 170 | |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 171 | #if defined(USE_SIMULATOR) |
| 172 | bool SimulatorHelper::FillRegisters(Isolate* isolate, |
| 173 | v8::RegisterState* state) { |
| 174 | Simulator *simulator = isolate->thread_local_top()->simulator_; |
| 175 | // Check if there is active simulator. |
| 176 | if (simulator == NULL) return false; |
| 177 | #if V8_TARGET_ARCH_ARM |
| 178 | if (!simulator->has_bad_pc()) { |
| 179 | state->pc = reinterpret_cast<Address>(simulator->get_pc()); |
| 180 | } |
| 181 | state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); |
| 182 | state->fp = reinterpret_cast<Address>(simulator->get_register( |
| 183 | Simulator::r11)); |
| 184 | #elif V8_TARGET_ARCH_ARM64 |
| 185 | state->pc = reinterpret_cast<Address>(simulator->pc()); |
| 186 | state->sp = reinterpret_cast<Address>(simulator->sp()); |
| 187 | state->fp = reinterpret_cast<Address>(simulator->fp()); |
| 188 | #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 |
| 189 | if (!simulator->has_bad_pc()) { |
| 190 | state->pc = reinterpret_cast<Address>(simulator->get_pc()); |
| 191 | } |
| 192 | state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); |
| 193 | state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); |
| 194 | #elif V8_TARGET_ARCH_PPC |
| 195 | if (!simulator->has_bad_pc()) { |
| 196 | state->pc = reinterpret_cast<Address>(simulator->get_pc()); |
| 197 | } |
| 198 | state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); |
| 199 | state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); |
| 200 | #elif V8_TARGET_ARCH_S390 |
| 201 | if (!simulator->has_bad_pc()) { |
| 202 | state->pc = reinterpret_cast<Address>(simulator->get_pc()); |
| 203 | } |
| 204 | state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); |
| 205 | state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); |
| 206 | #endif |
| 207 | if (state->sp == 0 || state->fp == 0) { |
| 208 | // It possible that the simulator is interrupted while it is updating |
| 209 | // the sp or fp register. ARM64 simulator does this in two steps: |
| 210 | // first setting it to zero and then setting it to the new value. |
| 211 | // Bailout if sp/fp doesn't contain the new value. |
| 212 | // |
| 213 | // FIXME: The above doesn't really solve the issue. |
| 214 | // If a 64-bit target is executed on a 32-bit host even the final |
| 215 | // write is non-atomic, so it might obtain a half of the result. |
| 216 | // Moreover as long as the register set code uses memcpy (as of now), |
| 217 | // it is not guaranteed to be atomic even when both host and target |
| 218 | // are of same bitness. |
| 219 | return false; |
| 220 | } |
| 221 | return true; |
| 222 | } |
| 223 | #endif // USE_SIMULATOR |
| 224 | |
| 225 | } // namespace internal |
| 226 | } // namespace v8 |