OLD | NEW |
---|---|
1 // Copyright 2013 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/profiler/tick-sample.h" | 5 #include "src/profiler/tick-sample.h" |
6 | 6 |
7 #include "include/v8-profiler.h" | 7 #include "include/v8-profiler.h" |
8 #include "src/frames-inl.h" | 8 #include "src/frames-inl.h" |
9 #include "src/msan.h" | 9 #include "src/msan.h" |
10 #include "src/simulator.h" | 10 #include "src/simulator.h" |
11 #include "src/vm-state-inl.h" | 11 #include "src/vm-state-inl.h" |
12 | 12 |
13 namespace v8 { | 13 namespace v8 { |
14 | |
15 namespace { | 14 namespace { |
16 | 15 |
17 bool IsSamePage(i::byte* ptr1, i::byte* ptr2) { | 16 bool IsSamePage(i::byte* ptr1, i::byte* ptr2) { |
18 const uint32_t kPageSize = 4096; | 17 const uint32_t kPageSize = 4096; |
19 uintptr_t mask = ~static_cast<uintptr_t>(kPageSize - 1); | 18 uintptr_t mask = ~static_cast<uintptr_t>(kPageSize - 1); |
20 return (reinterpret_cast<uintptr_t>(ptr1) & mask) == | 19 return (reinterpret_cast<uintptr_t>(ptr1) & mask) == |
21 (reinterpret_cast<uintptr_t>(ptr2) & mask); | 20 (reinterpret_cast<uintptr_t>(ptr2) & mask); |
22 } | 21 } |
23 | 22 |
24 // Check if the code at specified address could potentially be a | 23 // Check if the code at specified address could potentially be a |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
70 if (!memcmp(pc, pattern->bytes + offset, pattern->bytes_count - offset)) | 69 if (!memcmp(pc, pattern->bytes + offset, pattern->bytes_count - offset)) |
71 return true; | 70 return true; |
72 } | 71 } |
73 } | 72 } |
74 } | 73 } |
75 return false; | 74 return false; |
76 } | 75 } |
77 | 76 |
78 } // namespace | 77 } // namespace |
79 | 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 == NULL) return false; | |
95 #if V8_TARGET_ARCH_ARM | |
96 if (!simulator->has_bad_pc()) { | |
97 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
98 } | |
99 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
100 state->fp = | |
101 reinterpret_cast<Address>(simulator->get_register(Simulator::r11)); | |
102 #elif V8_TARGET_ARCH_ARM64 | |
103 state->pc = reinterpret_cast<Address>(simulator->pc()); | |
104 state->sp = reinterpret_cast<Address>(simulator->sp()); | |
105 state->fp = reinterpret_cast<Address>(simulator->fp()); | |
106 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 | |
107 if (!simulator->has_bad_pc()) { | |
108 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
109 } | |
110 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
111 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); | |
112 #elif V8_TARGET_ARCH_PPC | |
113 if (!simulator->has_bad_pc()) { | |
114 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
115 } | |
116 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
117 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); | |
118 #elif V8_TARGET_ARCH_S390 | |
119 if (!simulator->has_bad_pc()) { | |
120 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
121 } | |
122 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
123 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); | |
124 #endif | |
125 if (state->sp == 0 || state->fp == 0) { | |
126 // It possible that the simulator is interrupted while it is updating | |
127 // the sp or fp register. ARM64 simulator does this in two steps: | |
128 // first setting it to zero and then setting it to the new value. | |
129 // Bailout if sp/fp doesn't contain the new value. | |
130 // | |
131 // FIXME: The above doesn't really solve the issue. | |
132 // If a 64-bit target is executed on a 32-bit host even the final | |
133 // write is non-atomic, so it might obtain a half of the result. | |
134 // Moreover as long as the register set code uses memcpy (as of now), | |
135 // it is not guaranteed to be atomic even when both host and target | |
136 // are of same bitness. | |
137 return false; | |
138 } | |
139 return true; | |
140 } | |
141 #endif // USE_SIMULATOR | |
142 | |
143 } // namespace | |
144 } // namespace internal | |
145 | |
80 // | 146 // |
81 // StackTracer implementation | 147 // StackTracer implementation |
82 // | 148 // |
83 DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate, | 149 DISABLE_ASAN void TickSample::Init(Isolate* v8_isolate, |
84 const RegisterState& regs, | 150 const RegisterState& reg_state, |
alph
2016/08/09 19:03:41
state
lpy
2016/08/10 05:50:03
TickSample already has a different type of data me
| |
85 RecordCEntryFrame record_c_entry_frame, | 151 RecordCEntryFrame record_c_entry_frame, |
86 bool update_stats) { | 152 bool update_stats, bool update_state) { |
87 this->update_stats = update_stats; | 153 this->update_stats = update_stats; |
88 | |
89 SampleInfo info; | 154 SampleInfo info; |
90 if (GetStackSample(v8_isolate, const_cast<RegisterState&>(regs), | 155 RegisterState regs = reg_state; |
91 record_c_entry_frame, reinterpret_cast<void**>(&stack[0]), | 156 if (!GetStackSample(v8_isolate, ®s, record_c_entry_frame, stack, |
92 kMaxFramesCount, &info)) { | 157 kMaxFramesCount, &info, update_state)) { |
93 state = info.vm_state; | |
94 pc = 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 = info.external_callback_entry; | |
99 } else if (frames_count) { | |
100 // sp register may point at an arbitrary place in memory, make | |
101 // sure MSAN doesn't complain about it. | |
102 MSAN_MEMORY_IS_INITIALIZED(regs.sp, sizeof(void*)); | |
103 // Sample potential return address value for frameless invocation of | |
104 // stubs (we'll figure out later, if this value makes sense). | |
105 tos = i::Memory::Address_at(reinterpret_cast<i::Address>(regs.sp)); | |
106 } else { | |
107 tos = nullptr; | |
108 } | |
109 } else { | |
110 // It is executing JS but failed to collect a stack trace. | 158 // It is executing JS but failed to collect a stack trace. |
111 // Mark the sample as spoiled. | 159 // Mark the sample as spoiled. |
112 pc = nullptr; | 160 pc = nullptr; |
161 return; | |
162 } | |
163 | |
164 state = info.vm_state; | |
165 pc = regs.pc; | |
166 frames_count = static_cast<unsigned>(info.frames_count); | |
167 has_external_callback = info.external_callback_entry != nullptr; | |
168 if (has_external_callback) { | |
169 external_callback_entry = info.external_callback_entry; | |
170 } 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 = i::Memory::Address_at(reinterpret_cast<i::Address>(regs.sp)); | |
177 } else { | |
178 tos = nullptr; | |
113 } | 179 } |
114 } | 180 } |
115 | 181 |
116 bool TickSample::GetStackSample(Isolate* v8_isolate, const RegisterState& regs, | 182 bool TickSample::GetStackSample(Isolate* v8_isolate, RegisterState* regs, |
117 RecordCEntryFrame record_c_entry_frame, | 183 RecordCEntryFrame record_c_entry_frame, |
118 void** frames, size_t frames_limit, | 184 void** frames, size_t frames_limit, |
119 v8::SampleInfo* sample_info) { | 185 v8::SampleInfo* sample_info, |
186 bool update_state) { | |
120 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); | 187 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); |
121 sample_info->frames_count = 0; | 188 sample_info->frames_count = 0; |
122 sample_info->vm_state = isolate->current_vm_state(); | 189 sample_info->vm_state = isolate->current_vm_state(); |
123 sample_info->external_callback_entry = nullptr; | 190 sample_info->external_callback_entry = nullptr; |
124 if (sample_info->vm_state == GC) return true; | 191 if (sample_info->vm_state == GC) return true; |
125 | 192 |
126 i::Address js_entry_sp = isolate->js_entry_sp(); | 193 i::Address js_entry_sp = isolate->js_entry_sp(); |
127 if (js_entry_sp == nullptr) return true; // Not executing JS now. | 194 if (js_entry_sp == nullptr) return true; // Not executing JS now. |
128 DCHECK(regs.sp); | |
129 | 195 |
130 if (regs.pc && IsNoFrameRegion(static_cast<i::Address>(regs.pc))) { | 196 #if defined(USE_SIMULATOR) |
131 // Can't collect stack. | 197 if (update_state) { |
alph
2016/08/09 19:03:41
update_state is never referenced when not under si
lpy
2016/08/10 05:50:03
Done.
| |
198 if (!i::SimulatorHelper::FillRegisters(isolate, regs)) return false; | |
199 } | |
200 #endif | |
201 DCHECK(regs->sp); | |
202 | |
203 if (regs->pc && IsNoFrameRegion(static_cast<i::Address>(regs->pc))) { | |
204 // The frame is not setup, so it'd be hard to iterate the stack. Bailout. | |
132 return false; | 205 return false; |
133 } | 206 } |
134 | 207 |
135 i::ExternalCallbackScope* scope = isolate->external_callback_scope(); | 208 i::ExternalCallbackScope* scope = isolate->external_callback_scope(); |
136 i::Address handler = i::Isolate::handler(isolate->thread_local_top()); | 209 i::Address handler = i::Isolate::handler(isolate->thread_local_top()); |
137 // If there is a handler on top of the external callback scope then | 210 // If there is a handler on top of the external callback scope then |
138 // we have already entrered JavaScript again and the external callback | 211 // we have already entrered JavaScript again and the external callback |
139 // is not the top function. | 212 // is not the top function. |
140 if (scope && scope->scope_address() < handler) { | 213 if (scope && scope->scope_address() < handler) { |
141 sample_info->external_callback_entry = | 214 sample_info->external_callback_entry = |
142 *scope->callback_entrypoint_address(); | 215 *scope->callback_entrypoint_address(); |
143 } | 216 } |
144 | 217 |
145 i::SafeStackFrameIterator it(isolate, reinterpret_cast<i::Address>(regs.fp), | 218 i::SafeStackFrameIterator it(isolate, reinterpret_cast<i::Address>(regs->fp), |
146 reinterpret_cast<i::Address>(regs.sp), | 219 reinterpret_cast<i::Address>(regs->sp), |
147 js_entry_sp); | 220 js_entry_sp); |
148 | 221 |
149 // If at this point iterator does not see any frames, | 222 // If at this point iterator does not see any frames, |
150 // is usually means something is wrong with the FP, | 223 // is usually means something is wrong with the FP, |
151 // e.g. it is used as a general purpose register in the function. | 224 // e.g. it is used as a general purpose register in the function. |
152 // Bailout. | 225 // Bailout. |
153 if (it.done()) return false; | 226 if (it.done()) return false; |
154 | 227 |
155 size_t i = 0; | 228 size_t i = 0; |
156 if (record_c_entry_frame == kIncludeCEntryFrame && | 229 if (record_c_entry_frame == kIncludeCEntryFrame && |
(...skipping 16 matching lines...) Expand all Loading... | |
173 frames[i++] = bytecode_array + i::BytecodeArray::kHeaderSize + | 246 frames[i++] = bytecode_array + i::BytecodeArray::kHeaderSize + |
174 frame->GetBytecodeOffset(); | 247 frame->GetBytecodeOffset(); |
175 } | 248 } |
176 sample_info->frames_count = i; | 249 sample_info->frames_count = i; |
177 return true; | 250 return true; |
178 } | 251 } |
179 | 252 |
180 namespace internal { | 253 namespace internal { |
181 | 254 |
182 void TickSample::Init(Isolate* isolate, const v8::RegisterState& state, | 255 void TickSample::Init(Isolate* isolate, const v8::RegisterState& state, |
183 RecordCEntryFrame record_c_entry_frame, | 256 RecordCEntryFrame record_c_entry_frame, bool update_stats, |
184 bool update_stats) { | 257 bool update_state) { |
185 v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state, | 258 v8::TickSample::Init(reinterpret_cast<v8::Isolate*>(isolate), state, |
186 record_c_entry_frame, update_stats); | 259 record_c_entry_frame, update_stats, update_state); |
187 if (pc == nullptr) return; | 260 if (pc == nullptr) return; |
188 timestamp = base::TimeTicks::HighResolutionNow(); | 261 timestamp = base::TimeTicks::HighResolutionNow(); |
189 } | 262 } |
190 | 263 |
191 #if defined(USE_SIMULATOR) | |
192 bool SimulatorHelper::FillRegisters(Isolate* isolate, | |
193 v8::RegisterState* state) { | |
194 Simulator* simulator = isolate->thread_local_top()->simulator_; | |
195 // Check if there is active simulator. | |
196 if (simulator == NULL) return false; | |
197 #if V8_TARGET_ARCH_ARM | |
198 if (!simulator->has_bad_pc()) { | |
199 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
200 } | |
201 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
202 state->fp = | |
203 reinterpret_cast<Address>(simulator->get_register(Simulator::r11)); | |
204 #elif V8_TARGET_ARCH_ARM64 | |
205 state->pc = reinterpret_cast<Address>(simulator->pc()); | |
206 state->sp = reinterpret_cast<Address>(simulator->sp()); | |
207 state->fp = reinterpret_cast<Address>(simulator->fp()); | |
208 #elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 | |
209 if (!simulator->has_bad_pc()) { | |
210 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
211 } | |
212 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
213 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); | |
214 #elif V8_TARGET_ARCH_PPC | |
215 if (!simulator->has_bad_pc()) { | |
216 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
217 } | |
218 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
219 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); | |
220 #elif V8_TARGET_ARCH_S390 | |
221 if (!simulator->has_bad_pc()) { | |
222 state->pc = reinterpret_cast<Address>(simulator->get_pc()); | |
223 } | |
224 state->sp = reinterpret_cast<Address>(simulator->get_register(Simulator::sp)); | |
225 state->fp = reinterpret_cast<Address>(simulator->get_register(Simulator::fp)); | |
226 #endif | |
227 if (state->sp == 0 || state->fp == 0) { | |
228 // It possible that the simulator is interrupted while it is updating | |
229 // the sp or fp register. ARM64 simulator does this in two steps: | |
230 // first setting it to zero and then setting it to the new value. | |
231 // Bailout if sp/fp doesn't contain the new value. | |
232 // | |
233 // FIXME: The above doesn't really solve the issue. | |
234 // If a 64-bit target is executed on a 32-bit host even the final | |
235 // write is non-atomic, so it might obtain a half of the result. | |
236 // Moreover as long as the register set code uses memcpy (as of now), | |
237 // it is not guaranteed to be atomic even when both host and target | |
238 // are of same bitness. | |
239 return false; | |
240 } | |
241 return true; | |
242 } | |
243 #endif // USE_SIMULATOR | |
244 | |
245 } // namespace internal | 264 } // namespace internal |
246 } // namespace v8 | 265 } // namespace v8 |
OLD | NEW |