[compiler-rt] r263126 - [tsan] Add TSan debugger APIs
Kuba Brecka via llvm-commits
llvm-commits at lists.llvm.org
Thu Mar 10 09:00:29 PST 2016
Author: kuba.brecka
Date: Thu Mar 10 11:00:29 2016
New Revision: 263126
URL: http://llvm.org/viewvc/llvm-project?rev=263126&view=rev
Log:
[tsan] Add TSan debugger APIs
Currently, TSan only reports everything in a formatted textual form. The idea behind this patch is to provide a consistent API that can be used to query information contained in a TSan-produced report. User can use these APIs either in a debugger (via a script or directly), or they can use it directly from the process (e.g. in the __tsan_on_report callback). ASan already has a similar API, see http://reviews.llvm.org/D4466.
Differential Revision: http://reviews.llvm.org/D16191
Added:
compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc
compiler-rt/trunk/test/tsan/debugging.cc
Modified:
compiler-rt/trunk/lib/tsan/CMakeLists.txt
compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h
compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h
compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc
Modified: compiler-rt/trunk/lib/tsan/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/CMakeLists.txt?rev=263126&r1=263125&r2=263126&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/tsan/CMakeLists.txt Thu Mar 10 11:00:29 2016
@@ -24,6 +24,7 @@ append_list_if(COMPILER_RT_HAS_WGLOBAL_C
set(TSAN_SOURCES
rtl/tsan_clock.cc
+ rtl/tsan_debugging.cc
rtl/tsan_flags.cc
rtl/tsan_fd.cc
rtl/tsan_ignoreset.cc
Added: compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc?rev=263126&view=auto
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc (added)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc Thu Mar 10 11:00:29 2016
@@ -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;
+}
Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h?rev=263126&r1=263125&r2=263126&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h Thu Mar 10 11:00:29 2016
@@ -75,6 +75,61 @@ void __tsan_read_range(void *addr, unsig
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
Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h?rev=263126&r1=263125&r2=263126&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h Thu Mar 10 11:00:29 2016
@@ -404,6 +404,8 @@ struct ThreadState {
// 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,
Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc?rev=263126&r1=263125&r2=263126&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc Thu Mar 10 11:00:29 2016
@@ -56,6 +56,11 @@ bool OnReport(const ReportDesc *rep, boo
}
#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 @@ bool OutputReport(ThreadState *thr, cons
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 @@ bool OutputReport(ThreadState *thr, cons
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;
}
Added: compiler-rt/trunk/test/tsan/debugging.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/tsan/debugging.cc?rev=263126&view=auto
==============================================================================
--- compiler-rt/trunk/test/tsan/debugging.cc (added)
+++ compiler-rt/trunk/test/tsan/debugging.cc Thu Mar 10 11:00:29 2016
@@ -0,0 +1,88 @@
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: %deflake %run %t 2>&1 | FileCheck %s
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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
More information about the llvm-commits
mailing list