| OLD | NEW |
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 44 using v8::Object; | 44 using v8::Object; |
| 45 using v8::Script; | 45 using v8::Script; |
| 46 using v8::String; | 46 using v8::String; |
| 47 using v8::Value; | 47 using v8::Value; |
| 48 | 48 |
| 49 using v8::internal::byte; | 49 using v8::internal::byte; |
| 50 using v8::internal::Address; | 50 using v8::internal::Address; |
| 51 using v8::internal::Handle; | 51 using v8::internal::Handle; |
| 52 using v8::internal::Isolate; | 52 using v8::internal::Isolate; |
| 53 using v8::internal::JSFunction; | 53 using v8::internal::JSFunction; |
| 54 using v8::internal::StackTracer; | |
| 55 using v8::internal::TickSample; | 54 using v8::internal::TickSample; |
| 56 | 55 |
| 57 | 56 |
| 58 static struct { | 57 static struct { |
| 59 TickSample* sample; | 58 TickSample* sample; |
| 60 } trace_env = { NULL }; | 59 } trace_env = { NULL }; |
| 61 | 60 |
| 62 | 61 |
| 63 static void InitTraceEnv(TickSample* sample) { | 62 static void InitTraceEnv(TickSample* sample) { |
| 64 trace_env.sample = sample; | 63 trace_env.sample = sample; |
| 65 } | 64 } |
| 66 | 65 |
| 67 | 66 |
| 68 static void DoTrace(Address fp) { | 67 static void DoTrace(Address fp) { |
| 69 trace_env.sample->fp = fp; | 68 trace_env.sample->fp = fp; |
| 70 // sp is only used to define stack high bound | 69 // sp is only used to define stack high bound |
| 71 trace_env.sample->sp = | 70 trace_env.sample->sp = |
| 72 reinterpret_cast<Address>(trace_env.sample) - 10240; | 71 reinterpret_cast<Address>(trace_env.sample) - 10240; |
| 73 StackTracer::Trace(Isolate::Current(), trace_env.sample); | 72 trace_env.sample->Trace(Isolate::Current()); |
| 74 } | 73 } |
| 75 | 74 |
| 76 | 75 |
| 77 // Hide c_entry_fp to emulate situation when sampling is done while | 76 // Hide c_entry_fp to emulate situation when sampling is done while |
| 78 // pure JS code is being executed | 77 // pure JS code is being executed |
| 79 static void DoTraceHideCEntryFPAddress(Address fp) { | 78 static void DoTraceHideCEntryFPAddress(Address fp) { |
| 80 v8::internal::Address saved_c_frame_fp = | 79 v8::internal::Address saved_c_frame_fp = |
| 81 *(Isolate::Current()->c_entry_fp_address()); | 80 *(Isolate::Current()->c_entry_fp_address()); |
| 82 CHECK(saved_c_frame_fp); | 81 CHECK(saved_c_frame_fp); |
| 83 *(Isolate::Current()->c_entry_fp_address()) = 0; | 82 *(Isolate::Current()->c_entry_fp_address()) = 0; |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 251 // when called as a constructor. | 250 // when called as a constructor. |
| 252 CreateFramePointerGrabberConstructor("FPGrabber"); | 251 CreateFramePointerGrabberConstructor("FPGrabber"); |
| 253 | 252 |
| 254 // Compile the script. | 253 // Compile the script. |
| 255 CompileRun(trace_call_buf.start()); | 254 CompileRun(trace_call_buf.start()); |
| 256 } | 255 } |
| 257 | 256 |
| 258 | 257 |
| 259 // This test verifies that stack tracing works when called during | 258 // This test verifies that stack tracing works when called during |
| 260 // execution of a native function called from JS code. In this case, | 259 // execution of a native function called from JS code. In this case, |
| 261 // StackTracer uses Isolate::c_entry_fp as a starting point for stack | 260 // TickSample::Trace uses Isolate::c_entry_fp as a starting point for stack |
| 262 // walking. | 261 // walking. |
| 263 TEST(CFromJSStackTrace) { | 262 TEST(CFromJSStackTrace) { |
| 264 // BUG(1303) Inlining of JSFuncDoTrace() in JSTrace below breaks this test. | 263 // BUG(1303) Inlining of JSFuncDoTrace() in JSTrace below breaks this test. |
| 265 i::FLAG_use_inlining = false; | 264 i::FLAG_use_inlining = false; |
| 266 | 265 |
| 267 TickSample sample; | 266 TickSample sample; |
| 268 InitTraceEnv(&sample); | 267 InitTraceEnv(&sample); |
| 269 | 268 |
| 270 CcTest::InitializeVM(TRACE_EXTENSION); | 269 CcTest::InitializeVM(TRACE_EXTENSION); |
| 271 v8::HandleScope scope(CcTest::isolate()); | 270 v8::HandleScope scope(CcTest::isolate()); |
| 272 // Create global function JSFuncDoTrace which calls | 271 // Create global function JSFuncDoTrace which calls |
| 273 // extension function trace() with the current frame pointer value. | 272 // extension function trace() with the current frame pointer value. |
| 274 CreateTraceCallerFunction("JSFuncDoTrace", "trace"); | 273 CreateTraceCallerFunction("JSFuncDoTrace", "trace"); |
| 275 Local<Value> result = CompileRun( | 274 Local<Value> result = CompileRun( |
| 276 "function JSTrace() {" | 275 "function JSTrace() {" |
| 277 " JSFuncDoTrace();" | 276 " JSFuncDoTrace();" |
| 278 "};\n" | 277 "};\n" |
| 279 "JSTrace();\n" | 278 "JSTrace();\n" |
| 280 "true;"); | 279 "true;"); |
| 281 CHECK(!result.IsEmpty()); | 280 CHECK(!result.IsEmpty()); |
| 282 // When stack tracer is invoked, the stack should look as follows: | 281 // When stack tracer is invoked, the stack should look as follows: |
| 283 // script [JS] | 282 // script [JS] |
| 284 // JSTrace() [JS] | 283 // JSTrace() [JS] |
| 285 // JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi] | 284 // JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi] |
| 286 // trace(EBP) [native (extension)] | 285 // trace(EBP) [native (extension)] |
| 287 // DoTrace(EBP) [native] | 286 // DoTrace(EBP) [native] |
| 288 // StackTracer::Trace | 287 // TickSample::Trace |
| 289 | 288 |
| 290 CHECK(sample.external_callback); | 289 CHECK(sample.external_callback); |
| 291 CHECK_EQ(FUNCTION_ADDR(TraceExtension::Trace), sample.external_callback); | 290 CHECK_EQ(FUNCTION_ADDR(TraceExtension::Trace), sample.external_callback); |
| 292 | 291 |
| 293 // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace" | 292 // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace" |
| 294 int base = 0; | 293 int base = 0; |
| 295 CHECK_GT(sample.frames_count, base + 1); | 294 CHECK_GT(sample.frames_count, base + 1); |
| 296 | 295 |
| 297 CHECK(IsAddressWithinFuncCode("JSFuncDoTrace", sample.stack[base + 0])); | 296 CHECK(IsAddressWithinFuncCode("JSFuncDoTrace", sample.stack[base + 0])); |
| 298 CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 1])); | 297 CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 1])); |
| 299 } | 298 } |
| 300 | 299 |
| 301 | 300 |
| 302 // This test verifies that stack tracing works when called during | 301 // This test verifies that stack tracing works when called during |
| 303 // execution of JS code. However, as calling StackTracer requires | 302 // execution of JS code. However, as calling TickSample::Trace requires |
| 304 // entering native code, we can only emulate pure JS by erasing | 303 // entering native code, we can only emulate pure JS by erasing |
| 305 // Isolate::c_entry_fp value. In this case, StackTracer uses passed frame | 304 // Isolate::c_entry_fp value. In this case, TickSample::Trace uses passed frame |
| 306 // pointer value as a starting point for stack walking. | 305 // pointer value as a starting point for stack walking. |
| 307 TEST(PureJSStackTrace) { | 306 TEST(PureJSStackTrace) { |
| 308 // This test does not pass with inlining enabled since inlined functions | 307 // This test does not pass with inlining enabled since inlined functions |
| 309 // don't appear in the stack trace. | 308 // don't appear in the stack trace. |
| 310 i::FLAG_use_inlining = false; | 309 i::FLAG_use_inlining = false; |
| 311 | 310 |
| 312 TickSample sample; | 311 TickSample sample; |
| 313 InitTraceEnv(&sample); | 312 InitTraceEnv(&sample); |
| 314 | 313 |
| 315 CcTest::InitializeVM(TRACE_EXTENSION); | 314 CcTest::InitializeVM(TRACE_EXTENSION); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 327 "OuterJSTrace();\n" | 326 "OuterJSTrace();\n" |
| 328 "true;"); | 327 "true;"); |
| 329 CHECK(!result.IsEmpty()); | 328 CHECK(!result.IsEmpty()); |
| 330 // When stack tracer is invoked, the stack should look as follows: | 329 // When stack tracer is invoked, the stack should look as follows: |
| 331 // script [JS] | 330 // script [JS] |
| 332 // OuterJSTrace() [JS] | 331 // OuterJSTrace() [JS] |
| 333 // JSTrace() [JS] | 332 // JSTrace() [JS] |
| 334 // JSFuncDoTrace() [JS] | 333 // JSFuncDoTrace() [JS] |
| 335 // js_trace(EBP) [native (extension)] | 334 // js_trace(EBP) [native (extension)] |
| 336 // DoTraceHideCEntryFPAddress(EBP) [native] | 335 // DoTraceHideCEntryFPAddress(EBP) [native] |
| 337 // StackTracer::Trace | 336 // TickSample::Trace |
| 338 // | 337 // |
| 339 | 338 |
| 340 CHECK(sample.external_callback); | 339 CHECK(sample.external_callback); |
| 341 CHECK_EQ(FUNCTION_ADDR(TraceExtension::JSTrace), sample.external_callback); | 340 CHECK_EQ(FUNCTION_ADDR(TraceExtension::JSTrace), sample.external_callback); |
| 342 | 341 |
| 343 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" | 342 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" |
| 344 int base = 0; | 343 int base = 0; |
| 345 CHECK_GT(sample.frames_count, base + 1); | 344 CHECK_GT(sample.frames_count, base + 1); |
| 346 CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 0])); | 345 CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 0])); |
| 347 CHECK(IsAddressWithinFuncCode("OuterJSTrace", sample.stack[base + 1])); | 346 CHECK(IsAddressWithinFuncCode("OuterJSTrace", sample.stack[base + 1])); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 367 if (depth <= 0) { | 366 if (depth <= 0) { |
| 368 CFuncDoTrace(0); | 367 CFuncDoTrace(0); |
| 369 return 0; | 368 return 0; |
| 370 } else { | 369 } else { |
| 371 return CFunc(depth - 1) + 1; | 370 return CFunc(depth - 1) + 1; |
| 372 } | 371 } |
| 373 } | 372 } |
| 374 | 373 |
| 375 | 374 |
| 376 // This test verifies that stack tracing doesn't crash when called on | 375 // This test verifies that stack tracing doesn't crash when called on |
| 377 // pure native code. StackTracer only unrolls JS code, so we can't | 376 // pure native code. TickSample::Trace only unrolls JS code, so we can't |
| 378 // get any meaningful info here. | 377 // get any meaningful info here. |
| 379 TEST(PureCStackTrace) { | 378 TEST(PureCStackTrace) { |
| 380 TickSample sample; | 379 TickSample sample; |
| 381 InitTraceEnv(&sample); | 380 InitTraceEnv(&sample); |
| 382 CcTest::InitializeVM(TRACE_EXTENSION); | 381 CcTest::InitializeVM(TRACE_EXTENSION); |
| 383 // Check that sampler doesn't crash | 382 // Check that sampler doesn't crash |
| 384 CHECK_EQ(10, CFunc(10)); | 383 CHECK_EQ(10, CFunc(10)); |
| 385 } | 384 } |
| 386 | 385 |
| 387 | 386 |
| 388 TEST(JsEntrySp) { | 387 TEST(JsEntrySp) { |
| 389 CcTest::InitializeVM(TRACE_EXTENSION); | 388 CcTest::InitializeVM(TRACE_EXTENSION); |
| 390 v8::HandleScope scope(CcTest::isolate()); | 389 v8::HandleScope scope(CcTest::isolate()); |
| 391 CHECK_EQ(0, GetJsEntrySp()); | 390 CHECK_EQ(0, GetJsEntrySp()); |
| 392 CompileRun("a = 1; b = a + 1;"); | 391 CompileRun("a = 1; b = a + 1;"); |
| 393 CHECK_EQ(0, GetJsEntrySp()); | 392 CHECK_EQ(0, GetJsEntrySp()); |
| 394 CompileRun("js_entry_sp();"); | 393 CompileRun("js_entry_sp();"); |
| 395 CHECK_EQ(0, GetJsEntrySp()); | 394 CHECK_EQ(0, GetJsEntrySp()); |
| 396 CompileRun("js_entry_sp_level2();"); | 395 CompileRun("js_entry_sp_level2();"); |
| 397 CHECK_EQ(0, GetJsEntrySp()); | 396 CHECK_EQ(0, GetJsEntrySp()); |
| 398 } | 397 } |
| OLD | NEW |