Index: lib/tsan/rtl/tsan_debugging.cc =================================================================== --- lib/tsan/rtl/tsan_debugging.cc +++ lib/tsan/rtl/tsan_debugging.cc @@ -15,6 +15,8 @@ #include "tsan_report.h" #include "tsan_rtl.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + using namespace __tsan; static const char *ReportTypeDescription(ReportType typ) { @@ -160,3 +162,26 @@ *tid = rep->unique_tids[idx]; return 1; } + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id, + uptr *os_id) { + MBlock *b = 0; + Allocator *a = allocator(); + if (a->PointerIsMine((void *)addr)) { + void *block_begin = a->GetBlockBegin((void *)addr); + if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin); + } + if (b == 0) return 0; + + *thread_id = b->tid; + // No locking. This is supposed to be called from within the debugger when + // other threads are stopped. + ThreadContextBase *tctx = ctx->thread_registry->GetThreadLocked(b->tid); + *os_id = tctx->os_id; + + StackTrace stack = StackDepotGet(b->stk); + size = Min(size, (uptr)stack.size); + for (uptr i = 0; i < size; i++) trace[i] = stack.trace[stack.size - i - 1]; + return size; +} Index: lib/tsan/rtl/tsan_interface.h =================================================================== --- lib/tsan/rtl/tsan_interface.h +++ lib/tsan/rtl/tsan_interface.h @@ -136,6 +136,10 @@ SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid); +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id, + uptr *os_id); + #endif // SANITIZER_GO #ifdef __cplusplus Index: test/tsan/debug_alloc_stack.cc =================================================================== --- test/tsan/debug_alloc_stack.cc +++ test/tsan/debug_alloc_stack.cc @@ -0,0 +1,80 @@ +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: env %env_tsan_opts=stack_trace_format=DEFAULT %deflake %run %t 2>&1 | FileCheck %s + +#include "test.h" +#include +#include +#include + +#if !__APPLE__ +#include +#endif + +extern "C" int __tsan_get_alloc_stack(void *addr, void **trace, size_t size, + int *thread_id, void *os_id); + +char *mem; +void alloc_func() { mem = (char *)malloc(10); } + +void *AllocThread(void *context) { + uint64_t tid; +#if __APPLE__ + pthread_threadid_np(NULL, &tid); +#else + tid = gettid(); +#endif + fprintf(stderr, "alloc stack thread os id = 0x%llx\n", tid); + // CHECK: alloc stack thread os id = [[THREAD_OS_ID:0x[0-9a-f]+]] + alloc_func(); + return NULL; +} + +void *RaceThread(void *context) { + *mem = 'a'; + barrier_wait(&barrier); + return NULL; +} + +int main() { + pthread_t t; + barrier_init(&barrier, 2); + + pthread_create(&t, NULL, AllocThread, NULL); + pthread_join(t, NULL); + + void *trace[100]; + size_t num_frames = 100; + int thread_id; + void *thread_os_id; + num_frames = + __tsan_get_alloc_stack(mem, trace, num_frames, &thread_id, &thread_os_id); + + fprintf(stderr, "alloc stack retval %s\n", + (num_frames > 0 && num_frames < 10) ? "ok" : ""); + // CHECK: alloc stack retval ok + fprintf(stderr, "thread id = %d\n", thread_id); + // CHECK: thread id = 1 + fprintf(stderr, "thread os id = 0x%llx\n", (uint64_t)thread_os_id); + // CHECK: thread os id = [[THREAD_OS_ID]] + fprintf(stderr, "%p\n", trace[0]); + // CHECK: [[ALLOC_FRAME_0:0x[0-9a-f]+]] + fprintf(stderr, "%p\n", trace[1]); + // CHECK: [[ALLOC_FRAME_1:0x[0-9a-f]+]] + fprintf(stderr, "%p\n", trace[2]); + // CHECK: [[ALLOC_FRAME_2:0x[0-9a-f]+]] + + pthread_create(&t, NULL, RaceThread, NULL); + barrier_wait(&barrier); + mem[0] = 'b'; + pthread_join(t, NULL); + + free(mem); + + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is heap block of size 10 at {{.*}} allocated by thread T1 +// CHECK: #0 [[ALLOC_FRAME_0]] +// CHECK: #1 [[ALLOC_FRAME_1]] in alloc_func +// CHECK: #2 [[ALLOC_FRAME_2]] in AllocThread