OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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 // Tests the sampling API in include/v8.h | |
6 | |
7 #include <string> | |
8 #include "include/v8.h" | |
9 #include "src/simulator.h" | |
10 #include "src/utils.h" | |
11 #include "src/v8.h" | |
12 #include "test/cctest/cctest.h" | |
13 | |
14 #if V8_OS_POSIX && !V8_OS_CYGWIN && !V8_OS_MACOSX | |
15 #include <ucontext.h> | |
16 | |
17 #elif V8_OS_WIN || V8_OS_CYGWIN // V8_OS_POSIX && !V8_OS_CYGWIN | |
18 #include "src/base/win32-headers.h" | |
19 | |
20 #endif // V8_OS_WIN || V8_OS_CYGWIN | |
21 | |
22 using v8::Local; | |
yurys
2014/09/23 14:57:53
style nit: we don't usually declare such using in
alph
2014/09/24 13:12:18
Done.
| |
23 using v8::internal::Address; | |
24 using v8::internal::Isolate; | |
25 | |
26 namespace { | |
27 | |
28 class Sample { | |
29 public: | |
30 enum { kFramesLimit = 255 }; | |
31 | |
32 Sample() {} | |
33 | |
34 typedef const void* const* const_iterator; | |
35 const_iterator begin() const { return data_.start(); } | |
36 const_iterator end() const { return &data_[data_.length()]; } | |
37 | |
38 int size() const { return data_.length(); } | |
39 v8::internal::Vector<void*>& data() { return data_; } | |
40 | |
41 private: | |
42 v8::internal::EmbeddedVector<void*, kFramesLimit> data_; | |
43 }; | |
44 | |
45 | |
46 // The Sample which CollectSample fills up | |
47 Sample* sample; | |
48 | |
49 // The isolate used in the test | |
50 v8::Isolate* isolate; | |
51 | |
52 // Forward declaration | |
53 // (platform specific implementation at the bottom of this file) | |
54 void FillRegisterState(v8::RegisterState* state); | |
55 | |
56 // The JavaScript calls this function when on full stack depth. | |
57 void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
58 v8::RegisterState state; | |
59 FillRegisterState(&state); | |
60 v8::SampleInfo info; | |
61 isolate->GetStackSample(state, sample->data().start(), | |
62 static_cast<size_t>(sample->size()), &info); | |
63 size_t frames_count = info.frames_count; | |
64 CHECK_LE(frames_count, static_cast<size_t>(sample->size())); | |
65 sample->data().Truncate(static_cast<int>(frames_count)); | |
66 } | |
67 | |
68 | |
69 // A JavaScript function which takes stack depth | |
70 // (minimum value 2) as an argument. | |
71 // When at the bottom of the recursion, | |
72 // the JavaScript code calls into C++ test code, | |
73 // waiting for the sampler to take a sample. | |
74 static const char* test_function = | |
75 "function func(depth) {" | |
76 " if (depth == 2) CollectSample();" | |
77 " else return func(depth - 1);" | |
78 "}"; | |
79 | |
80 } // namespace | |
81 | |
82 | |
83 #define SAMPLER_API_TESTS_BOOTSTRAP() \ | |
yurys
2014/09/23 14:57:53
style: I'd use a class rather than a macro here.
alph
2014/09/24 13:12:18
Done.
| |
84 Sample test_sample; \ | |
85 sample = &test_sample; \ | |
86 isolate = CcTest::isolate(); \ | |
87 v8::HandleScope scope(isolate); \ | |
88 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); \ | |
89 global->Set(v8::String::NewFromUtf8(isolate, "CollectSample"), \ | |
90 v8::FunctionTemplate::New(isolate, CollectSample)); \ | |
91 LocalContext env(isolate, NULL, global) | |
92 | |
93 | |
94 TEST(StackDepthIsConsistent) { | |
95 SAMPLER_API_TESTS_BOOTSTRAP(); | |
96 | |
97 std::string source(test_function); | |
98 source.append("func(8);"); | |
99 v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run(); | |
100 | |
101 CHECK_EQ(8, sample->size()); | |
102 } | |
103 | |
104 | |
105 TEST(StackDepthDoesNotExceedMaxValue) { | |
106 SAMPLER_API_TESTS_BOOTSTRAP(); | |
107 | |
108 std::string source(test_function); | |
109 source.append("func(300);"); | |
110 v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run(); | |
111 | |
112 int MAX_SIZE = Sample::kFramesLimit; | |
yurys
2014/09/23 14:57:53
inline this
alph
2014/09/24 13:12:18
Done.
| |
113 CHECK_EQ(MAX_SIZE, sample->size()); | |
114 } | |
115 | |
116 | |
117 namespace { | |
118 std::vector<v8::JitCodeEvent> inner_funcs; | |
yurys
2014/09/23 14:57:53
JitCodeEvent contains Handle<Script>, is it a glob
alph
2014/09/24 13:12:18
Done.
| |
119 std::vector<v8::JitCodeEvent> outer_funcs; | |
120 | |
121 void TestJitCodeEventHandler(const v8::JitCodeEvent* event) { | |
122 if (event->type != v8::JitCodeEvent::CODE_ADDED) return; | |
yurys
2014/09/23 14:57:53
What about v8::JitCodeEvent::CODE_MOVE? How can we
alph
2014/09/24 13:12:18
Done.
| |
123 std::string name(event->name.str, event->name.len); | |
124 if (name.find("test_sampler_api_inner") != std::string::npos) | |
125 inner_funcs.push_back(*event); | |
126 if (name.find("test_sampler_api_outer") != std::string::npos) | |
127 outer_funcs.push_back(*event); | |
128 } | |
129 | |
130 | |
131 // Note: The arguments.callee stuff is there so that the | |
132 // functions are not optimized away. | |
133 static const char* test_script = | |
134 "function test_sampler_api_inner() {" | |
135 " CollectSample();" | |
136 " return arguments.callee.toString();" | |
137 "}" | |
138 "function test_sampler_api_outer() {" | |
139 " return test_sampler_api_inner() + arguments.callee.toString();" | |
140 "}" | |
141 "test_sampler_api_outer();"; | |
142 } | |
143 | |
144 | |
145 // The captured sample should have three pc values. | |
146 // They should fall in the range where the compiled code | |
yurys
2014/09/23 14:57:53
typo: ... where the compiled code resides.
alph
2014/09/24 13:12:18
Done.
| |
147 // The expected stack is: | |
148 // bottom of stack [{anon script}, outer, inner] top of stack | |
149 // ^ ^ ^ | |
150 // sample.stack indices 2 1 0 | |
151 TEST(StackFramesConsistent) { | |
152 SAMPLER_API_TESTS_BOOTSTRAP(); | |
153 | |
154 isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, | |
155 TestJitCodeEventHandler); | |
156 v8::Script::Compile(v8::String::NewFromUtf8(isolate, test_script))->Run(); | |
157 | |
158 CHECK_EQ(3, sample->size()); | |
159 | |
160 bool stack_top_is_inner = false; | |
161 bool below_inner_is_outer = false; | |
162 | |
163 for (unsigned i = 0; i < inner_funcs.size(); i++) { | |
164 void* start_addr = inner_funcs[i].code_start; | |
165 void* end_addr = reinterpret_cast<void*>( | |
166 (int64_t)inner_funcs[i].code_start + inner_funcs[i].code_len); | |
yurys
2014/09/23 14:57:53
static_cast<intptr_t>(inner_funcs[i].code_start) a
alph
2014/09/24 13:12:18
Done.
| |
167 if ((*sample->begin() >= start_addr) && (*sample->begin() < end_addr)) | |
168 stack_top_is_inner = true; | |
169 } | |
170 | |
171 for (unsigned i = 0; i < outer_funcs.size(); i++) { | |
172 void* start_addr = outer_funcs[i].code_start; | |
173 void* end_addr = reinterpret_cast<void*>( | |
174 (int64_t)outer_funcs[i].code_start + outer_funcs[i].code_len); | |
175 if ((*(sample->begin() + 1) >= start_addr) && | |
176 (*(sample->begin() + 1) < end_addr)) | |
177 below_inner_is_outer = true; | |
178 } | |
179 | |
180 CHECK(stack_top_is_inner); | |
181 CHECK(below_inner_is_outer); | |
182 } | |
183 | |
184 | |
185 namespace { | |
186 | |
187 #if defined(USE_SIMULATOR) | |
188 class SimulatorHelper { | |
yurys
2014/09/23 14:57:53
I don't think we need to pull all these platform-s
alph
2014/09/24 13:12:19
Done.
| |
189 public: | |
190 inline bool Init(v8::Isolate* isolate) { | |
191 simulator_ = reinterpret_cast<v8::internal::Isolate*>(isolate) | |
192 ->thread_local_top() | |
193 ->simulator_; | |
194 // Check if there is active simulator. | |
195 return simulator_ != NULL; | |
196 } | |
197 | |
198 inline void FillRegisters(v8::RegisterState* state) { | |
199 #if V8_TARGET_ARCH_ARM | |
200 state->pc = reinterpret_cast<Address>(simulator_->get_pc()); | |
201 state->sp = reinterpret_cast<Address>( | |
202 simulator_->get_register(v8::internal::Simulator::sp)); | |
203 state->fp = reinterpret_cast<Address>( | |
204 simulator_->get_register(v8::internal::Simulator::r11)); | |
205 #elif V8_TARGET_ARCH_ARM64 | |
206 if (simulator_->sp() == 0 || simulator_->fp() == 0) { | |
207 // It possible that the simulator is interrupted while it is updating | |
208 // the sp or fp register. ARM64 simulator does this in two steps: | |
209 // first setting it to zero and then setting it to the new value. | |
210 // Bailout if sp/fp doesn't contain the new value. | |
211 return; | |
212 } | |
213 state->pc = reinterpret_cast<Address>(simulator_->pc()); | |
214 state->sp = reinterpret_cast<Address>(simulator_->sp()); | |
215 state->fp = reinterpret_cast<Address>(simulator_->fp()); | |
216 #elif V8_TARGET_ARCH_MIPS | |
217 state->pc = reinterpret_cast<Address>(simulator_->get_pc()); | |
218 state->sp = | |
219 reinterpret_cast<Address>(simulator_->get_register(Simulator::sp)); | |
220 state->fp = | |
221 reinterpret_cast<Address>(simulator_->get_register(Simulator::fp)); | |
222 #elif V8_TARGET_ARCH_MIPS64 | |
223 state->pc = reinterpret_cast<Address>(simulator_->get_pc()); | |
224 state->sp = reinterpret_cast<Address>( | |
225 simulator_->get_register(v8::internal::Simulator::sp)); | |
226 state->fp = reinterpret_cast<Address>( | |
227 simulator_->get_register(v8::internal::Simulator::fp)); | |
228 #endif | |
229 } | |
230 | |
231 private: | |
232 v8::internal::Simulator* simulator_; | |
233 }; | |
234 #endif // USE_SIMULATOR | |
235 | |
236 // Platform specific implementation of FillRegisterState | |
237 void FillRegisterState(v8::RegisterState* state) { | |
238 #if defined(USE_SIMULATOR) | |
239 SimulatorHelper helper; | |
240 if (!helper.Init(isolate)) return; | |
241 helper.FillRegisters(state); | |
242 // It's possible that the simulator is interrupted while it is updating | |
243 // the sp or fp register. ARM64 simulator does this in two steps: | |
244 // first setting it to zero and then setting it to a new value. | |
245 // Bailout if sp/fp doesn't contain the new value. | |
246 if (state->sp == 0 || state->fp == 0) return; | |
247 | |
248 #elif V8_OS_MACOSX | |
249 // TODO(alph): MacOSX doesn't support getcontext, so do nothing | |
250 // at the moment. Consider enabling the test by obtaining the | |
251 // context via POSIX signals. | |
252 return; | |
253 | |
254 #elif V8_OS_POSIX && !V8_OS_CYGWIN // defined(USE_SIMULATOR) | |
255 ucontext_t ucontext; | |
256 getcontext(&ucontext); | |
257 #if !V8_OS_OPENBSD | |
258 mcontext_t& mcontext = ucontext.uc_mcontext; | |
259 #endif | |
260 #if V8_OS_LINUX | |
261 #if V8_HOST_ARCH_IA32 | |
262 state->pc = reinterpret_cast<void*>(mcontext.gregs[REG_EIP]); | |
263 state->sp = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]); | |
264 state->fp = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]); | |
265 #elif V8_HOST_ARCH_X64 | |
266 state->pc = reinterpret_cast<void*>(mcontext.gregs[REG_RIP]); | |
267 state->sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]); | |
268 state->fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]); | |
269 #elif V8_HOST_ARCH_ARM | |
270 #if defined(__GLIBC__) && !defined(__UCLIBC__) && \ | |
271 (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) | |
272 // Old GLibc ARM versions used a gregs[] array to access the register | |
273 // values from mcontext_t. | |
274 state->pc = reinterpret_cast<void*>(mcontext.gregs[R15]); | |
275 state->sp = reinterpret_cast<void*>(mcontext.gregs[R13]); | |
276 state->fp = reinterpret_cast<void*>(mcontext.gregs[R11]); | |
277 #else | |
278 state->pc = reinterpret_cast<void*>(mcontext.arm_pc); | |
279 state->sp = reinterpret_cast<void*>(mcontext.arm_sp); | |
280 state->fp = reinterpret_cast<void*>(mcontext.arm_fp); | |
281 #endif // defined(__GLIBC__) && !defined(__UCLIBC__) && | |
282 // (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) | |
283 #elif V8_HOST_ARCH_ARM64 | |
284 state->pc = reinterpret_cast<void*>(mcontext.pc); | |
285 state->sp = reinterpret_cast<void*>(mcontext.sp); | |
286 // FP is an alias for x29. | |
287 state->fp = reinterpret_cast<void*>(mcontext.regs[29]); | |
288 #elif V8_HOST_ARCH_MIPS | |
289 state->pc = reinterpret_cast<void*>(mcontext.pc); | |
290 state->sp = reinterpret_cast<void*>(mcontext.gregs[29]); | |
291 state->fp = reinterpret_cast<void*>(mcontext.gregs[30]); | |
292 #elif V8_HOST_ARCH_MIPS64 | |
293 state->pc = reinterpret_cast<void*>(mcontext.pc); | |
294 state->sp = reinterpret_cast<void*>(mcontext.gregs[29]); | |
295 state->fp = reinterpret_cast<void*>(mcontext.gregs[30]); | |
296 #endif // V8_HOST_ARCH_* | |
297 #elif V8_OS_MACOSX | |
298 #if V8_HOST_ARCH_X64 | |
299 #if __DARWIN_UNIX03 | |
300 state->pc = reinterpret_cast<void*>(mcontext->__ss.__rip); | |
301 state->sp = reinterpret_cast<void*>(mcontext->__ss.__rsp); | |
302 state->fp = reinterpret_cast<void*>(mcontext->__ss.__rbp); | |
303 #else // !__DARWIN_UNIX03 | |
304 state->pc = reinterpret_cast<void*>(mcontext->ss.rip); | |
305 state->sp = reinterpret_cast<void*>(mcontext->ss.rsp); | |
306 state->fp = reinterpret_cast<void*>(mcontext->ss.rbp); | |
307 #endif // __DARWIN_UNIX03 | |
308 #elif V8_HOST_ARCH_IA32 | |
309 #if __DARWIN_UNIX03 | |
310 state->pc = reinterpret_cast<void*>(mcontext->__ss.__eip); | |
311 state->sp = reinterpret_cast<void*>(mcontext->__ss.__esp); | |
312 state->fp = reinterpret_cast<void*>(mcontext->__ss.__ebp); | |
313 #else // !__DARWIN_UNIX03 | |
314 state->pc = reinterpret_cast<void*>(mcontext->ss.eip); | |
315 state->sp = reinterpret_cast<void*>(mcontext->ss.esp); | |
316 state->fp = reinterpret_cast<void*>(mcontext->ss.ebp); | |
317 #endif // __DARWIN_UNIX03 | |
318 #endif // V8_HOST_ARCH_IA32 | |
319 #elif V8_OS_FREEBSD | |
320 #if V8_HOST_ARCH_IA32 | |
321 state->pc = reinterpret_cast<void*>(mcontext.mc_eip); | |
322 state->sp = reinterpret_cast<void*>(mcontext.mc_esp); | |
323 state->fp = reinterpret_cast<void*>(mcontext.mc_ebp); | |
324 #elif V8_HOST_ARCH_X64 | |
325 state->pc = reinterpret_cast<void*>(mcontext.mc_rip); | |
326 state->sp = reinterpret_cast<void*>(mcontext.mc_rsp); | |
327 state->fp = reinterpret_cast<void*>(mcontext.mc_rbp); | |
328 #elif V8_HOST_ARCH_ARM | |
329 state->pc = reinterpret_cast<void*>(mcontext.mc_r15); | |
330 state->sp = reinterpret_cast<void*>(mcontext.mc_r13); | |
331 state->fp = reinterpret_cast<void*>(mcontext.mc_r11); | |
332 #endif // V8_HOST_ARCH_* | |
333 #elif V8_OS_NETBSD | |
334 #if V8_HOST_ARCH_IA32 | |
335 state->pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_EIP]); | |
336 state->sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_ESP]); | |
337 state->fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_EBP]); | |
338 #elif V8_HOST_ARCH_X64 | |
339 state->pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_RIP]); | |
340 state->sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RSP]); | |
341 state->fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RBP]); | |
342 #endif // V8_HOST_ARCH_* | |
343 #elif V8_OS_OPENBSD | |
344 #if V8_HOST_ARCH_IA32 | |
345 state->pc = reinterpret_cast<void*>(ucontext->sc_eip); | |
346 state->sp = reinterpret_cast<void*>(ucontext->sc_esp); | |
347 state->fp = reinterpret_cast<void*>(ucontext->sc_ebp); | |
348 #elif V8_HOST_ARCH_X64 | |
349 state->pc = reinterpret_cast<void*>(ucontext->sc_rip); | |
350 state->sp = reinterpret_cast<void*>(ucontext->sc_rsp); | |
351 state->fp = reinterpret_cast<void*>(ucontext->sc_rbp); | |
352 #endif // V8_HOST_ARCH_* | |
353 #elif V8_OS_SOLARIS | |
354 state->pc = reinterpret_cast<void*>(mcontext.gregs[REG_PC]); | |
355 state->sp = reinterpret_cast<void*>(mcontext.gregs[REG_SP]); | |
356 state->fp = reinterpret_cast<void*>(mcontext.gregs[REG_FP]); | |
357 #elif V8_OS_QNX | |
358 #if V8_HOST_ARCH_IA32 | |
359 state->pc = reinterpret_cast<void*>(mcontext.cpu.eip); | |
360 state->sp = reinterpret_cast<void*>(mcontext.cpu.esp); | |
361 state->fp = reinterpret_cast<void*>(mcontext.cpu.ebp); | |
362 #elif V8_HOST_ARCH_ARM | |
363 state->pc = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_PC]); | |
364 state->sp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_SP]); | |
365 state->fp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_FP]); | |
366 #endif // V8_HOST_ARCH_* | |
367 #endif // V8_OS_QNX | |
368 | |
369 #elif V8_OS_WIN || V8_OS_CYGWIN // V8_OS_POSIX && !V8_OS_CYGWIN | |
370 CONTEXT context; | |
371 memset(&context, 0, sizeof(context)); | |
372 context.ContextFlags = CONTEXT_FULL; | |
373 GetThreadContext(OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | | |
374 THREAD_QUERY_INFORMATION, | |
375 false, GetCurrentThreadId()), | |
376 &context); | |
377 #if V8_HOST_ARCH_X64 | |
378 state->pc = reinterpret_cast<void*>(context.Rip); | |
379 state->sp = reinterpret_cast<void*>(context.Rsp); | |
380 state->fp = reinterpret_cast<void*>(context.Rbp); | |
381 #else | |
382 state->pc = reinterpret_cast<void*>(context.Eip); | |
383 state->sp = reinterpret_cast<void*>(context.Esp); | |
384 state->fp = reinterpret_cast<void*>(context.Ebp); | |
385 #endif // V8_HOST_ARCH_X64 | |
386 #endif // V8_OS_WIN || V8_OS_CYGWIN | |
387 } | |
388 | |
389 } // namespace | |
OLD | NEW |