diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h @@ -196,5 +196,26 @@ uptr local_stack; \ uptr sp = (uptr)&local_stack +// GET_CURRENT_PC() is equivalent to StackTrace::GetCurrentPc(). +// Optimized x86 version is faster than GetCurrentPc because +// it does not involve a function call, instead it reads RIP register. +// Reads of RIP by an instruction return RIP pointing to the next +// instruction, which is exactly what we want here, thus 0 offset. +// It needs to be a macro because otherwise we will get the name +// of this function on the top of most stacks. Attribute artificial +// does not do what it claims to do, unfortunatley. And attribute +// __nodebug__ is clang-only. If we would have an attribute that +// would remove this function from debug info, we could simply make +// StackTrace::GetCurrentPc() faster. +#if defined(__x86_64__) +# define GET_CURRENT_PC() \ + ({ \ + uptr pc; \ + asm("lea 0(%%rip), %0" : "=r"(pc)); \ + pc; \ + }) +#else +# define GET_CURRENT_PC() StackTrace::GetCurrentPc() +#endif #endif // SANITIZER_STACKTRACE_H diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp @@ -271,6 +271,30 @@ EXPECT_EQ(bp, stack.top_frame_bp); } +TEST(GetCurrentPc, Basic) { + // Test that PCs obtained via GET_CURRENT_PC() + // and StackTrace::GetCurrentPc() are all different + // and are close to the function start. + struct Local { + static NOINLINE void Test() { + const uptr pcs[] = { + (uptr)&Local::Test, + GET_CURRENT_PC(), + StackTrace::GetCurrentPc(), + StackTrace::GetCurrentPc(), + }; + for (uptr i = 0; i < ARRAY_SIZE(pcs); i++) + Printf("pc%zu: %p\n", i, pcs[i]); + for (uptr i = 1; i < ARRAY_SIZE(pcs); i++) { + EXPECT_GT(pcs[i], pcs[0]); + EXPECT_LT(pcs[i], pcs[0] + 1000); + for (uptr j = 0; j < i; j++) EXPECT_NE(pcs[i], pcs[j]); + } + } + }; + Local::Test(); +} + // Dummy implementation. This should never be called, but is required to link // non-optimized builds of this test. void BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context, diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.h b/compiler-rt/lib/tsan/rtl/tsan_interceptors.h --- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.h +++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.h @@ -30,14 +30,14 @@ } // namespace __tsan -#define SCOPED_INTERCEPTOR_RAW(func, ...) \ - cur_thread_init(); \ - ThreadState *thr = cur_thread(); \ - const uptr caller_pc = GET_CALLER_PC(); \ - ScopedInterceptor si(thr, #func, caller_pc); \ - const uptr pc = StackTrace::GetCurrentPc(); \ - (void)pc; \ -/**/ +#define SCOPED_INTERCEPTOR_RAW(func, ...) \ + cur_thread_init(); \ + ThreadState *thr = cur_thread(); \ + const uptr caller_pc = GET_CALLER_PC(); \ + ScopedInterceptor si(thr, #func, caller_pc); \ + const uptr pc = GET_CURRENT_PC(); \ + (void)pc; \ + /**/ #define SCOPED_TSAN_INTERCEPTOR(func, ...) \ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \