| 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
|
|
|