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

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

Powered by Google App Engine
This is Rietveld 408576698