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 |