[compiler-rt] r199993 - tsan: do not deadlock on fork

Dmitry Vyukov dvyukov at google.com
Fri Jan 24 04:33:36 PST 2014


Author: dvyukov
Date: Fri Jan 24 06:33:35 2014
New Revision: 199993

URL: http://llvm.org/viewvc/llvm-project?rev=199993&view=rev
Log:
tsan: do not deadlock on fork

Currently correct programs can deadlock after fork, because atomic operations and async-signal-safe calls are not async-signal-safe under tsan.
With this change:
- if a single-threaded program forks, the child continues running with verification enabled (the tsan background thread is recreated as well)
- if a multi-threaded program forks, then the child runs with verification disabled (memory accesses, atomic operations and interceptors are disabled); it's expected that it will exec soon anyway
- if the child tries to create more threads after multi-threaded fork, the program aborts with error message
- die_after_fork flag is added that allows to continue running, but all bets are off

http://llvm-reviews.chandlerc.com/D2614



Added:
    compiler-rt/trunk/lib/tsan/lit_tests/fork_deadlock.cc
    compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded.cc
    compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded3.cc
Modified:
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_flags.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_flags.h
    compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_interface_atomic.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h
    compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_thread.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc
    compiler-rt/trunk/lib/tsan/tests/unit/tsan_flags_test.cc

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc?rev=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc Fri Jan 24 06:33:35 2014
@@ -539,6 +539,7 @@ class POSIXSymbolizer : public Symbolize
     }
     // Otherwise, fall back to external symbolizer.
     if (external_symbolizer_) {
+      SymbolizerScope sym_scope(this);
       return external_symbolizer_->SendCommand(is_data, module_name,
                                                module_offset);
     }

Added: compiler-rt/trunk/lib/tsan/lit_tests/fork_deadlock.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/lit_tests/fork_deadlock.cc?rev=199993&view=auto
==============================================================================
--- compiler-rt/trunk/lib/tsan/lit_tests/fork_deadlock.cc (added)
+++ compiler-rt/trunk/lib/tsan/lit_tests/fork_deadlock.cc Fri Jan 24 06:33:35 2014
@@ -0,0 +1,48 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="atexit_sleep_ms=50" %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+int counter;
+
+static void *incrementer(void *p) {
+  for (;;)
+    __sync_fetch_and_add(&counter, 1);
+  return 0;
+}
+
+static void *watchdog(void *p) {
+  sleep(100);
+  fprintf(stderr, "timed out after 100 seconds\n");
+  exit(1);
+  return 0;
+}
+
+int main() {
+  pthread_t th1, th2;
+  pthread_create(&th1, 0, incrementer, 0);
+  pthread_create(&th2, 0, watchdog, 0);
+  for (int i = 0; i < 10; i++) {
+    switch (fork()) {
+    default:  // parent
+      while (wait(0) < 0) {}
+      fprintf(stderr, ".");
+      break;
+    case 0:  // child
+      __sync_fetch_and_add(&counter, 1);
+      exit(0);
+      break;
+    case -1:  // error
+      fprintf(stderr, "failed to fork (%d)\n", errno);
+      exit(1);
+    }
+  }
+  fprintf(stderr, "OK\n");
+}
+
+// CHECK: OK
+

Added: compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded.cc?rev=199993&view=auto
==============================================================================
--- compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded.cc (added)
+++ compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded.cc Fri Jan 24 06:33:35 2014
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s -check-prefix=CHECK-DIE
+// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="die_after_fork=0" %t 2>&1 | FileCheck %s -check-prefix=CHECK-NODIE
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static void *sleeper(void *p) {
+  sleep(10);
+  return 0;
+}
+
+int main() {
+  pthread_t th;
+  pthread_create(&th, 0, sleeper, 0);
+  switch (fork()) {
+  default:  // parent
+    while (wait(0) < 0) {}
+    break;
+  case 0:  // child
+    {
+      pthread_t th2;
+      pthread_create(&th2, 0, sleeper, 0);
+      exit(0);
+      break;
+    }
+  case -1:  // error
+    fprintf(stderr, "failed to fork (%d)\n", errno);
+    exit(1);
+  }
+  fprintf(stderr, "OK\n");
+}
+
+// CHECK-DIE: ThreadSanitizer: starting new threads after muti-threaded fork is not supported
+// CHECK-DIE: OK
+
+// CHECK-NODIE-NOT: ThreadSanitizer: starting new threads after muti-threaded fork is not supported
+// CHECK-NODIE: OK
+

Added: compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded3.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded3.cc?rev=199993&view=auto
==============================================================================
--- compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded3.cc (added)
+++ compiler-rt/trunk/lib/tsan/lit_tests/fork_multithreaded3.cc Fri Jan 24 06:33:35 2014
@@ -0,0 +1,40 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static void *racer(void *p) {
+  *(int*)p = 42;
+  return 0;
+}
+
+int main() {
+  switch (fork()) {
+  default:  // parent
+    while (wait(0) < 0) {}
+    break;
+  case 0:  // child
+    {
+      int x = 0;
+      pthread_t th1, th2;
+      pthread_create(&th1, 0, racer, &x);
+      pthread_create(&th2, 0, racer, &x);
+      pthread_join(th1, 0);
+      pthread_join(th2, 0);
+      exit(0);
+      break;
+    }
+  case -1:  // error
+    fprintf(stderr, "failed to fork (%d)\n", errno);
+    exit(1);
+  }
+  fprintf(stderr, "OK\n");
+}
+
+// CHECK: ThreadSanitizer: data race
+// CHECK: OK
+

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_flags.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_flags.cc?rev=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_flags.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_flags.cc Fri Jan 24 06:33:35 2014
@@ -57,6 +57,7 @@ static void ParseFlags(Flags *f, const c
   ParseFlag(env, &f->running_on_valgrind, "running_on_valgrind");
   ParseFlag(env, &f->history_size, "history_size");
   ParseFlag(env, &f->io_sync, "io_sync");
+  ParseFlag(env, &f->die_after_fork, "die_after_fork");
 }
 
 void InitializeFlags(Flags *f, const char *env) {
@@ -87,6 +88,7 @@ void InitializeFlags(Flags *f, const cha
   f->running_on_valgrind = false;
   f->history_size = kGoMode ? 1 : 2;  // There are a lot of goroutines in Go.
   f->io_sync = 1;
+  f->die_after_fork = true;
 
   SetCommonFlagsDefaults(f);
 

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_flags.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_flags.h?rev=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_flags.h (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_flags.h Fri Jan 24 06:33:35 2014
@@ -81,6 +81,8 @@ struct Flags : CommonFlags {
   // 1 - reasonable level of synchronization (write->read)
   // 2 - global synchronization of all IO operations
   int io_sync;
+  // Die after multi-threaded fork if the child creates new threads.
+  bool die_after_fork;
 };
 
 Flags *flags();

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc?rev=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc Fri Jan 24 06:33:35 2014
@@ -863,6 +863,17 @@ extern "C" void *__tsan_thread_start_fun
 TSAN_INTERCEPTOR(int, pthread_create,
     void *th, void *attr, void *(*callback)(void*), void * param) {
   SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param);
+  if (CTX()->after_multithreaded_fork) {
+    if (flags()->die_after_fork) {
+      Printf("ThreadSanitizer: starting new threads after muti-threaded"
+          " fork is not supported. Dying (set die_after_fork=0 to override)\n");
+      Die();
+    } else {
+      VPrintf(1, "ThreadSanitizer: starting new threads after muti-threaded"
+          " fork is not supported. Continuing because die_after_fork=0,"
+          " but you are on your own\n");
+    }
+  }
   __sanitizer_pthread_attr_t myattr;
   if (attr == 0) {
     pthread_attr_init(&myattr);
@@ -1793,13 +1804,21 @@ TSAN_INTERCEPTOR(int, munlockall, void)
 }
 
 TSAN_INTERCEPTOR(int, fork, int fake) {
+  if (cur_thread()->in_symbolizer)
+    return REAL(fork)(fake);
   SCOPED_INTERCEPTOR_RAW(fork, fake);
+  ForkBefore(thr, pc);
   int pid = REAL(fork)(fake);
   if (pid == 0) {
     // child
+    ForkChildAfter(thr, pc);
     FdOnFork(thr, pc);
   } else if (pid > 0) {
     // parent
+    ForkParentAfter(thr, pc);
+  } else {
+    // error
+    ForkParentAfter(thr, pc);
   }
   return pid;
 }
@@ -1892,6 +1911,8 @@ static void HandleRecvmsg(ThreadState *t
 
 #define TSAN_SYSCALL() \
   ThreadState *thr = cur_thread(); \
+  if (thr->ignore_interceptors) \
+    return; \
   ScopedSyscall scoped_syscall(thr) \
 /**/
 
@@ -1944,15 +1965,21 @@ static USED void syscall_fd_release(uptr
 
 static void syscall_pre_fork(uptr pc) {
   TSAN_SYSCALL();
+  ForkBefore(thr, pc);
 }
 
-static void syscall_post_fork(uptr pc, int res) {
+static void syscall_post_fork(uptr pc, int pid) {
   TSAN_SYSCALL();
-  if (res == 0) {
+  if (pid == 0) {
     // child
+    ForkChildAfter(thr, pc);
     FdOnFork(thr, pc);
-  } else if (res > 0) {
+  } else if (pid > 0) {
     // parent
+    ForkParentAfter(thr, pc);
+  } else {
+    // error
+    ForkParentAfter(thr, pc);
   }
 }
 

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_interface_atomic.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_interface_atomic.cc?rev=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_interface_atomic.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_interface_atomic.cc Fri Jan 24 06:33:35 2014
@@ -21,6 +21,7 @@
 
 #include "sanitizer_common/sanitizer_placement_new.h"
 #include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_mutex.h"
 #include "tsan_flags.h"
 #include "tsan_rtl.h"
 
@@ -29,19 +30,20 @@ using namespace __tsan;  // NOLINT
 #define SCOPED_ATOMIC(func, ...) \
     const uptr callpc = (uptr)__builtin_return_address(0); \
     uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
-    mo = ConvertOrder(mo); \
     mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \
     ThreadState *const thr = cur_thread(); \
+    if (thr->ignore_interceptors) \
+      return NoTsanAtomic##func(__VA_ARGS__); \
     AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
     ScopedAtomic sa(thr, callpc, a, mo, __FUNCTION__); \
     return Atomic##func(thr, pc, __VA_ARGS__); \
 /**/
 
 // These should match declarations from public tsan_interface_atomic.h header.
-typedef char     a8;
-typedef short    a16;  // NOLINT
-typedef int      a32;
-typedef long     a64;  // NOLINT
+typedef unsigned char      a8;
+typedef unsigned short     a16;  // NOLINT
+typedef unsigned int       a32;
+typedef unsigned long long a64;  // NOLINT
 #if defined(__SIZEOF_INT128__) \
     || (__clang_major__ * 100 + __clang_minor__ >= 302)
 __extension__ typedef __int128 a128;
@@ -50,6 +52,9 @@ __extension__ typedef __int128 a128;
 # define __TSAN_HAS_INT128 0
 #endif
 
+// Protects emulation of 128-bit atomic operations.
+static StaticSpinMutex mutex128;
+
 // Part of ABI, do not change.
 // http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup
 typedef enum {
@@ -115,27 +120,6 @@ static bool IsAcqRelOrder(morder mo) {
   return mo == mo_acq_rel || mo == mo_seq_cst;
 }
 
-static morder ConvertOrder(morder mo) {
-  if (mo > (morder)100500) {
-    mo = morder(mo - 100500);
-    if (mo ==  morder(1 << 0))
-      mo = mo_relaxed;
-    else if (mo == morder(1 << 1))
-      mo = mo_consume;
-    else if (mo == morder(1 << 2))
-      mo = mo_acquire;
-    else if (mo == morder(1 << 3))
-      mo = mo_release;
-    else if (mo == morder(1 << 4))
-      mo = mo_acq_rel;
-    else if (mo == morder(1 << 5))
-      mo = mo_seq_cst;
-  }
-  CHECK_GE(mo, mo_relaxed);
-  CHECK_LE(mo, mo_seq_cst);
-  return mo;
-}
-
 template<typename T> T func_xchg(volatile T *v, T op) {
   T res = __sync_lock_test_and_set(v, op);
   // __sync_lock_test_and_set does not contain full barrier.
@@ -185,48 +169,56 @@ template<typename T> T func_cas(volatile
 // from non-instrumented code.
 #ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
 a128 func_xchg(volatile a128 *v, a128 op) {
+  SpinMutexLock lock(&mutex128);
   a128 cmp = *v;
   *v = op;
   return cmp;
 }
 
 a128 func_add(volatile a128 *v, a128 op) {
+  SpinMutexLock lock(&mutex128);
   a128 cmp = *v;
   *v = cmp + op;
   return cmp;
 }
 
 a128 func_sub(volatile a128 *v, a128 op) {
+  SpinMutexLock lock(&mutex128);
   a128 cmp = *v;
   *v = cmp - op;
   return cmp;
 }
 
 a128 func_and(volatile a128 *v, a128 op) {
+  SpinMutexLock lock(&mutex128);
   a128 cmp = *v;
   *v = cmp & op;
   return cmp;
 }
 
 a128 func_or(volatile a128 *v, a128 op) {
+  SpinMutexLock lock(&mutex128);
   a128 cmp = *v;
   *v = cmp | op;
   return cmp;
 }
 
 a128 func_xor(volatile a128 *v, a128 op) {
+  SpinMutexLock lock(&mutex128);
   a128 cmp = *v;
   *v = cmp ^ op;
   return cmp;
 }
 
 a128 func_nand(volatile a128 *v, a128 op) {
+  SpinMutexLock lock(&mutex128);
   a128 cmp = *v;
   *v = ~(cmp & op);
   return cmp;
 }
 
 a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) {
+  SpinMutexLock lock(&mutex128);
   a128 cur = *v;
   if (cur == cmp)
     *v = xch;
@@ -248,26 +240,74 @@ static int SizeLog() {
   // this leads to false negatives only in very obscure cases.
 }
 
+static atomic_uint8_t *to_atomic(const volatile a8 *a) {
+  return (atomic_uint8_t*)a;
+}
+
+static atomic_uint16_t *to_atomic(const volatile a16 *a) {
+  return (atomic_uint16_t*)a;
+}
+
+static atomic_uint32_t *to_atomic(const volatile a32 *a) {
+  return (atomic_uint32_t*)a;
+}
+
+static atomic_uint64_t *to_atomic(const volatile a64 *a) {
+  return (atomic_uint64_t*)a;
+}
+
+static memory_order to_mo(morder mo) {
+  switch (mo) {
+  case mo_relaxed: return memory_order_relaxed;
+  case mo_consume: return memory_order_consume;
+  case mo_acquire: return memory_order_acquire;
+  case mo_release: return memory_order_release;
+  case mo_acq_rel: return memory_order_acq_rel;
+  case mo_seq_cst: return memory_order_seq_cst;
+  }
+  CHECK(0);
+  return memory_order_seq_cst;
+}
+
+template<typename T>
+static T NoTsanAtomicLoad(const volatile T *a, morder mo) {
+  return atomic_load(to_atomic(a), to_mo(mo));
+}
+
+static a128 NoTsanAtomicLoad(const volatile a128 *a, morder mo) {
+  SpinMutexLock lock(&mutex128);
+  return *a;
+}
+
 template<typename T>
 static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a,
     morder mo) {
   CHECK(IsLoadOrder(mo));
   // This fast-path is critical for performance.
   // Assume the access is atomic.
-  if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) {
+  if (!IsAcquireOrder(mo)) {
     MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>());
-    return *a;  // as if atomic
+    return NoTsanAtomicLoad(a, mo);
   }
   SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false);
   AcquireImpl(thr, pc, &s->clock);
-  T v = *a;
+  T v = NoTsanAtomicLoad(a, mo);
   s->mtx.ReadUnlock();
-  __sync_synchronize();
   MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>());
   return v;
 }
 
 template<typename T>
+static void NoTsanAtomicStore(volatile T *a, T v, morder mo) {
+  atomic_store(to_atomic(a), v, to_mo(mo));
+}
+
+static void NoTsanAtomicStore(volatile a128 *a, a128 v, morder mo) {
+  SpinMutexLock lock(&mutex128);
+  *a = v;
+}
+
+template<typename T>
 static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
     morder mo) {
   CHECK(IsStoreOrder(mo));
@@ -276,8 +316,8 @@ static void AtomicStore(ThreadState *thr
   // Assume the access is atomic.
   // Strictly saying even relaxed store cuts off release sequence,
   // so must reset the clock.
-  if (!IsReleaseOrder(mo) && sizeof(T) <= sizeof(a)) {
-    *a = v;  // as if atomic
+  if (!IsReleaseOrder(mo)) {
+    NoTsanAtomicStore(a, v, mo);
     return;
   }
   __sync_synchronize();
@@ -286,11 +326,8 @@ static void AtomicStore(ThreadState *thr
   // Can't increment epoch w/o writing to the trace as well.
   TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
   ReleaseImpl(thr, pc, &s->clock);
-  *a = v;
+  NoTsanAtomicStore(a, v, mo);
   s->mtx.Unlock();
-  // Trainling memory barrier to provide sequential consistency
-  // for Dekker-like store-load synchronization.
-  __sync_synchronize();
 }
 
 template<typename T, T (*F)(volatile T *v, T op)>
@@ -316,6 +353,41 @@ static T AtomicRMW(ThreadState *thr, upt
 }
 
 template<typename T>
+static T NoTsanAtomicExchange(volatile T *a, T v, morder mo) {
+  return func_xchg(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchAdd(volatile T *a, T v, morder mo) {
+  return func_add(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchSub(volatile T *a, T v, morder mo) {
+  return func_sub(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchAnd(volatile T *a, T v, morder mo) {
+  return func_and(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchOr(volatile T *a, T v, morder mo) {
+  return func_or(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchXor(volatile T *a, T v, morder mo) {
+  return func_xor(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchNand(volatile T *a, T v, morder mo) {
+  return func_nand(a, v);
+}
+
+template<typename T>
 static T AtomicExchange(ThreadState *thr, uptr pc, volatile T *a, T v,
     morder mo) {
   return AtomicRMW<T, func_xchg>(thr, pc, a, v, mo);
@@ -358,6 +430,26 @@ static T AtomicFetchNand(ThreadState *th
 }
 
 template<typename T>
+static bool NoTsanAtomicCAS(volatile T *a, T *c, T v, morder mo, morder fmo) {
+  return atomic_compare_exchange_strong(to_atomic(a), c, v, to_mo(mo));
+}
+
+static bool NoTsanAtomicCAS(volatile a128 *a, a128 *c, a128 v,
+    morder mo, morder fmo) {
+  a128 old = *c;
+  a128 cur = func_cas(a, old, v);
+  if (cur == old)
+    return true;
+  *c = cur;
+  return false;
+}
+
+template<typename T>
+static bool NoTsanAtomicCAS(volatile T *a, T c, T v, morder mo, morder fmo) {
+  return NoTsanAtomicCAS(a, &c, v, mo, fmo);
+}
+
+template<typename T>
 static bool AtomicCAS(ThreadState *thr, uptr pc,
     volatile T *a, T *c, T v, morder mo, morder fmo) {
   (void)fmo;  // Unused because llvm does not pass it yet.
@@ -392,6 +484,10 @@ static T AtomicCAS(ThreadState *thr, upt
   return c;
 }
 
+static void NoTsanAtomicFence(morder mo) {
+  __sync_synchronize();
+}
+
 static void AtomicFence(ThreadState *thr, uptr pc, morder mo) {
   // FIXME(dvyukov): not implemented.
   __sync_synchronize();

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cc?rev=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cc Fri Jan 24 06:33:35 2014
@@ -314,6 +314,42 @@ int Finalize(ThreadState *thr) {
 }
 
 #ifndef TSAN_GO
+void ForkBefore(ThreadState *thr, uptr pc) {
+  Context *ctx = CTX();
+  ctx->report_mtx.Lock();
+  ctx->thread_registry->Lock();
+}
+
+void ForkParentAfter(ThreadState *thr, uptr pc) {
+  Context *ctx = CTX();
+  ctx->thread_registry->Unlock();
+  ctx->report_mtx.Unlock();
+}
+
+void ForkChildAfter(ThreadState *thr, uptr pc) {
+  Context *ctx = CTX();
+  ctx->thread_registry->Unlock();
+  ctx->report_mtx.Unlock();
+
+  uptr nthread = 0;
+  ctx->thread_registry->GetNumberOfThreads(0, 0, &nthread /* alive threads */);
+  VPrintf(1, "ThreadSanitizer: forked new process with pid %d,"
+      " parent had %d threads\n", (int)internal_getpid(), (int)nthread);
+  if (nthread == 1) {
+    internal_start_thread(&BackgroundThread, 0);
+  } else {
+    // We've just forked a multi-threaded process. We cannot reasonably function
+    // after that (some mutexes may be locked before fork). So just enable
+    // ignores for everything in the hope that we will exec soon.
+    ctx->after_multithreaded_fork = true;
+    thr->ignore_interceptors++;
+    ThreadIgnoreBegin(thr, pc);
+    ThreadIgnoreSyncBegin(thr, pc);
+  }
+}
+#endif
+
+#ifndef TSAN_GO
 u32 CurrentStackId(ThreadState *thr, uptr pc) {
   if (thr->shadow_stack_pos == 0)  // May happen during bootstrap.
     return 0;

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=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h Fri Jan 24 06:33:35 2014
@@ -529,6 +529,7 @@ struct Context {
   Context();
 
   bool initialized;
+  bool after_multithreaded_fork;
 
   SyncTab synctab;
 
@@ -615,6 +616,10 @@ void InitializeInterceptors();
 void InitializeLibIgnore();
 void InitializeDynamicAnnotations();
 
+void ForkBefore(ThreadState *thr, uptr pc);
+void ForkParentAfter(ThreadState *thr, uptr pc);
+void ForkChildAfter(ThreadState *thr, uptr pc);
+
 void ReportRace(ThreadState *thr);
 bool OutputReport(Context *ctx,
                   const ScopedReport &srep,

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_thread.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_thread.cc?rev=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_thread.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_thread.cc Fri Jan 24 06:33:35 2014
@@ -179,6 +179,8 @@ static void ReportIgnoresEnabled(ThreadC
 }
 
 static void ThreadCheckIgnore(ThreadState *thr) {
+  if (CTX()->after_multithreaded_fork)
+    return;
   if (thr->ignore_reads_and_writes)
     ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set);
   if (thr->ignore_sync)
@@ -257,6 +259,14 @@ void ThreadStart(ThreadState *thr, int t
   tr->Lock();
   thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid);
   tr->Unlock();
+
+#ifndef TSAN_GO
+  if (ctx->after_multithreaded_fork) {
+    thr->ignore_interceptors++;
+    ThreadIgnoreBegin(thr, 0);
+    ThreadIgnoreSyncBegin(thr, 0);
+  }
+#endif
 }
 
 void ThreadFinish(ThreadState *thr) {

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc?rev=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc Fri Jan 24 06:33:35 2014
@@ -27,6 +27,9 @@
 
 namespace __tsan {
 
+void EnterSymbolizer();
+void ExitSymbolizer();
+
 struct ModuleDesc {
   const char *fullname;
   const char *name;
@@ -48,6 +51,7 @@ struct DlIteratePhdrCtx {
 };
 
 static void NOINLINE InitModule(ModuleDesc *m) {
+  EnterSymbolizer();
   int outfd[2] = {};
   if (pipe(&outfd[0])) {
     Printf("ThreadSanitizer: outfd pipe() failed (%d)\n", errno);
@@ -80,6 +84,7 @@ static void NOINLINE InitModule(ModuleDe
   internal_close(infd[1]);
   m->inp_fd = infd[0];
   m->out_fd = outfd[1];
+  ExitSymbolizer();
 }
 
 static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {

Modified: compiler-rt/trunk/lib/tsan/tests/unit/tsan_flags_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/tests/unit/tsan_flags_test.cc?rev=199993&r1=199992&r2=199993&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/tests/unit/tsan_flags_test.cc (original)
+++ compiler-rt/trunk/lib/tsan/tests/unit/tsan_flags_test.cc Fri Jan 24 06:33:35 2014
@@ -59,6 +59,7 @@ static const char *options1 =
   " running_on_valgrind=0"
   " history_size=5"
   " io_sync=1"
+  " die_after_fork=true"
 
   " symbolize=0"
   " external_symbolizer_path=asdfgh"
@@ -100,6 +101,7 @@ static const char *options2 =
   " running_on_valgrind=true"
   " history_size=6"
   " io_sync=2"
+  " die_after_fork=false"
 
   " symbolize=true"
   " external_symbolizer_path=cccccc"
@@ -141,6 +143,7 @@ void VerifyOptions1(Flags *f) {
   EXPECT_EQ(f->running_on_valgrind, 0);
   EXPECT_EQ(f->history_size, 5);
   EXPECT_EQ(f->io_sync, 1);
+  EXPECT_EQ(f->die_after_fork, true);
 
   EXPECT_EQ(f->symbolize, 0);
   EXPECT_EQ(f->external_symbolizer_path, std::string(""));
@@ -182,6 +185,7 @@ void VerifyOptions2(Flags *f) {
   EXPECT_EQ(f->running_on_valgrind, true);
   EXPECT_EQ(f->history_size, 6);
   EXPECT_EQ(f->io_sync, 2);
+  EXPECT_EQ(f->die_after_fork, false);
 
   EXPECT_EQ(f->symbolize, true);
   EXPECT_EQ(f->external_symbolizer_path, std::string("cccccc"));





More information about the llvm-commits mailing list