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); |
+} |