| 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..ed89148766f5ab32927ba6cfc3fd01861f2c3f5d
|
| --- /dev/null
|
| +++ b/test/cctest/test-sampler-api.cc
|
| @@ -0,0 +1,218 @@
|
| +// 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 public sampling API in include/v8.h
|
| +
|
| +#include <string>
|
| +#include "include/v8.h"
|
| +#include "src/base/platform/platform.h"
|
| +#include "src/v8.h"
|
| +#include "test/cctest/cctest.h"
|
| +
|
| +#if V8_OS_POSIX && !V8_OS_CYGWIN
|
| +#include <pthread.h>
|
| +#include <signal.h>
|
| +#elif V8_OS_WIN || V8_OS_CYGWIN
|
| +#include "src/base/win32-headers.h"
|
| +#endif
|
| +
|
| +using v8::Local;
|
| +
|
| +namespace {
|
| + // Sampler thread waits on this semaphore.
|
| + v8::base::Semaphore* sampler_semaphore = new v8::base::Semaphore(0);
|
| +
|
| + // V8 thread (the JavaScript code) waits on this semaphore.
|
| + v8::base::Semaphore* v8_semaphore = new v8::base::Semaphore(0);
|
| +
|
| + // The JavaScript calls this function when on full stack depth.
|
| + void SignalAndWaitForSampler(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + // Tell the sampler that it can take a sample now.
|
| + sampler_semaphore->Signal();
|
| +
|
| + // Wait for the sampler to finish collecting a sample.
|
| + v8_semaphore->Wait();
|
| + }
|
| +
|
| + // A thread which collects samples from v8.
|
| + class SamplerThread : public v8::base::Thread {
|
| + public:
|
| + explicit SamplerThread(v8::Isolate* isolate)
|
| + : Thread(v8::base::Thread::Options("sampler-api-tester")),
|
| + isolate_(isolate) {
|
| +#if V8_OS_POSIX && !V8_OS_CYGWIN
|
| + sampled_thread_id_ = pthread_self();
|
| +#elif V8_OS_WIN || V8_OS_CYGWIN
|
| + HANDLE sampled_thread_ = OpenThread(THREAD_GET_CONTEXT |
|
| + THREAD_SUSPEND_RESUME |
|
| + THREAD_QUERY_INFORMATION,
|
| + false,
|
| + GetCurrentThreadId());
|
| +#endif
|
| + }
|
| +
|
| + virtual ~SamplerThread() {}
|
| +
|
| + virtual void Run() {
|
| + sampler_semaphore->Wait(); // Wait for JS to reach full stack depth.
|
| +
|
| +#if V8_OS_POSIX && !V8_OS_CYGWIN
|
| +
|
| + pthread_kill(sampled_thread_id_, SIGPROF);
|
| + isolate_->GetSample(&sample);
|
| +
|
| +#elif V8_OS_WIN || V8_OS_CYGWIN
|
| +
|
| + SuspendThread(sampled_thread_);
|
| + CONTEXT context;
|
| + memset(&context, 0, sizeof(context));
|
| + context.ContextFlags = CONTEXT_FULL;
|
| + GetThreadContext(sampled_thread_, &context);
|
| + isolate_->GetSample(context, &sample);
|
| + ResumeThread(sampled_thread_);
|
| +
|
| +#endif
|
| +
|
| + v8_semaphore->Signal(); // Tell JS that sample collection is done.
|
| + }
|
| +
|
| + v8::Sample sample;
|
| +
|
| + private:
|
| + v8::Isolate* isolate_;
|
| +#if V8_OS_POSIX && !V8_OS_CYGWIN
|
| + pthread_t sampled_thread_id_;
|
| +#elif V8_OS_WIN || V8_OS_CYGWIN
|
| + HANDLE sampled_thread_;
|
| +#endif
|
| + };
|
| +}
|
| +
|
| +
|
| +#define SAMPLER_API_TESTS_BOOTSTRAP() \
|
| + v8::internal::FLAG_new_sampler_api = true; \
|
| + v8::Isolate* isolate = CcTest::isolate(); \
|
| + v8::HandleScope scope(isolate); \
|
| + v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); \
|
| + global->Set(v8::String::NewFromUtf8(isolate, "SignalAndWaitForSampler"), \
|
| + v8::FunctionTemplate::New(isolate, SignalAndWaitForSampler)); \
|
| + LocalContext env(isolate, NULL, global); \
|
| + SamplerThread sampler(isolate)
|
| +
|
| +#define SAMPLER_API_TESTS_COLLECT_SAMPLE() \
|
| + sampler.Start(); \
|
| + script->Run(); \
|
| + sampler.Join()
|
| +
|
| +// 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* sampler_api_test_function = "function func(depth) {"
|
| +" if (depth == 2) SignalAndWaitForSampler();"
|
| +" else return func(depth - 1);"
|
| +"}";
|
| +
|
| +
|
| +TEST(StackDepthIsConsistent) {
|
| + SAMPLER_API_TESTS_BOOTSTRAP();
|
| +
|
| + std::string source(sampler_api_test_function);
|
| + source.append("func(8);");
|
| + Local<v8::Script> script = v8::Script::Compile(
|
| + v8::String::NewFromUtf8(isolate, source.c_str()));
|
| +
|
| + SAMPLER_API_TESTS_COLLECT_SAMPLE();
|
| +
|
| + CHECK_EQ(8, sampler.sample.size());
|
| +}
|
| +
|
| +
|
| +TEST(StackDepthDoesNotExceedMaxValue) {
|
| + SAMPLER_API_TESTS_BOOTSTRAP();
|
| +
|
| + std::string source(sampler_api_test_function);
|
| + source.append("func(300);");
|
| + Local<v8::Script> script = v8::Script::Compile(
|
| + v8::String::NewFromUtf8(isolate, source.c_str()));
|
| +
|
| + SAMPLER_API_TESTS_COLLECT_SAMPLE();
|
| +
|
| + int MAX_SIZE = v8::Sample::kMaxSize;
|
| + CHECK_EQ(MAX_SIZE, sampler.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
|
| +// functions are not optimized away.
|
| +static const char* sampler_api_test_script =
|
| +"function test_sampler_api_inner() {"
|
| +" SignalAndWaitForSampler();"
|
| +" 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);
|
| + Local<v8::Script> script = v8::Script::Compile(
|
| + v8::String::NewFromUtf8(isolate, sampler_api_test_script));
|
| +
|
| + SAMPLER_API_TESTS_COLLECT_SAMPLE();
|
| +
|
| + CHECK_EQ(3, sampler.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 ((*sampler.sample.begin() >= start_addr) &&
|
| + (*sampler.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 ((*(sampler.sample.begin() + 1) >= start_addr) &&
|
| + (*(sampler.sample.begin() + 1) < end_addr))
|
| + below_inner_is_outer = true;
|
| + }
|
| +
|
| + CHECK_EQ(true, stack_top_is_inner);
|
| + CHECK_EQ(true, below_inner_is_outer);
|
| +}
|
|
|