tsan: remove in_rtl counter

This is intended to address the following problem.
Episodically we see CHECK-failures when recursive interceptors call back into user code. Effectively we are not "in_rtl" at this point, but it's very complicated and fragile to properly maintain in_rtl property. Instead get rid of it. It was used mostly for sanity CHECKs, which basically never uncover real problems.
Instead introduce ignore_interceptors flag, which is used in very few narrow places to disable recursive interceptors (e.g. during runtime initialization).

llvm-svn: 197979
This commit is contained in:
Dmitry Vyukov 2013-12-24 12:55:56 +00:00
parent 27aea0b0b7
commit ce3721057d
23 changed files with 149 additions and 188 deletions

View File

@ -70,12 +70,14 @@ presubmit:
# 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)

View File

@ -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

View File

@ -88,17 +88,13 @@ static ThreadState *AllocGoroutine() {
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_func_exit(ThreadState *thr) {
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_free(void *p) {
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) {

View File

@ -0,0 +1,52 @@
// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
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

View File

@ -144,7 +144,6 @@ void InitializeLibIgnore() {
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 @@ class ScopedInterceptor {
private:
ThreadState *const thr_;
const uptr pc_;
const int in_rtl_;
bool in_ignored_lib_;
};
@ -169,16 +167,12 @@ ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
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 @@ ScopedInterceptor::~ScopedInterceptor() {
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 @@ ScopedInterceptor::~ScopedInterceptor() {
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 @@ struct BlockingCall {
}
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(int, nanosleep, void *req, void *rem) {
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 @@ class AtExitContext {
}
void exit(ThreadState *thr, uptr pc) {
CHECK_EQ(thr->in_rtl, 0);
for (;;) {
atexit_t f = 0;
void *arg = 0;
@ -315,14 +305,12 @@ class AtExitContext {
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 @@ static void LongJmp(ThreadState *thr, uptr *env) {
// 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 @@ static void thread_finalize(void *v) {
return;
}
{
ScopedInRtl in_rtl;
ThreadState *thr = cur_thread();
ThreadFinish(thr);
SignalContext *sctx = thr->signal_ctx;
@ -854,7 +840,8 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
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 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
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 @@ TSAN_INTERCEPTOR(int, pthread_create,
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_barrier_wait, void *b) {
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<atomic_uint32_t*>(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 @@ void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
// 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 @@ void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
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 @@ TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service,
// 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;
}
@ -1843,14 +1821,8 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
}
#include "sanitizer_common/sanitizer_platform_interceptors.h"
// Causes interceptor recursion (getpwuid_r() calls fopen())
#undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
#undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
// Causes interceptor recursion (getaddrinfo() and fopen())
#undef SANITIZER_INTERCEPT_GETADDRINFO
#undef SANITIZER_INTERCEPT_GETNAMEINFO
// Causes interceptor recursion (glob64() calls lstat64())
#undef SANITIZER_INTERCEPT_GLOB
#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \
@ -1924,15 +1896,11 @@ struct ScopedSyscall {
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 +2003,6 @@ static void finalize(void *arg) {
}
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 +2027,6 @@ void ProcessPendingSignals(ThreadState *thr) {
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 +2055,6 @@ static void unreachable() {
}
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;
@ -2283,8 +2247,7 @@ void InitializeInterceptors() {
}
void internal_start_thread(void(*func)(void *arg), void *arg) {
// Start the thread with signals blocked, otherwise it can steal users
// signals.
// Start the thread with signals blocked, otherwise it can steal user signals.
__sanitizer_kernel_sigset_t set, old;
internal_sigfillset(&set);
internal_sigprocmask(SIG_SETMASK, &set, &old);

View File

@ -33,22 +33,16 @@ class ScopedAnnotation {
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) \

View File

@ -66,15 +66,11 @@ class ScopedAtomic {
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:

View File

@ -79,13 +79,9 @@ class ScopedJavaFunc {
: 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.
}

View File

@ -102,7 +102,6 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
}
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_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
}
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 @@ void user_free(ThreadState *thr, uptr pc, void *p) {
}
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_free(ThreadState *thr, uptr pc, void *p) {
}
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 @@ void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
}
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 @@ MBlock *user_mblock(ThreadState *thr, void *p) {
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_malloc_hook(void *ptr, uptr size) {
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_alloc(MBlockType typ, uptr sz) {
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);

View File

@ -38,6 +38,10 @@ class MutexSet {
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 @@ class MutexSet {
#endif
void RemovePos(uptr i);
MutexSet(const MutexSet&);
};
// Go does not have mutexes, so do not spend memory and time.

View File

@ -61,27 +61,6 @@ namespace __tsan {
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 @@ void FlushShadowMemory() {
#ifndef TSAN_GO
static void ProtectRange(uptr beg, uptr end) {
ScopedInRtl in_rtl;
CHECK_LE(beg, end);
if (beg == end)
return;

View File

@ -91,7 +91,7 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
// 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 MemoryProfiler(Context *ctx, fd_t fd, int i) {
}
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 @@ void Initialize(ThreadState *thr) {
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 @@ void Initialize(ThreadState *thr) {
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 @@ void Initialize(ThreadState *thr) {
}
int Finalize(ThreadState *thr) {
ScopedInRtl in_rtl;
Context *ctx = __tsan::ctx;
bool failed = false;
@ -340,7 +340,6 @@ u32 CurrentStackId(ThreadState *thr, uptr pc) {
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 @@ void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) {
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 @@ void FuncEntry(ThreadState *thr, uptr pc) {
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();

View File

@ -433,11 +433,11 @@ struct ThreadState {
AllocatorCache alloc_cache;
InternalAllocatorCache internal_alloc_cache;
Vector<JmpBuf> 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 @@ struct Context {
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 @@ class ScopedReport {
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);

View File

@ -23,7 +23,6 @@ namespace __tsan {
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 MutexCreate(ThreadState *thr, uptr pc, uptr addr,
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 MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
}
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 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) {
}
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 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) {
}
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 MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
}
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 MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
}
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 MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
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 MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
}
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 AcquireGlobal(ThreadState *thr, uptr pc) {
}
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 Release(ThreadState *thr, uptr pc, uptr addr) {
}
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;

View File

@ -34,7 +34,10 @@ static ReportStack *SymbolizeStack(const StackTrace& trace);
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);
@ -605,10 +608,12 @@ static bool RaceBetweenAtomicAndFree(ThreadState *thr) {
}
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;

View File

@ -189,7 +189,6 @@ static void ThreadCheckIgnore(ThreadState *thr) {}
#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 @@ void ThreadFinalize(ThreadState *thr) {
}
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 ThreadCount(ThreadState *thr) {
}
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 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
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 ThreadStart(ThreadState *thr, int tid, uptr os_id) {
}
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 @@ static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) {
}
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 @@ int ThreadTid(ThreadState *thr, uptr pc, uptr uid) {
}
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 ThreadJoin(ThreadState *thr, uptr pc, int tid) {
}
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 ThreadDetach(ThreadState *thr, uptr pc, int tid) {
}
void ThreadSetName(ThreadState *thr, const char *name) {
CHECK_GT(thr->in_rtl, 0);
CTX()->thread_registry->SetThreadName(thr->tid, name);
}

View File

@ -58,10 +58,18 @@ class Vector {
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() {

View File

@ -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, VectorBasic) {
}
TEST(Clock, ChunkedBasic) {
ScopedInRtl in_rtl;
ThreadClock vector;
SyncClock chunked;
CHECK_EQ(vector.size(), 0);
@ -51,7 +49,6 @@ TEST(Clock, ChunkedBasic) {
}
TEST(Clock, AcquireRelease) {
ScopedInRtl in_rtl;
ThreadClock vector1;
vector1.tick(100);
SyncClock chunked;
@ -67,7 +64,6 @@ TEST(Clock, AcquireRelease) {
}
TEST(Clock, ManyThreads) {
ScopedInRtl in_rtl;
SyncClock chunked;
for (int i = 0; i < 100; i++) {
ThreadClock vector;
@ -85,7 +81,6 @@ TEST(Clock, ManyThreads) {
}
TEST(Clock, DifferentSizes) {
ScopedInRtl in_rtl;
{
ThreadClock vector1;
vector1.tick(10);

View File

@ -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, Basic) {
}
TEST(Flags, DefaultValues) {
ScopedInRtl in_rtl;
Flags f;
f.enable_annotations = false;
@ -206,7 +204,6 @@ extern "C" const char *__tsan_default_options() {
}
TEST(Flags, ParseDefaultOptions) {
ScopedInRtl in_rtl;
Flags f;
test_default_options = options1;
@ -219,7 +216,6 @@ TEST(Flags, ParseDefaultOptions) {
}
TEST(Flags, ParseEnvOptions) {
ScopedInRtl in_rtl;
Flags f;
InitializeFlags(&f, options1);
@ -230,7 +226,6 @@ TEST(Flags, ParseEnvOptions) {
}
TEST(Flags, ParsePriority) {
ScopedInRtl in_rtl;
Flags f;
test_default_options = options2;

View File

@ -28,7 +28,6 @@ uptr __tsan_get_allocated_size(void *p);
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, Internal) {
}
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, User) {
}
TEST(Mman, UserRealloc) {
ScopedInRtl in_rtl;
ThreadState *thr = cur_thread();
uptr pc = 0;
{
@ -118,7 +115,6 @@ TEST(Mman, UserRealloc) {
}
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, UsableSize) {
}
TEST(Mman, Stats) {
ScopedInRtl in_rtl;
ThreadState *thr = cur_thread();
uptr alloc0 = __tsan_get_current_allocated_bytes();

View File

@ -46,13 +46,11 @@ static void TestStackTrace(StackTrace *trace) {
}
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, StaticBasic) {
}
TEST(StackTrace, StaticTrim) {
ScopedInRtl in_rtl;
uptr buf[2];
StackTrace trace(buf, 2);

View File

@ -25,7 +25,6 @@ TEST(Sync, Table) {
const uintptr_t kIters = 512*1024;
const uintptr_t kRange = 10000;
ScopedInRtl in_rtl;
ThreadState *thr = cur_thread();
uptr pc = 0;

View File

@ -17,7 +17,6 @@
namespace __tsan {
TEST(Vector, Basic) {
ScopedInRtl in_rtl;
Vector<int> v(MBlockScopedBuf);
EXPECT_EQ(v.Size(), (uptr)0);
v.PushBack(42);
@ -30,7 +29,6 @@ TEST(Vector, Basic) {
}
TEST(Vector, Stride) {
ScopedInRtl in_rtl;
Vector<int> v(MBlockScopedBuf);
for (int i = 0; i < 1000; i++) {
v.PushBack(i);