forked from OSchip/llvm-project
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:
parent
27aea0b0b7
commit
ce3721057d
|
@ -70,12 +70,14 @@ presubmit:
|
||||||
# Release build with clang.
|
# Release build with clang.
|
||||||
$(MAKE) -f Makefile.old clean
|
$(MAKE) -f Makefile.old clean
|
||||||
$(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=$(CLANG) CXX=$(CLANG)++
|
$(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=$(CLANG) CXX=$(CLANG)++
|
||||||
|
./check_memcpy.sh
|
||||||
# Debug build with gcc
|
# Debug build with gcc
|
||||||
$(MAKE) -f Makefile.old clean
|
$(MAKE) -f Makefile.old clean
|
||||||
$(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=gcc CXX=g++
|
$(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=gcc CXX=g++
|
||||||
# Release build with gcc
|
# Release build with gcc
|
||||||
$(MAKE) -f Makefile.old clean
|
$(MAKE) -f Makefile.old clean
|
||||||
$(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=gcc CXX=g++
|
$(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=gcc CXX=g++
|
||||||
|
./check_memcpy.sh
|
||||||
./check_analyze.sh
|
./check_analyze.sh
|
||||||
# Sanity check for Go runtime
|
# Sanity check for Go runtime
|
||||||
(cd go && ./buildgo.sh)
|
(cd go && ./buildgo.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
|
|
@ -88,17 +88,13 @@ static ThreadState *AllocGoroutine() {
|
||||||
void __tsan_init(ThreadState **thrp) {
|
void __tsan_init(ThreadState **thrp) {
|
||||||
ThreadState *thr = AllocGoroutine();
|
ThreadState *thr = AllocGoroutine();
|
||||||
main_thr = *thrp = thr;
|
main_thr = *thrp = thr;
|
||||||
thr->in_rtl++;
|
|
||||||
Initialize(thr);
|
Initialize(thr);
|
||||||
thr->in_rtl--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __tsan_fini() {
|
void __tsan_fini() {
|
||||||
// FIXME: Not necessary thread 0.
|
// FIXME: Not necessary thread 0.
|
||||||
ThreadState *thr = main_thr;
|
ThreadState *thr = main_thr;
|
||||||
thr->in_rtl++;
|
|
||||||
int res = Finalize(thr);
|
int res = Finalize(thr);
|
||||||
thr->in_rtl--;
|
|
||||||
exit(res);
|
exit(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,9 +133,7 @@ void __tsan_func_exit(ThreadState *thr) {
|
||||||
void __tsan_malloc(ThreadState *thr, void *p, uptr sz, void *pc) {
|
void __tsan_malloc(ThreadState *thr, void *p, uptr sz, void *pc) {
|
||||||
if (thr == 0) // probably before __tsan_init()
|
if (thr == 0) // probably before __tsan_init()
|
||||||
return;
|
return;
|
||||||
thr->in_rtl++;
|
|
||||||
MemoryResetRange(thr, (uptr)pc, (uptr)p, sz);
|
MemoryResetRange(thr, (uptr)pc, (uptr)p, sz);
|
||||||
thr->in_rtl--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __tsan_free(void *p) {
|
void __tsan_free(void *p) {
|
||||||
|
@ -149,37 +143,25 @@ void __tsan_free(void *p) {
|
||||||
void __tsan_go_start(ThreadState *parent, ThreadState **pthr, void *pc) {
|
void __tsan_go_start(ThreadState *parent, ThreadState **pthr, void *pc) {
|
||||||
ThreadState *thr = AllocGoroutine();
|
ThreadState *thr = AllocGoroutine();
|
||||||
*pthr = thr;
|
*pthr = thr;
|
||||||
thr->in_rtl++;
|
|
||||||
parent->in_rtl++;
|
|
||||||
int goid = ThreadCreate(parent, (uptr)pc, 0, true);
|
int goid = ThreadCreate(parent, (uptr)pc, 0, true);
|
||||||
ThreadStart(thr, goid, 0);
|
ThreadStart(thr, goid, 0);
|
||||||
parent->in_rtl--;
|
|
||||||
thr->in_rtl--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __tsan_go_end(ThreadState *thr) {
|
void __tsan_go_end(ThreadState *thr) {
|
||||||
thr->in_rtl++;
|
|
||||||
ThreadFinish(thr);
|
ThreadFinish(thr);
|
||||||
thr->in_rtl--;
|
|
||||||
internal_free(thr);
|
internal_free(thr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __tsan_acquire(ThreadState *thr, void *addr) {
|
void __tsan_acquire(ThreadState *thr, void *addr) {
|
||||||
thr->in_rtl++;
|
|
||||||
Acquire(thr, 0, (uptr)addr);
|
Acquire(thr, 0, (uptr)addr);
|
||||||
thr->in_rtl--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __tsan_release(ThreadState *thr, void *addr) {
|
void __tsan_release(ThreadState *thr, void *addr) {
|
||||||
thr->in_rtl++;
|
|
||||||
ReleaseStore(thr, 0, (uptr)addr);
|
ReleaseStore(thr, 0, (uptr)addr);
|
||||||
thr->in_rtl--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __tsan_release_merge(ThreadState *thr, void *addr) {
|
void __tsan_release_merge(ThreadState *thr, void *addr) {
|
||||||
thr->in_rtl++;
|
|
||||||
Release(thr, 0, (uptr)addr);
|
Release(thr, 0, (uptr)addr);
|
||||||
thr->in_rtl--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __tsan_finalizer_goroutine(ThreadState *thr) {
|
void __tsan_finalizer_goroutine(ThreadState *thr) {
|
||||||
|
|
|
@ -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
|
|
@ -144,7 +144,6 @@ void InitializeLibIgnore() {
|
||||||
static SignalContext *SigCtx(ThreadState *thr) {
|
static SignalContext *SigCtx(ThreadState *thr) {
|
||||||
SignalContext *ctx = (SignalContext*)thr->signal_ctx;
|
SignalContext *ctx = (SignalContext*)thr->signal_ctx;
|
||||||
if (ctx == 0 && thr->is_alive) {
|
if (ctx == 0 && thr->is_alive) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext");
|
ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext");
|
||||||
MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
|
MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
|
||||||
thr->signal_ctx = ctx;
|
thr->signal_ctx = ctx;
|
||||||
|
@ -161,7 +160,6 @@ class ScopedInterceptor {
|
||||||
private:
|
private:
|
||||||
ThreadState *const thr_;
|
ThreadState *const thr_;
|
||||||
const uptr pc_;
|
const uptr pc_;
|
||||||
const int in_rtl_;
|
|
||||||
bool in_ignored_lib_;
|
bool in_ignored_lib_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,16 +167,12 @@ ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
|
||||||
uptr pc)
|
uptr pc)
|
||||||
: thr_(thr)
|
: thr_(thr)
|
||||||
, pc_(pc)
|
, pc_(pc)
|
||||||
, in_rtl_(thr->in_rtl)
|
|
||||||
, in_ignored_lib_(false) {
|
, in_ignored_lib_(false) {
|
||||||
if (thr_->in_rtl == 0) {
|
if (!thr_->ignore_interceptors) {
|
||||||
Initialize(thr);
|
Initialize(thr);
|
||||||
FuncEntry(thr, pc);
|
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)) {
|
if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) {
|
||||||
in_ignored_lib_ = true;
|
in_ignored_lib_ = true;
|
||||||
thr_->in_ignored_lib = true;
|
thr_->in_ignored_lib = true;
|
||||||
|
@ -191,12 +185,10 @@ ScopedInterceptor::~ScopedInterceptor() {
|
||||||
thr_->in_ignored_lib = false;
|
thr_->in_ignored_lib = false;
|
||||||
ThreadIgnoreEnd(thr_, pc_);
|
ThreadIgnoreEnd(thr_, pc_);
|
||||||
}
|
}
|
||||||
thr_->in_rtl--;
|
if (!thr_->ignore_interceptors) {
|
||||||
if (thr_->in_rtl == 0) {
|
|
||||||
FuncExit(thr_);
|
FuncExit(thr_);
|
||||||
ProcessPendingSignals(thr_);
|
ProcessPendingSignals(thr_);
|
||||||
}
|
}
|
||||||
CHECK_EQ(in_rtl_, thr_->in_rtl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SCOPED_INTERCEPTOR_RAW(func, ...) \
|
#define SCOPED_INTERCEPTOR_RAW(func, ...) \
|
||||||
|
@ -215,7 +207,7 @@ ScopedInterceptor::~ScopedInterceptor() {
|
||||||
Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
|
Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
|
||||||
Die(); \
|
Die(); \
|
||||||
} \
|
} \
|
||||||
if (thr->in_rtl > 1 || thr->in_ignored_lib) \
|
if (thr->ignore_interceptors || thr->in_ignored_lib) \
|
||||||
return REAL(func)(__VA_ARGS__); \
|
return REAL(func)(__VA_ARGS__); \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
|
@ -235,6 +227,13 @@ struct BlockingCall {
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalContext *ctx;
|
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) {
|
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) {
|
TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
|
||||||
SCOPED_INTERCEPTOR_RAW(dlopen, filename, 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);
|
void *res = REAL(dlopen)(filename, flag);
|
||||||
thr->in_rtl = 1;
|
|
||||||
libignore()->OnLibraryLoaded(filename);
|
libignore()->OnLibraryLoaded(filename);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
TSAN_INTERCEPTOR(int, dlclose, void *handle) {
|
TSAN_INTERCEPTOR(int, dlclose, void *handle) {
|
||||||
SCOPED_INTERCEPTOR_RAW(dlclose, 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);
|
int res = REAL(dlclose)(handle);
|
||||||
thr->in_rtl = 1;
|
|
||||||
libignore()->OnLibraryUnloaded();
|
libignore()->OnLibraryUnloaded();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -303,7 +294,6 @@ class AtExitContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
void exit(ThreadState *thr, uptr pc) {
|
void exit(ThreadState *thr, uptr pc) {
|
||||||
CHECK_EQ(thr->in_rtl, 0);
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
atexit_t f = 0;
|
atexit_t f = 0;
|
||||||
void *arg = 0;
|
void *arg = 0;
|
||||||
|
@ -315,14 +305,12 @@ class AtExitContext {
|
||||||
f = stack_[pos_];
|
f = stack_[pos_];
|
||||||
arg = args_[pos_];
|
arg = args_[pos_];
|
||||||
is_on_exit = is_on_exits_[pos_];
|
is_on_exit = is_on_exits_[pos_];
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Acquire(thr, pc, (uptr)this);
|
Acquire(thr, pc, (uptr)this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (f == 0)
|
if (f == 0)
|
||||||
break;
|
break;
|
||||||
DPrintf("#%d: executing atexit func %p\n", thr->tid, f);
|
DPrintf("#%d: executing atexit func %p\n", thr->tid, f);
|
||||||
CHECK_EQ(thr->in_rtl, 0);
|
|
||||||
if (is_on_exit)
|
if (is_on_exit)
|
||||||
((void(*)(int status, void *arg))f)(0, arg);
|
((void(*)(int status, void *arg))f)(0, arg);
|
||||||
else
|
else
|
||||||
|
@ -415,7 +403,6 @@ static void LongJmp(ThreadState *thr, uptr *env) {
|
||||||
|
|
||||||
// FIXME: put everything below into a common extern "C" block?
|
// FIXME: put everything below into a common extern "C" block?
|
||||||
extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) {
|
extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
SetJmp(cur_thread(), sp, mangled_sp);
|
SetJmp(cur_thread(), sp, mangled_sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,7 +816,6 @@ static void thread_finalize(void *v) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
ThreadFinish(thr);
|
ThreadFinish(thr);
|
||||||
SignalContext *sctx = thr->signal_ctx;
|
SignalContext *sctx = thr->signal_ctx;
|
||||||
|
@ -854,7 +840,8 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
|
||||||
int tid = 0;
|
int tid = 0;
|
||||||
{
|
{
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
ScopedInRtl in_rtl;
|
// Thread-local state is not initialized yet.
|
||||||
|
ScopedIgnoreInterceptors ignore;
|
||||||
if (pthread_setspecific(g_thread_finalize_key,
|
if (pthread_setspecific(g_thread_finalize_key,
|
||||||
(void *)kPthreadDestructorIterations)) {
|
(void *)kPthreadDestructorIterations)) {
|
||||||
Printf("ThreadSanitizer: failed to set thread key\n");
|
Printf("ThreadSanitizer: failed to set thread key\n");
|
||||||
|
@ -864,7 +851,6 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
|
||||||
pthread_yield();
|
pthread_yield();
|
||||||
atomic_store(&p->tid, 0, memory_order_release);
|
atomic_store(&p->tid, 0, memory_order_release);
|
||||||
ThreadStart(thr, tid, GetTid());
|
ThreadStart(thr, tid, GetTid());
|
||||||
CHECK_EQ(thr->in_rtl, 1);
|
|
||||||
}
|
}
|
||||||
void *res = callback(param);
|
void *res = callback(param);
|
||||||
// Prevent the callback from being tail called,
|
// Prevent the callback from being tail called,
|
||||||
|
@ -890,7 +876,12 @@ TSAN_INTERCEPTOR(int, pthread_create,
|
||||||
p.callback = callback;
|
p.callback = callback;
|
||||||
p.param = param;
|
p.param = param;
|
||||||
atomic_store(&p.tid, 0, memory_order_relaxed);
|
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) {
|
if (res == 0) {
|
||||||
int tid = ThreadCreate(thr, pc, *(uptr*)th, detached);
|
int tid = ThreadCreate(thr, pc, *(uptr*)th, detached);
|
||||||
CHECK_NE(tid, 0);
|
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)()) {
|
TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
|
||||||
SCOPED_INTERCEPTOR_RAW(pthread_once, o, 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)
|
if (o == 0 || f == 0)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o);
|
atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o);
|
||||||
u32 v = atomic_load(a, memory_order_acquire);
|
u32 v = atomic_load(a, memory_order_acquire);
|
||||||
if (v == 0 && atomic_compare_exchange_strong(a, &v, 1,
|
if (v == 0 && atomic_compare_exchange_strong(a, &v, 1,
|
||||||
memory_order_relaxed)) {
|
memory_order_relaxed)) {
|
||||||
const int old_in_rtl = thr->in_rtl;
|
|
||||||
thr->in_rtl = 0;
|
|
||||||
(*f)();
|
(*f)();
|
||||||
CHECK_EQ(thr->in_rtl, 0);
|
|
||||||
thr->in_rtl = old_in_rtl;
|
|
||||||
if (!thr->in_ignored_lib)
|
if (!thr->in_ignored_lib)
|
||||||
Release(thr, pc, (uptr)o);
|
Release(thr, pc, (uptr)o);
|
||||||
atomic_store(a, 2, memory_order_release);
|
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
|
// If we are in blocking function, we can safely process it now
|
||||||
// (but check if we are in a recursive interceptor,
|
// (but check if we are in a recursive interceptor,
|
||||||
// i.e. pthread_join()->munmap()).
|
// i.e. pthread_join()->munmap()).
|
||||||
(sctx && sctx->in_blocking_func == 1 && thr->in_rtl == 1)) {
|
(sctx && sctx->in_blocking_func == 1)) {
|
||||||
int in_rtl = thr->in_rtl;
|
|
||||||
thr->in_rtl = 0;
|
|
||||||
CHECK_EQ(thr->in_signal_handler, false);
|
CHECK_EQ(thr->in_signal_handler, false);
|
||||||
thr->in_signal_handler = true;
|
thr->in_signal_handler = true;
|
||||||
if (sigact)
|
if (sigact)
|
||||||
|
@ -1651,7 +1634,6 @@ void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
|
||||||
sigactions[sig].sa_handler(sig);
|
sigactions[sig].sa_handler(sig);
|
||||||
CHECK_EQ(thr->in_signal_handler, true);
|
CHECK_EQ(thr->in_signal_handler, true);
|
||||||
thr->in_signal_handler = false;
|
thr->in_signal_handler = false;
|
||||||
thr->in_rtl = in_rtl;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1771,11 +1753,7 @@ TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service,
|
||||||
// and can report false race between malloc and free
|
// and can report false race between malloc and free
|
||||||
// inside of getaddrinfo. So ignore memory accesses.
|
// inside of getaddrinfo. So ignore memory accesses.
|
||||||
ThreadIgnoreBegin(thr, pc);
|
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);
|
int res = REAL(getaddrinfo)(node, service, hints, rv);
|
||||||
thr->in_rtl++;
|
|
||||||
ThreadIgnoreEnd(thr, pc);
|
ThreadIgnoreEnd(thr, pc);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -1843,14 +1821,8 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "sanitizer_common/sanitizer_platform_interceptors.h"
|
#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())
|
// Causes interceptor recursion (getaddrinfo() and fopen())
|
||||||
#undef SANITIZER_INTERCEPT_GETADDRINFO
|
#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_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
|
||||||
#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \
|
#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \
|
||||||
|
@ -1924,15 +1896,11 @@ struct ScopedSyscall {
|
||||||
|
|
||||||
explicit ScopedSyscall(ThreadState *thr)
|
explicit ScopedSyscall(ThreadState *thr)
|
||||||
: thr(thr) {
|
: thr(thr) {
|
||||||
if (thr->in_rtl == 0)
|
Initialize(thr);
|
||||||
Initialize(thr);
|
|
||||||
thr->in_rtl++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~ScopedSyscall() {
|
~ScopedSyscall() {
|
||||||
thr->in_rtl--;
|
ProcessPendingSignals(thr);
|
||||||
if (thr->in_rtl == 0)
|
|
||||||
ProcessPendingSignals(thr);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2035,7 +2003,6 @@ static void finalize(void *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessPendingSignals(ThreadState *thr) {
|
void ProcessPendingSignals(ThreadState *thr) {
|
||||||
CHECK_EQ(thr->in_rtl, 0);
|
|
||||||
SignalContext *sctx = SigCtx(thr);
|
SignalContext *sctx = SigCtx(thr);
|
||||||
if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler)
|
if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler)
|
||||||
return;
|
return;
|
||||||
|
@ -2060,7 +2027,6 @@ void ProcessPendingSignals(ThreadState *thr) {
|
||||||
else
|
else
|
||||||
sigactions[sig].sa_handler(sig);
|
sigactions[sig].sa_handler(sig);
|
||||||
if (flags()->report_bugs && errno != 0) {
|
if (flags()->report_bugs && errno != 0) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
__tsan::StackTrace stack;
|
__tsan::StackTrace stack;
|
||||||
uptr pc = signal->sigaction ?
|
uptr pc = signal->sigaction ?
|
||||||
(uptr)sigactions[sig].sa_sigaction :
|
(uptr)sigactions[sig].sa_sigaction :
|
||||||
|
@ -2089,8 +2055,6 @@ static void unreachable() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeInterceptors() {
|
void InitializeInterceptors() {
|
||||||
CHECK_GT(cur_thread()->in_rtl, 0);
|
|
||||||
|
|
||||||
// We need to setup it early, because functions like dlsym() can call it.
|
// We need to setup it early, because functions like dlsym() can call it.
|
||||||
REAL(memset) = internal_memset;
|
REAL(memset) = internal_memset;
|
||||||
REAL(memcpy) = internal_memcpy;
|
REAL(memcpy) = internal_memcpy;
|
||||||
|
@ -2283,8 +2247,7 @@ void InitializeInterceptors() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void internal_start_thread(void(*func)(void *arg), void *arg) {
|
void internal_start_thread(void(*func)(void *arg), void *arg) {
|
||||||
// Start the thread with signals blocked, otherwise it can steal users
|
// Start the thread with signals blocked, otherwise it can steal user signals.
|
||||||
// signals.
|
|
||||||
__sanitizer_kernel_sigset_t set, old;
|
__sanitizer_kernel_sigset_t set, old;
|
||||||
internal_sigfillset(&set);
|
internal_sigfillset(&set);
|
||||||
internal_sigprocmask(SIG_SETMASK, &set, &old);
|
internal_sigprocmask(SIG_SETMASK, &set, &old);
|
||||||
|
|
|
@ -33,22 +33,16 @@ class ScopedAnnotation {
|
||||||
public:
|
public:
|
||||||
ScopedAnnotation(ThreadState *thr, const char *aname, const char *f, int l,
|
ScopedAnnotation(ThreadState *thr, const char *aname, const char *f, int l,
|
||||||
uptr pc)
|
uptr pc)
|
||||||
: thr_(thr)
|
: thr_(thr) {
|
||||||
, in_rtl_(thr->in_rtl) {
|
|
||||||
CHECK_EQ(thr_->in_rtl, 0);
|
|
||||||
FuncEntry(thr_, pc);
|
FuncEntry(thr_, pc);
|
||||||
thr_->in_rtl++;
|
|
||||||
DPrintf("#%d: annotation %s() %s:%d\n", thr_->tid, aname, f, l);
|
DPrintf("#%d: annotation %s() %s:%d\n", thr_->tid, aname, f, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ScopedAnnotation() {
|
~ScopedAnnotation() {
|
||||||
thr_->in_rtl--;
|
|
||||||
CHECK_EQ(in_rtl_, thr_->in_rtl);
|
|
||||||
FuncExit(thr_);
|
FuncExit(thr_);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
ThreadState *const thr_;
|
ThreadState *const thr_;
|
||||||
const int in_rtl_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SCOPED_ANNOTATION(typ) \
|
#define SCOPED_ANNOTATION(typ) \
|
||||||
|
|
|
@ -66,15 +66,11 @@ class ScopedAtomic {
|
||||||
ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a,
|
ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a,
|
||||||
morder mo, const char *func)
|
morder mo, const char *func)
|
||||||
: thr_(thr) {
|
: thr_(thr) {
|
||||||
CHECK_EQ(thr_->in_rtl, 0);
|
|
||||||
ProcessPendingSignals(thr);
|
ProcessPendingSignals(thr);
|
||||||
FuncEntry(thr_, pc);
|
FuncEntry(thr_, pc);
|
||||||
DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo);
|
DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo);
|
||||||
thr_->in_rtl++;
|
|
||||||
}
|
}
|
||||||
~ScopedAtomic() {
|
~ScopedAtomic() {
|
||||||
thr_->in_rtl--;
|
|
||||||
CHECK_EQ(thr_->in_rtl, 0);
|
|
||||||
FuncExit(thr_);
|
FuncExit(thr_);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -79,13 +79,9 @@ class ScopedJavaFunc {
|
||||||
: thr_(thr) {
|
: thr_(thr) {
|
||||||
Initialize(thr_);
|
Initialize(thr_);
|
||||||
FuncEntry(thr, pc);
|
FuncEntry(thr, pc);
|
||||||
CHECK_EQ(thr_->in_rtl, 0);
|
|
||||||
thr_->in_rtl++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~ScopedJavaFunc() {
|
~ScopedJavaFunc() {
|
||||||
thr_->in_rtl--;
|
|
||||||
CHECK_EQ(thr_->in_rtl, 0);
|
|
||||||
FuncExit(thr_);
|
FuncExit(thr_);
|
||||||
// FIXME(dvyukov): process pending signals.
|
// FIXME(dvyukov): process pending signals.
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,6 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
|
void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
if ((sz >= (1ull << 40)) || (align >= (1ull << 40)))
|
if ((sz >= (1ull << 40)) || (align >= (1ull << 40)))
|
||||||
return AllocatorReturnNull();
|
return AllocatorReturnNull();
|
||||||
void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
|
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) {
|
void user_free(ThreadState *thr, uptr pc, void *p) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
CHECK_NE(p, (void*)0);
|
CHECK_NE(p, (void*)0);
|
||||||
DPrintf("#%d: free(%p)\n", thr->tid, p);
|
DPrintf("#%d: free(%p)\n", thr->tid, p);
|
||||||
MBlock *b = (MBlock*)allocator()->GetMetaData(p);
|
MBlock *b = (MBlock*)allocator()->GetMetaData(p);
|
||||||
|
@ -138,7 +136,7 @@ void user_free(ThreadState *thr, uptr pc, void *p) {
|
||||||
}
|
}
|
||||||
b->ListReset();
|
b->ListReset();
|
||||||
}
|
}
|
||||||
if (CTX() && CTX()->initialized && thr->in_rtl == 1) {
|
if (CTX() && CTX()->initialized) {
|
||||||
if (thr->ignore_reads_and_writes == 0)
|
if (thr->ignore_reads_and_writes == 0)
|
||||||
MemoryRangeFreed(thr, pc, (uptr)p, b->Size());
|
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) {
|
void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
void *p2 = 0;
|
void *p2 = 0;
|
||||||
// FIXME: Handle "shrinking" more efficiently,
|
// FIXME: Handle "shrinking" more efficiently,
|
||||||
// it seems that some software actually does this.
|
// 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) {
|
uptr user_alloc_usable_size(ThreadState *thr, uptr pc, void *p) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
if (p == 0)
|
if (p == 0)
|
||||||
return 0;
|
return 0;
|
||||||
MBlock *b = (MBlock*)allocator()->GetMetaData(p);
|
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) {
|
void invoke_malloc_hook(void *ptr, uptr size) {
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
if (ctx == 0 || !ctx->initialized || thr->in_rtl)
|
if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
|
||||||
return;
|
return;
|
||||||
__tsan_malloc_hook(ptr, size);
|
__tsan_malloc_hook(ptr, size);
|
||||||
}
|
}
|
||||||
|
@ -194,14 +190,13 @@ void invoke_malloc_hook(void *ptr, uptr size) {
|
||||||
void invoke_free_hook(void *ptr) {
|
void invoke_free_hook(void *ptr) {
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
if (ctx == 0 || !ctx->initialized || thr->in_rtl)
|
if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
|
||||||
return;
|
return;
|
||||||
__tsan_free_hook(ptr);
|
__tsan_free_hook(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *internal_alloc(MBlockType typ, uptr sz) {
|
void *internal_alloc(MBlockType typ, uptr sz) {
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
CHECK_LE(sz, InternalSizeClassMap::kMaxSize);
|
CHECK_LE(sz, InternalSizeClassMap::kMaxSize);
|
||||||
if (thr->nomalloc) {
|
if (thr->nomalloc) {
|
||||||
thr->nomalloc = 0; // CHECK calls internal_malloc().
|
thr->nomalloc = 0; // CHECK calls internal_malloc().
|
||||||
|
@ -212,7 +207,6 @@ void *internal_alloc(MBlockType typ, uptr sz) {
|
||||||
|
|
||||||
void internal_free(void *p) {
|
void internal_free(void *p) {
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
if (thr->nomalloc) {
|
if (thr->nomalloc) {
|
||||||
thr->nomalloc = 0; // CHECK calls internal_malloc().
|
thr->nomalloc = 0; // CHECK calls internal_malloc().
|
||||||
CHECK(0);
|
CHECK(0);
|
||||||
|
|
|
@ -38,6 +38,10 @@ class MutexSet {
|
||||||
uptr Size() const;
|
uptr Size() const;
|
||||||
Desc Get(uptr i) const;
|
Desc Get(uptr i) const;
|
||||||
|
|
||||||
|
void operator=(const MutexSet &other) {
|
||||||
|
internal_memcpy(this, &other, sizeof(*this));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifndef TSAN_GO
|
#ifndef TSAN_GO
|
||||||
uptr size_;
|
uptr size_;
|
||||||
|
@ -45,6 +49,7 @@ class MutexSet {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void RemovePos(uptr i);
|
void RemovePos(uptr i);
|
||||||
|
MutexSet(const MutexSet&);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Go does not have mutexes, so do not spend memory and time.
|
// Go does not have mutexes, so do not spend memory and time.
|
||||||
|
|
|
@ -61,27 +61,6 @@ namespace __tsan {
|
||||||
|
|
||||||
const uptr kPageSize = 4096;
|
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,
|
void FillProfileCallback(uptr start, uptr rss, bool file,
|
||||||
uptr *mem, uptr stats_size) {
|
uptr *mem, uptr stats_size) {
|
||||||
CHECK_EQ(7, stats_size);
|
CHECK_EQ(7, stats_size);
|
||||||
|
@ -135,7 +114,6 @@ void FlushShadowMemory() {
|
||||||
|
|
||||||
#ifndef TSAN_GO
|
#ifndef TSAN_GO
|
||||||
static void ProtectRange(uptr beg, uptr end) {
|
static void ProtectRange(uptr beg, uptr end) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
CHECK_LE(beg, end);
|
CHECK_LE(beg, end);
|
||||||
if (beg == end)
|
if (beg == end)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -91,7 +91,7 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
|
||||||
// Do not touch these, rely on zero initialization,
|
// Do not touch these, rely on zero initialization,
|
||||||
// they may be accessed before the ctor.
|
// they may be accessed before the ctor.
|
||||||
// , ignore_reads_and_writes()
|
// , ignore_reads_and_writes()
|
||||||
// , in_rtl()
|
// , ignore_interceptors()
|
||||||
#ifndef TSAN_GO
|
#ifndef TSAN_GO
|
||||||
, jmp_bufs(MBlockJmpBuf)
|
, jmp_bufs(MBlockJmpBuf)
|
||||||
#endif
|
#endif
|
||||||
|
@ -116,8 +116,9 @@ static void MemoryProfiler(Context *ctx, fd_t fd, int i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BackgroundThread(void *arg) {
|
static void BackgroundThread(void *arg) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
|
// This is a non-initialized non-user thread, nothing to see here.
|
||||||
|
ScopedIgnoreInterceptors ignore;
|
||||||
const u64 kMs2Ns = 1000 * 1000;
|
const u64 kMs2Ns = 1000 * 1000;
|
||||||
|
|
||||||
fd_t mprof_fd = kInvalidFd;
|
fd_t mprof_fd = kInvalidFd;
|
||||||
|
@ -216,11 +217,12 @@ void Initialize(ThreadState *thr) {
|
||||||
if (is_initialized)
|
if (is_initialized)
|
||||||
return;
|
return;
|
||||||
is_initialized = true;
|
is_initialized = true;
|
||||||
|
// We are not ready to handle interceptors yet.
|
||||||
|
ScopedIgnoreInterceptors ignore;
|
||||||
SanitizerToolName = "ThreadSanitizer";
|
SanitizerToolName = "ThreadSanitizer";
|
||||||
// Install tool-specific callbacks in sanitizer_common.
|
// Install tool-specific callbacks in sanitizer_common.
|
||||||
SetCheckFailedCallback(TsanCheckFailed);
|
SetCheckFailedCallback(TsanCheckFailed);
|
||||||
|
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
#ifndef TSAN_GO
|
#ifndef TSAN_GO
|
||||||
InitializeAllocator();
|
InitializeAllocator();
|
||||||
#endif
|
#endif
|
||||||
|
@ -261,7 +263,6 @@ void Initialize(ThreadState *thr) {
|
||||||
int tid = ThreadCreate(thr, 0, 0, true);
|
int tid = ThreadCreate(thr, 0, 0, true);
|
||||||
CHECK_EQ(tid, 0);
|
CHECK_EQ(tid, 0);
|
||||||
ThreadStart(thr, tid, internal_getpid());
|
ThreadStart(thr, tid, internal_getpid());
|
||||||
CHECK_EQ(thr->in_rtl, 1);
|
|
||||||
ctx->initialized = true;
|
ctx->initialized = true;
|
||||||
|
|
||||||
if (flags()->stop_on_start) {
|
if (flags()->stop_on_start) {
|
||||||
|
@ -273,7 +274,6 @@ void Initialize(ThreadState *thr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int Finalize(ThreadState *thr) {
|
int Finalize(ThreadState *thr) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Context *ctx = __tsan::ctx;
|
Context *ctx = __tsan::ctx;
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
|
|
||||||
|
@ -340,7 +340,6 @@ u32 CurrentStackId(ThreadState *thr, uptr pc) {
|
||||||
|
|
||||||
void TraceSwitch(ThreadState *thr) {
|
void TraceSwitch(ThreadState *thr) {
|
||||||
thr->nomalloc++;
|
thr->nomalloc++;
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Trace *thr_trace = ThreadTrace(thr->tid);
|
Trace *thr_trace = ThreadTrace(thr->tid);
|
||||||
Lock l(&thr_trace->mtx);
|
Lock l(&thr_trace->mtx);
|
||||||
unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts();
|
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
|
ALWAYS_INLINE USED
|
||||||
void FuncEntry(ThreadState *thr, uptr pc) {
|
void FuncEntry(ThreadState *thr, uptr pc) {
|
||||||
DCHECK_EQ(thr->in_rtl, 0);
|
|
||||||
StatInc(thr, StatFuncEnter);
|
StatInc(thr, StatFuncEnter);
|
||||||
DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc);
|
DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc);
|
||||||
thr->fast_state.IncrementEpoch();
|
thr->fast_state.IncrementEpoch();
|
||||||
|
@ -687,7 +685,6 @@ void FuncEntry(ThreadState *thr, uptr pc) {
|
||||||
|
|
||||||
ALWAYS_INLINE USED
|
ALWAYS_INLINE USED
|
||||||
void FuncExit(ThreadState *thr) {
|
void FuncExit(ThreadState *thr) {
|
||||||
DCHECK_EQ(thr->in_rtl, 0);
|
|
||||||
StatInc(thr, StatFuncExit);
|
StatInc(thr, StatFuncExit);
|
||||||
DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid());
|
DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid());
|
||||||
thr->fast_state.IncrementEpoch();
|
thr->fast_state.IncrementEpoch();
|
||||||
|
|
|
@ -433,11 +433,11 @@ struct ThreadState {
|
||||||
AllocatorCache alloc_cache;
|
AllocatorCache alloc_cache;
|
||||||
InternalAllocatorCache internal_alloc_cache;
|
InternalAllocatorCache internal_alloc_cache;
|
||||||
Vector<JmpBuf> jmp_bufs;
|
Vector<JmpBuf> jmp_bufs;
|
||||||
|
int ignore_interceptors;
|
||||||
#endif
|
#endif
|
||||||
u64 stat[StatCnt];
|
u64 stat[StatCnt];
|
||||||
const int tid;
|
const int tid;
|
||||||
const int unique_id;
|
const int unique_id;
|
||||||
int in_rtl;
|
|
||||||
bool in_symbolizer;
|
bool in_symbolizer;
|
||||||
bool in_ignored_lib;
|
bool in_ignored_lib;
|
||||||
bool is_alive;
|
bool is_alive;
|
||||||
|
@ -551,14 +551,18 @@ struct Context {
|
||||||
u64 int_alloc_siz[MBlockTypeCount];
|
u64 int_alloc_siz[MBlockTypeCount];
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScopedInRtl {
|
struct ScopedIgnoreInterceptors {
|
||||||
public:
|
ScopedIgnoreInterceptors() {
|
||||||
ScopedInRtl();
|
#ifndef TSAN_GO
|
||||||
~ScopedInRtl();
|
cur_thread()->ignore_interceptors++;
|
||||||
private:
|
#endif
|
||||||
ThreadState*thr_;
|
}
|
||||||
int in_rtl_;
|
|
||||||
int errno_;
|
~ScopedIgnoreInterceptors() {
|
||||||
|
#ifndef TSAN_GO
|
||||||
|
cur_thread()->ignore_interceptors--;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScopedReport {
|
class ScopedReport {
|
||||||
|
@ -580,6 +584,9 @@ class ScopedReport {
|
||||||
private:
|
private:
|
||||||
Context *ctx_;
|
Context *ctx_;
|
||||||
ReportDesc *rep_;
|
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);
|
void AddMutex(u64 id);
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ namespace __tsan {
|
||||||
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
|
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
|
||||||
bool rw, bool recursive, bool linker_init) {
|
bool rw, bool recursive, bool linker_init) {
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr);
|
DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr);
|
||||||
StatInc(thr, StatMutexCreate);
|
StatInc(thr, StatMutexCreate);
|
||||||
if (!linker_init && IsAppMem(addr)) {
|
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) {
|
void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
|
DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
|
||||||
StatInc(thr, StatMutexDestroy);
|
StatInc(thr, StatMutexDestroy);
|
||||||
#ifndef TSAN_GO
|
#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) {
|
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);
|
DPrintf("#%d: MutexLock %zx rec=%d\n", thr->tid, addr, rec);
|
||||||
CHECK_GT(rec, 0);
|
CHECK_GT(rec, 0);
|
||||||
if (IsAppMem(addr))
|
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) {
|
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);
|
DPrintf("#%d: MutexUnlock %zx all=%d\n", thr->tid, addr, all);
|
||||||
if (IsAppMem(addr))
|
if (IsAppMem(addr))
|
||||||
MemoryReadAtomic(thr, pc, addr, kSizeLog1);
|
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) {
|
void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr);
|
DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr);
|
||||||
StatInc(thr, StatMutexReadLock);
|
StatInc(thr, StatMutexReadLock);
|
||||||
if (IsAppMem(addr))
|
if (IsAppMem(addr))
|
||||||
|
@ -169,7 +164,6 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MutexReadUnlock(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);
|
DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
|
||||||
StatInc(thr, StatMutexReadUnlock);
|
StatInc(thr, StatMutexReadUnlock);
|
||||||
if (IsAppMem(addr))
|
if (IsAppMem(addr))
|
||||||
|
@ -188,7 +182,6 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MutexReadOrWriteUnlock(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);
|
DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
|
||||||
if (IsAppMem(addr))
|
if (IsAppMem(addr))
|
||||||
MemoryReadAtomic(thr, pc, addr, kSizeLog1);
|
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) {
|
void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr);
|
DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr);
|
||||||
SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
|
SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
|
||||||
s->owner_tid = SyncVar::kInvalidTid;
|
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) {
|
void Acquire(ThreadState *thr, uptr pc, uptr addr) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
|
DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
|
||||||
if (thr->ignore_sync)
|
if (thr->ignore_sync)
|
||||||
return;
|
return;
|
||||||
|
@ -263,7 +254,6 @@ void AcquireGlobal(ThreadState *thr, uptr pc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Release(ThreadState *thr, uptr pc, uptr addr) {
|
void Release(ThreadState *thr, uptr pc, uptr addr) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
DPrintf("#%d: Release %zx\n", thr->tid, addr);
|
DPrintf("#%d: Release %zx\n", thr->tid, addr);
|
||||||
if (thr->ignore_sync)
|
if (thr->ignore_sync)
|
||||||
return;
|
return;
|
||||||
|
@ -276,7 +266,6 @@ void Release(ThreadState *thr, uptr pc, uptr addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReleaseStore(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);
|
DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
|
||||||
if (thr->ignore_sync)
|
if (thr->ignore_sync)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -34,7 +34,10 @@ static ReportStack *SymbolizeStack(const StackTrace& trace);
|
||||||
|
|
||||||
void TsanCheckFailed(const char *file, int line, const char *cond,
|
void TsanCheckFailed(const char *file, int line, const char *cond,
|
||||||
u64 v1, u64 v2) {
|
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: "
|
Printf("FATAL: ThreadSanitizer CHECK failed: "
|
||||||
"%s:%d \"%s\" (0x%zx, 0x%zx)\n",
|
"%s:%d \"%s\" (0x%zx, 0x%zx)\n",
|
||||||
file, line, cond, (uptr)v1, (uptr)v2);
|
file, line, cond, (uptr)v1, (uptr)v2);
|
||||||
|
@ -605,10 +608,12 @@ static bool RaceBetweenAtomicAndFree(ThreadState *thr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReportRace(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)
|
if (!flags()->report_bugs)
|
||||||
return;
|
return;
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
|
|
||||||
if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr))
|
if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,6 @@ static void ThreadCheckIgnore(ThreadState *thr) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ThreadFinalize(ThreadState *thr) {
|
void ThreadFinalize(ThreadState *thr) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
ThreadCheckIgnore(thr);
|
ThreadCheckIgnore(thr);
|
||||||
#ifndef TSAN_GO
|
#ifndef TSAN_GO
|
||||||
if (!flags()->report_thread_leaks)
|
if (!flags()->report_thread_leaks)
|
||||||
|
@ -208,7 +207,6 @@ void ThreadFinalize(ThreadState *thr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int ThreadCount(ThreadState *thr) {
|
int ThreadCount(ThreadState *thr) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
uptr result;
|
uptr result;
|
||||||
ctx->thread_registry->GetNumberOfThreads(0, 0, &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) {
|
int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
StatInc(thr, StatThreadCreate);
|
StatInc(thr, StatThreadCreate);
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
OnCreatedArgs args = { thr, pc };
|
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) {
|
void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
uptr stk_addr = 0;
|
uptr stk_addr = 0;
|
||||||
uptr stk_size = 0;
|
uptr stk_size = 0;
|
||||||
uptr tls_addr = 0;
|
uptr tls_addr = 0;
|
||||||
|
@ -264,7 +260,6 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadFinish(ThreadState *thr) {
|
void ThreadFinish(ThreadState *thr) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
ThreadCheckIgnore(thr);
|
ThreadCheckIgnore(thr);
|
||||||
StatInc(thr, StatThreadFinish);
|
StatInc(thr, StatThreadFinish);
|
||||||
if (thr->stk_addr && thr->stk_size)
|
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) {
|
int ThreadTid(ThreadState *thr, uptr pc, uptr uid) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid);
|
int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid);
|
||||||
DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res);
|
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) {
|
void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
CHECK_GT(tid, 0);
|
CHECK_GT(tid, 0);
|
||||||
CHECK_LT(tid, kMaxTid);
|
CHECK_LT(tid, kMaxTid);
|
||||||
DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid);
|
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) {
|
void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
CHECK_GT(tid, 0);
|
CHECK_GT(tid, 0);
|
||||||
CHECK_LT(tid, kMaxTid);
|
CHECK_LT(tid, kMaxTid);
|
||||||
Context *ctx = CTX();
|
Context *ctx = CTX();
|
||||||
|
@ -311,7 +303,6 @@ void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadSetName(ThreadState *thr, const char *name) {
|
void ThreadSetName(ThreadState *thr, const char *name) {
|
||||||
CHECK_GT(thr->in_rtl, 0);
|
|
||||||
CTX()->thread_registry->SetThreadName(thr->tid, name);
|
CTX()->thread_registry->SetThreadName(thr->tid, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,10 +58,18 @@ class Vector {
|
||||||
return begin_[i];
|
return begin_[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
T *PushBack(T v = T()) {
|
T *PushBack() {
|
||||||
EnsureSize(Size() + 1);
|
EnsureSize(Size() + 1);
|
||||||
end_[-1] = v;
|
T *p = &end_[-1];
|
||||||
return &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() {
|
void PopBack() {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
namespace __tsan {
|
namespace __tsan {
|
||||||
|
|
||||||
TEST(Clock, VectorBasic) {
|
TEST(Clock, VectorBasic) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ThreadClock clk;
|
ThreadClock clk;
|
||||||
CHECK_EQ(clk.size(), 0);
|
CHECK_EQ(clk.size(), 0);
|
||||||
clk.tick(0);
|
clk.tick(0);
|
||||||
|
@ -34,7 +33,6 @@ TEST(Clock, VectorBasic) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Clock, ChunkedBasic) {
|
TEST(Clock, ChunkedBasic) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ThreadClock vector;
|
ThreadClock vector;
|
||||||
SyncClock chunked;
|
SyncClock chunked;
|
||||||
CHECK_EQ(vector.size(), 0);
|
CHECK_EQ(vector.size(), 0);
|
||||||
|
@ -51,7 +49,6 @@ TEST(Clock, ChunkedBasic) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Clock, AcquireRelease) {
|
TEST(Clock, AcquireRelease) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ThreadClock vector1;
|
ThreadClock vector1;
|
||||||
vector1.tick(100);
|
vector1.tick(100);
|
||||||
SyncClock chunked;
|
SyncClock chunked;
|
||||||
|
@ -67,7 +64,6 @@ TEST(Clock, AcquireRelease) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Clock, ManyThreads) {
|
TEST(Clock, ManyThreads) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
SyncClock chunked;
|
SyncClock chunked;
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
ThreadClock vector;
|
ThreadClock vector;
|
||||||
|
@ -85,7 +81,6 @@ TEST(Clock, ManyThreads) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Clock, DifferentSizes) {
|
TEST(Clock, DifferentSizes) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
{
|
{
|
||||||
ThreadClock vector1;
|
ThreadClock vector1;
|
||||||
vector1.tick(10);
|
vector1.tick(10);
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
namespace __tsan {
|
namespace __tsan {
|
||||||
|
|
||||||
TEST(Flags, Basic) {
|
TEST(Flags, Basic) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
// At least should not crash.
|
// At least should not crash.
|
||||||
Flags f;
|
Flags f;
|
||||||
InitializeFlags(&f, 0);
|
InitializeFlags(&f, 0);
|
||||||
|
@ -26,7 +25,6 @@ TEST(Flags, Basic) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Flags, DefaultValues) {
|
TEST(Flags, DefaultValues) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Flags f;
|
Flags f;
|
||||||
|
|
||||||
f.enable_annotations = false;
|
f.enable_annotations = false;
|
||||||
|
@ -206,7 +204,6 @@ extern "C" const char *__tsan_default_options() {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Flags, ParseDefaultOptions) {
|
TEST(Flags, ParseDefaultOptions) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Flags f;
|
Flags f;
|
||||||
|
|
||||||
test_default_options = options1;
|
test_default_options = options1;
|
||||||
|
@ -219,7 +216,6 @@ TEST(Flags, ParseDefaultOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Flags, ParseEnvOptions) {
|
TEST(Flags, ParseEnvOptions) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Flags f;
|
Flags f;
|
||||||
|
|
||||||
InitializeFlags(&f, options1);
|
InitializeFlags(&f, options1);
|
||||||
|
@ -230,7 +226,6 @@ TEST(Flags, ParseEnvOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Flags, ParsePriority) {
|
TEST(Flags, ParsePriority) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Flags f;
|
Flags f;
|
||||||
|
|
||||||
test_default_options = options2;
|
test_default_options = options2;
|
||||||
|
|
|
@ -28,7 +28,6 @@ uptr __tsan_get_allocated_size(void *p);
|
||||||
namespace __tsan {
|
namespace __tsan {
|
||||||
|
|
||||||
TEST(Mman, Internal) {
|
TEST(Mman, Internal) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
char *p = (char*)internal_alloc(MBlockScopedBuf, 10);
|
char *p = (char*)internal_alloc(MBlockScopedBuf, 10);
|
||||||
EXPECT_NE(p, (char*)0);
|
EXPECT_NE(p, (char*)0);
|
||||||
char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20);
|
char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20);
|
||||||
|
@ -45,7 +44,6 @@ TEST(Mman, Internal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Mman, User) {
|
TEST(Mman, User) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
uptr pc = 0;
|
uptr pc = 0;
|
||||||
char *p = (char*)user_alloc(thr, pc, 10);
|
char *p = (char*)user_alloc(thr, pc, 10);
|
||||||
|
@ -72,7 +70,6 @@ TEST(Mman, User) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Mman, UserRealloc) {
|
TEST(Mman, UserRealloc) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
uptr pc = 0;
|
uptr pc = 0;
|
||||||
{
|
{
|
||||||
|
@ -118,7 +115,6 @@ TEST(Mman, UserRealloc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Mman, UsableSize) {
|
TEST(Mman, UsableSize) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
uptr pc = 0;
|
uptr pc = 0;
|
||||||
char *p = (char*)user_alloc(thr, pc, 10);
|
char *p = (char*)user_alloc(thr, pc, 10);
|
||||||
|
@ -131,7 +127,6 @@ TEST(Mman, UsableSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Mman, Stats) {
|
TEST(Mman, Stats) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
|
|
||||||
uptr alloc0 = __tsan_get_current_allocated_bytes();
|
uptr alloc0 = __tsan_get_current_allocated_bytes();
|
||||||
|
|
|
@ -46,13 +46,11 @@ static void TestStackTrace(StackTrace *trace) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StackTrace, Basic) {
|
TEST(StackTrace, Basic) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
StackTrace trace;
|
StackTrace trace;
|
||||||
TestStackTrace(&trace);
|
TestStackTrace(&trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StackTrace, StaticBasic) {
|
TEST(StackTrace, StaticBasic) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
uptr buf[10];
|
uptr buf[10];
|
||||||
StackTrace trace1(buf, 10);
|
StackTrace trace1(buf, 10);
|
||||||
TestStackTrace(&trace1);
|
TestStackTrace(&trace1);
|
||||||
|
@ -61,7 +59,6 @@ TEST(StackTrace, StaticBasic) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StackTrace, StaticTrim) {
|
TEST(StackTrace, StaticTrim) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
uptr buf[2];
|
uptr buf[2];
|
||||||
StackTrace trace(buf, 2);
|
StackTrace trace(buf, 2);
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ TEST(Sync, Table) {
|
||||||
const uintptr_t kIters = 512*1024;
|
const uintptr_t kIters = 512*1024;
|
||||||
const uintptr_t kRange = 10000;
|
const uintptr_t kRange = 10000;
|
||||||
|
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
ThreadState *thr = cur_thread();
|
ThreadState *thr = cur_thread();
|
||||||
uptr pc = 0;
|
uptr pc = 0;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
namespace __tsan {
|
namespace __tsan {
|
||||||
|
|
||||||
TEST(Vector, Basic) {
|
TEST(Vector, Basic) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Vector<int> v(MBlockScopedBuf);
|
Vector<int> v(MBlockScopedBuf);
|
||||||
EXPECT_EQ(v.Size(), (uptr)0);
|
EXPECT_EQ(v.Size(), (uptr)0);
|
||||||
v.PushBack(42);
|
v.PushBack(42);
|
||||||
|
@ -30,7 +29,6 @@ TEST(Vector, Basic) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Vector, Stride) {
|
TEST(Vector, Stride) {
|
||||||
ScopedInRtl in_rtl;
|
|
||||||
Vector<int> v(MBlockScopedBuf);
|
Vector<int> v(MBlockScopedBuf);
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
v.PushBack(i);
|
v.PushBack(i);
|
||||||
|
|
Loading…
Reference in New Issue