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