OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 // |
| 5 // Tests the sampling API in include/v8.h |
| 6 |
| 7 #include <map> |
| 8 #include <string> |
| 9 #include "include/v8.h" |
| 10 #include "test/cctest/cctest.h" |
| 11 |
| 12 namespace { |
| 13 |
| 14 class Sample { |
| 15 public: |
| 16 enum { kFramesLimit = 255 }; |
| 17 |
| 18 Sample() {} |
| 19 |
| 20 typedef const void* const* const_iterator; |
| 21 const_iterator begin() const { return data_.start(); } |
| 22 const_iterator end() const { return &data_[data_.length()]; } |
| 23 |
| 24 int size() const { return data_.length(); } |
| 25 v8::internal::Vector<void*>& data() { return data_; } |
| 26 |
| 27 private: |
| 28 v8::internal::EmbeddedVector<void*, kFramesLimit> data_; |
| 29 }; |
| 30 |
| 31 |
| 32 class SamplingTestHelper { |
| 33 public: |
| 34 struct CodeEventEntry { |
| 35 std::string name; |
| 36 const void* code_start; |
| 37 size_t code_len; |
| 38 }; |
| 39 typedef std::map<const void*, CodeEventEntry> CodeEntries; |
| 40 |
| 41 explicit SamplingTestHelper(const std::string& test_function) |
| 42 : sample_is_taken_(false), isolate_(CcTest::isolate()) { |
| 43 DCHECK_EQ(NULL, instance_); |
| 44 instance_ = this; |
| 45 v8::HandleScope scope(isolate_); |
| 46 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_); |
| 47 global->Set(v8::String::NewFromUtf8(isolate_, "CollectSample"), |
| 48 v8::FunctionTemplate::New(isolate_, CollectSample)); |
| 49 LocalContext env(isolate_, NULL, global); |
| 50 isolate_->SetJitCodeEventHandler(v8::kJitCodeEventDefault, |
| 51 JitCodeEventHandler); |
| 52 v8::Script::Compile( |
| 53 v8::String::NewFromUtf8(isolate_, test_function.c_str()))->Run(); |
| 54 } |
| 55 |
| 56 ~SamplingTestHelper() { |
| 57 isolate_->SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL); |
| 58 instance_ = NULL; |
| 59 } |
| 60 |
| 61 Sample& sample() { return sample_; } |
| 62 |
| 63 const CodeEventEntry* FindEventEntry(const void* address) { |
| 64 CodeEntries::const_iterator it = code_entries_.upper_bound(address); |
| 65 if (it == code_entries_.begin()) return NULL; |
| 66 const CodeEventEntry& entry = (--it)->second; |
| 67 const void* code_end = |
| 68 static_cast<const uint8_t*>(entry.code_start) + entry.code_len; |
| 69 return address < code_end ? &entry : NULL; |
| 70 } |
| 71 |
| 72 private: |
| 73 static void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 74 instance_->DoCollectSample(); |
| 75 } |
| 76 |
| 77 static void JitCodeEventHandler(const v8::JitCodeEvent* event) { |
| 78 instance_->DoJitCodeEventHandler(event); |
| 79 } |
| 80 |
| 81 // The JavaScript calls this function when on full stack depth. |
| 82 void DoCollectSample() { |
| 83 v8::RegisterState state; |
| 84 state.pc = NULL; |
| 85 state.fp = &state; |
| 86 state.sp = &state; |
| 87 v8::SampleInfo info; |
| 88 isolate_->GetStackSample(state, sample_.data().start(), |
| 89 static_cast<size_t>(sample_.size()), &info); |
| 90 size_t frames_count = info.frames_count; |
| 91 CHECK_LE(frames_count, static_cast<size_t>(sample_.size())); |
| 92 sample_.data().Truncate(static_cast<int>(frames_count)); |
| 93 sample_is_taken_ = true; |
| 94 } |
| 95 |
| 96 void DoJitCodeEventHandler(const v8::JitCodeEvent* event) { |
| 97 if (sample_is_taken_) return; |
| 98 switch (event->type) { |
| 99 case v8::JitCodeEvent::CODE_ADDED: { |
| 100 CodeEventEntry entry; |
| 101 entry.name = std::string(event->name.str, event->name.len); |
| 102 entry.code_start = event->code_start; |
| 103 entry.code_len = event->code_len; |
| 104 code_entries_.insert(std::make_pair(entry.code_start, entry)); |
| 105 break; |
| 106 } |
| 107 case v8::JitCodeEvent::CODE_MOVED: { |
| 108 CodeEntries::const_iterator it = code_entries_.find(event->code_start); |
| 109 CHECK(it != code_entries_.end()); |
| 110 code_entries_.erase(it); |
| 111 CodeEventEntry entry; |
| 112 entry.name = std::string(event->name.str, event->name.len); |
| 113 entry.code_start = event->new_code_start; |
| 114 entry.code_len = event->code_len; |
| 115 code_entries_.insert(std::make_pair(entry.code_start, entry)); |
| 116 break; |
| 117 } |
| 118 case v8::JitCodeEvent::CODE_REMOVED: |
| 119 code_entries_.erase(event->code_start); |
| 120 break; |
| 121 default: |
| 122 break; |
| 123 } |
| 124 } |
| 125 |
| 126 Sample sample_; |
| 127 bool sample_is_taken_; |
| 128 v8::Isolate* isolate_; |
| 129 CodeEntries code_entries_; |
| 130 |
| 131 static SamplingTestHelper* instance_; |
| 132 }; |
| 133 |
| 134 SamplingTestHelper* SamplingTestHelper::instance_; |
| 135 |
| 136 } // namespace |
| 137 |
| 138 |
| 139 // A JavaScript function which takes stack depth |
| 140 // (minimum value 2) as an argument. |
| 141 // When at the bottom of the recursion, |
| 142 // the JavaScript code calls into C++ test code, |
| 143 // waiting for the sampler to take a sample. |
| 144 static const char* test_function = |
| 145 "function func(depth) {" |
| 146 " if (depth == 2) CollectSample();" |
| 147 " else return func(depth - 1);" |
| 148 "}"; |
| 149 |
| 150 |
| 151 TEST(StackDepthIsConsistent) { |
| 152 SamplingTestHelper helper(std::string(test_function) + "func(8);"); |
| 153 CHECK_EQ(8, helper.sample().size()); |
| 154 } |
| 155 |
| 156 |
| 157 TEST(StackDepthDoesNotExceedMaxValue) { |
| 158 SamplingTestHelper helper(std::string(test_function) + "func(300);"); |
| 159 CHECK_EQ(Sample::kFramesLimit, helper.sample().size()); |
| 160 } |
| 161 |
| 162 |
| 163 // The captured sample should have three pc values. |
| 164 // They should fall in the range where the compiled code resides. |
| 165 // The expected stack is: |
| 166 // bottom of stack [{anon script}, outer, inner] top of stack |
| 167 // ^ ^ ^ |
| 168 // sample.stack indices 2 1 0 |
| 169 TEST(StackFramesConsistent) { |
| 170 // Note: The arguments.callee stuff is there so that the |
| 171 // functions are not optimized away. |
| 172 const char* test_script = |
| 173 "function test_sampler_api_inner() {" |
| 174 " CollectSample();" |
| 175 " return arguments.callee.toString();" |
| 176 "}" |
| 177 "function test_sampler_api_outer() {" |
| 178 " return test_sampler_api_inner() + arguments.callee.toString();" |
| 179 "}" |
| 180 "test_sampler_api_outer();"; |
| 181 |
| 182 SamplingTestHelper helper(test_script); |
| 183 Sample& sample = helper.sample(); |
| 184 CHECK_EQ(3, sample.size()); |
| 185 |
| 186 const SamplingTestHelper::CodeEventEntry* entry; |
| 187 entry = helper.FindEventEntry(sample.begin()[0]); |
| 188 CHECK_NE(NULL, entry); |
| 189 CHECK(std::string::npos != entry->name.find("test_sampler_api_inner")); |
| 190 |
| 191 entry = helper.FindEventEntry(sample.begin()[1]); |
| 192 CHECK_NE(NULL, entry); |
| 193 CHECK(std::string::npos != entry->name.find("test_sampler_api_outer")); |
| 194 } |
OLD | NEW |