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/asan.h"
9 : #include "src/counters.h"
10 : #include "src/frames-inl.h"
11 : #include "src/heap/heap-inl.h" // For MemoryAllocator::code_range.
12 : #include "src/msan.h"
13 : #include "src/simulator.h"
14 : #include "src/vm-state-inl.h"
15 :
16 : namespace v8 {
17 : namespace {
18 :
19 : bool IsSamePage(i::Address ptr1, i::Address ptr2) {
20 : const uint32_t kPageSize = 4096;
21 : i::Address mask = ~static_cast<i::Address>(kPageSize - 1);
22 1230 : return (ptr1 & mask) == (ptr2 & mask);
23 : }
24 :
25 : // Check if the code at specified address could potentially be a
26 : // frame setup code.
27 410 : bool IsNoFrameRegion(i::Address address) {
28 : struct Pattern {
29 : int bytes_count;
30 : i::byte bytes[8];
31 : int offsets[4];
32 : };
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 410 : i::byte* pc = reinterpret_cast<i::byte*>(address);
58 1640 : for (Pattern* pattern = patterns; pattern->bytes_count; ++pattern) {
59 3690 : for (int* offset_ptr = pattern->offsets; *offset_ptr != -1; ++offset_ptr) {
60 : int offset = *offset_ptr;
61 3690 : if (!offset || IsSamePage(address, address - offset)) {
62 : MSAN_MEMORY_IS_INITIALIZED(pc - offset, pattern->bytes_count);
63 2460 : if (!memcmp(pc - offset, pattern->bytes, pattern->bytes_count))
64 : return true;
65 : } else {
66 : // It is not safe to examine bytes on another page as it might not be
67 : // allocated thus causing a SEGFAULT.
68 : // Check the pattern part that's on the same page and
69 : // pessimistically assume it could be the entire pattern match.
70 : MSAN_MEMORY_IS_INITIALIZED(pc, pattern->bytes_count - offset);
71 0 : if (!memcmp(pc, pattern->bytes + offset, pattern->bytes_count - offset))
72 : return true;
73 : }
74 : }
75 : }
76 : return false;
77 : }
78 :
79 : } // namespace
80 :
81 : namespace internal {
82 : namespace {
83 :
84 : #if defined(USE_SIMULATOR)
85 : class SimulatorHelper {
86 : public:
87 : // Returns true if register values were successfully retrieved
88 : // from the simulator, otherwise returns false.
89 : static bool FillRegisters(Isolate* isolate, v8::RegisterState* state);
90 : };
91 :
92 : bool SimulatorHelper::FillRegisters(Isolate* isolate,
93 : v8::RegisterState* state) {
94 : Simulator* simulator = isolate->thread_local_top()->simulator_;
95 : // Check if there is active simulator.
96 : if (simulator == nullptr) return false;
97 : #if V8_TARGET_ARCH_ARM
98 : if (!simulator->has_bad_pc()) {
99 : state->pc = reinterpret_cast<void*>(simulator->get_pc());
100 : }
101 : state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
102 : state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::r11));
103 : #elif V8_TARGET_ARCH_ARM64
104 : state->pc = reinterpret_cast<void*>(simulator->pc());
105 : state->sp = reinterpret_cast<void*>(simulator->sp());
106 : state->fp = reinterpret_cast<void*>(simulator->fp());
107 : #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
108 : if (!simulator->has_bad_pc()) {
109 : state->pc = reinterpret_cast<void*>(simulator->get_pc());
110 : }
111 : state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
112 : state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::fp));
113 : #elif V8_TARGET_ARCH_PPC
114 : if (!simulator->has_bad_pc()) {
115 : state->pc = reinterpret_cast<void*>(simulator->get_pc());
116 : }
117 : state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
118 : state->fp = reinterpret_cast<void*>(simulator->get_register(Simulator::fp));
119 : #elif V8_TARGET_ARCH_S390
120 : if (!simulator->has_bad_pc()) {
121 : state->pc = reinterpret_cast<void*>(simulator->get_pc());
122 : }
123 : state->sp = reinterpret_cast<void*>(simulator->get_register(Simulator::sp));
124 : state->fp = reinterpret_cast<void*>(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 : //
150 28104 : 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 28104 : this->update_stats = update_stats;
156 : SampleInfo info;
157 28104 : RegisterState regs = reg_state;
158 28104 : if (!GetStackSample(v8_isolate, ®s, record_c_entry_frame, stack,
159 28104 : 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 0 : pc = nullptr;
163 28104 : return;
164 : }
165 :
166 28104 : state = info.vm_state;
167 28104 : pc = regs.pc;
168 28104 : frames_count = static_cast<unsigned>(info.frames_count);
169 28104 : has_external_callback = info.external_callback_entry != nullptr;
170 28104 : if (has_external_callback) {
171 12837 : external_callback_entry = info.external_callback_entry;
172 15267 : } else if (frames_count) {
173 : // sp register may point at an arbitrary place in memory, make
174 : // sure sanitizers don't complain about it.
175 : ASAN_UNPOISON_MEMORY_REGION(regs.sp, sizeof(void*));
176 : MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(void*));
177 : // Sample potential return address value for frameless invocation of
178 : // stubs (we'll figure out later, if this value makes sense).
179 25076 : tos = i::ReadUnalignedValue<void*>(reinterpret_cast<i::Address>(regs.sp));
180 : } else {
181 2729 : tos = nullptr;
182 : }
183 : }
184 :
185 28119 : bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs,
186 : RecordCEntryFrame record_c_entry_frame,
187 : void** frames, size_t frames_limit,
188 : v8::SampleInfo* sample_info,
189 : bool use_simulator_reg_state) {
190 : i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
191 28119 : sample_info->frames_count = 0;
192 28119 : sample_info->vm_state = isolate->current_vm_state();
193 28119 : sample_info->external_callback_entry = nullptr;
194 28119 : if (sample_info->vm_state == GC) return true;
195 :
196 : i::Address js_entry_sp = isolate->js_entry_sp();
197 27437 : if (js_entry_sp == 0) return true; // Not executing JS now.
198 :
199 : #if defined(USE_SIMULATOR)
200 : if (use_simulator_reg_state) {
201 : if (!i::SimulatorHelper::FillRegisters(isolate, regs)) return false;
202 : }
203 : #else
204 : USE(use_simulator_reg_state);
205 : #endif
206 : DCHECK(regs->sp);
207 :
208 : // Check whether we interrupted setup/teardown of a stack frame in JS code.
209 : // Avoid this check for C++ code, as that would trigger false positives.
210 51787 : if (regs->pc &&
211 : isolate->heap()->memory_allocator()->code_range().contains(
212 26316 : reinterpret_cast<i::Address>(regs->pc)) &&
213 410 : IsNoFrameRegion(reinterpret_cast<i::Address>(regs->pc))) {
214 : // The frame is not setup, so it'd be hard to iterate the stack. Bailout.
215 : return false;
216 : }
217 :
218 : i::ExternalCallbackScope* scope = isolate->external_callback_scope();
219 25906 : i::Address handler = i::Isolate::handler(isolate->thread_local_top());
220 : // If there is a handler on top of the external callback scope then
221 : // we have already entrered JavaScript again and the external callback
222 : // is not the top function.
223 38852 : if (scope && scope->scope_address() < handler) {
224 : i::Address* external_callback_entry_ptr =
225 : scope->callback_entrypoint_address();
226 : sample_info->external_callback_entry =
227 : external_callback_entry_ptr == nullptr
228 : ? nullptr
229 12852 : : reinterpret_cast<void*>(*external_callback_entry_ptr);
230 : }
231 :
232 : i::SafeStackFrameIterator it(isolate, reinterpret_cast<i::Address>(regs->fp),
233 : reinterpret_cast<i::Address>(regs->sp),
234 25906 : js_entry_sp);
235 25906 : if (it.done()) return true;
236 :
237 : size_t i = 0;
238 76072 : if (record_c_entry_frame == kIncludeCEntryFrame &&
239 37517 : (it.top_frame_type() == internal::StackFrame::EXIT ||
240 : it.top_frame_type() == internal::StackFrame::BUILTIN_EXIT)) {
241 13075 : frames[i++] = reinterpret_cast<void*>(isolate->c_function());
242 : }
243 0 : i::RuntimeCallTimer* timer =
244 : isolate->counters()->runtime_call_stats()->current_timer();
245 76502 : for (; !it.done() && i < frames_limit; it.Advance()) {
246 51112 : while (timer && reinterpret_cast<i::Address>(timer) < it.frame()->fp() &&
247 : i < frames_limit) {
248 0 : frames[i++] = reinterpret_cast<void*>(timer->counter());
249 : timer = timer->parent();
250 : }
251 51112 : if (i == frames_limit) break;
252 102224 : if (it.frame()->is_interpreted()) {
253 : // For interpreted frames use the bytecode array pointer as the pc.
254 : i::InterpretedFrame* frame =
255 40010 : 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 actually dereference it. We
258 : // avoid the frame->GetXXX functions since they call BytecodeArray::cast,
259 : // which has a heap access in its DCHECK.
260 : i::Address bytecode_array = i::Memory<i::Address>(
261 80020 : frame->fp() + i::InterpreterFrameConstants::kBytecodeArrayFromFp);
262 : i::Address bytecode_offset = i::Memory<i::Address>(
263 80020 : frame->fp() + i::InterpreterFrameConstants::kBytecodeOffsetFromFp);
264 :
265 : // If the bytecode array is a heap object and the bytecode offset is a
266 : // Smi, use those, otherwise fall back to using the frame's pc.
267 40010 : if (HAS_HEAP_OBJECT_TAG(bytecode_array) && HAS_SMI_TAG(bytecode_offset)) {
268 40010 : frames[i++] = reinterpret_cast<void*>(
269 40010 : bytecode_array + i::Internals::SmiValue(bytecode_offset));
270 40010 : continue;
271 : }
272 : }
273 22204 : frames[i++] = reinterpret_cast<void*>(it.frame()->pc());
274 : }
275 25390 : sample_info->frames_count = i;
276 25390 : return true;
277 : }
278 :
279 : namespace internal {
280 :
281 28089 : void TickSample::Init(Isolate* isolate, const v8::RegisterState& state,
282 : RecordCEntryFrame record_c_entry_frame, bool update_stats,
283 : bool use_simulator_reg_state) {
284 : v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state,
285 : record_c_entry_frame, update_stats,
286 28089 : use_simulator_reg_state);
287 56178 : if (pc == nullptr) return;
288 27359 : timestamp = base::TimeTicks::HighResolutionNow();
289 : }
290 :
291 0 : void TickSample::print() const {
292 0 : PrintF("TickSample: at %p\n", this);
293 0 : PrintF(" - state: %s\n", StateToString(state));
294 0 : PrintF(" - pc: %p\n", pc);
295 0 : PrintF(" - stack: (%u frames)\n", frames_count);
296 0 : for (unsigned i = 0; i < frames_count; i++) {
297 0 : PrintF(" %p\n", stack[i]);
298 : }
299 0 : PrintF(" - has_external_callback: %d\n", has_external_callback);
300 : PrintF(" - %s: %p\n",
301 0 : has_external_callback ? "external_callback_entry" : "tos", tos);
302 0 : PrintF(" - update_stats: %d\n", update_stats);
303 0 : PrintF("\n");
304 0 : }
305 :
306 : } // namespace internal
307 178779 : } // namespace v8
|