[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