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

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

Powered by Google App Engine
This is Rietveld 408576698