Index: compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc +++ compiler-rt/trunk/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,78 @@ *tid = rep->unique_tids[idx]; return 1; } + +SANITIZER_INTERFACE_ATTRIBUTE +const char *__tsan_locate_address(uptr addr, char *name, uptr name_size, + uptr *region_address_ptr, + uptr *region_size_ptr) { + uptr region_address = 0; + uptr region_size = 0; + const char *region_kind = nullptr; + if (name && name_size > 0) name[0] = 0; + + if (IsMetaMem(addr)) { + region_kind = "meta shadow"; + } else if (IsShadowMem(addr)) { + region_kind = "shadow"; + } else { + bool is_stack = false; + 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) { + region_address = (uptr)allocator()->GetBlockBegin((void *)addr); + region_size = b->siz; + region_kind = "heap"; + } else { + // TODO(kuba.brecka): We should not lock. This is supposed to be called + // from within the debugger when other threads are stopped. + ctx->thread_registry->Lock(); + ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack); + ctx->thread_registry->Unlock(); + if (tctx) { + region_kind = is_stack ? "stack" : "tls"; + } else { + region_kind = "global"; + DataInfo info; + if (Symbolizer::GetOrInit()->SymbolizeData(addr, &info)) { + internal_strncpy(name, info.name, name_size); + region_address = info.start; + region_size = info.size; + } + } + } + } + + CHECK(region_kind); + if (region_address_ptr) *region_address_ptr = region_address; + if (region_size_ptr) *region_size_ptr = region_size; + return region_kind; +} + +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: compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h +++ compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h @@ -136,6 +136,17 @@ SANITIZER_INTERFACE_ATTRIBUTE int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid); +// Returns the type of the pointer (heap, stack, global, ...) and if possible +// also the starting address (e.g. of a heap allocation) and size. +SANITIZER_INTERFACE_ATTRIBUTE +const char *__tsan_locate_address(uptr addr, char *name, uptr name_size, + uptr *region_address, uptr *region_size); + +// Returns the allocation stack for a heap pointer. +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: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h @@ -590,6 +590,7 @@ void operator = (const ScopedReport&); }; +ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack); void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, MutexSet *mset); Index: compiler-rt/trunk/test/tsan/debug_alloc_stack.cc =================================================================== --- compiler-rt/trunk/test/tsan/debug_alloc_stack.cc +++ compiler-rt/trunk/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 Index: compiler-rt/trunk/test/tsan/debug_locate.cc =================================================================== --- compiler-rt/trunk/test/tsan/debug_locate.cc +++ compiler-rt/trunk/test/tsan/debug_locate.cc @@ -0,0 +1,43 @@ +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include +#include + +extern "C" const char * +__tsan_locate_address(void *addr, char *name, size_t name_size, + void **region_address_ptr, size_t *region_size_ptr); + +long global_var; + +int main() { + long stack_var; + void *heap_var = malloc(10); + + fprintf(stderr, "stack_var = %p\n", &stack_var); + fprintf(stderr, "global_var = %p\n", &global_var); + fprintf(stderr, "heap_var = %p\n", heap_var); + // CHECK: stack_var = [[STACK_VAR:0x[0-9a-f]+]] + // CHECK: global_var = [[GLOBAL_VAR:0x[0-9a-f]+]] + // CHECK: heap_var = [[HEAP_VAR:0x[0-9a-f]+]] + + const char *type; + char name[128]; + void *start; + size_t size; + type = __tsan_locate_address(&stack_var, name, 128, &start, &size); + fprintf(stderr, "type: %s\n", type); + // CHECK: type: stack + + type = __tsan_locate_address(&global_var, name, 128, &start, &size); + fprintf(stderr, "type: %s, name = %s, start = %p, size = %zu\n", type, name, + start, size); + // CHECK: type: global, name = global_var, start = [[GLOBAL_VAR]], size = 8 + + type = __tsan_locate_address(heap_var, name, 128, &start, &size); + fprintf(stderr, "type: %s, start = %p, size = %zu\n", type, start, size); + // CHECK: type: heap, start = [[HEAP_VAR]], size = 10 + + free(heap_var); + return 0; +}