| OLD | NEW |
| 1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 21 matching lines...) Expand all Loading... |
| 32 #include <stdlib.h> | 32 #include <stdlib.h> |
| 33 | 33 |
| 34 #include "v8.h" | 34 #include "v8.h" |
| 35 | 35 |
| 36 #include "codegen.h" | 36 #include "codegen.h" |
| 37 #include "log.h" | 37 #include "log.h" |
| 38 #include "isolate.h" | 38 #include "isolate.h" |
| 39 #include "cctest.h" | 39 #include "cctest.h" |
| 40 #include "disassembler.h" | 40 #include "disassembler.h" |
| 41 #include "register-allocator-inl.h" | 41 #include "register-allocator-inl.h" |
| 42 #include "vm-state-inl.h" |
| 42 | 43 |
| 43 using v8::Function; | 44 using v8::Function; |
| 44 using v8::Local; | 45 using v8::Local; |
| 45 using v8::Object; | 46 using v8::Object; |
| 46 using v8::Script; | 47 using v8::Script; |
| 47 using v8::String; | 48 using v8::String; |
| 48 using v8::Value; | 49 using v8::Value; |
| 49 | 50 |
| 50 using v8::internal::byte; | 51 using v8::internal::byte; |
| 51 using v8::internal::Address; | 52 using v8::internal::Address; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 69 static void InitTraceEnv(TickSample* sample) { | 70 static void InitTraceEnv(TickSample* sample) { |
| 70 trace_env.sample = sample; | 71 trace_env.sample = sample; |
| 71 } | 72 } |
| 72 | 73 |
| 73 | 74 |
| 74 static void DoTrace(Address fp) { | 75 static void DoTrace(Address fp) { |
| 75 trace_env.sample->fp = fp; | 76 trace_env.sample->fp = fp; |
| 76 // sp is only used to define stack high bound | 77 // sp is only used to define stack high bound |
| 77 trace_env.sample->sp = | 78 trace_env.sample->sp = |
| 78 reinterpret_cast<Address>(trace_env.sample) - 10240; | 79 reinterpret_cast<Address>(trace_env.sample) - 10240; |
| 79 StackTracer::Trace(trace_env.sample); | 80 StackTracer::Trace(Isolate::Current(), trace_env.sample); |
| 80 } | 81 } |
| 81 | 82 |
| 82 | 83 |
| 83 // Hide c_entry_fp to emulate situation when sampling is done while | 84 // Hide c_entry_fp to emulate situation when sampling is done while |
| 84 // pure JS code is being executed | 85 // pure JS code is being executed |
| 85 static void DoTraceHideCEntryFPAddress(Address fp) { | 86 static void DoTraceHideCEntryFPAddress(Address fp) { |
| 86 v8::internal::Address saved_c_frame_fp = | 87 v8::internal::Address saved_c_frame_fp = |
| 87 *(Isolate::Current()->c_entry_fp_address()); | 88 *(Isolate::Current()->c_entry_fp_address()); |
| 88 CHECK(saved_c_frame_fp); | 89 CHECK(saved_c_frame_fp); |
| 89 *(Isolate::Current()->c_entry_fp_address()) = 0; | 90 *(Isolate::Current()->c_entry_fp_address()) = 0; |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 194 const char* extensions[] = { "v8/trace" }; | 195 const char* extensions[] = { "v8/trace" }; |
| 195 v8::ExtensionConfiguration config(1, extensions); | 196 v8::ExtensionConfiguration config(1, extensions); |
| 196 env = v8::Context::New(&config); | 197 env = v8::Context::New(&config); |
| 197 } | 198 } |
| 198 v8::HandleScope scope; | 199 v8::HandleScope scope; |
| 199 env->Enter(); | 200 env->Enter(); |
| 200 } | 201 } |
| 201 | 202 |
| 202 | 203 |
| 203 static void CheckJSFunctionAtAddress(const char* func_name, Address addr) { | 204 static void CheckJSFunctionAtAddress(const char* func_name, Address addr) { |
| 205 CHECK(HEAP->Contains(addr)); |
| 204 i::Object* obj = i::HeapObject::FromAddress(addr); | 206 i::Object* obj = i::HeapObject::FromAddress(addr); |
| 205 CHECK(obj->IsJSFunction()); | 207 CHECK(obj->IsJSFunction()); |
| 206 CHECK(JSFunction::cast(obj)->shared()->name()->IsString()); | 208 CHECK(JSFunction::cast(obj)->shared()->name()->IsString()); |
| 207 i::SmartPointer<char> found_name = | 209 i::SmartPointer<char> found_name = |
| 208 i::String::cast( | 210 i::String::cast( |
| 209 JSFunction::cast( | 211 JSFunction::cast( |
| 210 obj)->shared()->name())->ToCString(); | 212 obj)->shared()->name())->ToCString(); |
| 211 CHECK_EQ(func_name, *found_name); | 213 CHECK_EQ(func_name, *found_name); |
| 212 } | 214 } |
| 213 | 215 |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 "JSTrace();\n" | 294 "JSTrace();\n" |
| 293 "true;"); | 295 "true;"); |
| 294 CHECK(!result.IsEmpty()); | 296 CHECK(!result.IsEmpty()); |
| 295 // When stack tracer is invoked, the stack should look as follows: | 297 // When stack tracer is invoked, the stack should look as follows: |
| 296 // script [JS] | 298 // script [JS] |
| 297 // JSTrace() [JS] | 299 // JSTrace() [JS] |
| 298 // JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi] | 300 // JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi] |
| 299 // trace(EBP) [native (extension)] | 301 // trace(EBP) [native (extension)] |
| 300 // DoTrace(EBP) [native] | 302 // DoTrace(EBP) [native] |
| 301 // StackTracer::Trace | 303 // StackTracer::Trace |
| 302 CHECK_GT(sample.frames_count, 1); | 304 |
| 305 // The VM state tracking keeps track of external callbacks and puts |
| 306 // them at the top of the sample stack. |
| 307 int base = 0; |
| 308 CHECK(sample.stack[0] == FUNCTION_ADDR(TraceExtension::Trace)); |
| 309 base++; |
| 310 |
| 303 // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace" | 311 // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace" |
| 304 CheckJSFunctionAtAddress("JSFuncDoTrace", sample.stack[0]); | 312 CHECK_GT(sample.frames_count, base + 1); |
| 305 CheckJSFunctionAtAddress("JSTrace", sample.stack[1]); | 313 CheckJSFunctionAtAddress("JSFuncDoTrace", sample.stack[base + 0]); |
| 314 CheckJSFunctionAtAddress("JSTrace", sample.stack[base + 1]); |
| 306 } | 315 } |
| 307 | 316 |
| 308 | 317 |
| 309 // This test verifies that stack tracing works when called during | 318 // This test verifies that stack tracing works when called during |
| 310 // execution of JS code. However, as calling StackTracer requires | 319 // execution of JS code. However, as calling StackTracer requires |
| 311 // entering native code, we can only emulate pure JS by erasing | 320 // entering native code, we can only emulate pure JS by erasing |
| 312 // Isolate::c_entry_fp value. In this case, StackTracer uses passed frame | 321 // Isolate::c_entry_fp value. In this case, StackTracer uses passed frame |
| 313 // pointer value as a starting point for stack walking. | 322 // pointer value as a starting point for stack walking. |
| 314 TEST(PureJSStackTrace) { | 323 TEST(PureJSStackTrace) { |
| 324 // This test does not pass with inlining enabled since inlined functions |
| 325 // don't appear in the stack trace. |
| 326 i::FLAG_use_inlining = false; |
| 327 |
| 315 TickSample sample; | 328 TickSample sample; |
| 316 InitTraceEnv(&sample); | 329 InitTraceEnv(&sample); |
| 317 | 330 |
| 318 InitializeVM(); | 331 InitializeVM(); |
| 319 v8::HandleScope scope; | 332 v8::HandleScope scope; |
| 320 // Create global function JSFuncDoTrace which calls | 333 // Create global function JSFuncDoTrace which calls |
| 321 // extension function js_trace() with the current frame pointer value. | 334 // extension function js_trace() with the current frame pointer value. |
| 322 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); | 335 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); |
| 323 Local<Value> result = CompileRun( | 336 Local<Value> result = CompileRun( |
| 324 "function JSTrace() {" | 337 "function JSTrace() {" |
| (...skipping 10 matching lines...) Expand all Loading... |
| 335 // OuterJSTrace() [JS] | 348 // OuterJSTrace() [JS] |
| 336 // JSTrace() [JS] | 349 // JSTrace() [JS] |
| 337 // JSFuncDoTrace() [JS] | 350 // JSFuncDoTrace() [JS] |
| 338 // js_trace(EBP) [native (extension)] | 351 // js_trace(EBP) [native (extension)] |
| 339 // DoTraceHideCEntryFPAddress(EBP) [native] | 352 // DoTraceHideCEntryFPAddress(EBP) [native] |
| 340 // StackTracer::Trace | 353 // StackTracer::Trace |
| 341 // | 354 // |
| 342 // The last JS function called. It is only visible through | 355 // The last JS function called. It is only visible through |
| 343 // sample.function, as its return address is above captured EBP value. | 356 // sample.function, as its return address is above captured EBP value. |
| 344 CheckJSFunctionAtAddress("JSFuncDoTrace", sample.function); | 357 CheckJSFunctionAtAddress("JSFuncDoTrace", sample.function); |
| 345 CHECK_GT(sample.frames_count, 1); | 358 |
| 359 // The VM state tracking keeps track of external callbacks and puts |
| 360 // them at the top of the sample stack. |
| 361 int base = 0; |
| 362 CHECK(sample.stack[0] == FUNCTION_ADDR(TraceExtension::JSTrace)); |
| 363 base++; |
| 364 |
| 346 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" | 365 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" |
| 347 CheckJSFunctionAtAddress("JSTrace", sample.stack[0]); | 366 CHECK_GT(sample.frames_count, base + 1); |
| 348 CheckJSFunctionAtAddress("OuterJSTrace", sample.stack[1]); | 367 CheckJSFunctionAtAddress("JSTrace", sample.stack[base + 0]); |
| 368 CheckJSFunctionAtAddress("OuterJSTrace", sample.stack[base + 1]); |
| 349 } | 369 } |
| 350 | 370 |
| 351 | 371 |
| 352 static void CFuncDoTrace(byte dummy_parameter) { | 372 static void CFuncDoTrace(byte dummy_parameter) { |
| 353 Address fp; | 373 Address fp; |
| 354 #ifdef __GNUC__ | 374 #ifdef __GNUC__ |
| 355 fp = reinterpret_cast<Address>(__builtin_frame_address(0)); | 375 fp = reinterpret_cast<Address>(__builtin_frame_address(0)); |
| 356 #elif defined _MSC_VER | 376 #elif defined _MSC_VER |
| 357 // Approximate a frame pointer address. We compile without base pointers, | 377 // Approximate a frame pointer address. We compile without base pointers, |
| 358 // so we can't trust ebp/rbp. | 378 // so we can't trust ebp/rbp. |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 CHECK_EQ(0, GetJsEntrySp()); | 411 CHECK_EQ(0, GetJsEntrySp()); |
| 392 CompileRun("a = 1; b = a + 1;"); | 412 CompileRun("a = 1; b = a + 1;"); |
| 393 CHECK_EQ(0, GetJsEntrySp()); | 413 CHECK_EQ(0, GetJsEntrySp()); |
| 394 CompileRun("js_entry_sp();"); | 414 CompileRun("js_entry_sp();"); |
| 395 CHECK_EQ(0, GetJsEntrySp()); | 415 CHECK_EQ(0, GetJsEntrySp()); |
| 396 CompileRun("js_entry_sp_level2();"); | 416 CompileRun("js_entry_sp_level2();"); |
| 397 CHECK_EQ(0, GetJsEntrySp()); | 417 CHECK_EQ(0, GetJsEntrySp()); |
| 398 } | 418 } |
| 399 | 419 |
| 400 #endif // ENABLE_LOGGING_AND_PROFILING | 420 #endif // ENABLE_LOGGING_AND_PROFILING |
| OLD | NEW |