Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(407)

Side by Side Diff: test/cctest/test-sampler-api.cc

Issue 596533002: Initial implementation of GetStackSample sampling profiler API. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Fixing Win64 compile Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« src/sampler.cc ('K') | « test/cctest/cctest.status ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 <string>
8 #include "include/v8.h"
9 #include "src/simulator.h"
10 #include "src/utils.h"
11 #include "src/v8.h"
12 #include "test/cctest/cctest.h"
13
14 #if V8_OS_POSIX && !V8_OS_CYGWIN && !V8_OS_MACOSX
15 #include <ucontext.h>
16
17 #elif V8_OS_WIN || V8_OS_CYGWIN // V8_OS_POSIX && !V8_OS_CYGWIN
18 #include "src/base/win32-headers.h"
19
20 #endif // V8_OS_WIN || V8_OS_CYGWIN
21
22 using v8::Local;
23 using v8::internal::Address;
24 using v8::internal::Isolate;
25
26 namespace {
27
28 class Sample {
29 public:
30 enum { kFramesLimit = 255 };
31
32 Sample() {}
33
34 typedef const void* const* const_iterator;
35 const_iterator begin() const { return data_.start(); }
36 const_iterator end() const { return &data_[data_.length()]; }
37
38 int size() const { return data_.length(); }
39 v8::internal::Vector<void*>& data() { return data_; }
40
41 private:
42 v8::internal::EmbeddedVector<void*, kFramesLimit> data_;
43 };
44
45
46 // The Sample which CollectSample fills up
47 Sample* sample;
48
49 // The isolate used in the test
50 v8::Isolate* isolate;
51
52 // Forward declaration
53 // (platform specific implementation at the bottom of this file)
54 void FillRegisterState(v8::RegisterState* state);
55
56 // The JavaScript calls this function when on full stack depth.
57 void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) {
58 v8::RegisterState state;
59 FillRegisterState(&state);
60 size_t frames_count = isolate->GetStackSample(
61 state, sample->data().start(), static_cast<size_t>(sample->size()));
62 CHECK_LE(frames_count, static_cast<size_t>(sample->size()));
63 sample->data().Truncate(static_cast<int>(frames_count));
64 }
65
66
67 // A JavaScript function which takes stack depth
68 // (minimum value 2) as an argument.
69 // When at the bottom of the recursion,
70 // the JavaScript code calls into C++ test code,
71 // waiting for the sampler to take a sample.
72 static const char* test_function =
73 "function func(depth) {"
74 " if (depth == 2) CollectSample();"
75 " else return func(depth - 1);"
76 "}";
77
78 } // namespace
79
80
81 #define SAMPLER_API_TESTS_BOOTSTRAP() \
82 Sample test_sample; \
83 sample = &test_sample; \
84 isolate = CcTest::isolate(); \
85 v8::HandleScope scope(isolate); \
86 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); \
87 global->Set(v8::String::NewFromUtf8(isolate, "CollectSample"), \
88 v8::FunctionTemplate::New(isolate, CollectSample)); \
89 LocalContext env(isolate, NULL, global)
90
91
92 TEST(StackDepthIsConsistent) {
93 SAMPLER_API_TESTS_BOOTSTRAP();
94
95 std::string source(test_function);
96 source.append("func(8);");
97 v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run();
98
99 CHECK_EQ(8, sample->size());
100 }
101
102
103 TEST(StackDepthDoesNotExceedMaxValue) {
104 SAMPLER_API_TESTS_BOOTSTRAP();
105
106 std::string source(test_function);
107 source.append("func(300);");
108 v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run();
109
110 int MAX_SIZE = Sample::kFramesLimit;
111 CHECK_EQ(MAX_SIZE, sample->size());
112 }
113
114
115 namespace {
116 std::vector<v8::JitCodeEvent> inner_funcs;
117 std::vector<v8::JitCodeEvent> outer_funcs;
118
119 void TestJitCodeEventHandler(const v8::JitCodeEvent* event) {
120 if (event->type != v8::JitCodeEvent::CODE_ADDED) return;
121 std::string name(event->name.str, event->name.len);
122 if (name.find("test_sampler_api_inner") != std::string::npos)
123 inner_funcs.push_back(*event);
124 if (name.find("test_sampler_api_outer") != std::string::npos)
125 outer_funcs.push_back(*event);
126 }
127
128
129 // Note: The arguments.callee stuff is there so that the
130 // functions are not optimized away.
131 static const char* test_script =
132 "function test_sampler_api_inner() {"
133 " CollectSample();"
134 " return arguments.callee.toString();"
135 "}"
136 "function test_sampler_api_outer() {"
137 " return test_sampler_api_inner() + arguments.callee.toString();"
138 "}"
139 "test_sampler_api_outer();";
140 }
141
142
143 // The captured sample should have three pc values.
144 // They should fall in the range where the compiled code
145 // The expected stack is:
146 // bottom of stack [{anon script}, outer, inner] top of stack
147 // ^ ^ ^
148 // sample.stack indices 2 1 0
149 TEST(StackFramesConsistent) {
150 SAMPLER_API_TESTS_BOOTSTRAP();
151
152 isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault,
153 TestJitCodeEventHandler);
154 v8::Script::Compile(v8::String::NewFromUtf8(isolate, test_script))->Run();
155
156 CHECK_EQ(3, sample->size());
157
158 bool stack_top_is_inner = false;
159 bool below_inner_is_outer = false;
160
161 for (unsigned i = 0; i < inner_funcs.size(); i++) {
162 void* start_addr = inner_funcs[i].code_start;
163 void* end_addr = reinterpret_cast<void*>(
164 (int64_t)inner_funcs[i].code_start + inner_funcs[i].code_len);
165 if ((*sample->begin() >= start_addr) && (*sample->begin() < end_addr))
166 stack_top_is_inner = true;
167 }
168
169 for (unsigned i = 0; i < outer_funcs.size(); i++) {
170 void* start_addr = outer_funcs[i].code_start;
171 void* end_addr = reinterpret_cast<void*>(
172 (int64_t)outer_funcs[i].code_start + outer_funcs[i].code_len);
173 if ((*(sample->begin() + 1) >= start_addr) &&
174 (*(sample->begin() + 1) < end_addr))
175 below_inner_is_outer = true;
176 }
177
178 CHECK(stack_top_is_inner);
179 CHECK(below_inner_is_outer);
180 }
181
182
183 namespace {
184
185 #if defined(USE_SIMULATOR)
186 class SimulatorHelper {
187 public:
188 inline bool Init(v8::Isolate* isolate) {
189 simulator_ = reinterpret_cast<v8::internal::Isolate*>(isolate)
190 ->thread_local_top()
191 ->simulator_;
192 // Check if there is active simulator.
193 return simulator_ != NULL;
194 }
195
196 inline void FillRegisters(v8::RegisterState* state) {
197 #if V8_TARGET_ARCH_ARM
198 state->pc = reinterpret_cast<Address>(simulator_->get_pc());
199 state->sp = reinterpret_cast<Address>(
200 simulator_->get_register(v8::internal::Simulator::sp));
201 state->fp = reinterpret_cast<Address>(
202 simulator_->get_register(v8::internal::Simulator::r11));
203 #elif V8_TARGET_ARCH_ARM64
204 if (simulator_->sp() == 0 || simulator_->fp() == 0) {
205 // It possible that the simulator is interrupted while it is updating
206 // the sp or fp register. ARM64 simulator does this in two steps:
207 // first setting it to zero and then setting it to the new value.
208 // Bailout if sp/fp doesn't contain the new value.
209 return;
210 }
211 state->pc = reinterpret_cast<Address>(simulator_->pc());
212 state->sp = reinterpret_cast<Address>(simulator_->sp());
213 state->fp = reinterpret_cast<Address>(simulator_->fp());
214 #elif V8_TARGET_ARCH_MIPS
215 state->pc = reinterpret_cast<Address>(simulator_->get_pc());
216 state->sp =
217 reinterpret_cast<Address>(simulator_->get_register(Simulator::sp));
218 state->fp =
219 reinterpret_cast<Address>(simulator_->get_register(Simulator::fp));
220 #elif V8_TARGET_ARCH_MIPS64
221 state->pc = reinterpret_cast<Address>(simulator_->get_pc());
222 state->sp = reinterpret_cast<Address>(
223 simulator_->get_register(v8::internal::Simulator::sp));
224 state->fp = reinterpret_cast<Address>(
225 simulator_->get_register(v8::internal::Simulator::fp));
226 #endif
227 }
228
229 private:
230 v8::internal::Simulator* simulator_;
231 };
232 #endif // USE_SIMULATOR
233
234 // Platform specific implementation of FillRegisterState
235 void FillRegisterState(v8::RegisterState* state) {
236 #if defined(USE_SIMULATOR)
237 SimulatorHelper helper;
238 if (!helper.Init(isolate)) return;
239 helper.FillRegisters(state);
240 // It's possible that the simulator is interrupted while it is updating
241 // the sp or fp register. ARM64 simulator does this in two steps:
242 // first setting it to zero and then setting it to a new value.
243 // Bailout if sp/fp doesn't contain the new value.
244 if (state->sp == 0 || state->fp == 0) return;
245
246 #elif V8_OS_MACOSX
247 // TODO(alph): MacOSX doesn't support getcontext, so do nothing
248 // at the moment. Consider enabling the test by obtaining the
249 // context via POSIX signals.
250 return;
251
252 #elif V8_OS_POSIX && !V8_OS_CYGWIN // defined(USE_SIMULATOR)
253 ucontext_t ucontext;
254 getcontext(&ucontext);
255 #if !V8_OS_OPENBSD
256 mcontext_t& mcontext = ucontext.uc_mcontext;
257 #endif
258 #if V8_OS_LINUX
259 #if V8_HOST_ARCH_IA32
260 state->pc = reinterpret_cast<void*>(mcontext.gregs[REG_EIP]);
261 state->sp = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]);
262 state->fp = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]);
263 #elif V8_HOST_ARCH_X64
264 state->pc = reinterpret_cast<void*>(mcontext.gregs[REG_RIP]);
265 state->sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]);
266 state->fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]);
267 #elif V8_HOST_ARCH_ARM
268 #if defined(__GLIBC__) && !defined(__UCLIBC__) && \
269 (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
270 // Old GLibc ARM versions used a gregs[] array to access the register
271 // values from mcontext_t.
272 state->pc = reinterpret_cast<void*>(mcontext.gregs[R15]);
273 state->sp = reinterpret_cast<void*>(mcontext.gregs[R13]);
274 state->fp = reinterpret_cast<void*>(mcontext.gregs[R11]);
275 #else
276 state->pc = reinterpret_cast<void*>(mcontext.arm_pc);
277 state->sp = reinterpret_cast<void*>(mcontext.arm_sp);
278 state->fp = reinterpret_cast<void*>(mcontext.arm_fp);
279 #endif // defined(__GLIBC__) && !defined(__UCLIBC__) &&
280 // (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
281 #elif V8_HOST_ARCH_ARM64
282 state->pc = reinterpret_cast<void*>(mcontext.pc);
283 state->sp = reinterpret_cast<void*>(mcontext.sp);
284 // FP is an alias for x29.
285 state->fp = reinterpret_cast<void*>(mcontext.regs[29]);
286 #elif V8_HOST_ARCH_MIPS
287 state->pc = reinterpret_cast<void*>(mcontext.pc);
288 state->sp = reinterpret_cast<void*>(mcontext.gregs[29]);
289 state->fp = reinterpret_cast<void*>(mcontext.gregs[30]);
290 #elif V8_HOST_ARCH_MIPS64
291 state->pc = reinterpret_cast<void*>(mcontext.pc);
292 state->sp = reinterpret_cast<void*>(mcontext.gregs[29]);
293 state->fp = reinterpret_cast<void*>(mcontext.gregs[30]);
294 #endif // V8_HOST_ARCH_*
295 #elif V8_OS_MACOSX
296 #if V8_HOST_ARCH_X64
297 #if __DARWIN_UNIX03
298 state->pc = reinterpret_cast<void*>(mcontext->__ss.__rip);
299 state->sp = reinterpret_cast<void*>(mcontext->__ss.__rsp);
300 state->fp = reinterpret_cast<void*>(mcontext->__ss.__rbp);
301 #else // !__DARWIN_UNIX03
302 state->pc = reinterpret_cast<void*>(mcontext->ss.rip);
303 state->sp = reinterpret_cast<void*>(mcontext->ss.rsp);
304 state->fp = reinterpret_cast<void*>(mcontext->ss.rbp);
305 #endif // __DARWIN_UNIX03
306 #elif V8_HOST_ARCH_IA32
307 #if __DARWIN_UNIX03
308 state->pc = reinterpret_cast<void*>(mcontext->__ss.__eip);
309 state->sp = reinterpret_cast<void*>(mcontext->__ss.__esp);
310 state->fp = reinterpret_cast<void*>(mcontext->__ss.__ebp);
311 #else // !__DARWIN_UNIX03
312 state->pc = reinterpret_cast<void*>(mcontext->ss.eip);
313 state->sp = reinterpret_cast<void*>(mcontext->ss.esp);
314 state->fp = reinterpret_cast<void*>(mcontext->ss.ebp);
315 #endif // __DARWIN_UNIX03
316 #endif // V8_HOST_ARCH_IA32
317 #elif V8_OS_FREEBSD
318 #if V8_HOST_ARCH_IA32
319 state->pc = reinterpret_cast<void*>(mcontext.mc_eip);
320 state->sp = reinterpret_cast<void*>(mcontext.mc_esp);
321 state->fp = reinterpret_cast<void*>(mcontext.mc_ebp);
322 #elif V8_HOST_ARCH_X64
323 state->pc = reinterpret_cast<void*>(mcontext.mc_rip);
324 state->sp = reinterpret_cast<void*>(mcontext.mc_rsp);
325 state->fp = reinterpret_cast<void*>(mcontext.mc_rbp);
326 #elif V8_HOST_ARCH_ARM
327 state->pc = reinterpret_cast<void*>(mcontext.mc_r15);
328 state->sp = reinterpret_cast<void*>(mcontext.mc_r13);
329 state->fp = reinterpret_cast<void*>(mcontext.mc_r11);
330 #endif // V8_HOST_ARCH_*
331 #elif V8_OS_NETBSD
332 #if V8_HOST_ARCH_IA32
333 state->pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_EIP]);
334 state->sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_ESP]);
335 state->fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_EBP]);
336 #elif V8_HOST_ARCH_X64
337 state->pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_RIP]);
338 state->sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RSP]);
339 state->fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RBP]);
340 #endif // V8_HOST_ARCH_*
341 #elif V8_OS_OPENBSD
342 #if V8_HOST_ARCH_IA32
343 state->pc = reinterpret_cast<void*>(ucontext->sc_eip);
344 state->sp = reinterpret_cast<void*>(ucontext->sc_esp);
345 state->fp = reinterpret_cast<void*>(ucontext->sc_ebp);
346 #elif V8_HOST_ARCH_X64
347 state->pc = reinterpret_cast<void*>(ucontext->sc_rip);
348 state->sp = reinterpret_cast<void*>(ucontext->sc_rsp);
349 state->fp = reinterpret_cast<void*>(ucontext->sc_rbp);
350 #endif // V8_HOST_ARCH_*
351 #elif V8_OS_SOLARIS
352 state->pc = reinterpret_cast<void*>(mcontext.gregs[REG_PC]);
353 state->sp = reinterpret_cast<void*>(mcontext.gregs[REG_SP]);
354 state->fp = reinterpret_cast<void*>(mcontext.gregs[REG_FP]);
355 #elif V8_OS_QNX
356 #if V8_HOST_ARCH_IA32
357 state->pc = reinterpret_cast<void*>(mcontext.cpu.eip);
358 state->sp = reinterpret_cast<void*>(mcontext.cpu.esp);
359 state->fp = reinterpret_cast<void*>(mcontext.cpu.ebp);
360 #elif V8_HOST_ARCH_ARM
361 state->pc = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_PC]);
362 state->sp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_SP]);
363 state->fp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_FP]);
364 #endif // V8_HOST_ARCH_*
365 #endif // V8_OS_QNX
366
367 #elif V8_OS_WIN || V8_OS_CYGWIN // V8_OS_POSIX && !V8_OS_CYGWIN
368 CONTEXT context;
369 memset(&context, 0, sizeof(context));
370 context.ContextFlags = CONTEXT_FULL;
371 GetThreadContext(OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME |
372 THREAD_QUERY_INFORMATION,
373 false, GetCurrentThreadId()),
374 &context);
375 #if V8_HOST_ARCH_X64
376 state->pc = reinterpret_cast<void*>(context.Rip);
377 state->sp = reinterpret_cast<void*>(context.Rsp);
378 state->fp = reinterpret_cast<void*>(context.Rbp);
379 #else
380 state->pc = reinterpret_cast<void*>(context.Eip);
381 state->sp = reinterpret_cast<void*>(context.Esp);
382 state->fp = reinterpret_cast<void*>(context.Ebp);
383 #endif // V8_HOST_ARCH_X64
384 #endif // V8_OS_WIN || V8_OS_CYGWIN
385 }
386
387 } // namespace
OLDNEW
« src/sampler.cc ('K') | « test/cctest/cctest.status ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698