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