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
IsSamePage(i::byte * ptr1,i::byte * ptr2)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.
IsNoFrameRegion(i::Address address)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
FillRegisters(Isolate * isolate,v8::RegisterState * state)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 //
Init(Isolate * v8_isolate,const RegisterState & reg_state,RecordCEntryFrame record_c_entry_frame,bool update_stats,bool use_simulator_reg_state)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
GetStackSample(Isolate * v8_isolate,RegisterState * regs,RecordCEntryFrame record_c_entry_frame,void ** frames,size_t frames_limit,v8::SampleInfo * sample_info,bool use_simulator_reg_state)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
Init(Isolate * isolate,const v8::RegisterState & state,RecordCEntryFrame record_c_entry_frame,bool update_stats,bool use_simulator_reg_state)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