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..f18b42a83bafeda964ec373fec492dab61e98666 |
--- /dev/null |
+++ b/test/cctest/test-sampler-api.cc |
@@ -0,0 +1,206 @@ |
+// 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 <map> |
+#include <string> |
+#include "include/v8.h" |
+#include "src/simulator.h" |
yurys
2014/09/24 13:44:07
Do we still need this include?
alph
2014/09/24 14:00:51
Done.
|
+#include "src/utils.h" |
+#include "src/v8.h" |
+#include "test/cctest/cctest.h" |
+ |
+namespace { |
+ |
+class Sample { |
+ public: |
+ enum { kFramesLimit = 255 }; |
+ |
+ Sample() {} |
+ |
+ typedef const void* const* const_iterator; |
+ const_iterator begin() const { return data_.start(); } |
+ const_iterator end() const { return &data_[data_.length()]; } |
+ |
+ int size() const { return data_.length(); } |
+ v8::internal::Vector<void*>& data() { return data_; } |
+ |
+ private: |
+ v8::internal::EmbeddedVector<void*, kFramesLimit> data_; |
+}; |
+ |
+ |
+template <class T> |
+class Singleton { |
+ public: |
+ static T* instance() { return instance_; } |
+ |
+ protected: |
+ Singleton() { |
+ DCHECK_EQ(NULL, instance_); |
yurys
2014/09/24 13:44:07
I'd rather inline this check and singlton implemen
alph
2014/09/24 14:00:51
Done.
|
+ instance_ = static_cast<T*>(this); |
+ } |
+ |
+ ~Singleton() { instance_ = NULL; } |
+ |
+ private: |
+ static T* instance_; |
+}; |
+ |
+template <class T> |
+T* Singleton<T>::instance_; |
+ |
+ |
+class SamplingTestHelper : public Singleton<SamplingTestHelper> { |
+ public: |
+ struct CodeEventEntry { |
+ std::string name; |
+ const void* code_start; |
+ size_t code_len; |
+ }; |
+ typedef std::map<const void*, CodeEventEntry> CodeEntries; |
+ |
+ explicit SamplingTestHelper(const std::string& test_function) { |
+ 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); |
+ isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, |
+ JitCodeEventHandler); |
+ v8::Script::Compile(v8::String::NewFromUtf8(isolate, test_function.c_str())) |
+ ->Run(); |
+ } |
+ |
+ ~SamplingTestHelper() { isolate = NULL; } |
+ |
+ Sample& get_sample() { return sample_; } |
yurys
2014/09/24 13:44:07
style: no get_ prefix on simple getters.
alph
2014/09/24 14:00:51
Done.
|
+ |
+ static void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ instance()->DoCollectSample(); |
+ } |
+ |
+ // The JavaScript calls this function when on full stack depth. |
+ void DoCollectSample() { |
yurys
2014/09/24 13:44:07
This should be private
alph
2014/09/24 14:00:51
Done.
|
+ v8::RegisterState state; |
+ state.pc = NULL; |
+ state.fp = &state; |
+ state.sp = &state; |
+ v8::SampleInfo info; |
+ isolate->GetStackSample(state, sample_.data().start(), |
+ static_cast<size_t>(sample_.size()), &info); |
+ size_t frames_count = info.frames_count; |
+ CHECK_LE(frames_count, static_cast<size_t>(sample_.size())); |
+ sample_.data().Truncate(static_cast<int>(frames_count)); |
+ } |
+ |
+ static void JitCodeEventHandler(const v8::JitCodeEvent* event) { |
+ return instance()->DoJitCodeEventHandler(event); |
+ } |
+ |
+ void DoJitCodeEventHandler(const v8::JitCodeEvent* event) { |
+ switch (event->type) { |
yurys
2014/09/24 13:44:07
We should ignore code events after the sample is t
alph
2014/09/24 14:00:51
Nice catch! Done.
|
+ case v8::JitCodeEvent::CODE_ADDED: { |
+ CodeEventEntry entry; |
+ entry.name = std::string(event->name.str, event->name.len); |
+ entry.code_start = event->code_start; |
+ entry.code_len = event->code_len; |
+ code_entries.insert(std::make_pair(entry.code_start, entry)); |
+ break; |
+ } |
+ case v8::JitCodeEvent::CODE_MOVED: { |
+ CodeEntries::const_iterator it = code_entries.find(event->code_start); |
+ CHECK(it != code_entries.end()); |
+ code_entries.erase(it); |
+ CodeEventEntry entry; |
+ entry.name = std::string(event->name.str, event->name.len); |
+ entry.code_start = event->new_code_start; |
+ entry.code_len = event->code_len; |
+ code_entries.insert(std::make_pair(entry.code_start, entry)); |
+ break; |
+ } |
+ case v8::JitCodeEvent::CODE_REMOVED: |
+ code_entries.erase(event->code_start); |
+ break; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ const CodeEventEntry* FindEventEntry(const void* address) { |
+ CodeEntries::const_iterator it = code_entries.upper_bound(address); |
+ if (it == code_entries.begin()) return NULL; |
+ const CodeEventEntry& entry = (--it)->second; |
+ const void* code_end = |
+ static_cast<const uint8_t*>(entry.code_start) + entry.code_len; |
+ return address < code_end ? &entry : NULL; |
+ } |
+ |
+ private: |
+ Sample sample_; |
+ v8::Isolate* isolate; |
+ CodeEntries code_entries; |
+}; |
+ |
+} // namespace |
+ |
+ |
+// 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);" |
+ "}"; |
+ |
+ |
+TEST(StackDepthIsConsistent) { |
+ SamplingTestHelper helper(std::string(test_function) + "func(8);"); |
+ CHECK_EQ(8, helper.get_sample().size()); |
+} |
+ |
+ |
+TEST(StackDepthDoesNotExceedMaxValue) { |
+ SamplingTestHelper helper(std::string(test_function) + "func(300);"); |
+ CHECK_EQ(Sample::kFramesLimit, helper.get_sample().size()); |
+} |
+ |
+ |
+// The captured sample should have three pc values. |
+// They should fall in the range where the compiled code resides. |
+// The expected stack is: |
+// bottom of stack [{anon script}, outer, inner] top of stack |
+// ^ ^ ^ |
+// sample.stack indices 2 1 0 |
+TEST(StackFramesConsistent) { |
+ // Note: The arguments.callee stuff is there so that the |
+ // functions are not optimized away. |
+ 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();"; |
+ |
+ SamplingTestHelper helper(test_script); |
+ Sample& sample = helper.get_sample(); |
+ CHECK_EQ(3, sample.size()); |
+ |
+ const SamplingTestHelper::CodeEventEntry* entry; |
+ entry = helper.FindEventEntry(sample.begin()[0]); |
+ CHECK_NE(NULL, entry); |
+ CHECK(std::string::npos != entry->name.find("test_sampler_api_inner")); |
+ |
+ entry = helper.FindEventEntry(sample.begin()[1]); |
+ CHECK_NE(NULL, entry); |
+ CHECK(std::string::npos != entry->name.find("test_sampler_api_outer")); |
+} |