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