Index: Makefile.old =================================================================== --- Makefile.old +++ Makefile.old @@ -70,12 +70,14 @@ # Release build with clang. $(MAKE) -f Makefile.old clean $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=$(CLANG) CXX=$(CLANG)++ + ./check_memcpy.sh # Debug build with gcc $(MAKE) -f Makefile.old clean $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=gcc CXX=g++ # Release build with gcc $(MAKE) -f Makefile.old clean $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=gcc CXX=g++ + ./check_memcpy.sh ./check_analyze.sh # Sanity check for Go runtime (cd go && ./buildgo.sh) Index: check_memcpy.sh =================================================================== --- check_memcpy.sh +++ check_memcpy.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Ensure that tsan runtime does not contain compiler-emitted memcpy and memset calls. + +set -eu + +ROOTDIR=$(dirname $0) + +: ${CXX:=clang++} +CFLAGS="-fsanitize=thread -fPIE -O1 -g" +LDFLAGS="-pie -lpthread -ldl -lrt -lm -Wl,--whole-archive $ROOTDIR/rtl/libtsan.a -Wl,--no-whole-archive" + +SRC=$ROOTDIR/lit_tests/simple_race.cc +OBJ=$SRC.o +EXE=$SRC.exe +$CXX $SRC $CFLAGS -c -o $OBJ +$CXX $OBJ $LDFLAGS -o $EXE + +NCALL=$(objdump -d $EXE | egrep "callq .*__interceptor_mem(cpy|set)" | wc -l) +if [ "$NCALL" != "0" ]; then + echo FAIL: found $NCALL memcpy/memset calls + exit 1 +fi Index: go/tsan_go.cc =================================================================== --- go/tsan_go.cc +++ go/tsan_go.cc @@ -88,17 +88,13 @@ void __tsan_init(ThreadState **thrp) { ThreadState *thr = AllocGoroutine(); main_thr = *thrp = thr; - thr->in_rtl++; Initialize(thr); - thr->in_rtl--; } void __tsan_fini() { // FIXME: Not necessary thread 0. ThreadState *thr = main_thr; - thr->in_rtl++; int res = Finalize(thr); - thr->in_rtl--; exit(res); } @@ -137,9 +133,7 @@ void __tsan_malloc(ThreadState *thr, void *p, uptr sz, void *pc) { if (thr == 0) // probably before __tsan_init() return; - thr->in_rtl++; MemoryResetRange(thr, (uptr)pc, (uptr)p, sz); - thr->in_rtl--; } void __tsan_free(void *p) { @@ -149,37 +143,25 @@ void __tsan_go_start(ThreadState *parent, ThreadState **pthr, void *pc) { ThreadState *thr = AllocGoroutine(); *pthr = thr; - thr->in_rtl++; - parent->in_rtl++; int goid = ThreadCreate(parent, (uptr)pc, 0, true); ThreadStart(thr, goid, 0); - parent->in_rtl--; - thr->in_rtl--; } void __tsan_go_end(ThreadState *thr) { - thr->in_rtl++; ThreadFinish(thr); - thr->in_rtl--; internal_free(thr); } void __tsan_acquire(ThreadState *thr, void *addr) { - thr->in_rtl++; Acquire(thr, 0, (uptr)addr); - thr->in_rtl--; } void __tsan_release(ThreadState *thr, void *addr) { - thr->in_rtl++; ReleaseStore(thr, 0, (uptr)addr); - thr->in_rtl--; } void __tsan_release_merge(ThreadState *thr, void *addr) { - thr->in_rtl++; Release(thr, 0, (uptr)addr); - thr->in_rtl--; } void __tsan_finalizer_goroutine(ThreadState *thr) { Index: lit_tests/malloc_hook.cc =================================================================== --- lit_tests/malloc_hook.cc +++ lit_tests/malloc_hook.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +static int malloc_count; +static int free_count; + +extern "C" { +void __tsan_malloc_hook(void *ptr, size_t size) { + (void)ptr; + (void)size; + __sync_fetch_and_add(&malloc_count, 1); +} + +void __tsan_free_hook(void *ptr) { + (void)ptr; + __sync_fetch_and_add(&free_count, 1); +} +} + +void *Thread1(void *x) { + ((int*)x)[0]++; + return 0; +} + +void *Thread2(void *x) { + sleep(1); + ((int*)x)[0]++; + return 0; +} + +int main() { + int *x = new int; + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, x); + pthread_create(&t[1], 0, Thread2, x); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + delete x; + if (malloc_count == 0 || free_count == 0) { + fprintf(stderr, "FAILED %d %d\n", malloc_count, free_count); + exit(1); + } + fprintf(stderr, "DONE\n"); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAILED +// CHECK: DONE Index: rtl/tsan_interceptors.cc =================================================================== --- rtl/tsan_interceptors.cc +++ rtl/tsan_interceptors.cc @@ -144,7 +144,6 @@ static SignalContext *SigCtx(ThreadState *thr) { SignalContext *ctx = (SignalContext*)thr->signal_ctx; if (ctx == 0 && thr->is_alive) { - ScopedInRtl in_rtl; ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext"); MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx)); thr->signal_ctx = ctx; @@ -161,7 +160,6 @@ private: ThreadState *const thr_; const uptr pc_; - const int in_rtl_; bool in_ignored_lib_; }; @@ -169,16 +167,12 @@ uptr pc) : thr_(thr) , pc_(pc) - , in_rtl_(thr->in_rtl) , in_ignored_lib_(false) { - if (thr_->in_rtl == 0) { + if (!thr_->ignore_interceptors) { Initialize(thr); FuncEntry(thr, pc); - thr_->in_rtl++; - DPrintf("#%d: intercept %s()\n", thr_->tid, fname); - } else { - thr_->in_rtl++; } + DPrintf("#%d: intercept %s()\n", thr_->tid, fname); if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) { in_ignored_lib_ = true; thr_->in_ignored_lib = true; @@ -191,12 +185,10 @@ thr_->in_ignored_lib = false; ThreadIgnoreEnd(thr_, pc_); } - thr_->in_rtl--; - if (thr_->in_rtl == 0) { + if (!thr_->ignore_interceptors) { FuncExit(thr_); ProcessPendingSignals(thr_); } - CHECK_EQ(in_rtl_, thr_->in_rtl); } #define SCOPED_INTERCEPTOR_RAW(func, ...) \ @@ -215,7 +207,7 @@ Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ Die(); \ } \ - if (thr->in_rtl > 1 || thr->in_ignored_lib) \ + if (thr->ignore_interceptors || thr->in_ignored_lib) \ return REAL(func)(__VA_ARGS__); \ /**/ @@ -235,6 +227,13 @@ } SignalContext *ctx; + + // When we are in a "blocking call", we process signals asynchronously + // (right when they arrive). In this context we do not expect to be + // executing any user/runtime code. The known interceptor sequence when + // this is not true is: pthread_join -> munmap(stack). It's fine + // to ignore munmap in this case -- we handle stack shadow separately. + ScopedIgnoreInterceptors ignore_interceptors; }; TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) { @@ -260,22 +259,14 @@ TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) { SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag); - // dlopen will execute global constructors, so it must be not in rtl. - CHECK_EQ(thr->in_rtl, 1); - thr->in_rtl = 0; void *res = REAL(dlopen)(filename, flag); - thr->in_rtl = 1; libignore()->OnLibraryLoaded(filename); return res; } TSAN_INTERCEPTOR(int, dlclose, void *handle) { SCOPED_INTERCEPTOR_RAW(dlclose, handle); - // dlclose will execute global destructors, so it must be not in rtl. - CHECK_EQ(thr->in_rtl, 1); - thr->in_rtl = 0; int res = REAL(dlclose)(handle); - thr->in_rtl = 1; libignore()->OnLibraryUnloaded(); return res; } @@ -303,7 +294,6 @@ } void exit(ThreadState *thr, uptr pc) { - CHECK_EQ(thr->in_rtl, 0); for (;;) { atexit_t f = 0; void *arg = 0; @@ -315,14 +305,12 @@ f = stack_[pos_]; arg = args_[pos_]; is_on_exit = is_on_exits_[pos_]; - ScopedInRtl in_rtl; Acquire(thr, pc, (uptr)this); } } if (f == 0) break; DPrintf("#%d: executing atexit func %p\n", thr->tid, f); - CHECK_EQ(thr->in_rtl, 0); if (is_on_exit) ((void(*)(int status, void *arg))f)(0, arg); else @@ -415,7 +403,6 @@ // FIXME: put everything below into a common extern "C" block? extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) { - ScopedInRtl in_rtl; SetJmp(cur_thread(), sp, mangled_sp); } @@ -829,7 +816,6 @@ return; } { - ScopedInRtl in_rtl; ThreadState *thr = cur_thread(); ThreadFinish(thr); SignalContext *sctx = thr->signal_ctx; @@ -854,7 +840,8 @@ int tid = 0; { ThreadState *thr = cur_thread(); - ScopedInRtl in_rtl; + // Thread-local state is not initialized yet. + ScopedIgnoreInterceptors ignore; if (pthread_setspecific(g_thread_finalize_key, (void *)kPthreadDestructorIterations)) { Printf("ThreadSanitizer: failed to set thread key\n"); @@ -864,7 +851,6 @@ pthread_yield(); atomic_store(&p->tid, 0, memory_order_release); ThreadStart(thr, tid, GetTid()); - CHECK_EQ(thr->in_rtl, 1); } void *res = callback(param); // Prevent the callback from being tail called, @@ -890,7 +876,12 @@ p.callback = callback; p.param = param; atomic_store(&p.tid, 0, memory_order_relaxed); - int res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p); + int res = -1; + { + // Otherwise we see false positives in pthread stack manipulation. + ScopedIgnoreInterceptors ignore; + res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p); + } if (res == 0) { int tid = ThreadCreate(thr, pc, *(uptr*)th, detached); CHECK_NE(tid, 0); @@ -1134,19 +1125,13 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { SCOPED_INTERCEPTOR_RAW(pthread_once, o, f); - // Using SCOPED_INTERCEPTOR_RAW, because if we are called from an ignored lib, - // the user callback must be executed with thr->in_rtl == 0. if (o == 0 || f == 0) return EINVAL; atomic_uint32_t *a = static_cast(o); u32 v = atomic_load(a, memory_order_acquire); if (v == 0 && atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) { - const int old_in_rtl = thr->in_rtl; - thr->in_rtl = 0; (*f)(); - CHECK_EQ(thr->in_rtl, 0); - thr->in_rtl = old_in_rtl; if (!thr->in_ignored_lib) Release(thr, pc, (uptr)o); atomic_store(a, 2, memory_order_release); @@ -1640,9 +1625,7 @@ // If we are in blocking function, we can safely process it now // (but check if we are in a recursive interceptor, // i.e. pthread_join()->munmap()). - (sctx && sctx->in_blocking_func == 1 && thr->in_rtl == 1)) { - int in_rtl = thr->in_rtl; - thr->in_rtl = 0; + (sctx && sctx->in_blocking_func == 1)) { CHECK_EQ(thr->in_signal_handler, false); thr->in_signal_handler = true; if (sigact) @@ -1651,7 +1634,6 @@ sigactions[sig].sa_handler(sig); CHECK_EQ(thr->in_signal_handler, true); thr->in_signal_handler = false; - thr->in_rtl = in_rtl; return; } @@ -1771,11 +1753,7 @@ // and can report false race between malloc and free // inside of getaddrinfo. So ignore memory accesses. ThreadIgnoreBegin(thr, pc); - // getaddrinfo calls fopen, which can be intercepted by user. - thr->in_rtl--; - CHECK_EQ(thr->in_rtl, 0); int res = REAL(getaddrinfo)(node, service, hints, rv); - thr->in_rtl++; ThreadIgnoreEnd(thr, pc); return res; } @@ -1924,15 +1902,11 @@ explicit ScopedSyscall(ThreadState *thr) : thr(thr) { - if (thr->in_rtl == 0) - Initialize(thr); - thr->in_rtl++; + Initialize(thr); } ~ScopedSyscall() { - thr->in_rtl--; - if (thr->in_rtl == 0) - ProcessPendingSignals(thr); + ProcessPendingSignals(thr); } }; @@ -2035,7 +2009,6 @@ } void ProcessPendingSignals(ThreadState *thr) { - CHECK_EQ(thr->in_rtl, 0); SignalContext *sctx = SigCtx(thr); if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler) return; @@ -2060,7 +2033,6 @@ else sigactions[sig].sa_handler(sig); if (flags()->report_bugs && errno != 0) { - ScopedInRtl in_rtl; __tsan::StackTrace stack; uptr pc = signal->sigaction ? (uptr)sigactions[sig].sa_sigaction : @@ -2089,8 +2061,6 @@ } void InitializeInterceptors() { - CHECK_GT(cur_thread()->in_rtl, 0); - // We need to setup it early, because functions like dlsym() can call it. REAL(memset) = internal_memset; REAL(memcpy) = internal_memcpy; Index: rtl/tsan_interface_ann.cc =================================================================== --- rtl/tsan_interface_ann.cc +++ rtl/tsan_interface_ann.cc @@ -33,22 +33,16 @@ public: ScopedAnnotation(ThreadState *thr, const char *aname, const char *f, int l, uptr pc) - : thr_(thr) - , in_rtl_(thr->in_rtl) { - CHECK_EQ(thr_->in_rtl, 0); + : thr_(thr) { FuncEntry(thr_, pc); - thr_->in_rtl++; DPrintf("#%d: annotation %s() %s:%d\n", thr_->tid, aname, f, l); } ~ScopedAnnotation() { - thr_->in_rtl--; - CHECK_EQ(in_rtl_, thr_->in_rtl); FuncExit(thr_); } private: ThreadState *const thr_; - const int in_rtl_; }; #define SCOPED_ANNOTATION(typ) \ Index: rtl/tsan_interface_atomic.cc =================================================================== --- rtl/tsan_interface_atomic.cc +++ rtl/tsan_interface_atomic.cc @@ -66,15 +66,11 @@ ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a, morder mo, const char *func) : thr_(thr) { - CHECK_EQ(thr_->in_rtl, 0); ProcessPendingSignals(thr); FuncEntry(thr_, pc); DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo); - thr_->in_rtl++; } ~ScopedAtomic() { - thr_->in_rtl--; - CHECK_EQ(thr_->in_rtl, 0); FuncExit(thr_); } private: Index: rtl/tsan_interface_java.cc =================================================================== --- rtl/tsan_interface_java.cc +++ rtl/tsan_interface_java.cc @@ -79,13 +79,9 @@ : thr_(thr) { Initialize(thr_); FuncEntry(thr, pc); - CHECK_EQ(thr_->in_rtl, 0); - thr_->in_rtl++; } ~ScopedJavaFunc() { - thr_->in_rtl--; - CHECK_EQ(thr_->in_rtl, 0); FuncExit(thr_); // FIXME(dvyukov): process pending signals. } Index: rtl/tsan_mman.cc =================================================================== --- rtl/tsan_mman.cc +++ rtl/tsan_mman.cc @@ -102,7 +102,6 @@ } void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) { - CHECK_GT(thr->in_rtl, 0); if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) return AllocatorReturnNull(); void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); @@ -122,7 +121,6 @@ } void user_free(ThreadState *thr, uptr pc, void *p) { - CHECK_GT(thr->in_rtl, 0); CHECK_NE(p, (void*)0); DPrintf("#%d: free(%p)\n", thr->tid, p); MBlock *b = (MBlock*)allocator()->GetMetaData(p); @@ -138,7 +136,7 @@ } b->ListReset(); } - if (CTX() && CTX()->initialized && thr->in_rtl == 1) { + if (CTX() && CTX()->initialized) { if (thr->ignore_reads_and_writes == 0) MemoryRangeFreed(thr, pc, (uptr)p, b->Size()); } @@ -147,7 +145,6 @@ } void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { - CHECK_GT(thr->in_rtl, 0); void *p2 = 0; // FIXME: Handle "shrinking" more efficiently, // it seems that some software actually does this. @@ -167,7 +164,6 @@ } uptr user_alloc_usable_size(ThreadState *thr, uptr pc, void *p) { - CHECK_GT(thr->in_rtl, 0); if (p == 0) return 0; MBlock *b = (MBlock*)allocator()->GetMetaData(p); @@ -186,7 +182,7 @@ void invoke_malloc_hook(void *ptr, uptr size) { Context *ctx = CTX(); ThreadState *thr = cur_thread(); - if (ctx == 0 || !ctx->initialized || thr->in_rtl) + if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) return; __tsan_malloc_hook(ptr, size); } @@ -194,14 +190,13 @@ void invoke_free_hook(void *ptr) { Context *ctx = CTX(); ThreadState *thr = cur_thread(); - if (ctx == 0 || !ctx->initialized || thr->in_rtl) + if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) return; __tsan_free_hook(ptr); } void *internal_alloc(MBlockType typ, uptr sz) { ThreadState *thr = cur_thread(); - CHECK_GT(thr->in_rtl, 0); CHECK_LE(sz, InternalSizeClassMap::kMaxSize); if (thr->nomalloc) { thr->nomalloc = 0; // CHECK calls internal_malloc(). @@ -212,7 +207,6 @@ void internal_free(void *p) { ThreadState *thr = cur_thread(); - CHECK_GT(thr->in_rtl, 0); if (thr->nomalloc) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); Index: rtl/tsan_mutexset.h =================================================================== --- rtl/tsan_mutexset.h +++ rtl/tsan_mutexset.h @@ -38,6 +38,10 @@ uptr Size() const; Desc Get(uptr i) const; + void operator=(const MutexSet &other) { + internal_memcpy(this, &other, sizeof(*this)); + } + private: #ifndef TSAN_GO uptr size_; @@ -45,6 +49,7 @@ #endif void RemovePos(uptr i); + MutexSet(const MutexSet&); }; // Go does not have mutexes, so do not spend memory and time. Index: rtl/tsan_platform_linux.cc =================================================================== --- rtl/tsan_platform_linux.cc +++ rtl/tsan_platform_linux.cc @@ -61,27 +61,6 @@ const uptr kPageSize = 4096; -#ifndef TSAN_GO -ScopedInRtl::ScopedInRtl() - : thr_(cur_thread()) { - in_rtl_ = thr_->in_rtl; - thr_->in_rtl++; - errno_ = errno; -} - -ScopedInRtl::~ScopedInRtl() { - thr_->in_rtl--; - errno = errno_; - CHECK_EQ(in_rtl_, thr_->in_rtl); -} -#else -ScopedInRtl::ScopedInRtl() { -} - -ScopedInRtl::~ScopedInRtl() { -} -#endif - void FillProfileCallback(uptr start, uptr rss, bool file, uptr *mem, uptr stats_size) { CHECK_EQ(7, stats_size); @@ -135,7 +114,6 @@ #ifndef TSAN_GO static void ProtectRange(uptr beg, uptr end) { - ScopedInRtl in_rtl; CHECK_LE(beg, end); if (beg == end) return; Index: rtl/tsan_rtl.h =================================================================== --- rtl/tsan_rtl.h +++ rtl/tsan_rtl.h @@ -433,11 +433,11 @@ AllocatorCache alloc_cache; InternalAllocatorCache internal_alloc_cache; Vector jmp_bufs; + int ignore_interceptors; #endif u64 stat[StatCnt]; const int tid; const int unique_id; - int in_rtl; bool in_symbolizer; bool in_ignored_lib; bool is_alive; @@ -551,14 +551,18 @@ u64 int_alloc_siz[MBlockTypeCount]; }; -class ScopedInRtl { - public: - ScopedInRtl(); - ~ScopedInRtl(); - private: - ThreadState*thr_; - int in_rtl_; - int errno_; +struct ScopedIgnoreInterceptors { + ScopedIgnoreInterceptors() { +#ifndef TSAN_GO + cur_thread()->ignore_interceptors++; +#endif + } + + ~ScopedIgnoreInterceptors() { +#ifndef TSAN_GO + cur_thread()->ignore_interceptors--; +#endif + } }; class ScopedReport { @@ -580,6 +584,9 @@ private: Context *ctx_; ReportDesc *rep_; + // Symbolizer makes lots of intercepted calls. If we try to process them, + // at best it will cause deadlocks on internal mutexes. + ScopedIgnoreInterceptors ignore_interceptors_; void AddMutex(u64 id); Index: rtl/tsan_rtl.cc =================================================================== --- rtl/tsan_rtl.cc +++ rtl/tsan_rtl.cc @@ -91,7 +91,7 @@ // Do not touch these, rely on zero initialization, // they may be accessed before the ctor. // , ignore_reads_and_writes() - // , in_rtl() + // , ignore_interceptors() #ifndef TSAN_GO , jmp_bufs(MBlockJmpBuf) #endif @@ -116,8 +116,9 @@ } static void BackgroundThread(void *arg) { - ScopedInRtl in_rtl; Context *ctx = CTX(); + // This is a non-initialized non-user thread, nothing to see here. + ScopedIgnoreInterceptors ignore; const u64 kMs2Ns = 1000 * 1000; fd_t mprof_fd = kInvalidFd; @@ -216,11 +217,12 @@ if (is_initialized) return; is_initialized = true; + // We are not ready to handle interceptors yet. + ScopedIgnoreInterceptors ignore; SanitizerToolName = "ThreadSanitizer"; // Install tool-specific callbacks in sanitizer_common. SetCheckFailedCallback(TsanCheckFailed); - ScopedInRtl in_rtl; #ifndef TSAN_GO InitializeAllocator(); #endif @@ -261,7 +263,6 @@ int tid = ThreadCreate(thr, 0, 0, true); CHECK_EQ(tid, 0); ThreadStart(thr, tid, internal_getpid()); - CHECK_EQ(thr->in_rtl, 1); ctx->initialized = true; if (flags()->stop_on_start) { @@ -273,7 +274,6 @@ } int Finalize(ThreadState *thr) { - ScopedInRtl in_rtl; Context *ctx = __tsan::ctx; bool failed = false; @@ -340,7 +340,6 @@ void TraceSwitch(ThreadState *thr) { thr->nomalloc++; - ScopedInRtl in_rtl; Trace *thr_trace = ThreadTrace(thr->tid); Lock l(&thr_trace->mtx); unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts(); @@ -657,7 +656,6 @@ ALWAYS_INLINE USED void FuncEntry(ThreadState *thr, uptr pc) { - DCHECK_EQ(thr->in_rtl, 0); StatInc(thr, StatFuncEnter); DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc); thr->fast_state.IncrementEpoch(); @@ -687,7 +685,6 @@ ALWAYS_INLINE USED void FuncExit(ThreadState *thr) { - DCHECK_EQ(thr->in_rtl, 0); StatInc(thr, StatFuncExit); DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid()); thr->fast_state.IncrementEpoch(); Index: rtl/tsan_rtl_mutex.cc =================================================================== --- rtl/tsan_rtl_mutex.cc +++ rtl/tsan_rtl_mutex.cc @@ -23,7 +23,6 @@ void MutexCreate(ThreadState *thr, uptr pc, uptr addr, bool rw, bool recursive, bool linker_init) { Context *ctx = CTX(); - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr); StatInc(thr, StatMutexCreate); if (!linker_init && IsAppMem(addr)) { @@ -41,7 +40,6 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { Context *ctx = CTX(); - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr); StatInc(thr, StatMutexDestroy); #ifndef TSAN_GO @@ -80,7 +78,6 @@ } void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) { - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexLock %zx rec=%d\n", thr->tid, addr, rec); CHECK_GT(rec, 0); if (IsAppMem(addr)) @@ -111,7 +108,6 @@ } int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexUnlock %zx all=%d\n", thr->tid, addr, all); if (IsAppMem(addr)) MemoryReadAtomic(thr, pc, addr, kSizeLog1); @@ -149,7 +145,6 @@ } void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr); StatInc(thr, StatMutexReadLock); if (IsAppMem(addr)) @@ -169,7 +164,6 @@ } void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr); StatInc(thr, StatMutexReadUnlock); if (IsAppMem(addr)) @@ -188,7 +182,6 @@ } void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr); if (IsAppMem(addr)) MemoryReadAtomic(thr, pc, addr, kSizeLog1); @@ -226,7 +219,6 @@ void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { Context *ctx = CTX(); - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr); SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true); s->owner_tid = SyncVar::kInvalidTid; @@ -235,7 +227,6 @@ } void Acquire(ThreadState *thr, uptr pc, uptr addr) { - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Acquire %zx\n", thr->tid, addr); if (thr->ignore_sync) return; @@ -263,7 +254,6 @@ } void Release(ThreadState *thr, uptr pc, uptr addr) { - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Release %zx\n", thr->tid, addr); if (thr->ignore_sync) return; @@ -276,7 +266,6 @@ } void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) { - CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr); if (thr->ignore_sync) return; Index: rtl/tsan_rtl_report.cc =================================================================== --- rtl/tsan_rtl_report.cc +++ rtl/tsan_rtl_report.cc @@ -34,7 +34,10 @@ void TsanCheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { - ScopedInRtl in_rtl; + // There is high probability that interceptors will check-fail as well, + // on the other hand there is no sense in processing interceptors + // since we are going to die soon. + ScopedIgnoreInterceptors ignore; Printf("FATAL: ThreadSanitizer CHECK failed: " "%s:%d \"%s\" (0x%zx, 0x%zx)\n", file, line, cond, (uptr)v1, (uptr)v2); @@ -597,7 +600,8 @@ CHECK(!(s0.IsAtomic() && s1.IsAtomic())); if (!s0.IsAtomic() && !s1.IsAtomic()) return true; - if (s0.IsAtomic() && s1.IsFreed()) + if (s0.IsAtomic() && s1 + .IsFreed()) return true; if (s1.IsAtomic() && thr->is_freeing) return true; @@ -605,10 +609,12 @@ } void ReportRace(ThreadState *thr) { + // Symbolizer makes lots of intercepted calls. If we try to process them, + // at best it will cause deadlocks on internal mutexes. + ScopedIgnoreInterceptors ignore; + if (!flags()->report_bugs) return; - ScopedInRtl in_rtl; - if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr)) return; Index: rtl/tsan_rtl_thread.cc =================================================================== --- rtl/tsan_rtl_thread.cc +++ rtl/tsan_rtl_thread.cc @@ -189,7 +189,6 @@ #endif void ThreadFinalize(ThreadState *thr) { - CHECK_GT(thr->in_rtl, 0); ThreadCheckIgnore(thr); #ifndef TSAN_GO if (!flags()->report_thread_leaks) @@ -208,7 +207,6 @@ } int ThreadCount(ThreadState *thr) { - CHECK_GT(thr->in_rtl, 0); Context *ctx = CTX(); uptr result; ctx->thread_registry->GetNumberOfThreads(0, 0, &result); @@ -216,7 +214,6 @@ } int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) { - CHECK_GT(thr->in_rtl, 0); StatInc(thr, StatThreadCreate); Context *ctx = CTX(); OnCreatedArgs args = { thr, pc }; @@ -228,7 +225,6 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) { Context *ctx = CTX(); - CHECK_GT(thr->in_rtl, 0); uptr stk_addr = 0; uptr stk_size = 0; uptr tls_addr = 0; @@ -264,7 +260,6 @@ } void ThreadFinish(ThreadState *thr) { - CHECK_GT(thr->in_rtl, 0); ThreadCheckIgnore(thr); StatInc(thr, StatThreadFinish); if (thr->stk_addr && thr->stk_size) @@ -286,7 +281,6 @@ } int ThreadTid(ThreadState *thr, uptr pc, uptr uid) { - CHECK_GT(thr->in_rtl, 0); Context *ctx = CTX(); int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid); DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res); @@ -294,7 +288,6 @@ } void ThreadJoin(ThreadState *thr, uptr pc, int tid) { - CHECK_GT(thr->in_rtl, 0); CHECK_GT(tid, 0); CHECK_LT(tid, kMaxTid); DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid); @@ -303,7 +296,6 @@ } void ThreadDetach(ThreadState *thr, uptr pc, int tid) { - CHECK_GT(thr->in_rtl, 0); CHECK_GT(tid, 0); CHECK_LT(tid, kMaxTid); Context *ctx = CTX(); @@ -311,7 +303,6 @@ } void ThreadSetName(ThreadState *thr, const char *name) { - CHECK_GT(thr->in_rtl, 0); CTX()->thread_registry->SetThreadName(thr->tid, name); } Index: rtl/tsan_vector.h =================================================================== --- rtl/tsan_vector.h +++ rtl/tsan_vector.h @@ -58,10 +58,18 @@ return begin_[i]; } - T *PushBack(T v = T()) { + T *PushBack() { EnsureSize(Size() + 1); - end_[-1] = v; - return &end_[-1]; + T *p = &end_[-1]; + internal_memset(p, 0, sizeof(*p)); + return p; + } + + T *PushBack(const T& v) { + EnsureSize(Size() + 1); + T *p = &end_[-1]; + internal_memcpy(p, &v, sizeof(*p)); + return p; } void PopBack() { Index: tests/unit/tsan_clock_test.cc =================================================================== --- tests/unit/tsan_clock_test.cc +++ tests/unit/tsan_clock_test.cc @@ -17,7 +17,6 @@ namespace __tsan { TEST(Clock, VectorBasic) { - ScopedInRtl in_rtl; ThreadClock clk; CHECK_EQ(clk.size(), 0); clk.tick(0); @@ -34,7 +33,6 @@ } TEST(Clock, ChunkedBasic) { - ScopedInRtl in_rtl; ThreadClock vector; SyncClock chunked; CHECK_EQ(vector.size(), 0); @@ -51,7 +49,6 @@ } TEST(Clock, AcquireRelease) { - ScopedInRtl in_rtl; ThreadClock vector1; vector1.tick(100); SyncClock chunked; @@ -67,7 +64,6 @@ } TEST(Clock, ManyThreads) { - ScopedInRtl in_rtl; SyncClock chunked; for (int i = 0; i < 100; i++) { ThreadClock vector; @@ -85,7 +81,6 @@ } TEST(Clock, DifferentSizes) { - ScopedInRtl in_rtl; { ThreadClock vector1; vector1.tick(10); Index: tests/unit/tsan_flags_test.cc =================================================================== --- tests/unit/tsan_flags_test.cc +++ tests/unit/tsan_flags_test.cc @@ -18,7 +18,6 @@ namespace __tsan { TEST(Flags, Basic) { - ScopedInRtl in_rtl; // At least should not crash. Flags f; InitializeFlags(&f, 0); @@ -26,7 +25,6 @@ } TEST(Flags, DefaultValues) { - ScopedInRtl in_rtl; Flags f; f.enable_annotations = false; @@ -206,7 +204,6 @@ } TEST(Flags, ParseDefaultOptions) { - ScopedInRtl in_rtl; Flags f; test_default_options = options1; @@ -219,7 +216,6 @@ } TEST(Flags, ParseEnvOptions) { - ScopedInRtl in_rtl; Flags f; InitializeFlags(&f, options1); @@ -230,7 +226,6 @@ } TEST(Flags, ParsePriority) { - ScopedInRtl in_rtl; Flags f; test_default_options = options2; Index: tests/unit/tsan_mman_test.cc =================================================================== --- tests/unit/tsan_mman_test.cc +++ tests/unit/tsan_mman_test.cc @@ -28,7 +28,6 @@ namespace __tsan { TEST(Mman, Internal) { - ScopedInRtl in_rtl; char *p = (char*)internal_alloc(MBlockScopedBuf, 10); EXPECT_NE(p, (char*)0); char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20); @@ -45,7 +44,6 @@ } TEST(Mman, User) { - ScopedInRtl in_rtl; ThreadState *thr = cur_thread(); uptr pc = 0; char *p = (char*)user_alloc(thr, pc, 10); @@ -72,7 +70,6 @@ } TEST(Mman, UserRealloc) { - ScopedInRtl in_rtl; ThreadState *thr = cur_thread(); uptr pc = 0; { @@ -118,7 +115,6 @@ } TEST(Mman, UsableSize) { - ScopedInRtl in_rtl; ThreadState *thr = cur_thread(); uptr pc = 0; char *p = (char*)user_alloc(thr, pc, 10); @@ -131,7 +127,6 @@ } TEST(Mman, Stats) { - ScopedInRtl in_rtl; ThreadState *thr = cur_thread(); uptr alloc0 = __tsan_get_current_allocated_bytes(); Index: tests/unit/tsan_stack_test.cc =================================================================== --- tests/unit/tsan_stack_test.cc +++ tests/unit/tsan_stack_test.cc @@ -46,13 +46,11 @@ } TEST(StackTrace, Basic) { - ScopedInRtl in_rtl; StackTrace trace; TestStackTrace(&trace); } TEST(StackTrace, StaticBasic) { - ScopedInRtl in_rtl; uptr buf[10]; StackTrace trace1(buf, 10); TestStackTrace(&trace1); @@ -61,7 +59,6 @@ } TEST(StackTrace, StaticTrim) { - ScopedInRtl in_rtl; uptr buf[2]; StackTrace trace(buf, 2); Index: tests/unit/tsan_sync_test.cc =================================================================== --- tests/unit/tsan_sync_test.cc +++ tests/unit/tsan_sync_test.cc @@ -25,7 +25,6 @@ const uintptr_t kIters = 512*1024; const uintptr_t kRange = 10000; - ScopedInRtl in_rtl; ThreadState *thr = cur_thread(); uptr pc = 0; Index: tests/unit/tsan_vector_test.cc =================================================================== --- tests/unit/tsan_vector_test.cc +++ tests/unit/tsan_vector_test.cc @@ -17,7 +17,6 @@ namespace __tsan { TEST(Vector, Basic) { - ScopedInRtl in_rtl; Vector v(MBlockScopedBuf); EXPECT_EQ(v.Size(), (uptr)0); v.PushBack(42); @@ -30,7 +29,6 @@ } TEST(Vector, Stride) { - ScopedInRtl in_rtl; Vector v(MBlockScopedBuf); for (int i = 0; i < 1000; i++) { v.PushBack(i);