summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKuba Mracek <mracek@apple.com>2017-02-02 13:17:05 +0000
committerKuba Mracek <mracek@apple.com>2017-02-02 13:17:05 +0000
commitaa78ad5fea0d38f51756031ef991b78d2ae0bfaa (patch)
tree917aa4990f215729cc26bc2d6d3f2fb9114c0a29
parent5c882715280599d5da41644610c67eddc7023275 (diff)
downloadbcm5719-llvm-aa78ad5fea0d38f51756031ef991b78d2ae0bfaa.tar.gz
bcm5719-llvm-aa78ad5fea0d38f51756031ef991b78d2ae0bfaa.zip
[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 llvm-svn: 293885
-rw-r--r--compiler-rt/lib/tsan/CMakeLists.txt1
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_debugging.cc1
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_defs.h3
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_external.cc78
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_interface.h9
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_report.cc34
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_report.h3
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_rtl.h5
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc11
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_suppressions.cc2
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_sync.cc1
-rw-r--r--compiler-rt/test/tsan/Darwin/external.cc153
12 files changed, 289 insertions, 12 deletions
diff --git a/compiler-rt/lib/tsan/CMakeLists.txt b/compiler-rt/lib/tsan/CMakeLists.txt
index d5195459656..195ecb5dfe8 100644
--- a/compiler-rt/lib/tsan/CMakeLists.txt
+++ b/compiler-rt/lib/tsan/CMakeLists.txt
@@ -25,6 +25,7 @@ append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors
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
diff --git a/compiler-rt/lib/tsan/rtl/tsan_debugging.cc b/compiler-rt/lib/tsan/rtl/tsan_debugging.cc
index d9fb6861bc0..36087948120 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_debugging.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_debugging.cc
@@ -24,6 +24,7 @@ static const char *ReportTypeDescription(ReportType typ) {
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";
diff --git a/compiler-rt/lib/tsan/rtl/tsan_defs.h b/compiler-rt/lib/tsan/rtl/tsan_defs.h
index 55580a5c443..8a0381e61ab 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_defs.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_defs.h
@@ -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;
};
diff --git a/compiler-rt/lib/tsan/rtl/tsan_external.cc b/compiler-rt/lib/tsan/rtl/tsan_external.cc
new file mode 100644
index 00000000000..dc8ec62322c
--- /dev/null
+++ b/compiler-rt/lib/tsan/rtl/tsan_external.cc
@@ -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
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface.h b/compiler-rt/lib/tsan/rtl/tsan_interface.h
index 4e342a58a06..e02888c6314 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface.h
@@ -79,6 +79,15 @@ SANITIZER_INTERFACE_ATTRIBUTE void __tsan_ignore_thread_begin();
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
diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cc b/compiler-rt/lib/tsan/rtl/tsan_report.cc
index 29f5b749c41..7de00840cdb 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_report.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_report.cc
@@ -90,6 +90,8 @@ static const char *ReportTypeString(ReportType typ) {
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, bool write, bool atomic) {
: (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 ReportLocation *loc) {
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));
diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.h b/compiler-rt/lib/tsan/rtl/tsan_report.h
index 32b79d76a3f..8d8ae0fd8f5 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_report.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_report.h
@@ -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;
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
index 531fb822519..88539414c95 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
@@ -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, ReportType type, StackTrace trace);
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
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc
index cfb492770ff..31b9e97898b 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc
@@ -164,8 +164,8 @@ void ScopedReport::AddStack(StackTrace stack, bool suppressable) {
(*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 addr, Shadow s, StackTrace stack,
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, uptr size) {
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++) {
diff --git a/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc b/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc
index bfb64e0018f..e39702b7d22 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc
@@ -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)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_sync.cc b/compiler-rt/lib/tsan/rtl/tsan_sync.cc
index 44c6a26a1e8..2be047462fc 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_sync.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_sync.cc
@@ -64,6 +64,7 @@ void MetaMap::AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz) {
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);
diff --git a/compiler-rt/test/tsan/Darwin/external.cc b/compiler-rt/test/tsan/Darwin/external.cc
new file mode 100644
index 00000000000..f1c6ebb802f
--- /dev/null
+++ b/compiler-rt/test/tsan/Darwin/external.cc
@@ -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)
OpenPOWER on IntegriCloud