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..45dc09b4901ff7f732aa6dc33534028d230a432d |
| --- /dev/null |
| +++ b/test/cctest/test-sampler-api.cc |
| @@ -0,0 +1,197 @@ |
| +// 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/v8.h" |
|
alph
2014/09/08 12:47:40
style: no need for extra lines between includes.
gholap
2014/09/08 23:57:38
Done.
|
| + |
| +#include "src/base/platform/platform.h" |
| + |
| +#include "test/cctest/cctest.h" |
| + |
| +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 V8_FINAL SamplerThread : public v8::base::Thread { |
| + public: |
| + explicit SamplerThread(v8::Isolate* isolate) |
| + : Thread(v8::base::Thread::Options("sampler-api-tester")), |
| + isolate_(isolate) { |
| + } |
| + |
| + virtual ~SamplerThread() {} |
| + |
| + virtual void Run() { |
| + sampler_semaphore->Wait(); // Wait for JS to reach full stack depth. |
| + isolate_->GetSample(&sample); |
| + v8_semaphore->Signal(); // Tell JS that sample collection is done. |
| + } |
| + |
| + v8::Sample sample; |
| + |
| + private: |
| + v8::Isolate* isolate_; |
| +}; |
| + |
| + |
| +// 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) { |
| + 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); |
| + 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())); |
| + SamplerThread sampler(isolate); |
| + |
| + sampler.Start(); |
| + script->Run(); |
| + sampler.Join(); |
| + |
| + CHECK_EQ(8, sampler.sample.size()); |
| +} |
| + |
| + |
| +TEST(StackDepthDoesNotExceedMaxValue) { |
| + 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); |
| + 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())); |
| + SamplerThread sampler(isolate); |
| + |
| + sampler.Start(); |
| + script->Run(); |
| + sampler.Join(); |
| + |
| + 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) { |
| + v8::Isolate* isolate = CcTest::isolate(); |
| + v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, |
| + TestJitCodeEventHandler); |
| + 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); |
| + Local<v8::Script> script = v8::Script::Compile( |
| + v8::String::NewFromUtf8(isolate, sampler_api_test_script)); |
| + SamplerThread sampler(isolate); |
| + |
| + sampler.Start(); |
| + script->Run(); |
| + sampler.Join(); |
| + |
| + 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); |
| +} |