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/base/platform/platform.h" |
| 10 #include "src/sampler.h" |
| 11 #include "src/v8.h" |
| 12 #include "test/cctest/cctest.h" |
| 13 |
| 14 using v8::Local; |
| 15 |
| 16 namespace { |
| 17 static const int kMaxSampleDepth = 10; |
| 18 |
| 19 // The Sample which CollectSample fills up |
| 20 void* sample[kMaxSampleDepth]; |
| 21 int sample_depth; |
| 22 |
| 23 // The isolate used in the test |
| 24 v8::Isolate* isolate; |
| 25 |
| 26 #if V8_OS_POSIX && !V8_OS_CYGWIN |
| 27 v8::RegisterState GetRegisterState(void* context); |
| 28 |
| 29 // Signaal handler signals on this semaphore once it finishes. |
| 30 v8::base::Semaphore* semaphore; |
| 31 |
| 32 static void HandleSigprof(int signal, siginfo_t* info, void* context) { |
| 33 USE(info); |
| 34 if (signal != SIGPROF) return; |
| 35 fprintf(stderr, "inside the handler\n"); |
| 36 sample_depth = isolate->GetSample(GetRegisterState(context), |
| 37 sample, |
| 38 kMaxSampleDepth); |
| 39 semaphore->Signal(); |
| 40 } |
| 41 |
| 42 |
| 43 void InstallSignalHandler() { |
| 44 struct sigaction sa; |
| 45 sa.sa_sigaction = &HandleSigprof; |
| 46 sigemptyset(&sa.sa_mask); |
| 47 #if V8_OS_QNX |
| 48 sa.sa_flags = SA_SIGINFO; |
| 49 #else |
| 50 sa.sa_flags = SA_RESTART | SA_SIGINFO; |
| 51 #endif |
| 52 sigaction(SIGPROF, &sa, 0); |
| 53 } |
| 54 |
| 55 |
| 56 // The JavaScript calls this function when on full stack depth. |
| 57 void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 58 semaphore = new v8::base::Semaphore(0); |
| 59 InstallSignalHandler(); |
| 60 pthread_kill(pthread_self(), SIGPROF); |
| 61 fprintf(stderr, "Sent the signal\n"); |
| 62 semaphore->Wait(); |
| 63 delete semaphore; |
| 64 semaphore = NULL; |
| 65 } |
| 66 |
| 67 #elif V8_OS_WIN || V8_OS_CYGWIN |
| 68 v8::RegisterState GetRegisterState(); |
| 69 |
| 70 // The JavaScript calls this function when on full stack depth. |
| 71 void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 72 sample_depth = isolate->GetSample(GetRegisterState(), |
| 73 sample, |
| 74 kMaxSampleDepth); |
| 75 } |
| 76 #endif // V8_OS_POSIX && !V8_OS_CYGWIN / V8_OS_WIN || V8_OS_CYGWIN |
| 77 |
| 78 // A JavaScript function which takes stack depth |
| 79 // (minimum value 2) as an argument. |
| 80 // When at the bottom of the recursion, |
| 81 // the JavaScript code calls into C++ test code, |
| 82 // waiting for the sampler to take a sample. |
| 83 static const char* test_function = |
| 84 "function func(depth) {" |
| 85 " if (depth == 2) CollectSample();" |
| 86 " else return func(depth - 1);" |
| 87 "}"; |
| 88 } // namespace |
| 89 |
| 90 |
| 91 #define SAMPLER_API_TESTS_BOOTSTRAP() \ |
| 92 isolate = CcTest::isolate(); \ |
| 93 sample_depth = 0; \ |
| 94 v8::HandleScope scope(isolate); \ |
| 95 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); \ |
| 96 global->Set(v8::String::NewFromUtf8(isolate, "CollectSample"), \ |
| 97 v8::FunctionTemplate::New(isolate, CollectSample)); \ |
| 98 LocalContext env(isolate, NULL, global) |
| 99 |
| 100 |
| 101 TEST(StackDepthIsConsistent) { |
| 102 SAMPLER_API_TESTS_BOOTSTRAP(); |
| 103 |
| 104 std::string source(test_function); |
| 105 source.append("func(8);"); |
| 106 v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run(); |
| 107 |
| 108 CHECK_EQ(8, sample_depth); |
| 109 } |
| 110 |
| 111 |
| 112 // TEST(StackDepthDoesNotExceedMaxValue) { |
| 113 // SAMPLER_API_TESTS_BOOTSTRAP(); |
| 114 |
| 115 // std::string source(test_function); |
| 116 // source.append("func(300);"); |
| 117 // v8::Script::Compile(v8::String::NewFromUtf8(isolate, source.c_str()))->Run(
); |
| 118 |
| 119 // CHECK_EQ(kMaxSampleDepth, sample_depth); |
| 120 // } |
| 121 |
| 122 |
| 123 // namespace { |
| 124 // std::vector<v8::JitCodeEvent> inner_funcs; |
| 125 // std::vector<v8::JitCodeEvent> outer_funcs; |
| 126 |
| 127 // void TestJitCodeEventHandler(const v8::JitCodeEvent* event) { |
| 128 // if (event->type != v8::JitCodeEvent::CODE_ADDED) return; |
| 129 // std::string name(event->name.str, event->name.len); |
| 130 // if (name.find("test_sampler_api_inner") != std::string::npos) |
| 131 // inner_funcs.push_back(*event); |
| 132 // if (name.find("test_sampler_api_outer") != std::string::npos) |
| 133 // outer_funcs.push_back(*event); |
| 134 // } |
| 135 |
| 136 |
| 137 // // Note: The arguments.callee stuff is there so that the |
| 138 // // functions are not optimized away. |
| 139 // static const char* test_script = |
| 140 // "function test_sampler_api_inner() {" |
| 141 // " CollectSample();" |
| 142 // " return arguments.callee.toString();" |
| 143 // "}" |
| 144 // "function test_sampler_api_outer() {" |
| 145 // " return test_sampler_api_inner() + arguments.callee.toString();" |
| 146 // "}" |
| 147 // "test_sampler_api_outer();"; |
| 148 // } // namespace |
| 149 |
| 150 |
| 151 // // The captured sample should have three pc values. |
| 152 // // They should fall in the range where the compiled code |
| 153 // // The expected stack is: |
| 154 // // bottom of stack [{anon script}, outer, inner] top of stack |
| 155 // // ^ ^ ^ |
| 156 // // sample.stack indices 2 1 0 |
| 157 // TEST(StackFramesConsistent) { |
| 158 // SAMPLER_API_TESTS_BOOTSTRAP(); |
| 159 |
| 160 // isolate->SetJitCodeEventHandler(v8::kJitCodeEventDefault, |
| 161 // TestJitCodeEventHandler); |
| 162 // v8::Script::Compile(v8::String::NewFromUtf8(isolate, test_script))->Run(); |
| 163 |
| 164 // CHECK_EQ(3, sample_depth); |
| 165 |
| 166 // bool stack_top_is_inner = false; |
| 167 // bool below_inner_is_outer = false; |
| 168 |
| 169 // for (unsigned i = 0; i < inner_funcs.size(); i++) { |
| 170 // void* start_addr = inner_funcs[i].code_start; |
| 171 // void* end_addr = reinterpret_cast<void*>( |
| 172 // (int64_t)inner_funcs[i].code_start + inner_funcs[i].code_len); |
| 173 // if ((sample[0] >= start_addr) && (sample[0] < end_addr)) |
| 174 // stack_top_is_inner = true; |
| 175 // } |
| 176 |
| 177 // for (unsigned i = 0; i < outer_funcs.size(); i++) { |
| 178 // void* start_addr = outer_funcs[i].code_start; |
| 179 // void* end_addr = reinterpret_cast<void*>( |
| 180 // (int64_t)outer_funcs[i].code_start + outer_funcs[i].code_len); |
| 181 // if ((sample[1] >= start_addr) && |
| 182 // (sample[1] < end_addr)) |
| 183 // below_inner_is_outer = true; |
| 184 // } |
| 185 |
| 186 // CHECK(stack_top_is_inner); |
| 187 // CHECK(below_inner_is_outer); |
| 188 // } |
| 189 |
| 190 |
| 191 // Platform specific implementation of GetRegisterState |
| 192 namespace { |
| 193 #if V8_OS_POSIX && !V8_OS_CYGWIN |
| 194 v8::RegisterState GetRegisterState(void* context) { |
| 195 v8::RegisterState state; |
| 196 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); |
| 197 #if !V8_OS_OPENBSD |
| 198 mcontext_t& mcontext = ucontext->uc_mcontext; |
| 199 #endif |
| 200 #if V8_OS_LINUX |
| 201 #if V8_HOST_ARCH_IA32 |
| 202 state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_EIP]); |
| 203 state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_ESP]); |
| 204 state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_EBP]); |
| 205 #elif V8_HOST_ARCH_X64 |
| 206 state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_RIP]); |
| 207 state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]); |
| 208 state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]); |
| 209 #elif V8_HOST_ARCH_ARM |
| 210 #if defined(__GLIBC__) && !defined(__UCLIBC__) && \ |
| 211 (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) |
| 212 // Old GLibc ARM versions used a gregs[] array to access the register |
| 213 // values from mcontext_t. |
| 214 state.pc = reinterpret_cast<void*>(mcontext.gregs[R15]); |
| 215 state.sp = reinterpret_cast<void*>(mcontext.gregs[R13]); |
| 216 state.fp = reinterpret_cast<void*>(mcontext.gregs[R11]); |
| 217 #else |
| 218 state.pc = reinterpret_cast<void*>(mcontext.arm_pc); |
| 219 state.sp = reinterpret_cast<void*>(mcontext.arm_sp); |
| 220 state.fp = reinterpret_cast<void*>(mcontext.arm_fp); |
| 221 #endif // defined(__GLIBC__) && !defined(__UCLIBC__) && |
| 222 // (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) |
| 223 #elif V8_HOST_ARCH_ARM64 |
| 224 state.pc = reinterpret_cast<void*>(mcontext.pc); |
| 225 state.sp = reinterpret_cast<void*>(mcontext.sp); |
| 226 // FP is an alias for x29. |
| 227 state.fp = reinterpret_cast<void*>(mcontext.regs[29]); |
| 228 #elif V8_HOST_ARCH_MIPS |
| 229 state.pc = reinterpret_cast<void*>(mcontext.pc); |
| 230 state.sp = reinterpret_cast<void*>(mcontext.gregs[29]); |
| 231 state.fp = reinterpret_cast<void*>(mcontext.gregs[30]); |
| 232 #elif V8_HOST_ARCH_MIPS64 |
| 233 state.pc = reinterpret_cast<void*>(mcontext.pc); |
| 234 state.sp = reinterpret_cast<void*>(mcontext.gregs[29]); |
| 235 state.fp = reinterpret_cast<void*>(mcontext.gregs[30]); |
| 236 #endif // V8_HOST_ARCH_* |
| 237 #elif V8_OS_MACOSX |
| 238 #if V8_HOST_ARCH_X64 |
| 239 #if __DARWIN_UNIX03 |
| 240 state.pc = reinterpret_cast<void*>(mcontext->__ss.__rip); |
| 241 state.sp = reinterpret_cast<void*>(mcontext->__ss.__rsp); |
| 242 state.fp = reinterpret_cast<void*>(mcontext->__ss.__rbp); |
| 243 #else // !__DARWIN_UNIX03 |
| 244 state.pc = reinterpret_cast<void*>(mcontext->ss.rip); |
| 245 state.sp = reinterpret_cast<void*>(mcontext->ss.rsp); |
| 246 state.fp = reinterpret_cast<void*>(mcontext->ss.rbp); |
| 247 #endif // __DARWIN_UNIX03 |
| 248 #elif V8_HOST_ARCH_IA32 |
| 249 #if __DARWIN_UNIX03 |
| 250 state.pc = reinterpret_cast<void*>(mcontext->__ss.__eip); |
| 251 state.sp = reinterpret_cast<void*>(mcontext->__ss.__esp); |
| 252 state.fp = reinterpret_cast<void*>(mcontext->__ss.__ebp); |
| 253 #else // !__DARWIN_UNIX03 |
| 254 state.pc = reinterpret_cast<void*>(mcontext->ss.eip); |
| 255 state.sp = reinterpret_cast<void*>(mcontext->ss.esp); |
| 256 state.fp = reinterpret_cast<void*>(mcontext->ss.ebp); |
| 257 #endif // __DARWIN_UNIX03 |
| 258 #endif // V8_HOST_ARCH_IA32 |
| 259 #elif V8_OS_FREEBSD |
| 260 #if V8_HOST_ARCH_IA32 |
| 261 state.pc = reinterpret_cast<void*>(mcontext.mc_eip); |
| 262 state.sp = reinterpret_cast<void*>(mcontext.mc_esp); |
| 263 state.fp = reinterpret_cast<void*>(mcontext.mc_ebp); |
| 264 #elif V8_HOST_ARCH_X64 |
| 265 state.pc = reinterpret_cast<void*>(mcontext.mc_rip); |
| 266 state.sp = reinterpret_cast<void*>(mcontext.mc_rsp); |
| 267 state.fp = reinterpret_cast<void*>(mcontext.mc_rbp); |
| 268 #elif V8_HOST_ARCH_ARM |
| 269 state.pc = reinterpret_cast<void*>(mcontext.mc_r15); |
| 270 state.sp = reinterpret_cast<void*>(mcontext.mc_r13); |
| 271 state.fp = reinterpret_cast<void*>(mcontext.mc_r11); |
| 272 #endif // V8_HOST_ARCH_* |
| 273 #elif V8_OS_NETBSD |
| 274 #if V8_HOST_ARCH_IA32 |
| 275 state.pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_EIP]); |
| 276 state.sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_ESP]); |
| 277 state.fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_EBP]); |
| 278 #elif V8_HOST_ARCH_X64 |
| 279 state.pc = reinterpret_cast<void*>(mcontext.__gregs[_REG_RIP]); |
| 280 state.sp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RSP]); |
| 281 state.fp = reinterpret_cast<void*>(mcontext.__gregs[_REG_RBP]); |
| 282 #endif // V8_HOST_ARCH_* |
| 283 #elif V8_OS_OPENBSD |
| 284 #if V8_HOST_ARCH_IA32 |
| 285 state.pc = reinterpret_cast<void*>(ucontext->sc_eip); |
| 286 state.sp = reinterpret_cast<void*>(ucontext->sc_esp); |
| 287 state.fp = reinterpret_cast<void*>(ucontext->sc_ebp); |
| 288 #elif V8_HOST_ARCH_X64 |
| 289 state.pc = reinterpret_cast<void*>(ucontext->sc_rip); |
| 290 state.sp = reinterpret_cast<void*>(ucontext->sc_rsp); |
| 291 state.fp = reinterpret_cast<void*>(ucontext->sc_rbp); |
| 292 #endif // V8_HOST_ARCH_* |
| 293 #elif V8_OS_SOLARIS |
| 294 state.pc = reinterpret_cast<void*>(mcontext.gregs[REG_PC]); |
| 295 state.sp = reinterpret_cast<void*>(mcontext.gregs[REG_SP]); |
| 296 state.fp = reinterpret_cast<void*>(mcontext.gregs[REG_FP]); |
| 297 #elif V8_OS_QNX |
| 298 #if V8_HOST_ARCH_IA32 |
| 299 state.pc = reinterpret_cast<void*>(mcontext.cpu.eip); |
| 300 state.sp = reinterpret_cast<void*>(mcontext.cpu.esp); |
| 301 state.fp = reinterpret_cast<void*>(mcontext.cpu.ebp); |
| 302 #elif V8_HOST_ARCH_ARM |
| 303 state.pc = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_PC]); |
| 304 state.sp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_SP]); |
| 305 state.fp = reinterpret_cast<void*>(mcontext.cpu.gpr[ARM_REG_FP]); |
| 306 #endif // V8_HOST_ARCH_* |
| 307 #endif // V8_OS_QNX |
| 308 return state; |
| 309 } |
| 310 |
| 311 #elif V8_OS_WIN || V8_OS_CYGWIN |
| 312 v8::RegisterState GetRegisterState() { |
| 313 CONTEXT context; |
| 314 v8::RegisterState state; |
| 315 memset(&context, 0, sizeof(context)); |
| 316 context.ContextFlags = CONTEXT_FULL; |
| 317 GetThreadContext(OpenThread(THREAD_GET_CONTEXT | |
| 318 THREAD_SUSPEND_RESUME | |
| 319 THREAD_QUERY_INFORMATION, |
| 320 false, |
| 321 GetCurrentThreadId()), |
| 322 &context); |
| 323 #if V8_HOST_ARCH_X64 |
| 324 state.pc = reinterpret_cast<void*>(context.Rip); |
| 325 state.sp = reinterpret_cast<void*>(context.Rsp); |
| 326 state.fp = reinterpret_cast<void*>(context.Rbp); |
| 327 #else |
| 328 state.pc = reinterpret_cast<void*>(context.Eip); |
| 329 state.sp = reinterpret_cast<void*>(context.Esp); |
| 330 state.fp = reinterpret_cast<void*>(context.Ebp); |
| 331 #endif |
| 332 return state; |
| 333 } |
| 334 #endif // V8_OS_POSIX && !V8_OS_CYGWIN / V8_OS_WIN || V8_OS_CYGWIN |
| 335 } // namespace |
OLD | NEW |