Index: test/cctest/test-sampler-api.cc |
diff --git a/test/cctest/test-sampler-api.cc b/test/cctest/test-sampler-api.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..070315578c716ca86cdae29051c4bc2504d036f5 |
--- /dev/null |
+++ b/test/cctest/test-sampler-api.cc |
@@ -0,0 +1,335 @@ |
+// Copyright 2014 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+// |
+// Tests the sampling API in include/v8.h |
+ |
+#include <string> |
+#include "include/v8.h" |
+#include "src/base/platform/platform.h" |
+#include "src/sampler.h" |
+#include "src/v8.h" |
+#include "test/cctest/cctest.h" |
+ |
+using v8::Local; |
+ |
+namespace { |
+static const int kMaxSampleDepth = 10; |
+ |
+// The Sample which CollectSample fills up |
+void* sample[kMaxSampleDepth]; |
+int sample_depth; |
+ |
+// The isolate used in the test |
+v8::Isolate* isolate; |
+ |
+#if V8_OS_POSIX && !V8_OS_CYGWIN |
+v8::RegisterState GetRegisterState(void* context); |
+ |
+// Signaal handler signals on this semaphore once it finishes. |
+v8::base::Semaphore* semaphore; |
+ |
+static void HandleSigprof(int signal, siginfo_t* info, void* context) { |
+ USE(info); |
+ if (signal != SIGPROF) return; |
+ fprintf(stderr, "inside the handler\n"); |
+ sample_depth = isolate->GetSample(GetRegisterState(context), |
+ sample, |
+ kMaxSampleDepth); |
+ semaphore->Signal(); |
+} |
+ |
+ |
+void InstallSignalHandler() { |
+ struct sigaction sa; |
+ sa.sa_sigaction = &HandleSigprof; |
+ sigemptyset(&sa.sa_mask); |
+#if V8_OS_QNX |
+ sa.sa_flags = SA_SIGINFO; |
+#else |
+ sa.sa_flags = SA_RESTART | SA_SIGINFO; |
+#endif |
+ sigaction(SIGPROF, &sa, 0); |
+} |
+ |
+ |
+// The JavaScript calls this function when on full stack depth. |
+void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ semaphore = new v8::base::Semaphore(0); |
+ InstallSignalHandler(); |
+ pthread_kill(pthread_self(), SIGPROF); |
+ fprintf(stderr, "Sent the signal\n"); |
+ semaphore->Wait(); |
+ delete semaphore; |
+ semaphore = NULL; |
+} |
+ |
+#elif V8_OS_WIN || V8_OS_CYGWIN |
+v8::RegisterState GetRegisterState(); |
+ |
+// The JavaScript calls this function when on full stack depth. |
+void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ sample_depth = isolate->GetSample(GetRegisterState(), |
+ sample, |
+ kMaxSampleDepth); |
+} |
+#endif // V8_OS_POSIX && !V8_OS_CYGWIN / V8_OS_WIN || V8_OS_CYGWIN |
+ |
+// A JavaScript function which takes stack depth |
+// (minimum value 2) as an argument. |
+// When at the bottom of the recursion, |
+// the JavaScript code calls into C++ test code, |
+// waiting for the sampler to take a sample. |
+static const char* test_function = |
+ "function func(depth) {" |
+ " if (depth == 2) CollectSample();" |
+ " else return func(depth - 1);" |
+ "}"; |
+} // namespace |
+ |
+ |
+#define SAMPLER_API_TESTS_BOOTSTRAP() \ |
+ isolate = CcTest::isolate(); \ |
+ sample_depth = 0; \ |
+ v8::HandleScope scope(isolate); \ |
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); \ |
+ global->Set(v8::String::NewFromUtf8(isolate, "CollectSample"), \ |
+ v8::FunctionTemplate::New(isolate, CollectSample)); \ |
+ LocalContext env(isolate, NULL, global) |
+ |
+ |
+TEST(StackDepthIsConsistent) { |
+ SAMPLER_API_TESTS_BOOTSTRAP(); |
+ |
+ std::string source(test_function); |
+ source.append("func(8);"); |
+ v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run(); |
+ |
+ CHECK_EQ(8, sample_depth); |
+} |
+ |
+ |
+// TEST(StackDepthDoesNotExceedMaxValue) { |
+// SAMPLER_API_TESTS_BOOTSTRAP(); |
+ |
+// std::string source(test_function); |
+// source.append("func(300);"); |
+// v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run(); |
+ |
+// CHECK_EQ(kMaxSampleDepth, sample_depth); |
+// } |
+ |
+ |
+// namespace { |
+// std::vector<v8::JitCodeEvent> inner_funcs; |
+// std::vector<v8::JitCodeEvent> outer_funcs; |
+ |
+// void TestJitCodeEventHandler(const v8::JitCodeEvent* event) { |
+// if (event->type != v8::JitCodeEvent::CODE_ADDED) return; |
+// std::string name(event->name.str, event->name.len); |
+// if (name.find("test_sampler_api_inner") != std::string::npos) |
+// inner_funcs.push_back(*event); |
+// if (name.find("test_sampler_api_outer") != std::string::npos) |
+// outer_funcs.push_back(*event); |
+// } |
+ |
+ |
+// // Note: The arguments.callee stuff is there so that the |
+// // functions are not optimized away. |
+// static const char* test_script = |
+// "function test_sampler_api_inner() {" |
+// " CollectSample();" |
+// " return arguments.callee.toString();" |
+// "}" |
+// "function test_sampler_api_outer() {" |
+// " return test_sampler_api_inner() + arguments.callee.toString();" |
+// "}" |
+// "test_sampler_api_outer();"; |
+// } // namespace |
+ |
+ |
+// // The captured sample should have three pc values. |
+// // They should fall in the range where the compiled code |
+// // The expected stack is: |
+// // bottom of stack [{anon script}, outer, inner] top of stack |
+// // ^ ^ ^ |
+// // sample.stack indices 2 1 0 |
+// TEST(StackFramesConsistent) { |
+// SAMPLER_API_TESTS_BOOTSTRAP(); |
+ |
+// isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, |
+// TestJitCodeEventHandler); |
+// v8::Script::Compile(v8::String::NewFromUtf8(isolate, test_script))->Run(); |
+ |
+// CHECK_EQ(3, sample_depth); |
+ |
+// bool stack_top_is_inner = false; |
+// bool below_inner_is_outer = false; |
+ |
+// for (unsigned i = 0; i < inner_funcs.size(); i++) { |
+// void* start_addr = inner_funcs[i].code_start; |
+// void* end_addr = reinterpret_cast<void*>( |
+// (int64_t)inner_funcs[i].code_start + inner_funcs[i].code_len); |
+// if ((sample[0] >= start_addr) && (sample[0] < end_addr)) |
+// stack_top_is_inner = true; |
+// } |
+ |
+// for (unsigned i = 0; i < outer_funcs.size(); i++) { |
+// void* start_addr = outer_funcs[i].code_start; |
+// void* end_addr = reinterpret_cast<void*>( |
+// (int64_t)outer_funcs[i].code_start + outer_funcs[i].code_len); |
+// if ((sample[1] >= start_addr) && |
+// (sample[1] < end_addr)) |
+// below_inner_is_outer = true; |
+// } |
+ |
+// CHECK(stack_top_is_inner); |
+// CHECK(below_inner_is_outer); |
+// } |
+ |
+ |
+// Platform specific implementation of GetRegisterState |
+namespace { |
+#if V8_OS_POSIX && !V8_OS_CYGWIN |
+v8::RegisterState GetRegisterState(void* context) { |
+ v8::RegisterState state; |
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); |
+#if !V8_OS_OPENBSD |
+ mcontext_t& mcontext = ucontext->uc_mcontext; |
+#endif |
+#if V8_OS_LINUX |
+#if V8_HOST_ARCH_IA32 |
+ state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_EIP]); |
+ state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]); |
+ state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]); |
+#elif V8_HOST_ARCH_X64 |
+ state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_RIP]); |
+ state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]); |
+ state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]); |
+#elif V8_HOST_ARCH_ARM |
+#if defined(__GLIBC__) && !defined(__UCLIBC__) && \ |
+ (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) |
+ // Old GLibc ARM versions used a gregs[] array to access the register |
+ // values from mcontext_t. |
+ state.pc = reinterpret_cast<void*>(mcontext.gregs[R15]); |
+ state.sp = reinterpret_cast<void*>(mcontext.gregs[R13]); |
+ state.fp = reinterpret_cast<void*>(mcontext.gregs[R11]); |
+#else |
+ state.pc = reinterpret_cast<void*>(mcontext.arm_pc); |
+ state.sp = reinterpret_cast<void*>(mcontext.arm_sp); |
+ state.fp = reinterpret_cast<void*>(mcontext.arm_fp); |
+#endif // defined(__GLIBC__) && !defined(__UCLIBC__) && |
+ // (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) |
+#elif V8_HOST_ARCH_ARM64 |
+ state.pc = reinterpret_cast<void*>(mcontext.pc); |
+ state.sp = reinterpret_cast<void*>(mcontext.sp); |
+ // FP is an alias for x29. |
+ state.fp = reinterpret_cast<void*>(mcontext.regs[29]); |
+#elif V8_HOST_ARCH_MIPS |
+ state.pc = reinterpret_cast<void*>(mcontext.pc); |
+ state.sp = reinterpret_cast<void*>(mcontext.gregs[29]); |
+ state.fp = reinterpret_cast<void*>(mcontext.gregs[30]); |
+#elif V8_HOST_ARCH_MIPS64 |
+ state.pc = reinterpret_cast<void*>(mcontext.pc); |
+ state.sp = reinterpret_cast<void*>(mcontext.gregs[29]); |
+ state.fp = reinterpret_cast<void*>(mcontext.gregs[30]); |
+#endif // V8_HOST_ARCH_* |
+#elif V8_OS_MACOSX |
+#if V8_HOST_ARCH_X64 |
+#if __DARWIN_UNIX03 |
+ state.pc = reinterpret_cast<void*>(mcontext->__ss.__rip); |
+ state.sp = reinterpret_cast<void*>(mcontext->__ss.__rsp); |
+ state.fp = reinterpret_cast<void*>(mcontext->__ss.__rbp); |
+#else // !__DARWIN_UNIX03 |
+ state.pc = reinterpret_cast<void*>(mcontext->ss.rip); |
+ state.sp = reinterpret_cast<void*>(mcontext->ss.rsp); |
+ state.fp = reinterpret_cast<void*>(mcontext->ss.rbp); |
+#endif // __DARWIN_UNIX03 |
+#elif V8_HOST_ARCH_IA32 |
+#if __DARWIN_UNIX03 |
+ state.pc = reinterpret_cast<void*>(mcontext->__ss.__eip); |
+ state.sp = reinterpret_cast<void*>(mcontext->__ss.__esp); |
+ state.fp = reinterpret_cast<void*>(mcontext->__ss.__ebp); |
+#else // !__DARWIN_UNIX03 |
+ state.pc = reinterpret_cast<void*>(mcontext->ss.eip); |
+ state.sp = reinterpret_cast<void*>(mcontext->ss.esp); |
+ state.fp = reinterpret_cast<void*>(mcontext->ss.ebp); |
+#endif // __DARWIN_UNIX03 |
+#endif // V8_HOST_ARCH_IA32 |
+#elif V8_OS_FREEBSD |
+#if V8_HOST_ARCH_IA32 |
+ state.pc = reinterpret_cast<void*>(mcontext.mc_eip); |
+ state.sp = reinterpret_cast<void*>(mcontext.mc_esp); |
+ state.fp = reinterpret_cast<void*>(mcontext.mc_ebp); |
+#elif V8_HOST_ARCH_X64 |
+ state.pc = reinterpret_cast<void*>(mcontext.mc_rip); |
+ state.sp = reinterpret_cast<void*>(mcontext.mc_rsp); |
+ state.fp = reinterpret_cast<void*>(mcontext.mc_rbp); |
+#elif V8_HOST_ARCH_ARM |
+ state.pc = reinterpret_cast<void*>(mcontext.mc_r15); |
+ state.sp = reinterpret_cast<void*>(mcontext.mc_r13); |
+ state.fp = reinterpret_cast<void*>(mcontext.mc_r11); |
+#endif // V8_HOST_ARCH_* |
+#elif V8_OS_NETBSD |
+#if V8_HOST_ARCH_IA32 |
+ state.pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_EIP]); |
+ state.sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_ESP]); |
+ state.fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_EBP]); |
+#elif V8_HOST_ARCH_X64 |
+ state.pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_RIP]); |
+ state.sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RSP]); |
+ state.fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RBP]); |
+#endif // V8_HOST_ARCH_* |
+#elif V8_OS_OPENBSD |
+#if V8_HOST_ARCH_IA32 |
+ state.pc = reinterpret_cast<void*>(ucontext->sc_eip); |
+ state.sp = reinterpret_cast<void*>(ucontext->sc_esp); |
+ state.fp = reinterpret_cast<void*>(ucontext->sc_ebp); |
+#elif V8_HOST_ARCH_X64 |
+ state.pc = reinterpret_cast<void*>(ucontext->sc_rip); |
+ state.sp = reinterpret_cast<void*>(ucontext->sc_rsp); |
+ state.fp = reinterpret_cast<void*>(ucontext->sc_rbp); |
+#endif // V8_HOST_ARCH_* |
+#elif V8_OS_SOLARIS |
+ state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_PC]); |
+ state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_SP]); |
+ state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_FP]); |
+#elif V8_OS_QNX |
+#if V8_HOST_ARCH_IA32 |
+ state.pc = reinterpret_cast<void*>(mcontext.cpu.eip); |
+ state.sp = reinterpret_cast<void*>(mcontext.cpu.esp); |
+ state.fp = reinterpret_cast<void*>(mcontext.cpu.ebp); |
+#elif V8_HOST_ARCH_ARM |
+ state.pc = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_PC]); |
+ state.sp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_SP]); |
+ state.fp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_FP]); |
+#endif // V8_HOST_ARCH_* |
+#endif // V8_OS_QNX |
+ return state; |
+} |
+ |
+#elif V8_OS_WIN || V8_OS_CYGWIN |
+v8::RegisterState GetRegisterState() { |
+ CONTEXT context; |
+ v8::RegisterState state; |
+ memset(&context, 0, sizeof(context)); |
+ context.ContextFlags = CONTEXT_FULL; |
+ GetThreadContext(OpenThread(THREAD_GET_CONTEXT | |
+ THREAD_SUSPEND_RESUME | |
+ THREAD_QUERY_INFORMATION, |
+ false, |
+ GetCurrentThreadId()), |
+ &context); |
+#if V8_HOST_ARCH_X64 |
+ state.pc = reinterpret_cast<void*>(context.Rip); |
+ state.sp = reinterpret_cast<void*>(context.Rsp); |
+ state.fp = reinterpret_cast<void*>(context.Rbp); |
+#else |
+ state.pc = reinterpret_cast<void*>(context.Eip); |
+ state.sp = reinterpret_cast<void*>(context.Esp); |
+ state.fp = reinterpret_cast<void*>(context.Ebp); |
+#endif |
+ return state; |
+} |
+#endif // V8_OS_POSIX && !V8_OS_CYGWIN / V8_OS_WIN || V8_OS_CYGWIN |
+} // namespace |