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
IsSamePage(byte * ptr1,byte * ptr2)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 // Check if the code at specified address could potentially be a
24 // frame setup code.
IsNoFrameRegion(Address address)25 bool IsNoFrameRegion(Address address) {
26 struct Pattern {
27 int bytes_count;
28 byte bytes[8];
29 int offsets[4];
30 };
31 byte* pc = reinterpret_cast<byte*>(address);
32 static Pattern patterns[] = {
33 #if V8_HOST_ARCH_IA32
34 // push %ebp
35 // mov %esp,%ebp
36 {3, {0x55, 0x89, 0xe5}, {0, 1, -1}},
37 // pop %ebp
38 // ret N
39 {2, {0x5d, 0xc2}, {0, 1, -1}},
40 // pop %ebp
41 // ret
42 {2, {0x5d, 0xc3}, {0, 1, -1}},
43 #elif V8_HOST_ARCH_X64
44 // pushq %rbp
45 // movq %rsp,%rbp
46 {4, {0x55, 0x48, 0x89, 0xe5}, {0, 1, -1}},
47 // popq %rbp
48 // ret N
49 {2, {0x5d, 0xc2}, {0, 1, -1}},
50 // popq %rbp
51 // ret
52 {2, {0x5d, 0xc3}, {0, 1, -1}},
53 #endif
54 {0, {}, {}}
55 };
56 for (Pattern* pattern = patterns; pattern->bytes_count; ++pattern) {
57 for (int* offset_ptr = pattern->offsets; *offset_ptr != -1; ++offset_ptr) {
58 int offset = *offset_ptr;
59 if (!offset || IsSamePage(pc, pc - offset)) {
60 MSAN_MEMORY_IS_INITIALIZED(pc - offset, pattern->bytes_count);
61 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 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 //
80 // StackTracer implementation
81 //
Init(Isolate * isolate,const v8::RegisterState & regs,RecordCEntryFrame record_c_entry_frame,bool update_stats)82 DISABLE_ASAN void TickSample::Init(Isolate* isolate,
83 const v8::RegisterState& regs,
84 RecordCEntryFrame record_c_entry_frame,
85 bool update_stats) {
86 timestamp = base::TimeTicks::HighResolutionNow();
87 this->update_stats = update_stats;
88
89 SampleInfo info;
90 if (GetStackSample(isolate, regs, record_c_entry_frame,
91 reinterpret_cast<void**>(&stack[0]), kMaxFramesCount,
92 &info)) {
93 state = info.vm_state;
94 pc = static_cast<Address>(regs.pc);
95 frames_count = static_cast<unsigned>(info.frames_count);
96 has_external_callback = info.external_callback_entry != nullptr;
97 if (has_external_callback) {
98 external_callback_entry =
99 static_cast<Address>(info.external_callback_entry);
100 } else if (frames_count) {
101 // sp register may point at an arbitrary place in memory, make
102 // sure MSAN doesn't complain about it.
103 MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(Address));
104 // Sample potential return address value for frameless invocation of
105 // stubs (we'll figure out later, if this value makes sense).
106 tos = Memory::Address_at(reinterpret_cast<Address>(regs.sp));
107 } else {
108 tos = nullptr;
109 }
110 } else {
111 // It is executing JS but failed to collect a stack trace.
112 // Mark the sample as spoiled.
113 timestamp = base::TimeTicks();
114 pc = nullptr;
115 }
116 }
117
GetStackSample(Isolate * isolate,const v8::RegisterState & regs,RecordCEntryFrame record_c_entry_frame,void ** frames,size_t frames_limit,v8::SampleInfo * sample_info)118 bool TickSample::GetStackSample(Isolate* isolate, const v8::RegisterState& regs,
119 RecordCEntryFrame record_c_entry_frame,
120 void** frames, size_t frames_limit,
121 v8::SampleInfo* sample_info) {
122 sample_info->frames_count = 0;
123 sample_info->vm_state = isolate->current_vm_state();
124 sample_info->external_callback_entry = nullptr;
125 if (sample_info->vm_state == GC) return true;
126
127 Address js_entry_sp = isolate->js_entry_sp();
128 if (js_entry_sp == 0) return true; // Not executing JS now.
129
130 if (regs.pc && IsNoFrameRegion(static_cast<Address>(regs.pc))) {
131 // Can't collect stack.
132 return false;
133 }
134
135 ExternalCallbackScope* scope = isolate->external_callback_scope();
136 Address handler = Isolate::handler(isolate->thread_local_top());
137 // If there is a handler on top of the external callback scope then
138 // we have already entrered JavaScript again and the external callback
139 // is not the top function.
140 if (scope && scope->scope_address() < handler) {
141 sample_info->external_callback_entry =
142 *scope->callback_entrypoint_address();
143 }
144
145 SafeStackFrameIterator it(isolate, reinterpret_cast<Address>(regs.fp),
146 reinterpret_cast<Address>(regs.sp), js_entry_sp);
147 size_t i = 0;
148 if (record_c_entry_frame == kIncludeCEntryFrame && !it.done() &&
149 it.top_frame_type() == StackFrame::EXIT) {
150 frames[i++] = isolate->c_function();
151 }
152 while (!it.done() && i < frames_limit) {
153 if (it.frame()->is_interpreted()) {
154 // For interpreted frames use the bytecode array pointer as the pc.
155 InterpretedFrame* frame = static_cast<InterpretedFrame*>(it.frame());
156 // Since the sampler can interrupt execution at any point the
157 // bytecode_array might be garbage, so don't dereference it.
158 Address bytecode_array =
159 reinterpret_cast<Address>(frame->GetBytecodeArray()) - kHeapObjectTag;
160 frames[i++] = bytecode_array + BytecodeArray::kHeaderSize +
161 frame->GetBytecodeOffset();
162 } else {
163 frames[i++] = it.frame()->pc();
164 }
165 it.Advance();
166 }
167 sample_info->frames_count = i;
168 return true;
169 }
170
171 #if defined(USE_SIMULATOR)
FillRegisters(Isolate * isolate,v8::RegisterState * state)172 bool SimulatorHelper::FillRegisters(Isolate* isolate,
173 v8::RegisterState* state) {
174 Simulator *simulator = isolate->thread_local_top()->simulator_;
175 // Check if there is active simulator.
176 if (simulator == NULL) return false;
177 #if V8_TARGET_ARCH_ARM
178 if (!simulator->has_bad_pc()) {
179 state->pc = reinterpret_cast<Address>(simulator->get_pc());
180 }
181 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
182 state->fp = reinterpret_cast<Address>(simulator->get_register(
183 Simulator::r11));
184 #elif V8_TARGET_ARCH_ARM64
185 state->pc = reinterpret_cast<Address>(simulator->pc());
186 state->sp = reinterpret_cast<Address>(simulator->sp());
187 state->fp = reinterpret_cast<Address>(simulator->fp());
188 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
189 if (!simulator->has_bad_pc()) {
190 state->pc = reinterpret_cast<Address>(simulator->get_pc());
191 }
192 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
193 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
194 #elif V8_TARGET_ARCH_PPC
195 if (!simulator->has_bad_pc()) {
196 state->pc = reinterpret_cast<Address>(simulator->get_pc());
197 }
198 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
199 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
200 #elif V8_TARGET_ARCH_S390
201 if (!simulator->has_bad_pc()) {
202 state->pc = reinterpret_cast<Address>(simulator->get_pc());
203 }
204 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp));
205 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp));
206 #endif
207 if (state->sp == 0 || state->fp == 0) {
208 // It possible that the simulator is interrupted while it is updating
209 // the sp or fp register. ARM64 simulator does this in two steps:
210 // first setting it to zero and then setting it to the new value.
211 // Bailout if sp/fp doesn't contain the new value.
212 //
213 // FIXME: The above doesn't really solve the issue.
214 // If a 64-bit target is executed on a 32-bit host even the final
215 // write is non-atomic, so it might obtain a half of the result.
216 // Moreover as long as the register set code uses memcpy (as of now),
217 // it is not guaranteed to be atomic even when both host and target
218 // are of same bitness.
219 return false;
220 }
221 return true;
222 }
223 #endif // USE_SIMULATOR
224
225 } // namespace internal
226 } // namespace v8
227