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::byte* ptr1, i::byte* ptr2) {
18 : const uint32_t kPageSize = 4096;
19 : uintptr_t mask = ~static_cast<uintptr_t>(kPageSize - 1);
20 706203 : 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.
26 236129 : 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 941602 : for (Pattern* pattern = patterns; pattern->bytes_count; ++pattern) {
58 2118260 : for (int* offset_ptr = pattern->offsets; *offset_ptr != -1; ++offset_ptr) {
59 : int offset = *offset_ptr;
60 2118990 : if (!offset || IsSamePage(pc, pc - offset)) {
61 : MSAN_MEMORY_IS_INITIALIZED(pc - offset, pattern->bytes_count);
62 1412712 : 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 75 : 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 :
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 : //
150 232638 : 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 232638 : this->update_stats = update_stats;
156 : SampleInfo info;
157 232638 : RegisterState regs = reg_state;
158 232638 : if (!GetStackSample(v8_isolate, ®s, record_c_entry_frame, stack,
159 232638 : 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 991 : pc = nullptr;
163 233629 : return;
164 : }
165 :
166 231647 : state = info.vm_state;
167 231647 : pc = regs.pc;
168 231647 : frames_count = static_cast<unsigned>(info.frames_count);
169 231647 : has_external_callback = info.external_callback_entry != nullptr;
170 231647 : if (has_external_callback) {
171 27615 : external_callback_entry = info.external_callback_entry;
172 204032 : } 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 197771 : tos = i::Memory::Address_at(reinterpret_cast<i::Address>(regs.sp));
179 : } else {
180 6261 : tos = nullptr;
181 : }
182 : }
183 :
184 239575 : 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 1146204 : i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
190 239575 : sample_info->frames_count = 0;
191 239575 : sample_info->vm_state = isolate->current_vm_state();
192 239575 : sample_info->external_callback_entry = nullptr;
193 239575 : if (sample_info->vm_state == GC) return true;
194 :
195 : i::Address js_entry_sp = isolate->js_entry_sp();
196 237041 : 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 236164 : 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 235053 : 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 235053 : 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 27926 : : *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 235053 : js_entry_sp);
228 235053 : if (it.done()) return true;
229 :
230 : size_t i = 0;
231 685848 : if (record_c_entry_frame == kIncludeCEntryFrame &&
232 419154 : (it.top_frame_type() == internal::StackFrame::EXIT ||
233 : it.top_frame_type() == internal::StackFrame::BUILTIN_EXIT)) {
234 204146 : frames[i++] = isolate->c_function();
235 : }
236 0 : i::RuntimeCallTimer* timer =
237 : isolate->counters()->runtime_call_stats()->current_timer();
238 501820 : for (; !it.done() && i < frames_limit; it.Advance()) {
239 271431 : while (timer && reinterpret_cast<i::Address>(timer) < it.frame()->fp() &&
240 : i < frames_limit) {
241 0 : frames[i++] = reinterpret_cast<i::Address>(timer->counter());
242 : timer = timer->parent();
243 : }
244 271431 : if (i == frames_limit) break;
245 542862 : if (!it.frame()->is_interpreted()) {
246 181068 : frames[i++] = it.frame()->pc();
247 90534 : continue;
248 : }
249 : // For interpreted frames use the bytecode array pointer as the pc.
250 180897 : i::InterpretedFrame* frame = static_cast<i::InterpretedFrame*>(it.frame());
251 : // Since the sampler can interrupt execution at any point the
252 : // bytecode_array might be garbage, so don't dereference it.
253 : i::Address bytecode_array =
254 180897 : reinterpret_cast<i::Address>(frame->GetBytecodeArray()) -
255 180897 : i::kHeapObjectTag;
256 361794 : frames[i++] = bytecode_array + i::BytecodeArray::kHeaderSize +
257 361794 : frame->GetBytecodeOffset();
258 : }
259 230389 : sample_info->frames_count = i;
260 230389 : return true;
261 : }
262 :
263 : namespace internal {
264 :
265 232617 : void TickSample::Init(Isolate* isolate, const v8::RegisterState& state,
266 : RecordCEntryFrame record_c_entry_frame, bool update_stats,
267 : bool use_simulator_reg_state) {
268 : v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state,
269 : record_c_entry_frame, update_stats,
270 232617 : use_simulator_reg_state);
271 465234 : if (pc == nullptr) return;
272 231343 : timestamp = base::TimeTicks::HighResolutionNow();
273 : }
274 :
275 : } // namespace internal
276 : } // namespace v8
|