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 public 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/v8.h" |
| 11 #include "test/cctest/cctest.h" |
| 12 |
| 13 #if V8_OS_POSIX && !V8_OS_CYGWIN |
| 14 #include <pthread.h> |
| 15 #include <signal.h> |
| 16 #elif V8_OS_WIN || V8_OS_CYGWIN |
| 17 #include "src/base/win32-headers.h" |
| 18 #endif |
| 19 |
| 20 using v8::Local; |
| 21 |
| 22 namespace { |
| 23 // Sampler thread waits on this semaphore. |
| 24 v8::base::Semaphore* sampler_semaphore = new v8::base::Semaphore(0); |
| 25 |
| 26 // V8 thread (the JavaScript code) waits on this semaphore. |
| 27 v8::base::Semaphore* v8_semaphore = new v8::base::Semaphore(0); |
| 28 |
| 29 // The JavaScript calls this function when on full stack depth. |
| 30 void SignalAndWaitForSampler( |
| 31 const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 32 // Tell the sampler that it can take a sample now. |
| 33 sampler_semaphore->Signal(); |
| 34 |
| 35 // Wait for the sampler to finish collecting a sample. |
| 36 v8_semaphore->Wait(); |
| 37 } |
| 38 |
| 39 // A thread which collects samples from v8. |
| 40 class SamplerThread : public v8::base::Thread { |
| 41 public: |
| 42 explicit SamplerThread(v8::Isolate* isolate) |
| 43 : Thread(v8::base::Thread::Options("sampler-api-tester")), |
| 44 isolate_(isolate) { |
| 45 #if V8_OS_POSIX && !V8_OS_CYGWIN |
| 46 sampled_thread_id_ = pthread_self(); |
| 47 #elif V8_OS_WIN || V8_OS_CYGWIN |
| 48 HANDLE sampled_thread_ = OpenThread(THREAD_GET_CONTEXT | |
| 49 THREAD_SUSPEND_RESUME | |
| 50 THREAD_QUERY_INFORMATION, |
| 51 false, |
| 52 GetCurrentThreadId()); |
| 53 #endif |
| 54 } |
| 55 |
| 56 virtual ~SamplerThread() {} |
| 57 |
| 58 virtual void Run() { |
| 59 sampler_semaphore->Wait(); // Wait for JS to reach full stack depth. |
| 60 |
| 61 #if V8_OS_POSIX && !V8_OS_CYGWIN |
| 62 |
| 63 pthread_kill(sampled_thread_id_, SIGPROF); |
| 64 isolate_->GetSample(&sample); |
| 65 |
| 66 #elif V8_OS_WIN || V8_OS_CYGWIN |
| 67 |
| 68 SuspendThread(sampled_thread_); |
| 69 CONTEXT context; |
| 70 memset(&context, 0, sizeof(context)); |
| 71 context.ContextFlags = CONTEXT_FULL; |
| 72 GetThreadContext(sampled_thread_, &context); |
| 73 isolate_->GetSample(context, &sample); |
| 74 ResumeThread(sampled_thread_); |
| 75 |
| 76 #endif |
| 77 |
| 78 v8_semaphore->Signal(); // Tell JS that sample collection is done. |
| 79 } |
| 80 |
| 81 v8::Sample sample; |
| 82 |
| 83 private: |
| 84 v8::Isolate* isolate_; |
| 85 #if V8_OS_POSIX && !V8_OS_CYGWIN |
| 86 pthread_t sampled_thread_id_; |
| 87 #elif V8_OS_WIN || V8_OS_CYGWIN |
| 88 HANDLE sampled_thread_; |
| 89 #endif |
| 90 }; |
| 91 } |
| 92 |
| 93 |
| 94 #define SAMPLER_API_TESTS_BOOTSTRAP() \ |
| 95 v8::internal::FLAG_new_sampler_api = true; \ |
| 96 v8::Isolate* isolate = CcTest::isolate(); \ |
| 97 v8::HandleScope scope(isolate); \ |
| 98 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); \ |
| 99 global->Set(v8::String::NewFromUtf8(isolate, "SignalAndWaitForSampler"), \ |
| 100 v8::FunctionTemplate::New(isolate, SignalAndWaitForSampler)); \ |
| 101 LocalContext env(isolate, NULL, global); \ |
| 102 SamplerThread sampler(isolate) |
| 103 |
| 104 #define SAMPLER_API_TESTS_COLLECT_SAMPLE() \ |
| 105 sampler.Start(); \ |
| 106 script->Run(); \ |
| 107 sampler.Join() |
| 108 |
| 109 // A JavaScript function which takes stack depth |
| 110 // (minimum value 2) as an argument. |
| 111 // When at the bottom of the recursion, |
| 112 // the JavaScript code calls into C++ test code, |
| 113 // waiting for the sampler to take a sample. |
| 114 static const char* sampler_api_test_function = "function func(depth) {" |
| 115 " if (depth == 2) SignalAndWaitForSampler();" |
| 116 " else return func(depth - 1);" |
| 117 "}"; |
| 118 |
| 119 |
| 120 TEST(StackDepthIsConsistent) { |
| 121 SAMPLER_API_TESTS_BOOTSTRAP(); |
| 122 |
| 123 std::string source(sampler_api_test_function); |
| 124 source.append("func(8);"); |
| 125 Local<v8::Script> script = v8::Script::Compile( |
| 126 v8::String::NewFromUtf8(isolate, source.c_str())); |
| 127 |
| 128 SAMPLER_API_TESTS_COLLECT_SAMPLE(); |
| 129 |
| 130 CHECK_EQ(8, sampler.sample.size()); |
| 131 } |
| 132 |
| 133 |
| 134 TEST(StackDepthDoesNotExceedMaxValue) { |
| 135 SAMPLER_API_TESTS_BOOTSTRAP(); |
| 136 |
| 137 std::string source(sampler_api_test_function); |
| 138 source.append("func(300);"); |
| 139 Local<v8::Script> script = v8::Script::Compile( |
| 140 v8::String::NewFromUtf8(isolate, source.c_str())); |
| 141 |
| 142 SAMPLER_API_TESTS_COLLECT_SAMPLE(); |
| 143 |
| 144 int MAX_SIZE = v8::Sample::kMaxSize; |
| 145 CHECK_EQ(MAX_SIZE, sampler.sample.size()); |
| 146 } |
| 147 |
| 148 |
| 149 namespace { |
| 150 std::vector<v8::JitCodeEvent> inner_funcs; |
| 151 std::vector<v8::JitCodeEvent> outer_funcs; |
| 152 |
| 153 void TestJitCodeEventHandler(const v8::JitCodeEvent* event) { |
| 154 if (event->type != v8::JitCodeEvent::CODE_ADDED) return; |
| 155 std::string name(event->name.str, event->name.len); |
| 156 if (name.find("test_sampler_api_inner") != std::string::npos) |
| 157 inner_funcs.push_back(*event); |
| 158 if (name.find("test_sampler_api_outer") != std::string::npos) |
| 159 outer_funcs.push_back(*event); |
| 160 } |
| 161 } |
| 162 |
| 163 |
| 164 // Note: The argumnets.callee stuff is there so that the |
| 165 // functions are not optimized away. |
| 166 static const char* sampler_api_test_script = |
| 167 "function test_sampler_api_inner() {" |
| 168 " SignalAndWaitForSampler();" |
| 169 " return arguments.callee.toString();" |
| 170 "}" |
| 171 "function test_sampler_api_outer() {" |
| 172 " return test_sampler_api_inner() + arguments.callee.toString();" |
| 173 "}" |
| 174 "test_sampler_api_outer();"; |
| 175 |
| 176 |
| 177 // The captured sample should have three pc values. |
| 178 // They should fall in the range where the compiled code |
| 179 // The expected stack is: |
| 180 // bottom of stack [{anon script}, outer, inner] top of stack |
| 181 // ^ ^ ^ |
| 182 // sample.stack indices 2 1 0 |
| 183 TEST(StackFramesConsistent) { |
| 184 SAMPLER_API_TESTS_BOOTSTRAP(); |
| 185 |
| 186 v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, |
| 187 TestJitCodeEventHandler); |
| 188 Local<v8::Script> script = v8::Script::Compile( |
| 189 v8::String::NewFromUtf8(isolate, sampler_api_test_script)); |
| 190 |
| 191 SAMPLER_API_TESTS_COLLECT_SAMPLE(); |
| 192 |
| 193 CHECK_EQ(3, sampler.sample.size()); |
| 194 |
| 195 bool stack_top_is_inner = false; |
| 196 bool below_inner_is_outer = false; |
| 197 |
| 198 for (unsigned i = 0; i < inner_funcs.size(); i++) { |
| 199 void* start_addr = inner_funcs[i].code_start; |
| 200 void* end_addr = reinterpret_cast<void*>((int64_t)inner_funcs[i].code_start |
| 201 + inner_funcs[i].code_len); |
| 202 if ((*sampler.sample.begin() >= start_addr) && |
| 203 (*sampler.sample.begin() < end_addr)) |
| 204 stack_top_is_inner = true; |
| 205 } |
| 206 |
| 207 for (unsigned i = 0; i < outer_funcs.size(); i++) { |
| 208 void* start_addr = outer_funcs[i].code_start; |
| 209 void* end_addr = reinterpret_cast<void*>((int64_t)outer_funcs[i].code_start |
| 210 + outer_funcs[i].code_len); |
| 211 if ((*(sampler.sample.begin() + 1) >= start_addr) && |
| 212 (*(sampler.sample.begin() + 1) < end_addr)) |
| 213 below_inner_is_outer = true; |
| 214 } |
| 215 |
| 216 CHECK_EQ(true, stack_top_is_inner); |
| 217 CHECK_EQ(true, below_inner_is_outer); |
| 218 } |
OLD | NEW |