Line data Source code
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 39 : return (ptr1 & mask) == (ptr2 & mask);
21 : }
22 :
23 : // Check if the code at specified address could potentially be a
24 : // frame setup code.
25 13 : 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 13 : i::byte* pc = reinterpret_cast<i::byte*>(address);
56 52 : for (Pattern* pattern = patterns; pattern->bytes_count; ++pattern) {
57 117 : for (int* offset_ptr = pattern->offsets; *offset_ptr != -1; ++offset_ptr) {
58 : int offset = *offset_ptr;
59 117 : if (!offset || IsSamePage(address, address - offset)) {
60 : MSAN_MEMORY_IS_INITIALIZED(pc - offset, pattern->bytes_count);
61 75 : 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 3 : 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 27840 : 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 27840 : this->update_stats = update_stats;
154 : SampleInfo info;
155 27840 : RegisterState regs = reg_state;
156 27840 : if (!GetStackSample(v8_isolate, ®s, record_c_entry_frame, stack,
157 27840 : 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 0 : pc = nullptr;
161 27840 : return;
162 : }
163 :
164 27840 : state = info.vm_state;
165 27840 : pc = regs.pc;
166 27840 : frames_count = static_cast<unsigned>(info.frames_count);
167 27840 : has_external_callback = info.external_callback_entry != nullptr;
168 27840 : if (has_external_callback) {
169 12566 : external_callback_entry = info.external_callback_entry;
170 15274 : } 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 12626 : i::Memory<i::Address>(reinterpret_cast<i::Address>(regs.sp)));
178 : } else {
179 2648 : tos = nullptr;
180 : }
181 : }
182 :
183 27855 : 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 92836 : i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
189 27855 : sample_info->frames_count = 0;
190 27855 : sample_info->vm_state = isolate->current_vm_state();
191 27855 : sample_info->external_callback_entry = nullptr;
192 27855 : if (sample_info->vm_state == GC) return true;
193 :
194 : i::Address js_entry_sp = isolate->js_entry_sp();
195 26837 : 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 50591 : if (regs->pc &&
209 25283 : isolate->heap()->memory_allocator()->code_range().contains(
210 25321 : reinterpret_cast<i::Address>(regs->pc)) &&
211 13 : 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 25308 : 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 37992 : 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 12579 : : 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 25308 : js_entry_sp);
233 25308 : if (it.done()) return true;
234 :
235 : size_t i = 0;
236 75523 : if (record_c_entry_frame == kIncludeCEntryFrame &&
237 37389 : (it.top_frame_type() == internal::StackFrame::EXIT ||
238 : it.top_frame_type() == internal::StackFrame::BUILTIN_EXIT)) {
239 12836 : frames[i++] = reinterpret_cast<void*>(isolate->c_function());
240 : }
241 0 : i::RuntimeCallTimer* timer =
242 : isolate->counters()->runtime_call_stats()->current_timer();
243 76454 : for (; !it.done() && i < frames_limit; it.Advance()) {
244 51247 : while (timer && reinterpret_cast<i::Address>(timer) < it.frame()->fp() &&
245 : i < frames_limit) {
246 0 : frames[i++] = reinterpret_cast<void*>(timer->counter());
247 : timer = timer->parent();
248 : }
249 51247 : if (i == frames_limit) break;
250 102494 : if (it.frame()->is_interpreted()) {
251 : // For interpreted frames use the bytecode array pointer as the pc.
252 : i::InterpretedFrame* frame =
253 40328 : 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::Address bytecode_array = i::Memory<i::Address>(
259 80656 : frame->fp() + i::InterpreterFrameConstants::kBytecodeArrayFromFp);
260 : i::Address bytecode_offset = i::Memory<i::Address>(
261 80656 : 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 40328 : if (HAS_HEAP_OBJECT_TAG(bytecode_array) && HAS_SMI_TAG(bytecode_offset)) {
266 40328 : frames[i++] = reinterpret_cast<void*>(
267 40328 : bytecode_array + i::Internals::SmiValue(bytecode_offset));
268 40328 : continue;
269 : }
270 : }
271 21838 : frames[i++] = reinterpret_cast<void*>(it.frame()->pc());
272 : }
273 25207 : sample_info->frames_count = i;
274 25207 : return true;
275 : }
276 :
277 : namespace internal {
278 :
279 27825 : void TickSample::Init(Isolate* isolate, const v8::RegisterState& state,
280 : RecordCEntryFrame record_c_entry_frame, bool update_stats,
281 : bool use_simulator_reg_state) {
282 : v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state,
283 : record_c_entry_frame, update_stats,
284 27825 : use_simulator_reg_state);
285 55650 : if (pc == nullptr) return;
286 27075 : timestamp = base::TimeTicks::HighResolutionNow();
287 : }
288 :
289 0 : void TickSample::print() const {
290 0 : PrintF("TickSample: at %p\n", this);
291 0 : PrintF(" - state: %s\n", StateToString(state));
292 0 : PrintF(" - pc: %p\n", pc);
293 0 : PrintF(" - stack: (%u frames)\n", frames_count);
294 0 : for (unsigned i = 0; i < frames_count; i++) {
295 0 : PrintF(" %p\n", stack[i]);
296 : }
297 0 : PrintF(" - has_external_callback: %d\n", has_external_callback);
298 : PrintF(" - %s: %p\n",
299 0 : has_external_callback ? "external_callback_entry" : "tos", tos);
300 0 : PrintF(" - update_stats: %d\n", update_stats);
301 0 : PrintF("\n");
302 0 : }
303 :
304 : } // namespace internal
305 183867 : } // namespace v8
|