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/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 |
OLD | NEW |