Skip to content

Commit

Permalink
[tsan] Implement __tsan_get_alloc_stack and __tsan_locate_address to …
Browse files Browse the repository at this point in the history
…query pointer types and allocation stacks of heap pointers

In ASan, we have __asan_locate_address and __asan_get_alloc_stack, which is used in LLDB/Xcode to show the allocation backtrace for a heap memory object. This patch implements the same for TSan.

Differential Revision: https://reviews.llvm.org/D27656

llvm-svn: 290119
  • Loading branch information
kubamracek committed Dec 19, 2016
1 parent dd46b52 commit 1187cbd
Showing 5 changed files with 212 additions and 0 deletions.
77 changes: 77 additions & 0 deletions compiler-rt/lib/tsan/rtl/tsan_debugging.cc
Original file line number Diff line number Diff line change
@@ -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 @@ int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid) {
*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;
}
11 changes: 11 additions & 0 deletions compiler-rt/lib/tsan/rtl/tsan_interface.h
Original file line number Diff line number Diff line change
@@ -136,6 +136,17 @@ int __tsan_get_report_thread(void *report, uptr idx, int *tid, uptr *os_id,
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
1 change: 1 addition & 0 deletions compiler-rt/lib/tsan/rtl/tsan_rtl.h
Original file line number Diff line number Diff line change
@@ -590,6 +590,7 @@ class ScopedReport {
void operator = (const ScopedReport&);
};

ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack);
void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
MutexSet *mset);

80 changes: 80 additions & 0 deletions compiler-rt/test/tsan/debug_alloc_stack.cc
Original file line number Diff line number Diff line change
@@ -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 <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#if !__APPLE__
#include <sys/types.h>
#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
43 changes: 43 additions & 0 deletions compiler-rt/test/tsan/debug_locate.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: %clangxx_tsan -O0 %s -o %t
// RUN: %run %t 2>&1 | FileCheck %s

#include <stdio.h>
#include <stdlib.h>

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;
}

0 comments on commit 1187cbd

Please sign in to comment.