| OLD | NEW |
| (Empty) |
| 1 // Copyright 2006-2009 the V8 project authors. All rights reserved. | |
| 2 // | |
| 3 // Tests of profiler-related functions from log.h | |
| 4 | |
| 5 #ifdef ENABLE_LOGGING_AND_PROFILING | |
| 6 | |
| 7 #include <stdlib.h> | |
| 8 | |
| 9 #include "v8.h" | |
| 10 | |
| 11 #include "codegen.h" | |
| 12 #include "log.h" | |
| 13 #include "top.h" | |
| 14 #include "cctest.h" | |
| 15 #include "disassembler.h" | |
| 16 #include "register-allocator-inl.h" | |
| 17 | |
| 18 using v8::Function; | |
| 19 using v8::Local; | |
| 20 using v8::Object; | |
| 21 using v8::Script; | |
| 22 using v8::String; | |
| 23 using v8::Value; | |
| 24 | |
| 25 using v8::internal::byte; | |
| 26 using v8::internal::Address; | |
| 27 using v8::internal::Handle; | |
| 28 using v8::internal::JSFunction; | |
| 29 using v8::internal::StackTracer; | |
| 30 using v8::internal::TickSample; | |
| 31 using v8::internal::Top; | |
| 32 | |
| 33 namespace i = v8::internal; | |
| 34 | |
| 35 | |
| 36 static v8::Persistent<v8::Context> env; | |
| 37 | |
| 38 | |
| 39 static struct { | |
| 40 TickSample* sample; | |
| 41 } trace_env = { NULL }; | |
| 42 | |
| 43 | |
| 44 static void InitTraceEnv(TickSample* sample) { | |
| 45 trace_env.sample = sample; | |
| 46 } | |
| 47 | |
| 48 | |
| 49 static void DoTrace(Address fp) { | |
| 50 trace_env.sample->fp = reinterpret_cast<uintptr_t>(fp); | |
| 51 // sp is only used to define stack high bound | |
| 52 trace_env.sample->sp = | |
| 53 reinterpret_cast<unsigned int>(trace_env.sample) - 10240; | |
| 54 StackTracer::Trace(trace_env.sample); | |
| 55 } | |
| 56 | |
| 57 | |
| 58 // Hide c_entry_fp to emulate situation when sampling is done while | |
| 59 // pure JS code is being executed | |
| 60 static void DoTraceHideCEntryFPAddress(Address fp) { | |
| 61 v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address()); | |
| 62 CHECK(saved_c_frame_fp); | |
| 63 *(Top::c_entry_fp_address()) = 0; | |
| 64 DoTrace(fp); | |
| 65 *(Top::c_entry_fp_address()) = saved_c_frame_fp; | |
| 66 } | |
| 67 | |
| 68 | |
| 69 static void CheckRetAddrIsInFunction(const char* func_name, | |
| 70 Address ret_addr, | |
| 71 Address func_start_addr, | |
| 72 unsigned int func_len) { | |
| 73 printf("CheckRetAddrIsInFunction \"%s\": %p %p %p\n", | |
| 74 func_name, func_start_addr, ret_addr, func_start_addr + func_len); | |
| 75 CHECK_GE(ret_addr, func_start_addr); | |
| 76 CHECK_GE(func_start_addr + func_len, ret_addr); | |
| 77 } | |
| 78 | |
| 79 | |
| 80 static void CheckRetAddrIsInJSFunction(const char* func_name, | |
| 81 Address ret_addr, | |
| 82 Handle<JSFunction> func) { | |
| 83 v8::internal::Code* func_code = func->code(); | |
| 84 CheckRetAddrIsInFunction( | |
| 85 func_name, ret_addr, | |
| 86 func_code->instruction_start(), | |
| 87 func_code->ExecutableSize()); | |
| 88 } | |
| 89 | |
| 90 | |
| 91 // --- T r a c e E x t e n s i o n --- | |
| 92 | |
| 93 class TraceExtension : public v8::Extension { | |
| 94 public: | |
| 95 TraceExtension() : v8::Extension("v8/trace", kSource) { } | |
| 96 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( | |
| 97 v8::Handle<String> name); | |
| 98 static v8::Handle<v8::Value> Trace(const v8::Arguments& args); | |
| 99 static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args); | |
| 100 static v8::Handle<v8::Value> JSEntrySP(const v8::Arguments& args); | |
| 101 static v8::Handle<v8::Value> JSEntrySPLevel2(const v8::Arguments& args); | |
| 102 private: | |
| 103 static Address GetFP(const v8::Arguments& args); | |
| 104 static const char* kSource; | |
| 105 }; | |
| 106 | |
| 107 | |
| 108 const char* TraceExtension::kSource = | |
| 109 "native function trace();" | |
| 110 "native function js_trace();" | |
| 111 "native function js_entry_sp();" | |
| 112 "native function js_entry_sp_level2();"; | |
| 113 | |
| 114 v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( | |
| 115 v8::Handle<String> name) { | |
| 116 if (name->Equals(String::New("trace"))) { | |
| 117 return v8::FunctionTemplate::New(TraceExtension::Trace); | |
| 118 } else if (name->Equals(String::New("js_trace"))) { | |
| 119 return v8::FunctionTemplate::New(TraceExtension::JSTrace); | |
| 120 } else if (name->Equals(String::New("js_entry_sp"))) { | |
| 121 return v8::FunctionTemplate::New(TraceExtension::JSEntrySP); | |
| 122 } else if (name->Equals(String::New("js_entry_sp_level2"))) { | |
| 123 return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2); | |
| 124 } else { | |
| 125 CHECK(false); | |
| 126 return v8::Handle<v8::FunctionTemplate>(); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 | |
| 131 Address TraceExtension::GetFP(const v8::Arguments& args) { | |
| 132 CHECK_EQ(1, args.Length()); | |
| 133 Address fp = reinterpret_cast<Address>(args[0]->Int32Value() << 2); | |
| 134 printf("Trace: %p\n", fp); | |
| 135 return fp; | |
| 136 } | |
| 137 | |
| 138 | |
| 139 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { | |
| 140 DoTrace(GetFP(args)); | |
| 141 return v8::Undefined(); | |
| 142 } | |
| 143 | |
| 144 | |
| 145 v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { | |
| 146 DoTraceHideCEntryFPAddress(GetFP(args)); | |
| 147 return v8::Undefined(); | |
| 148 } | |
| 149 | |
| 150 | |
| 151 static Address GetJsEntrySp() { | |
| 152 CHECK_NE(NULL, Top::GetCurrentThread()); | |
| 153 return Top::js_entry_sp(Top::GetCurrentThread()); | |
| 154 } | |
| 155 | |
| 156 | |
| 157 v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) { | |
| 158 CHECK_NE(0, GetJsEntrySp()); | |
| 159 return v8::Undefined(); | |
| 160 } | |
| 161 | |
| 162 | |
| 163 static void CompileRun(const char* source) { | |
| 164 Script::Compile(String::New(source))->Run(); | |
| 165 } | |
| 166 | |
| 167 | |
| 168 v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2( | |
| 169 const v8::Arguments& args) { | |
| 170 v8::HandleScope scope; | |
| 171 const Address js_entry_sp = GetJsEntrySp(); | |
| 172 CHECK_NE(0, js_entry_sp); | |
| 173 CompileRun("js_entry_sp();"); | |
| 174 CHECK_EQ(js_entry_sp, GetJsEntrySp()); | |
| 175 return v8::Undefined(); | |
| 176 } | |
| 177 | |
| 178 | |
| 179 static TraceExtension kTraceExtension; | |
| 180 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); | |
| 181 | |
| 182 | |
| 183 static void InitializeVM() { | |
| 184 if (env.IsEmpty()) { | |
| 185 v8::HandleScope scope; | |
| 186 const char* extensions[] = { "v8/trace" }; | |
| 187 v8::ExtensionConfiguration config(1, extensions); | |
| 188 env = v8::Context::New(&config); | |
| 189 } | |
| 190 v8::HandleScope scope; | |
| 191 env->Enter(); | |
| 192 } | |
| 193 | |
| 194 | |
| 195 static Handle<JSFunction> CompileFunction(const char* source) { | |
| 196 return v8::Utils::OpenHandle(*Script::Compile(String::New(source))); | |
| 197 } | |
| 198 | |
| 199 | |
| 200 static Local<Value> GetGlobalProperty(const char* name) { | |
| 201 return env->Global()->Get(String::New(name)); | |
| 202 } | |
| 203 | |
| 204 | |
| 205 static Handle<JSFunction> GetGlobalJSFunction(const char* name) { | |
| 206 Handle<JSFunction> js_func(JSFunction::cast( | |
| 207 *(v8::Utils::OpenHandle( | |
| 208 *GetGlobalProperty(name))))); | |
| 209 return js_func; | |
| 210 } | |
| 211 | |
| 212 | |
| 213 static void CheckRetAddrIsInJSFunction(const char* func_name, | |
| 214 Address ret_addr) { | |
| 215 CheckRetAddrIsInJSFunction(func_name, ret_addr, | |
| 216 GetGlobalJSFunction(func_name)); | |
| 217 } | |
| 218 | |
| 219 | |
| 220 static void SetGlobalProperty(const char* name, Local<Value> value) { | |
| 221 env->Global()->Set(String::New(name), value); | |
| 222 } | |
| 223 | |
| 224 | |
| 225 static Handle<v8::internal::String> NewString(const char* s) { | |
| 226 return i::Factory::NewStringFromAscii(i::CStrVector(s)); | |
| 227 } | |
| 228 | |
| 229 | |
| 230 namespace v8 { | |
| 231 namespace internal { | |
| 232 | |
| 233 class CodeGeneratorPatcher { | |
| 234 public: | |
| 235 CodeGeneratorPatcher() { | |
| 236 CodeGenerator::InlineRuntimeLUT genGetFramePointer = | |
| 237 {&CodeGenerator::GenerateGetFramePointer, "_GetFramePointer"}; | |
| 238 // _FastCharCodeAt is not used in our tests. | |
| 239 bool result = CodeGenerator::PatchInlineRuntimeEntry( | |
| 240 NewString("_FastCharCodeAt"), | |
| 241 genGetFramePointer, &oldInlineEntry); | |
| 242 CHECK(result); | |
| 243 } | |
| 244 | |
| 245 ~CodeGeneratorPatcher() { | |
| 246 CHECK(CodeGenerator::PatchInlineRuntimeEntry( | |
| 247 NewString("_GetFramePointer"), | |
| 248 oldInlineEntry, NULL)); | |
| 249 } | |
| 250 | |
| 251 private: | |
| 252 CodeGenerator::InlineRuntimeLUT oldInlineEntry; | |
| 253 }; | |
| 254 | |
| 255 } } // namespace v8::internal | |
| 256 | |
| 257 | |
| 258 // Creates a global function named 'func_name' that calls the tracing | |
| 259 // function 'trace_func_name' with an actual EBP register value, | |
| 260 // shifted right to be presented as Smi. | |
| 261 static void CreateTraceCallerFunction(const char* func_name, | |
| 262 const char* trace_func_name) { | |
| 263 i::EmbeddedVector<char, 256> trace_call_buf; | |
| 264 i::OS::SNPrintF(trace_call_buf, "%s(%%_GetFramePointer());", trace_func_name); | |
| 265 | |
| 266 // Compile the script. | |
| 267 i::CodeGeneratorPatcher patcher; | |
| 268 bool allow_natives_syntax = i::FLAG_allow_natives_syntax; | |
| 269 i::FLAG_allow_natives_syntax = true; | |
| 270 Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); | |
| 271 CHECK(!func.is_null()); | |
| 272 i::FLAG_allow_natives_syntax = allow_natives_syntax; | |
| 273 | |
| 274 #ifdef DEBUG | |
| 275 v8::internal::Code* func_code = func->code(); | |
| 276 CHECK(func_code->IsCode()); | |
| 277 func_code->Print(); | |
| 278 #endif | |
| 279 | |
| 280 SetGlobalProperty(func_name, v8::ToApi<Value>(func)); | |
| 281 } | |
| 282 | |
| 283 | |
| 284 TEST(CFromJSStackTrace) { | |
| 285 TickSample sample; | |
| 286 InitTraceEnv(&sample); | |
| 287 | |
| 288 InitializeVM(); | |
| 289 v8::HandleScope scope; | |
| 290 CreateTraceCallerFunction("JSFuncDoTrace", "trace"); | |
| 291 CompileRun( | |
| 292 "function JSTrace() {" | |
| 293 " JSFuncDoTrace();" | |
| 294 "};\n" | |
| 295 "JSTrace();"); | |
| 296 CHECK_GT(sample.frames_count, 1); | |
| 297 // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace" | |
| 298 CheckRetAddrIsInJSFunction("JSFuncDoTrace", | |
| 299 sample.stack[0]); | |
| 300 CheckRetAddrIsInJSFunction("JSTrace", | |
| 301 sample.stack[1]); | |
| 302 } | |
| 303 | |
| 304 | |
| 305 TEST(PureJSStackTrace) { | |
| 306 TickSample sample; | |
| 307 InitTraceEnv(&sample); | |
| 308 | |
| 309 InitializeVM(); | |
| 310 v8::HandleScope scope; | |
| 311 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); | |
| 312 CompileRun( | |
| 313 "function JSTrace() {" | |
| 314 " JSFuncDoTrace();" | |
| 315 "};\n" | |
| 316 "function OuterJSTrace() {" | |
| 317 " JSTrace();" | |
| 318 "};\n" | |
| 319 "OuterJSTrace();"); | |
| 320 CHECK_GT(sample.frames_count, 1); | |
| 321 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" | |
| 322 CheckRetAddrIsInJSFunction("JSTrace", | |
| 323 sample.stack[0]); | |
| 324 CheckRetAddrIsInJSFunction("OuterJSTrace", | |
| 325 sample.stack[1]); | |
| 326 } | |
| 327 | |
| 328 | |
| 329 static void CFuncDoTrace() { | |
| 330 Address fp; | |
| 331 #ifdef __GNUC__ | |
| 332 fp = reinterpret_cast<Address>(__builtin_frame_address(0)); | |
| 333 #elif defined _MSC_VER | |
| 334 __asm mov [fp], ebp // NOLINT | |
| 335 #endif | |
| 336 DoTrace(fp); | |
| 337 } | |
| 338 | |
| 339 | |
| 340 static int CFunc(int depth) { | |
| 341 if (depth <= 0) { | |
| 342 CFuncDoTrace(); | |
| 343 return 0; | |
| 344 } else { | |
| 345 return CFunc(depth - 1) + 1; | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 | |
| 350 TEST(PureCStackTrace) { | |
| 351 TickSample sample; | |
| 352 InitTraceEnv(&sample); | |
| 353 // Check that sampler doesn't crash | |
| 354 CHECK_EQ(10, CFunc(10)); | |
| 355 } | |
| 356 | |
| 357 | |
| 358 TEST(JsEntrySp) { | |
| 359 InitializeVM(); | |
| 360 v8::HandleScope scope; | |
| 361 CHECK_EQ(0, GetJsEntrySp()); | |
| 362 CompileRun("a = 1; b = a + 1;"); | |
| 363 CHECK_EQ(0, GetJsEntrySp()); | |
| 364 CompileRun("js_entry_sp();"); | |
| 365 CHECK_EQ(0, GetJsEntrySp()); | |
| 366 CompileRun("js_entry_sp_level2();"); | |
| 367 CHECK_EQ(0, GetJsEntrySp()); | |
| 368 } | |
| 369 | |
| 370 #endif // ENABLE_LOGGING_AND_PROFILING | |
| OLD | NEW |