[compiler-rt] r293885 - [tsan] Provide API for libraries for race detection on custom objects

Kuba Mracek via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 2 05:17:07 PST 2017


Author: kuba.brecka
Date: Thu Feb  2 07:17:05 2017
New Revision: 293885

URL: http://llvm.org/viewvc/llvm-project?rev=293885&view=rev
Log:
[tsan] Provide API for libraries for race detection on custom objects

This patch allows a non-instrumented library to call into TSan runtime, and tell us about "readonly" and "modifying" accesses to an arbitrary "object" and provide the caller and tag (type of object).  This allows TSan to detect violations of API threading contracts where "read-only" methods can be called simulatenously from multiple threads, while modifying methods must be exclusive.

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


Added:
    compiler-rt/trunk/lib/tsan/rtl/tsan_external.cc
    compiler-rt/trunk/test/tsan/Darwin/external.cc
Modified:
    compiler-rt/trunk/lib/tsan/CMakeLists.txt
    compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_defs.h
    compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h
    compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_report.h
    compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h
    compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_suppressions.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_sync.cc

Modified: compiler-rt/trunk/lib/tsan/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/CMakeLists.txt?rev=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/tsan/CMakeLists.txt Thu Feb  2 07:17:05 2017
@@ -25,6 +25,7 @@ append_list_if(COMPILER_RT_HAS_WGLOBAL_C
 set(TSAN_SOURCES
   rtl/tsan_clock.cc
   rtl/tsan_debugging.cc
+  rtl/tsan_external.cc
   rtl/tsan_fd.cc
   rtl/tsan_flags.cc
   rtl/tsan_ignoreset.cc

Modified: 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=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_debugging.cc Thu Feb  2 07:17:05 2017
@@ -24,6 +24,7 @@ static const char *ReportTypeDescription
   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 == ReportTypeExternalRace) return "external-race";
   if (typ == ReportTypeThreadLeak) return "thread-leak";
   if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy";
   if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock";

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_defs.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_defs.h?rev=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_defs.h (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_defs.h Thu Feb  2 07:17:05 2017
@@ -149,7 +149,8 @@ class RegionAlloc;
 
 // Descriptor of user's memory block.
 struct MBlock {
-  u64  siz;
+  u64  siz : 48;
+  u64  tag : 16;
   u32  stk;
   u16  tid;
 };

Added: compiler-rt/trunk/lib/tsan/rtl/tsan_external.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_external.cc?rev=293885&view=auto
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_external.cc (added)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_external.cc Thu Feb  2 07:17:05 2017
@@ -0,0 +1,78 @@
+//===-- tsan_external.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.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+#define CALLERPC ((uptr)__builtin_return_address(0))
+
+const uptr kMaxTag = 128;  // Limited to 65,536, since MBlock only stores tags
+                           // as 16-bit values, see tsan_defs.h.
+
+const char *registered_tags[kMaxTag];
+static atomic_uint32_t used_tags{1};  // Tag 0 means "no tag". NOLINT
+
+const char *GetObjectTypeFromTag(uptr tag) {
+  if (tag == 0) return nullptr;
+  // Invalid/corrupted tag?  Better return NULL and let the caller deal with it.
+  if (tag >= atomic_load(&used_tags, memory_order_relaxed)) return nullptr;
+  return registered_tags[tag];
+}
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__tsan_external_register_tag(const char *object_type) {
+  uptr new_tag = atomic_fetch_add(&used_tags, 1, memory_order_relaxed);
+  CHECK_LT(new_tag, kMaxTag);
+  registered_tags[new_tag] = internal_strdup(object_type);
+  return (void *)new_tag;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_assign_tag(void *addr, void *tag) {
+  CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
+  Allocator *a = allocator();
+  MBlock *b = nullptr;
+  if (a->PointerIsMine((void *)addr)) {
+    void *block_begin = a->GetBlockBegin((void *)addr);
+    if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
+  }
+  if (b) {
+    b->tag = (uptr)tag;
+  }
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_read(void *addr, void *caller_pc, void *tag) {
+  CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
+  ThreadState *thr = cur_thread();
+  thr->external_tag = (uptr)tag;
+  FuncEntry(thr, (uptr)caller_pc);
+  MemoryRead(thr, CALLERPC, (uptr)addr, kSizeLog8);
+  FuncExit(thr);
+  thr->external_tag = 0;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_write(void *addr, void *caller_pc, void *tag) {
+  CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
+  ThreadState *thr = cur_thread();
+  thr->external_tag = (uptr)tag;
+  FuncEntry(thr, (uptr)caller_pc);
+  MemoryWrite(thr, CALLERPC, (uptr)addr, kSizeLog8);
+  FuncExit(thr);
+  thr->external_tag = 0;
+}
+}  // extern "C"
+
+}  // namespace __tsan

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=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_interface.h Thu Feb  2 07:17:05 2017
@@ -79,6 +79,15 @@ SANITIZER_INTERFACE_ATTRIBUTE void __tsa
 SANITIZER_INTERFACE_ATTRIBUTE void __tsan_ignore_thread_end();
 
 SANITIZER_INTERFACE_ATTRIBUTE
+void *__tsan_external_register_tag(const char *object_type);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_assign_tag(void *addr, void *tag);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_read(void *addr, void *caller_pc, void *tag);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_write(void *addr, void *caller_pc, void *tag);
+
+SANITIZER_INTERFACE_ATTRIBUTE
 void __tsan_read_range(void *addr, unsigned long size);  // NOLINT
 SANITIZER_INTERFACE_ATTRIBUTE
 void __tsan_write_range(void *addr, unsigned long size);  // NOLINT

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc?rev=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc Thu Feb  2 07:17:05 2017
@@ -90,6 +90,8 @@ static const char *ReportTypeString(Repo
     return "heap-use-after-free";
   if (typ == ReportTypeVptrUseAfterFree)
     return "heap-use-after-free (virtual call vs free)";
+  if (typ == ReportTypeExternalRace)
+    return "race on a library object";
   if (typ == ReportTypeThreadLeak)
     return "thread leak";
   if (typ == ReportTypeMutexDestroyLocked)
@@ -152,14 +154,25 @@ static const char *MopDesc(bool first, b
                 : (write ? "Previous write" : "Previous read"));
 }
 
+static const char *ExternalMopDesc(bool first, bool write) {
+  return first ? (write ? "Mutating" : "Read-only")
+               : (write ? "Previous mutating" : "Previous read-only");
+}
+
 static void PrintMop(const ReportMop *mop, bool first) {
   Decorator d;
   char thrbuf[kThreadBufSize];
   Printf("%s", d.Access());
-  Printf("  %s of size %d at %p by %s",
-      MopDesc(first, mop->write, mop->atomic),
-      mop->size, (void*)mop->addr,
-      thread_name(thrbuf, mop->tid));
+  const char *object_type = GetObjectTypeFromTag(mop->external_tag);
+  if (!object_type) {
+    Printf("  %s of size %d at %p by %s",
+           MopDesc(first, mop->write, mop->atomic), mop->size,
+           (void *)mop->addr, thread_name(thrbuf, mop->tid));
+  } else {
+    Printf("  %s access of object %s at %p by %s",
+           ExternalMopDesc(first, mop->write), object_type,
+           (void *)mop->addr, thread_name(thrbuf, mop->tid));
+  }
   PrintMutexSet(mop->mset);
   Printf(":\n");
   Printf("%s", d.EndAccess());
@@ -183,9 +196,16 @@ static void PrintLocation(const ReportLo
              global.module_offset);
   } else if (loc->type == ReportLocationHeap) {
     char thrbuf[kThreadBufSize];
-    Printf("  Location is heap block of size %zu at %p allocated by %s:\n",
-           loc->heap_chunk_size, loc->heap_chunk_start,
-           thread_name(thrbuf, loc->tid));
+    const char *object_type = GetObjectTypeFromTag(loc->external_tag);
+    if (!object_type) {
+      Printf("  Location is heap block of size %zu at %p allocated by %s:\n",
+             loc->heap_chunk_size, loc->heap_chunk_start,
+             thread_name(thrbuf, loc->tid));
+    } else {
+      Printf("  Location is %s object of size %zu at %p allocated by %s:\n",
+             object_type, loc->heap_chunk_size, loc->heap_chunk_start,
+             thread_name(thrbuf, loc->tid));
+    }
     print_stack = true;
   } else if (loc->type == ReportLocationStack) {
     Printf("  Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_report.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_report.h?rev=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_report.h (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_report.h Thu Feb  2 07:17:05 2017
@@ -24,6 +24,7 @@ enum ReportType {
   ReportTypeVptrRace,
   ReportTypeUseAfterFree,
   ReportTypeVptrUseAfterFree,
+  ReportTypeExternalRace,
   ReportTypeThreadLeak,
   ReportTypeMutexDestroyLocked,
   ReportTypeMutexDoubleLock,
@@ -56,6 +57,7 @@ struct ReportMop {
   int size;
   bool write;
   bool atomic;
+  uptr external_tag;
   Vector<ReportMopMutex> mset;
   ReportStack *stack;
 
@@ -75,6 +77,7 @@ struct ReportLocation {
   DataInfo global;
   uptr heap_chunk_start;
   uptr heap_chunk_size;
+  uptr external_tag;
   int tid;
   int fd;
   bool suppressable;

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=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h Thu Feb  2 07:17:05 2017
@@ -410,6 +410,7 @@ struct ThreadState {
   bool is_dead;
   bool is_freeing;
   bool is_vptr_access;
+  uptr external_tag;
   const uptr stk_addr;
   const uptr stk_size;
   const uptr tls_addr;
@@ -564,7 +565,7 @@ class ScopedReport {
   explicit ScopedReport(ReportType typ);
   ~ScopedReport();
 
-  void AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
+  void AddMemoryAccess(uptr addr, uptr external_tag, Shadow s, StackTrace stack,
                        const MutexSet *mset);
   void AddStack(StackTrace stack, bool suppressable = false);
   void AddThread(const ThreadContext *tctx, bool suppressable = false);
@@ -640,6 +641,8 @@ bool IsFiredSuppression(Context *ctx, Re
 bool IsExpectedReport(uptr addr, uptr size);
 void PrintMatchedBenignRaces();
 
+const char *GetObjectTypeFromTag(uptr tag);
+
 #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1
 # define DPrintf Printf
 #else

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=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc Thu Feb  2 07:17:05 2017
@@ -164,8 +164,8 @@ void ScopedReport::AddStack(StackTrace s
   (*rs)->suppressable = suppressable;
 }
 
-void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
-                                   const MutexSet *mset) {
+void ScopedReport::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
+                                   StackTrace stack, const MutexSet *mset) {
   void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
   ReportMop *mop = new(mem) ReportMop;
   rep_->mops.PushBack(mop);
@@ -175,6 +175,7 @@ void ScopedReport::AddMemoryAccess(uptr
   mop->write = s.IsWrite();
   mop->atomic = s.IsAtomic();
   mop->stack = SymbolizeStack(stack);
+  mop->external_tag = external_tag;
   if (mop->stack)
     mop->stack->suppressable = true;
   for (uptr i = 0; i < mset->Size(); i++) {
@@ -337,6 +338,7 @@ void ScopedReport::AddLocation(uptr addr
     ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
     loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr);
     loc->heap_chunk_size = b->siz;
+    loc->external_tag = b->tag;
     loc->tid = tctx ? tctx->tid : b->tid;
     loc->stack = SymbolizeStackId(b->stk);
     rep_->locs.PushBack(loc);
@@ -623,6 +625,8 @@ void ReportRace(ThreadState *thr) {
     typ = ReportTypeVptrRace;
   else if (freed)
     typ = ReportTypeUseAfterFree;
+  else if (thr->external_tag > 0)
+    typ = ReportTypeExternalRace;
 
   if (IsFiredSuppression(ctx, typ, addr))
     return;
@@ -651,7 +655,8 @@ void ReportRace(ThreadState *thr) {
   ScopedReport rep(typ);
   for (uptr i = 0; i < kMop; i++) {
     Shadow s(thr->racy_state[i]);
-    rep.AddMemoryAccess(addr, s, traces[i], i == 0 ? &thr->mset : mset2);
+    rep.AddMemoryAccess(addr, thr->external_tag, s, traces[i],
+                        i == 0 ? &thr->mset : mset2);
   }
 
   for (uptr i = 0; i < kMop; i++) {

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_suppressions.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_suppressions.cc?rev=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_suppressions.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_suppressions.cc Thu Feb  2 07:17:05 2017
@@ -74,6 +74,8 @@ static const char *conv(ReportType typ)
     return kSuppressionRace;
   else if (typ == ReportTypeVptrUseAfterFree)
     return kSuppressionRace;
+  else if (typ == ReportTypeExternalRace)
+    return kSuppressionRace;
   else if (typ == ReportTypeThreadLeak)
     return kSuppressionThread;
   else if (typ == ReportTypeMutexDestroyLocked)

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_sync.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_sync.cc?rev=293885&r1=293884&r2=293885&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_sync.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_sync.cc Thu Feb  2 07:17:05 2017
@@ -64,6 +64,7 @@ void MetaMap::AllocBlock(ThreadState *th
   u32 idx = block_alloc_.Alloc(&thr->proc()->block_cache);
   MBlock *b = block_alloc_.Map(idx);
   b->siz = sz;
+  b->tag = 0;
   b->tid = thr->tid;
   b->stk = CurrentStackId(thr, pc);
   u32 *meta = MemToMeta(p);

Added: compiler-rt/trunk/test/tsan/Darwin/external.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/tsan/Darwin/external.cc?rev=293885&view=auto
==============================================================================
--- compiler-rt/trunk/test/tsan/Darwin/external.cc (added)
+++ compiler-rt/trunk/test/tsan/Darwin/external.cc Thu Feb  2 07:17:05 2017
@@ -0,0 +1,153 @@
+// RUN: %clangxx_tsan %s -o %t-lib-instrumented.dylib              -shared -DSHARED_LIB
+// RUN: %clangxx_tsan %s -o %t-lib-noninstrumented.dylib           -shared -DSHARED_LIB -fno-sanitize=thread
+// RUN: %clangxx_tsan %s -o %t-lib-noninstrumented-callbacks.dylib -shared -DSHARED_LIB -fno-sanitize=thread -DUSE_TSAN_CALLBACKS
+// RUN: %clangxx_tsan %s %t-lib-instrumented.dylib -o %t-lib-instrumented
+// RUN: %clangxx_tsan %s %t-lib-noninstrumented.dylib -o %t-lib-noninstrumented
+// RUN: %clangxx_tsan %s %t-lib-noninstrumented-callbacks.dylib -o %t-lib-noninstrumented-callbacks
+
+// RUN: %deflake %run %t-lib-instrumented              2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK --check-prefix=TEST1
+// RUN:          %run %t-lib-noninstrumented           2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK --check-prefix=TEST2
+// RUN: %deflake %run %t-lib-noninstrumented-callbacks 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK --check-prefix=TEST3
+
+#include <thread>
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+
+struct MyObject;
+typedef MyObject *MyObjectRef;
+extern "C" {
+  void InitializeLibrary();
+  MyObject *ObjectCreate();
+  long ObjectRead(MyObject *);
+  void ObjectWrite(MyObject *, long);
+  void ObjectWriteAnother(MyObject *, long);
+}
+
+#if defined(SHARED_LIB)
+
+struct MyObject {
+  long _val;
+  long _another;
+};
+
+#if defined(USE_TSAN_CALLBACKS)
+static void *tag;
+void *(*callback_register_tag)(const char *object_type);
+void *(*callback_assign_tag)(void *addr, void *tag);
+void (*callback_read)(void *addr, void *caller_pc, void *tag);
+void (*callback_write)(void *addr, void *caller_pc, void *tag);
+#endif
+
+void InitializeLibrary() {
+#if defined(USE_TSAN_CALLBACKS)
+  callback_register_tag = (decltype(callback_register_tag))dlsym(RTLD_DEFAULT, "__tsan_external_register_tag");
+  callback_assign_tag = (decltype(callback_assign_tag))dlsym(RTLD_DEFAULT, "__tsan_external_assign_tag");
+  callback_read = (decltype(callback_read))dlsym(RTLD_DEFAULT, "__tsan_external_read");
+  callback_write = (decltype(callback_write))dlsym(RTLD_DEFAULT, "__tsan_external_write");
+  tag = callback_register_tag("MyLibrary::MyObject");
+#endif
+}
+
+MyObject *ObjectCreate() {
+  MyObject *ref = (MyObject *)malloc(sizeof(MyObject));
+#if defined(USE_TSAN_CALLBACKS)
+  callback_assign_tag(ref, tag);
+#endif
+  return ref;
+}
+
+long ObjectRead(MyObject *ref) {
+#if defined(USE_TSAN_CALLBACKS)
+  callback_read(ref, __builtin_return_address(0), tag);
+#endif
+  return ref->_val;
+}
+
+void ObjectWrite(MyObject *ref, long val) {
+#if defined(USE_TSAN_CALLBACKS)
+  callback_write(ref, __builtin_return_address(0), tag);
+#endif
+  ref->_val = val;
+}
+
+void ObjectWriteAnother(MyObject *ref, long val) {
+#if defined(USE_TSAN_CALLBACKS)
+  callback_write(ref, __builtin_return_address(0), tag);
+#endif
+  ref->_another = val;
+}
+
+#else  // defined(SHARED_LIB)
+
+int main(int argc, char *argv[]) {
+  InitializeLibrary();
+  
+  {
+    MyObjectRef ref = ObjectCreate();
+    std::thread t1([ref]{ ObjectRead(ref); });
+    std::thread t2([ref]{ ObjectRead(ref); });
+    t1.join();
+    t2.join();
+  }
+  
+  // CHECK-NOT: WARNING: ThreadSanitizer
+  
+  fprintf(stderr, "RR test done\n");
+  // CHECK: RR test done
+
+  {
+    MyObjectRef ref = ObjectCreate();
+    std::thread t1([ref]{ ObjectRead(ref); });
+    std::thread t2([ref]{ ObjectWrite(ref, 66); });
+    t1.join();
+    t2.join();
+  }
+  
+  // TEST1: WARNING: ThreadSanitizer: data race
+  // TEST1: {{Write|Read}} of size 8 at
+  // TEST1: Previous {{write|read}} of size 8 at
+  // TEST1: Location is heap block of size 16 at
+  
+  // TEST2-NOT: WARNING: ThreadSanitizer
+  
+  // TEST3: WARNING: ThreadSanitizer: race on a library object
+  // TEST3: {{Mutating|read-only}} access of object MyLibrary::MyObject at
+  // TEST3: {{ObjectWrite|ObjectRead}}
+  // TEST3: Previous {{mutating|read-only}} access of object MyLibrary::MyObject at
+  // TEST3: {{ObjectWrite|ObjectRead}}
+  // TEST3: Location is MyLibrary::MyObject object of size 16 at
+  // TEST3: {{ObjectCreate}}
+
+  fprintf(stderr, "RW test done\n");
+  // CHECK: RW test done
+
+  {
+    MyObjectRef ref = ObjectCreate();
+    std::thread t1([ref]{ ObjectWrite(ref, 76); });
+    std::thread t2([ref]{ ObjectWriteAnother(ref, 77); });
+    t1.join();
+    t2.join();
+  }
+  
+  // TEST1-NOT: WARNING: ThreadSanitizer: data race
+  
+  // TEST2-NOT: WARNING: ThreadSanitizer
+  
+  // TEST3: WARNING: ThreadSanitizer: race on a library object
+  // TEST3: Mutating access of object MyLibrary::MyObject at
+  // TEST3: {{ObjectWrite|ObjectWriteAnother}}
+  // TEST3: Previous mutating access of object MyLibrary::MyObject at
+  // TEST3: {{ObjectWrite|ObjectWriteAnother}}
+  // TEST3: Location is MyLibrary::MyObject object of size 16 at
+  // TEST3: {{ObjectCreate}}
+
+  fprintf(stderr, "WW test done\n");
+  // CHECK: WW test done
+}
+
+#endif  // defined(SHARED_LIB)




More information about the llvm-commits mailing list