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 <string> | |
8 #include "include/v8.h" | |
9 #include "src/v8.h" | |
10 #include "test/cctest/cctest.h" | |
11 | |
12 #if V8_OS_POSIX && !V8_OS_CYGWIN | |
13 #include <ucontext.h> | |
14 #elif V8_OS_WIN || V8_OS_CYGWIN | |
15 #include "src/base/win32-headers.h" | |
16 #endif | |
17 | |
18 using v8::Local; | |
19 | |
20 namespace { | |
21 // The Sample which CollectSample fills up | |
22 v8::Sample* sample; | |
23 | |
24 // The isolate used in the test | |
25 v8::Isolate* isolate; | |
26 | |
27 // Forward declaration | |
28 // (platform specific implementation at the bottom of this file) | |
29 void GetStackAndFramePointers(void** sp, void** fp); | |
30 | |
31 // The JavaScript calls this function when on full stack depth. | |
32 void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
33 void* sp = NULL; | |
34 void* fp = NULL; | |
35 GetStackAndFramePointers(&sp, &fp); | |
36 isolate->GetSample(sp, fp, sample); | |
37 } | |
38 | |
39 | |
40 // A JavaScript function which takes stack depth | |
41 // (minimum value 2) as an argument. | |
42 // When at the bottom of the recursion, | |
43 // the JavaScript code calls into C++ test code, | |
44 // waiting for the sampler to take a sample. | |
45 static const char* test_function = | |
46 "function func(depth) {" | |
47 " if (depth == 2) CollectSample();" | |
48 " else return func(depth - 1);" | |
49 "}"; | |
50 } | |
51 | |
52 | |
53 #define SAMPLER_API_TESTS_BOOTSTRAP() \ | |
54 v8::Sample sample_; \ | |
55 sample = &sample_; \ | |
56 isolate = CcTest::isolate(); \ | |
57 v8::HandleScope scope(isolate); \ | |
58 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); \ | |
59 global->Set(v8::String::NewFromUtf8(isolate, "CollectSample"), \ | |
60 v8::FunctionTemplate::New(isolate, CollectSample)); \ | |
61 LocalContext env(isolate, NULL, global) | |
62 | |
63 | |
64 TEST(StackDepthIsConsistent) { | |
65 SAMPLER_API_TESTS_BOOTSTRAP(); | |
66 | |
67 std::string source(test_function); | |
68 source.append("func(8);"); | |
69 v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run(); | |
70 | |
71 CHECK_EQ(8, sample->size()); | |
72 } | |
73 | |
74 | |
75 TEST(StackDepthDoesNotExceedMaxValue) { | |
76 SAMPLER_API_TESTS_BOOTSTRAP(); | |
77 | |
78 std::string source(test_function); | |
79 source.append("func(300);"); | |
80 v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run(); | |
81 | |
82 int MAX_SIZE = v8::Sample::kMaxSize; | |
83 CHECK_EQ(MAX_SIZE, sample->size()); | |
84 } | |
85 | |
86 | |
87 namespace { | |
88 std::vector<v8::JitCodeEvent> inner_funcs; | |
89 std::vector<v8::JitCodeEvent> outer_funcs; | |
90 | |
91 void TestJitCodeEventHandler(const v8::JitCodeEvent* event) { | |
92 if (event->type != v8::JitCodeEvent::CODE_ADDED) return; | |
93 std::string name(event->name.str, event->name.len); | |
94 if (name.find("test_sampler_api_inner") != std::string::npos) | |
95 inner_funcs.push_back(*event); | |
96 if (name.find("test_sampler_api_outer") != std::string::npos) | |
97 outer_funcs.push_back(*event); | |
98 } | |
99 | |
100 | |
101 // Note: The argumnets.callee stuff is there so that the | |
alph
2014/09/18 11:34:29
typo
gholap
2014/09/18 23:24:15
Done.
| |
102 // functions are not optimized away. | |
103 static const char* test_script = | |
104 "function test_sampler_api_inner() {" | |
105 " CollectSample();" | |
106 " return arguments.callee.toString();" | |
107 "}" | |
108 "function test_sampler_api_outer() {" | |
109 " return test_sampler_api_inner() + arguments.callee.toString();" | |
110 "}" | |
111 "test_sampler_api_outer();"; | |
112 } | |
113 | |
114 | |
115 // The captured sample should have three pc values. | |
116 // They should fall in the range where the compiled code | |
117 // The expected stack is: | |
118 // bottom of stack [{anon script}, outer, inner] top of stack | |
119 // ^ ^ ^ | |
120 // sample.stack indices 2 1 0 | |
121 TEST(StackFramesConsistent) { | |
122 SAMPLER_API_TESTS_BOOTSTRAP(); | |
123 | |
124 v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, | |
125 TestJitCodeEventHandler); | |
126 v8::Script::Compile(v8::String::NewFromUtf8(isolate, test_script))->Run(); | |
127 | |
128 CHECK_EQ(3, sample->size()); | |
129 | |
130 bool stack_top_is_inner = false; | |
131 bool below_inner_is_outer = false; | |
132 | |
133 for (unsigned i = 0; i < inner_funcs.size(); i++) { | |
134 void* start_addr = inner_funcs[i].code_start; | |
135 void* end_addr = reinterpret_cast<void*>( | |
136 (int64_t)inner_funcs[i].code_start + inner_funcs[i].code_len); | |
137 if ((*sample->begin() >= start_addr) && (*sample->begin() < end_addr)) | |
138 stack_top_is_inner = true; | |
139 } | |
140 | |
141 for (unsigned i = 0; i < outer_funcs.size(); i++) { | |
142 void* start_addr = outer_funcs[i].code_start; | |
143 void* end_addr = reinterpret_cast<void*>( | |
144 (int64_t)outer_funcs[i].code_start + outer_funcs[i].code_len); | |
145 if ((*(sample->begin() + 1) >= start_addr) && | |
146 (*(sample->begin() + 1) < end_addr)) | |
147 below_inner_is_outer = true; | |
148 } | |
149 | |
150 CHECK_EQ(true, stack_top_is_inner); | |
alph
2014/09/18 11:34:29
CHECK
gholap
2014/09/18 23:24:15
Done.
| |
151 CHECK_EQ(true, below_inner_is_outer); | |
152 } | |
153 | |
154 | |
155 // Platform specific implementation of GetStackAndFramePointers | |
loislo
2014/09/18 14:42:44
this is doesn't look as a good idea.
I mean we ha
gholap
2014/09/18 23:24:15
Oh! I did not know that...
I checked the TickSampl
| |
156 namespace { | |
157 #if V8_OS_POSIX && !V8_OS_CYGWIN | |
158 void GetStackAndFramePointers(void** sp, void** fp) { | |
159 ucontext_t ucontext; | |
160 getcontext(&ucontext); | |
161 #if !V8_OS_OPENBSD | |
162 mcontext_t& mcontext = ucontext.uc_mcontext; | |
163 #endif | |
164 #if V8_OS_LINUX | |
165 #if V8_HOST_ARCH_IA32 | |
166 *sp = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]); | |
167 *fp = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]); | |
168 #elif V8_HOST_ARCH_X64 | |
169 *sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]); | |
170 *fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]); | |
171 #elif V8_HOST_ARCH_ARM | |
172 #if defined(__GLIBC__) && !defined(__UCLIBC__) && \ | |
173 (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) | |
174 // Old GLibc ARM versions used a gregs[] array to access the register | |
175 // values from mcontext_t. | |
176 *sp = reinterpret_cast<void*>(mcontext.gregs[R13]); | |
177 *fp = reinterpret_cast<void*>(mcontext.gregs[R11]); | |
178 #else | |
179 *sp = reinterpret_cast<void*>(mcontext.arm_sp); | |
180 *fp = reinterpret_cast<void*>(mcontext.arm_fp); | |
181 #endif // defined(__GLIBC__) && !defined(__UCLIBC__) && | |
182 // (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) | |
183 #elif V8_HOST_ARCH_ARM64 | |
184 *sp = reinterpret_cast<void*>(mcontext.sp); | |
185 // FP is an alias for x29. | |
186 *fp = reinterpret_cast<void*>(mcontext.regs[29]); | |
187 #elif V8_HOST_ARCH_MIPS | |
188 *sp = reinterpret_cast<void*>(mcontext.gregs[29]); | |
189 *fp = reinterpret_cast<void*>(mcontext.gregs[30]); | |
190 #endif // V8_HOST_ARCH_* | |
191 #elif V8_OS_MACOSX | |
192 #if V8_HOST_ARCH_X64 | |
193 #if __DARWIN_UNIX03 | |
194 *sp = reinterpret_cast<void*>(mcontext->__ss.__rsp); | |
195 *fp = reinterpret_cast<void*>(mcontext->__ss.__rbp); | |
196 #else // !__DARWIN_UNIX03 | |
197 *sp = reinterpret_cast<void*>(mcontext->ss.rsp); | |
198 *fp = reinterpret_cast<void*>(mcontext->ss.rbp); | |
199 #endif // __DARWIN_UNIX03 | |
200 #elif V8_HOST_ARCH_IA32 | |
201 #if __DARWIN_UNIX03 | |
202 *sp = mcontext->__ss.__esp); | |
203 *fp = mcontext->__ss.__ebp); | |
204 #else // !__DARWIN_UNIX03 | |
205 *sp = mcontext->ss.esp); | |
206 *fp = mcontext->ss.ebp); | |
207 #endif // __DARWIN_UNIX03 | |
208 #endif // V8_HOST_ARCH_IA32 | |
209 #elif V8_OS_FREEBSD | |
210 #if V8_HOST_ARCH_IA32 | |
211 *sp = reinterpret_cast<void*>(mcontext.mc_esp); | |
212 *fp = reinterpret_cast<void*>(mcontext.mc_ebp); | |
213 #elif V8_HOST_ARCH_X64 | |
214 *sp = reinterpret_cast<void*>(mcontext.mc_rsp); | |
215 *fp = reinterpret_cast<void*>(mcontext.mc_rbp); | |
216 #elif V8_HOST_ARCH_ARM | |
217 *sp = reinterpret_cast<void*>(mcontext.mc_r13); | |
218 *fp = reinterpret_cast<void*>(mcontext.mc_r11); | |
219 #endif // V8_HOST_ARCH_* | |
220 #elif V8_OS_NETBSD | |
221 #if V8_HOST_ARCH_IA32 | |
222 *sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_ESP]); | |
223 *fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_EBP]); | |
224 #elif V8_HOST_ARCH_X64 | |
225 *sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RSP]); | |
226 *fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RBP]); | |
227 #endif // V8_HOST_ARCH_* | |
228 #elif V8_OS_OPENBSD | |
229 #if V8_HOST_ARCH_IA32 | |
230 *sp = reinterpret_cast<void*>(ucontext.sc_esp); | |
231 *fp = reinterpret_cast<void*>(ucontext.sc_ebp); | |
232 #elif V8_HOST_ARCH_X64 | |
233 *sp = reinterpret_cast<void*>(ucontext.sc_rsp); | |
234 *fp = reinterpret_cast<void*>(ucontext.sc_rbp); | |
235 #endif // V8_HOST_ARCH_* | |
236 #elif V8_OS_SOLARIS | |
237 *sp = reinterpret_cast<void*>(mcontext.gregs[REG_SP]); | |
238 *fp = reinterpret_cast<void*>(mcontext.gregs[REG_FP]); | |
239 #elif V8_OS_QNX | |
240 #if V8_HOST_ARCH_IA32 | |
241 *sp = reinterpret_cast<void*>(mcontext.cpu.esp); | |
242 *fp = reinterpret_cast<void*>(mcontext.cpu.ebp); | |
243 #elif V8_HOST_ARCH_ARM | |
244 *sp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_SP]); | |
245 *fp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_FP]); | |
246 #endif // V8_HOST_ARCH_* | |
247 #endif // V8_OS_QNX | |
248 } | |
249 #elif V8_OS_WIN || V8_OS_CYGWIN | |
250 void GetStackAndFramePointers(void** sp, void** fp) { | |
alph
2014/09/18 11:34:29
you can move the function name & args out of #ifde
gholap
2014/09/18 23:24:15
Done.
| |
251 CONTEXT context; | |
252 memset(&context, 0, sizeof(context)); | |
253 context.ContextFlags = CONTEXT_FULL; | |
254 GetThreadContext(OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | | |
255 THREAD_QUERY_INFORMATION, | |
256 false, GetCurrentThreadId()), | |
257 &context); | |
258 #if V8_HOST_ARCH_X64 | |
259 *sp = reinterpret_cast<void*>(context.Rsp); | |
260 *fp = reinterpret_cast<void*>(context.Rbp); | |
261 #else | |
262 *sp = reinterpret_cast<void*>(context.Esp); | |
263 *fp = reinterpret_cast<void*>(context.Ebp); | |
264 #endif // V8_HOST_ARCH_X64 | |
265 } | |
266 #endif // V8_OS_POSIX && !V8_OS_CYGWIN / V8_OS_WIN || V8_OS_CYGWIN | |
267 } | |
OLD | NEW |