Index: compiler-rt/trunk/lib/tsan/CMakeLists.txt =================================================================== --- compiler-rt/trunk/lib/tsan/CMakeLists.txt +++ compiler-rt/trunk/lib/tsan/CMakeLists.txt @@ -24,6 +24,7 @@ set(TSAN_SOURCES rtl/tsan_clock.cc + rtl/tsan_debugging.cc rtl/tsan_flags.cc rtl/tsan_fd.cc rtl/tsan_ignoreset.cc 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 @@ -0,0 +1,162 @@ +//===-- tsan_debugging.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// TSan debugging API implementation. +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_report.h" +#include "tsan_rtl.h" + +using namespace __tsan; + +static const char *ReportTypeDescription(ReportType typ) { + if (typ == ReportTypeRace) return "data-race"; + if (typ == ReportTypeVptrRace) return "data-race-vptr"; + if (typ == ReportTypeUseAfterFree) return "heap-use-after-free"; + if (typ == ReportTypeVptrUseAfterFree) return "heap-use-after-free-vptr"; + if (typ == ReportTypeThreadLeak) return "thread-leak"; + if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy"; + if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock"; + if (typ == ReportTypeMutexBadUnlock) return "mutex-bad-unlock"; + if (typ == ReportTypeMutexBadReadLock) return "mutex-bad-read-lock"; + if (typ == ReportTypeMutexBadReadUnlock) return "mutex-bad-read-unlock"; + if (typ == ReportTypeSignalUnsafe) return "signal-unsafe-call"; + if (typ == ReportTypeErrnoInSignal) return "errno-in-signal-handler"; + if (typ == ReportTypeDeadlock) return "lock-order-inversion"; + return ""; +} + +static const char *ReportLocationTypeDescription(ReportLocationType typ) { + if (typ == ReportLocationGlobal) return "global"; + if (typ == ReportLocationHeap) return "heap"; + if (typ == ReportLocationStack) return "stack"; + if (typ == ReportLocationTLS) return "tls"; + if (typ == ReportLocationFD) return "fd"; + return ""; +} + +static void CopyTrace(SymbolizedStack *first_frame, void **trace, + uptr trace_size) { + uptr i = 0; + for (SymbolizedStack *frame = first_frame; frame != nullptr; + frame = frame->next) { + trace[i++] = (void *)frame->info.address; + if (i >= trace_size) break; + } +} + +// Meant to be called by the debugger. +SANITIZER_INTERFACE_ATTRIBUTE +void *__tsan_get_current_report() { + const ReportDesc *rep = cur_thread()->current_report; + return (void *)rep; +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_data(void *report, const char **description, int *count, + int *stack_count, int *mop_count, int *loc_count, + int *mutex_count, int *thread_count, + int *unique_tid_count, void **sleep_trace, + uptr trace_size) { + const ReportDesc *rep = (ReportDesc *)report; + *description = ReportTypeDescription(rep->typ); + *count = rep->count; + *stack_count = rep->stacks.Size(); + *mop_count = rep->mops.Size(); + *loc_count = rep->locs.Size(); + *mutex_count = rep->mutexes.Size(); + *thread_count = rep->threads.Size(); + *unique_tid_count = rep->unique_tids.Size(); + if (rep->sleep) CopyTrace(rep->sleep->frames, sleep_trace, trace_size); + return 1; +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_stack(void *report, uptr idx, void **trace, + uptr trace_size) { + const ReportDesc *rep = (ReportDesc *)report; + CHECK_LT(idx, rep->stacks.Size()); + ReportStack *stack = rep->stacks[idx]; + CopyTrace(stack->frames, trace, trace_size); + return 1; +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_mop(void *report, uptr idx, int *tid, void **addr, + int *size, int *write, int *atomic, void **trace, + uptr trace_size) { + const ReportDesc *rep = (ReportDesc *)report; + CHECK_LT(idx, rep->mops.Size()); + ReportMop *mop = rep->mops[idx]; + *tid = mop->tid; + *addr = (void *)mop->addr; + *size = mop->size; + *write = mop->write ? 1 : 0; + *atomic = mop->atomic ? 1 : 0; + CopyTrace(mop->stack->frames, trace, trace_size); + return 1; +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_loc(void *report, uptr idx, const char **type, + void **addr, uptr *start, uptr *size, int *tid, + int *fd, int *suppressable, void **trace, + uptr trace_size) { + const ReportDesc *rep = (ReportDesc *)report; + CHECK_LT(idx, rep->locs.Size()); + ReportLocation *loc = rep->locs[idx]; + *type = ReportLocationTypeDescription(loc->type); + *addr = (void *)loc->global.start; + *start = loc->heap_chunk_start; + *size = loc->heap_chunk_size; + *tid = loc->tid; + *fd = loc->fd; + *suppressable = loc->suppressable; + if (loc->stack) CopyTrace(loc->stack->frames, trace, trace_size); + return 1; +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr, + int *destroyed, void **trace, uptr trace_size) { + const ReportDesc *rep = (ReportDesc *)report; + CHECK_LT(idx, rep->mutexes.Size()); + ReportMutex *mutex = rep->mutexes[idx]; + *mutex_id = mutex->id; + *addr = (void *)mutex->addr; + *destroyed = mutex->destroyed; + CopyTrace(mutex->stack->frames, trace, trace_size); + return 1; +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_thread(void *report, uptr idx, int *tid, uptr *pid, + int *running, const char **name, int *parent_tid, + void **trace, uptr trace_size) { + const ReportDesc *rep = (ReportDesc *)report; + CHECK_LT(idx, rep->threads.Size()); + ReportThread *thread = rep->threads[idx]; + *tid = thread->id; + *pid = thread->pid; + *running = thread->running; + *name = thread->name; + *parent_tid = thread->parent_tid; + CopyTrace(thread->stack->frames, trace, trace_size); + return 1; +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid) { + const ReportDesc *rep = (ReportDesc *)report; + CHECK_LT(idx, rep->unique_tids.Size()); + *tid = rep->unique_tids[idx]; + return 1; +} 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 @@ -75,6 +75,61 @@ SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write_range(void *addr, unsigned long size); // NOLINT +// User may provide function that would be called right when TSan detects +// an error. The argument 'report' is an opaque pointer that can be used to +// gather additional information using other TSan report API functions. +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_on_report(void *report); + +// If TSan is currently reporting a detected issue on the current thread, +// returns an opaque pointer to the current report. Otherwise returns NULL. +SANITIZER_INTERFACE_ATTRIBUTE +void *__tsan_get_current_report(); + +// Returns a report's description (issue type), number of duplicate issues +// found, counts of array data (stack traces, memory operations, locations, +// mutexes, threads, unique thread IDs) and a stack trace of a sleep() call (if +// one was involved in the issue). +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_data(void *report, const char **description, int *count, + int *stack_count, int *mop_count, int *loc_count, + int *mutex_count, int *thread_count, + int *unique_tid_count, void **sleep_trace, + uptr trace_size); + +// Returns information about stack traces included in the report. +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_stack(void *report, uptr idx, void **trace, + uptr trace_size); + +// Returns information about memory operations included in the report. +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_mop(void *report, uptr idx, int *tid, void **addr, + int *size, int *write, int *atomic, void **trace, + uptr trace_size); + +// Returns information about locations included in the report. +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_loc(void *report, uptr idx, const char **type, + void **addr, uptr *start, uptr *size, int *tid, + int *fd, int *suppressable, void **trace, + uptr trace_size); + +// Returns information about mutexes included in the report. +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr, + int *destroyed, void **trace, uptr trace_size); + +// Returns information about threads included in the report. +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_thread(void *report, uptr idx, int *tid, uptr *pid, + int *running, const char **name, int *parent_tid, + void **trace, uptr trace_size); + +// Returns information about unique thread IDs included in the report. +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid); + #ifdef __cplusplus } // extern "C" #endif 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 @@ -404,6 +404,8 @@ // If set, malloc must not be called. int nomalloc; + const ReportDesc *current_report; + explicit ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, unsigned reuse_count, uptr stk_addr, uptr stk_size, Index: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc @@ -56,6 +56,11 @@ } #endif +SANITIZER_WEAK_DEFAULT_IMPL +void __tsan_on_report(const ReportDesc *rep) { + (void)rep; +} + static void StackStripMain(SymbolizedStack *frames) { SymbolizedStack *last_frame = nullptr; SymbolizedStack *last_frame2 = nullptr; @@ -492,6 +497,8 @@ return false; atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime()); const ReportDesc *rep = srep.GetReport(); + CHECK_EQ(thr->current_report, nullptr); + thr->current_report = rep; Suppression *supp = 0; uptr pc_or_addr = 0; for (uptr i = 0; pc_or_addr == 0 && i < rep->mops.Size(); i++) @@ -512,13 +519,17 @@ thr->is_freeing = false; bool suppressed = OnReport(rep, pc_or_addr != 0); thr->is_freeing = old_is_freeing; - if (suppressed) + if (suppressed) { + thr->current_report = nullptr; return false; + } } PrintReport(rep); + __tsan_on_report(rep); ctx->nreported++; if (flags()->halt_on_error) Die(); + thr->current_report = nullptr; return true; } Index: compiler-rt/trunk/test/tsan/debugging.cc =================================================================== --- compiler-rt/trunk/test/tsan/debugging.cc +++ compiler-rt/trunk/test/tsan/debugging.cc @@ -0,0 +1,88 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: %deflake %run %t 2>&1 | FileCheck %s + +#include +#include +#include +#include + +#include "test.h" + +extern "C" { +void __tsan_on_report(void *report); +void *__tsan_get_current_report(); +int __tsan_get_report_data(void *report, const char **description, int *count, + int *stack_count, int *mop_count, int *loc_count, + int *mutex_count, int *thread_count, + int *unique_tid_count, void **sleep_trace, + unsigned long trace_size); +int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, + void **addr, int *size, int *write, int *atomic, + void **trace, unsigned long trace_size); +} + +long my_global; + +void *Thread(void *a) { + barrier_wait(&barrier); + my_global = 42; + return NULL; +} + +int main() { + barrier_init(&barrier, 2); + fprintf(stderr, "&my_global = %p\n", &my_global); + // CHECK: &my_global = [[GLOBAL:0x[0-9a-f]+]] + pthread_t t; + pthread_create(&t, 0, Thread, 0); + my_global = 41; + barrier_wait(&barrier); + pthread_join(t, 0); + fprintf(stderr, "Done.\n"); +} + +void __tsan_on_report(void *report) { + fprintf(stderr, "__tsan_on_report(%p)\n", report); + fprintf(stderr, "__tsan_get_current_report() = %p\n", + __tsan_get_current_report()); + // CHECK: __tsan_on_report([[REPORT:0x[0-9a-f]+]]) + // CHECK: __tsan_get_current_report() = [[REPORT]] + + const char *description; + int count; + int stack_count, mop_count, loc_count, mutex_count, thread_count, + unique_tid_count; + void *sleep_trace[16] = {0}; + __tsan_get_report_data(report, &description, &count, &stack_count, &mop_count, + &loc_count, &mutex_count, &thread_count, + &unique_tid_count, sleep_trace, 16); + fprintf(stderr, "report type = '%s', count = %d\n", description, count); + // CHECK: report type = 'data-race', count = 0 + + fprintf(stderr, "mop_count = %d\n", mop_count); + // CHECK: mop_count = 2 + + int tid; + void *addr; + int size, write, atomic; + void *trace[16] = {0}; + + __tsan_get_report_mop(report, 0, &tid, &addr, &size, &write, &atomic, trace, + 16); + fprintf(stderr, "tid = %d, addr = %p, size = %d, write = %d, atomic = %d\n", + tid, addr, size, write, atomic); + // CHECK: tid = 1, addr = [[GLOBAL]], size = 8, write = 1, atomic = 0 + fprintf(stderr, "trace[0] = %p, trace[1] = %p\n", trace[0], trace[1]); + // CHECK: trace[0] = 0x{{[0-9a-f]+}}, trace[1] = 0x0 + + __tsan_get_report_mop(report, 1, &tid, &addr, &size, &write, &atomic, trace, + 16); + fprintf(stderr, "tid = %d, addr = %p, size = %d, write = %d, atomic = %d\n", + tid, addr, size, write, atomic); + // CHECK: tid = 0, addr = [[GLOBAL]], size = 8, write = 1, atomic = 0 + fprintf(stderr, "trace[0] = %p, trace[1] = %p\n", trace[0], trace[1]); + // CHECK: trace[0] = 0x{{[0-9a-f]+}}, trace[1] = 0x0 +} + +// CHECK: Done. +// CHECK: ThreadSanitizer: reported 1 warnings