blob: e2c1c25638ba46fad1b23ec262bb95568ef501e0 [file] [log] [blame]
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001// 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"
11#include "test/cctest/cctest.h"
12
13namespace {
14
15class Sample {
16 public:
17 enum { kFramesLimit = 255 };
18
19 Sample() {}
20
21 typedef const void* const* const_iterator;
22 const_iterator begin() const { return data_.start(); }
23 const_iterator end() const { return &data_[data_.length()]; }
24
25 int size() const { return data_.length(); }
26 v8::internal::Vector<void*>& data() { return data_; }
27
28 private:
29 v8::internal::EmbeddedVector<void*, kFramesLimit> data_;
30};
31
32
33#if defined(USE_SIMULATOR)
34class SimulatorHelper {
35 public:
36 inline bool Init(v8::Isolate* isolate) {
37 simulator_ = reinterpret_cast<v8::internal::Isolate*>(isolate)
38 ->thread_local_top()
39 ->simulator_;
40 // Check if there is active simulator.
41 return simulator_ != NULL;
42 }
43
44 inline void FillRegisters(v8::RegisterState* state) {
45#if V8_TARGET_ARCH_ARM
46 state->pc = reinterpret_cast<void*>(simulator_->get_pc());
47 state->sp = reinterpret_cast<void*>(
48 simulator_->get_register(v8::internal::Simulator::sp));
49 state->fp = reinterpret_cast<void*>(
50 simulator_->get_register(v8::internal::Simulator::r11));
51#elif V8_TARGET_ARCH_ARM64
52 if (simulator_->sp() == 0 || simulator_->fp() == 0) {
53 // It's possible that the simulator is interrupted while it is updating
54 // the sp or fp register. ARM64 simulator does this in two steps:
55 // first setting it to zero and then setting it to a new value.
56 // Bailout if sp/fp doesn't contain the new value.
57 return;
58 }
59 state->pc = reinterpret_cast<void*>(simulator_->pc());
60 state->sp = reinterpret_cast<void*>(simulator_->sp());
61 state->fp = reinterpret_cast<void*>(simulator_->fp());
62#elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
63 state->pc = reinterpret_cast<void*>(simulator_->get_pc());
64 state->sp = reinterpret_cast<void*>(
65 simulator_->get_register(v8::internal::Simulator::sp));
66 state->fp = reinterpret_cast<void*>(
67 simulator_->get_register(v8::internal::Simulator::fp));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000068#elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
69 state->pc = reinterpret_cast<void*>(simulator_->get_pc());
70 state->sp = reinterpret_cast<void*>(
71 simulator_->get_register(v8::internal::Simulator::sp));
72 state->fp = reinterpret_cast<void*>(
73 simulator_->get_register(v8::internal::Simulator::fp));
Ben Murdochda12d292016-06-02 14:46:10 +010074#elif V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
75 state->pc = reinterpret_cast<void*>(simulator_->get_pc());
76 state->sp = reinterpret_cast<void*>(
77 simulator_->get_register(v8::internal::Simulator::sp));
78 state->fp = reinterpret_cast<void*>(
79 simulator_->get_register(v8::internal::Simulator::fp));
Emily Bernierd0a1eb72015-03-24 16:35:39 -040080#endif
81 }
82
83 private:
84 v8::internal::Simulator* simulator_;
85};
86#endif // USE_SIMULATOR
87
88
89class SamplingTestHelper {
90 public:
91 struct CodeEventEntry {
92 std::string name;
93 const void* code_start;
94 size_t code_len;
95 };
96 typedef std::map<const void*, CodeEventEntry> CodeEntries;
97
98 explicit SamplingTestHelper(const std::string& test_function)
99 : sample_is_taken_(false), isolate_(CcTest::isolate()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000100 CHECK(!instance_);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400101 instance_ = this;
102 v8::HandleScope scope(isolate_);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000103 v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);
104 global->Set(v8_str("CollectSample"),
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400105 v8::FunctionTemplate::New(isolate_, CollectSample));
106 LocalContext env(isolate_, NULL, global);
107 isolate_->SetJitCodeEventHandler(v8::kJitCodeEventDefault,
108 JitCodeEventHandler);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000109 CompileRun(v8_str(test_function.c_str()));
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400110 }
111
112 ~SamplingTestHelper() {
113 isolate_->SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL);
114 instance_ = NULL;
115 }
116
117 Sample& sample() { return sample_; }
118
119 const CodeEventEntry* FindEventEntry(const void* address) {
120 CodeEntries::const_iterator it = code_entries_.upper_bound(address);
121 if (it == code_entries_.begin()) return NULL;
122 const CodeEventEntry& entry = (--it)->second;
123 const void* code_end =
124 static_cast<const uint8_t*>(entry.code_start) + entry.code_len;
125 return address < code_end ? &entry : NULL;
126 }
127
128 private:
129 static void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) {
130 instance_->DoCollectSample();
131 }
132
133 static void JitCodeEventHandler(const v8::JitCodeEvent* event) {
134 instance_->DoJitCodeEventHandler(event);
135 }
136
137 // The JavaScript calls this function when on full stack depth.
138 void DoCollectSample() {
139 v8::RegisterState state;
140#if defined(USE_SIMULATOR)
141 SimulatorHelper simulator_helper;
142 if (!simulator_helper.Init(isolate_)) return;
143 simulator_helper.FillRegisters(&state);
144#else
145 state.pc = NULL;
146 state.fp = &state;
147 state.sp = &state;
148#endif
149 v8::SampleInfo info;
150 isolate_->GetStackSample(state, sample_.data().start(),
151 static_cast<size_t>(sample_.size()), &info);
152 size_t frames_count = info.frames_count;
153 CHECK_LE(frames_count, static_cast<size_t>(sample_.size()));
154 sample_.data().Truncate(static_cast<int>(frames_count));
155 sample_is_taken_ = true;
156 }
157
158 void DoJitCodeEventHandler(const v8::JitCodeEvent* event) {
159 if (sample_is_taken_) return;
160 switch (event->type) {
161 case v8::JitCodeEvent::CODE_ADDED: {
162 CodeEventEntry entry;
163 entry.name = std::string(event->name.str, event->name.len);
164 entry.code_start = event->code_start;
165 entry.code_len = event->code_len;
166 code_entries_.insert(std::make_pair(entry.code_start, entry));
167 break;
168 }
169 case v8::JitCodeEvent::CODE_MOVED: {
170 CodeEntries::iterator it = code_entries_.find(event->code_start);
171 CHECK(it != code_entries_.end());
172 code_entries_.erase(it);
173 CodeEventEntry entry;
174 entry.name = std::string(event->name.str, event->name.len);
175 entry.code_start = event->new_code_start;
176 entry.code_len = event->code_len;
177 code_entries_.insert(std::make_pair(entry.code_start, entry));
178 break;
179 }
180 case v8::JitCodeEvent::CODE_REMOVED:
181 code_entries_.erase(event->code_start);
182 break;
183 default:
184 break;
185 }
186 }
187
188 Sample sample_;
189 bool sample_is_taken_;
190 v8::Isolate* isolate_;
191 CodeEntries code_entries_;
192
193 static SamplingTestHelper* instance_;
194};
195
196SamplingTestHelper* SamplingTestHelper::instance_;
197
198} // namespace
199
200
201// A JavaScript function which takes stack depth
202// (minimum value 2) as an argument.
203// When at the bottom of the recursion,
204// the JavaScript code calls into C++ test code,
205// waiting for the sampler to take a sample.
206static const char* test_function =
207 "function func(depth) {"
208 " if (depth == 2) CollectSample();"
209 " else return func(depth - 1);"
210 "}";
211
212
213TEST(StackDepthIsConsistent) {
214 SamplingTestHelper helper(std::string(test_function) + "func(8);");
215 CHECK_EQ(8, helper.sample().size());
216}
217
218
219TEST(StackDepthDoesNotExceedMaxValue) {
220 SamplingTestHelper helper(std::string(test_function) + "func(300);");
221 CHECK_EQ(Sample::kFramesLimit, helper.sample().size());
222}
223
224
225// The captured sample should have three pc values.
226// They should fall in the range where the compiled code resides.
227// The expected stack is:
228// bottom of stack [{anon script}, outer, inner] top of stack
229// ^ ^ ^
230// sample.stack indices 2 1 0
231TEST(StackFramesConsistent) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000232 i::FLAG_allow_natives_syntax = true;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400233 const char* test_script =
234 "function test_sampler_api_inner() {"
235 " CollectSample();"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000236 " return 0;"
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400237 "}"
238 "function test_sampler_api_outer() {"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000239 " return test_sampler_api_inner();"
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400240 "}"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000241 "%NeverOptimizeFunction(test_sampler_api_inner);"
242 "%NeverOptimizeFunction(test_sampler_api_outer);"
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400243 "test_sampler_api_outer();";
244
245 SamplingTestHelper helper(test_script);
246 Sample& sample = helper.sample();
247 CHECK_EQ(3, sample.size());
248
249 const SamplingTestHelper::CodeEventEntry* entry;
250 entry = helper.FindEventEntry(sample.begin()[0]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000251 CHECK(entry);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400252 CHECK(std::string::npos != entry->name.find("test_sampler_api_inner"));
253
254 entry = helper.FindEventEntry(sample.begin()[1]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000255 CHECK(entry);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400256 CHECK(std::string::npos != entry->name.find("test_sampler_api_outer"));
257}