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