OLD | NEW |
(Empty) | |
| 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 |
| 23 |
| 24 // Check if the code at specified address could potentially be a |
| 25 // frame setup code. |
| 26 bool IsNoFrameRegion(Address address) { |
| 27 struct Pattern { |
| 28 int bytes_count; |
| 29 byte bytes[8]; |
| 30 int offsets[4]; |
| 31 }; |
| 32 byte* pc = reinterpret_cast<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 |
| 81 // |
| 82 // StackTracer implementation |
| 83 // |
| 84 DISABLE_ASAN void TickSample::Init(Isolate* isolate, |
| 85 const v8::RegisterState& regs, |
| 86 RecordCEntryFrame record_c_entry_frame, |
| 87 bool update_stats) { |
| 88 timestamp = base::TimeTicks::HighResolutionNow(); |
| 89 pc = reinterpret_cast<Address>(regs.pc); |
| 90 state = isolate->current_vm_state(); |
| 91 this->update_stats = update_stats; |
| 92 |
| 93 // Avoid collecting traces while doing GC. |
| 94 if (state == GC) return; |
| 95 |
| 96 Address js_entry_sp = isolate->js_entry_sp(); |
| 97 if (js_entry_sp == 0) return; // Not executing JS now. |
| 98 |
| 99 if (pc && IsNoFrameRegion(pc)) { |
| 100 // Can't collect stack. Mark the sample as spoiled. |
| 101 timestamp = base::TimeTicks(); |
| 102 pc = 0; |
| 103 return; |
| 104 } |
| 105 |
| 106 ExternalCallbackScope* scope = isolate->external_callback_scope(); |
| 107 Address handler = Isolate::handler(isolate->thread_local_top()); |
| 108 // If there is a handler on top of the external callback scope then |
| 109 // we have already entrered JavaScript again and the external callback |
| 110 // is not the top function. |
| 111 if (scope && scope->scope_address() < handler) { |
| 112 external_callback_entry = *scope->callback_entrypoint_address(); |
| 113 has_external_callback = true; |
| 114 } else { |
| 115 // sp register may point at an arbitrary place in memory, make |
| 116 // sure MSAN doesn't complain about it. |
| 117 MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(Address)); |
| 118 // Sample potential return address value for frameless invocation of |
| 119 // stubs (we'll figure out later, if this value makes sense). |
| 120 tos = Memory::Address_at(reinterpret_cast<Address>(regs.sp)); |
| 121 has_external_callback = false; |
| 122 } |
| 123 |
| 124 SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp), |
| 125 reinterpret_cast<Address>(regs.sp), js_entry_sp); |
| 126 top_frame_type = it.top_frame_type(); |
| 127 |
| 128 SampleInfo info; |
| 129 GetStackSample(isolate, regs, record_c_entry_frame, |
| 130 reinterpret_cast<void**>(&stack[0]), kMaxFramesCount, &info); |
| 131 frames_count = static_cast<unsigned>(info.frames_count); |
| 132 if (!frames_count) { |
| 133 // It is executing JS but failed to collect a stack trace. |
| 134 // Mark the sample as spoiled. |
| 135 timestamp = base::TimeTicks(); |
| 136 pc = 0; |
| 137 } |
| 138 } |
| 139 |
| 140 |
| 141 void TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs, |
| 142 RecordCEntryFrame record_c_entry_frame, |
| 143 void** frames, size_t frames_limit, |
| 144 v8::SampleInfo* sample_info) { |
| 145 sample_info->frames_count = 0; |
| 146 sample_info->vm_state = isolate->current_vm_state(); |
| 147 if (sample_info->vm_state == GC) return; |
| 148 |
| 149 Address js_entry_sp = isolate->js_entry_sp(); |
| 150 if (js_entry_sp == 0) return; // Not executing JS now. |
| 151 |
| 152 SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp), |
| 153 reinterpret_cast<Address>(regs.sp), js_entry_sp); |
| 154 size_t i = 0; |
| 155 if (record_c_entry_frame == kIncludeCEntryFrame && !it.done() && |
| 156 it.top_frame_type() == StackFrame::EXIT) { |
| 157 frames[i++] = isolate->c_function(); |
| 158 } |
| 159 while (!it.done() && i < frames_limit) { |
| 160 if (it.frame()->is_interpreted()) { |
| 161 // For interpreted frames use the bytecode array pointer as the pc. |
| 162 InterpretedFrame* frame = static_cast<InterpretedFrame*>(it.frame()); |
| 163 // Since the sampler can interrupt execution at any point the |
| 164 // bytecode_array might be garbage, so don't dereference it. |
| 165 Address bytecode_array = |
| 166 reinterpret_cast<Address>(frame->GetBytecodeArray()) - kHeapObjectTag; |
| 167 frames[i++] = bytecode_array + BytecodeArray::kHeaderSize + |
| 168 frame->GetBytecodeOffset(); |
| 169 } else { |
| 170 frames[i++] = it.frame()->pc(); |
| 171 } |
| 172 it.Advance(); |
| 173 } |
| 174 sample_info->frames_count = i; |
| 175 } |
| 176 |
| 177 |
| 178 } // namespace internal |
| 179 } // namespace v8 |
OLD | NEW |