[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