Chromium Code Reviews| 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..9629ecd61dd9c3a44fee66d3a646f0b86a936bea |
| --- /dev/null |
| +++ b/test/cctest/test-sampler-api.cc |
| @@ -0,0 +1,267 @@ |
| +// 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/v8.h" |
| +#include "test/cctest/cctest.h" |
| + |
| +#if V8_OS_POSIX && !V8_OS_CYGWIN |
| +#include <ucontext.h> |
| +#elif V8_OS_WIN || V8_OS_CYGWIN |
| +#include "src/base/win32-headers.h" |
| +#endif |
| + |
| +using v8::Local; |
| + |
| +namespace { |
| +// The Sample which CollectSample fills up |
| +v8::Sample* sample; |
| + |
| +// The isolate used in the test |
| +v8::Isolate* isolate; |
| + |
| +// Forward declaration |
| +// (platform specific implementation at the bottom of this file) |
| +void GetStackAndFramePointers(void** sp, void** fp); |
| + |
| +// The JavaScript calls this function when on full stack depth. |
| +void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| + void* sp = NULL; |
| + void* fp = NULL; |
| + GetStackAndFramePointers(&sp, &fp); |
| + isolate->GetSample(sp, fp, sample); |
| +} |
| + |
| + |
| +// 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);" |
| + "}"; |
| +} |
| + |
| + |
| +#define SAMPLER_API_TESTS_BOOTSTRAP() \ |
| + v8::Sample sample_; \ |
| + sample = &sample_; \ |
| + isolate = CcTest::isolate(); \ |
| + 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->size()); |
| +} |
| + |
| + |
| +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(); |
| + |
| + int MAX_SIZE = v8::Sample::kMaxSize; |
| + CHECK_EQ(MAX_SIZE, sample->size()); |
| +} |
| + |
| + |
| +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 argumnets.callee stuff is there so that the |
|
alph
2014/09/18 11:34:29
typo
gholap
2014/09/18 23:24:15
Done.
|
| +// 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();"; |
| +} |
| + |
| + |
| +// 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(); |
| + |
| + v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, |
| + TestJitCodeEventHandler); |
| + v8::Script::Compile(v8::String::NewFromUtf8(isolate, test_script))->Run(); |
| + |
| + CHECK_EQ(3, sample->size()); |
| + |
| + 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->begin() >= start_addr) && (*sample->begin() < 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->begin() + 1) >= start_addr) && |
| + (*(sample->begin() + 1) < end_addr)) |
| + below_inner_is_outer = true; |
| + } |
| + |
| + CHECK_EQ(true, stack_top_is_inner); |
|
alph
2014/09/18 11:34:29
CHECK
gholap
2014/09/18 23:24:15
Done.
|
| + CHECK_EQ(true, below_inner_is_outer); |
| +} |
| + |
| + |
| +// Platform specific implementation of GetStackAndFramePointers |
|
loislo
2014/09/18 14:42:44
this is doesn't look as a good idea.
I mean we ha
gholap
2014/09/18 23:24:15
Oh! I did not know that...
I checked the TickSampl
|
| +namespace { |
| +#if V8_OS_POSIX && !V8_OS_CYGWIN |
| +void GetStackAndFramePointers(void** sp, void** fp) { |
| + ucontext_t ucontext; |
| + getcontext(&ucontext); |
| +#if !V8_OS_OPENBSD |
| + mcontext_t& mcontext = ucontext.uc_mcontext; |
| +#endif |
| +#if V8_OS_LINUX |
| +#if V8_HOST_ARCH_IA32 |
| + *sp = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]); |
| + *fp = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]); |
| +#elif V8_HOST_ARCH_X64 |
| + *sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]); |
| + *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. |
| + *sp = reinterpret_cast<void*>(mcontext.gregs[R13]); |
| + *fp = reinterpret_cast<void*>(mcontext.gregs[R11]); |
| +#else |
| + *sp = reinterpret_cast<void*>(mcontext.arm_sp); |
| + *fp = reinterpret_cast<void*>(mcontext.arm_fp); |
| +#endif // defined(__GLIBC__) && !defined(__UCLIBC__) && |
| +// (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) |
| +#elif V8_HOST_ARCH_ARM64 |
| + *sp = reinterpret_cast<void*>(mcontext.sp); |
| + // FP is an alias for x29. |
| + *fp = reinterpret_cast<void*>(mcontext.regs[29]); |
| +#elif V8_HOST_ARCH_MIPS |
| + *sp = reinterpret_cast<void*>(mcontext.gregs[29]); |
| + *fp = reinterpret_cast<void*>(mcontext.gregs[30]); |
| +#endif // V8_HOST_ARCH_* |
| +#elif V8_OS_MACOSX |
| +#if V8_HOST_ARCH_X64 |
| +#if __DARWIN_UNIX03 |
| + *sp = reinterpret_cast<void*>(mcontext->__ss.__rsp); |
| + *fp = reinterpret_cast<void*>(mcontext->__ss.__rbp); |
| +#else // !__DARWIN_UNIX03 |
| + *sp = reinterpret_cast<void*>(mcontext->ss.rsp); |
| + *fp = reinterpret_cast<void*>(mcontext->ss.rbp); |
| +#endif // __DARWIN_UNIX03 |
| +#elif V8_HOST_ARCH_IA32 |
| +#if __DARWIN_UNIX03 |
| + *sp = mcontext->__ss.__esp); |
| + *fp = mcontext->__ss.__ebp); |
| +#else // !__DARWIN_UNIX03 |
| + *sp = mcontext->ss.esp); |
| + *fp = mcontext->ss.ebp); |
| +#endif // __DARWIN_UNIX03 |
| +#endif // V8_HOST_ARCH_IA32 |
| +#elif V8_OS_FREEBSD |
| +#if V8_HOST_ARCH_IA32 |
| + *sp = reinterpret_cast<void*>(mcontext.mc_esp); |
| + *fp = reinterpret_cast<void*>(mcontext.mc_ebp); |
| +#elif V8_HOST_ARCH_X64 |
| + *sp = reinterpret_cast<void*>(mcontext.mc_rsp); |
| + *fp = reinterpret_cast<void*>(mcontext.mc_rbp); |
| +#elif V8_HOST_ARCH_ARM |
| + *sp = reinterpret_cast<void*>(mcontext.mc_r13); |
| + *fp = reinterpret_cast<void*>(mcontext.mc_r11); |
| +#endif // V8_HOST_ARCH_* |
| +#elif V8_OS_NETBSD |
| +#if V8_HOST_ARCH_IA32 |
| + *sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_ESP]); |
| + *fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_EBP]); |
| +#elif V8_HOST_ARCH_X64 |
| + *sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RSP]); |
| + *fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RBP]); |
| +#endif // V8_HOST_ARCH_* |
| +#elif V8_OS_OPENBSD |
| +#if V8_HOST_ARCH_IA32 |
| + *sp = reinterpret_cast<void*>(ucontext.sc_esp); |
| + *fp = reinterpret_cast<void*>(ucontext.sc_ebp); |
| +#elif V8_HOST_ARCH_X64 |
| + *sp = reinterpret_cast<void*>(ucontext.sc_rsp); |
| + *fp = reinterpret_cast<void*>(ucontext.sc_rbp); |
| +#endif // V8_HOST_ARCH_* |
| +#elif V8_OS_SOLARIS |
| + *sp = reinterpret_cast<void*>(mcontext.gregs[REG_SP]); |
| + *fp = reinterpret_cast<void*>(mcontext.gregs[REG_FP]); |
| +#elif V8_OS_QNX |
| +#if V8_HOST_ARCH_IA32 |
| + *sp = reinterpret_cast<void*>(mcontext.cpu.esp); |
| + *fp = reinterpret_cast<void*>(mcontext.cpu.ebp); |
| +#elif V8_HOST_ARCH_ARM |
| + *sp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_SP]); |
| + *fp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_FP]); |
| +#endif // V8_HOST_ARCH_* |
| +#endif // V8_OS_QNX |
| +} |
| +#elif V8_OS_WIN || V8_OS_CYGWIN |
| +void GetStackAndFramePointers(void** sp, void** fp) { |
|
alph
2014/09/18 11:34:29
you can move the function name & args out of #ifde
gholap
2014/09/18 23:24:15
Done.
|
| + CONTEXT context; |
| + 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 |
| + *sp = reinterpret_cast<void*>(context.Rsp); |
| + *fp = reinterpret_cast<void*>(context.Rbp); |
| +#else |
| + *sp = reinterpret_cast<void*>(context.Esp); |
| + *fp = reinterpret_cast<void*>(context.Ebp); |
| +#endif // V8_HOST_ARCH_X64 |
| +} |
| +#endif // V8_OS_POSIX && !V8_OS_CYGWIN / V8_OS_WIN || V8_OS_CYGWIN |
| +} |