diff options
author | kcc <kcc@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-02-13 10:46:01 +0000 |
---|---|---|
committer | kcc <kcc@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-02-13 10:46:01 +0000 |
commit | 7da89e8076fc3bc8b0eb22721afdce421d225d71 (patch) | |
tree | c4504a71a4de65630ff00dd7aa8e062235fc5076 /libsanitizer/tsan | |
parent | c31c80df29f1ac080fd4b89965599e1b337b15a1 (diff) | |
download | gcc-7da89e8076fc3bc8b0eb22721afdce421d225d71.tar.gz |
libsanitizer merge from upstream r175049
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@196009 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libsanitizer/tsan')
30 files changed, 675 insertions, 190 deletions
diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h index 6683a4e1abb..b2937a428f3 100644 --- a/libsanitizer/tsan/tsan_defs.h +++ b/libsanitizer/tsan/tsan_defs.h @@ -26,16 +26,19 @@ namespace __tsan { const bool kGoMode = true; const bool kCppMode = false; const char *const kTsanOptionsEnv = "GORACE"; +// Go linker does not support weak symbols. +#define CPP_WEAK #else const bool kGoMode = false; const bool kCppMode = true; const char *const kTsanOptionsEnv = "TSAN_OPTIONS"; +#define CPP_WEAK WEAK #endif const int kTidBits = 13; const unsigned kMaxTid = 1 << kTidBits; const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit. -const int kClkBits = 43; +const int kClkBits = 42; #ifndef TSAN_GO const int kShadowStackSize = 4 * 1024; const int kTraceStackSize = 256; diff --git a/libsanitizer/tsan/tsan_fd.cc b/libsanitizer/tsan/tsan_fd.cc index f640c4f893e..a75d9bde08a 100644 --- a/libsanitizer/tsan/tsan_fd.cc +++ b/libsanitizer/tsan/tsan_fd.cc @@ -148,7 +148,7 @@ void FdAcquire(ThreadState *thr, uptr pc, int fd) { FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); - MemoryRead8Byte(thr, pc, (uptr)d); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); if (s) Acquire(thr, pc, (uptr)s); } @@ -159,20 +159,20 @@ void FdRelease(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); if (s) Release(thr, pc, (uptr)s); - MemoryRead8Byte(thr, pc, (uptr)d); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdAccess(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd); FdDesc *d = fddesc(thr, pc, fd); - MemoryRead8Byte(thr, pc, (uptr)d); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdClose(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); FdDesc *d = fddesc(thr, pc, fd); // To catch races between fd usage and close. - MemoryWrite8Byte(thr, pc, (uptr)d); + MemoryWrite(thr, pc, (uptr)d, kSizeLog8); // We need to clear it, because if we do not intercept any call out there // that creates fd, we will hit false postives. MemoryResetRange(thr, pc, (uptr)d, 8); @@ -191,7 +191,7 @@ void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); // Ignore the case when user dups not yet connected socket. FdDesc *od = fddesc(thr, pc, oldfd); - MemoryRead8Byte(thr, pc, (uptr)od); + MemoryRead(thr, pc, (uptr)od, kSizeLog8); FdClose(thr, pc, newfd); init(thr, pc, newfd, ref(od->sync)); } diff --git a/libsanitizer/tsan/tsan_flags.cc b/libsanitizer/tsan/tsan_flags.cc index 630bd75769b..ae748a13e15 100644 --- a/libsanitizer/tsan/tsan_flags.cc +++ b/libsanitizer/tsan/tsan_flags.cc @@ -43,6 +43,7 @@ void InitializeFlags(Flags *f, const char *env) { f->report_thread_leaks = true; f->report_destroy_locked = true; f->report_signal_unsafe = true; + f->report_atomic_races = true; f->force_seq_cst_atomics = false; f->strip_path_prefix = ""; f->suppressions = ""; @@ -70,6 +71,7 @@ void InitializeFlags(Flags *f, const char *env) { ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); + ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix"); ParseFlag(env, &f->suppressions, "suppressions"); diff --git a/libsanitizer/tsan/tsan_flags.h b/libsanitizer/tsan/tsan_flags.h index ed27363c2ff..480b41538f9 100644 --- a/libsanitizer/tsan/tsan_flags.h +++ b/libsanitizer/tsan/tsan_flags.h @@ -41,6 +41,8 @@ struct Flags { // Report violations of async signal-safety // (e.g. malloc() call from a signal handler). bool report_signal_unsafe; + // Report races between atomic and plain memory accesses. + bool report_atomic_races; // If set, all atomics are effectively sequentially consistent (seq_cst), // regardless of what user actually specified. bool force_seq_cst_atomics; @@ -84,6 +86,6 @@ struct Flags { Flags *flags(); void InitializeFlags(Flags *flags, const char *env); -} +} // namespace __tsan #endif // TSAN_FLAGS_H diff --git a/libsanitizer/tsan/tsan_interceptors.cc b/libsanitizer/tsan/tsan_interceptors.cc index d8f66de2327..8a54511b6e0 100644 --- a/libsanitizer/tsan/tsan_interceptors.cc +++ b/libsanitizer/tsan/tsan_interceptors.cc @@ -51,9 +51,12 @@ extern "C" int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset); extern "C" int sigfillset(sigset_t *set); extern "C" void *pthread_self(); extern "C" void _exit(int status); -extern "C" int __cxa_atexit(void (*func)(void *arg), void *arg, void *dso); extern "C" int *__errno_location(); extern "C" int fileno_unlocked(void *stream); +extern "C" void *__libc_malloc(uptr size); +extern "C" void *__libc_calloc(uptr size, uptr n); +extern "C" void *__libc_realloc(void *ptr, uptr size); +extern "C" void __libc_free(void *ptr); const int PTHREAD_MUTEX_RECURSIVE = 1; const int PTHREAD_MUTEX_RECURSIVE_NP = 1; const int kPthreadAttrSize = 56; @@ -122,7 +125,7 @@ struct SignalContext { int pending_signal_count; SignalDesc pending_signals[kSigCount]; }; -} +} // namespace __tsan static SignalContext *SigCtx(ThreadState *thr) { SignalContext *ctx = (SignalContext*)thr->signal_ctx; @@ -238,12 +241,15 @@ class AtExitContext { typedef void(*atexit_t)(); - int atexit(ThreadState *thr, uptr pc, atexit_t f) { + int atexit(ThreadState *thr, uptr pc, bool is_on_exit, + atexit_t f, void *arg) { Lock l(&mtx_); if (pos_ == kMaxAtExit) return 1; Release(thr, pc, (uptr)this); stack_[pos_] = f; + args_[pos_] = arg; + is_on_exits_[pos_] = is_on_exit; pos_++; return 0; } @@ -252,11 +258,15 @@ class AtExitContext { CHECK_EQ(thr->in_rtl, 0); for (;;) { atexit_t f = 0; + void *arg = 0; + bool is_on_exit = false; { Lock l(&mtx_); if (pos_) { pos_--; f = stack_[pos_]; + arg = args_[pos_]; + is_on_exit = is_on_exits_[pos_]; ScopedInRtl in_rtl; Acquire(thr, pc, (uptr)this); } @@ -265,7 +275,10 @@ class AtExitContext { break; DPrintf("#%d: executing atexit func %p\n", thr->tid, f); CHECK_EQ(thr->in_rtl, 0); - f(); + if (is_on_exit) + ((void(*)(int status, void *arg))f)(0, arg); + else + ((void(*)(void *arg, void *dso))f)(arg, 0); } } @@ -273,6 +286,8 @@ class AtExitContext { static const int kMaxAtExit = 128; Mutex mtx_; atexit_t stack_[kMaxAtExit]; + void *args_[kMaxAtExit]; + bool is_on_exits_[kMaxAtExit]; int pos_; }; @@ -282,18 +297,32 @@ static void finalize(void *arg) { ThreadState * thr = cur_thread(); uptr pc = 0; atexit_ctx->exit(thr, pc); - { - ScopedInRtl in_rtl; - DestroyAndFree(atexit_ctx); - } int status = Finalize(cur_thread()); if (status) _exit(status); } TSAN_INTERCEPTOR(int, atexit, void (*f)()) { + if (cur_thread()->in_symbolizer) + return 0; SCOPED_TSAN_INTERCEPTOR(atexit, f); - return atexit_ctx->atexit(thr, pc, f); + return atexit_ctx->atexit(thr, pc, false, (void(*)())f, 0); +} + +TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) { + if (cur_thread()->in_symbolizer) + return 0; + SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg); + return atexit_ctx->atexit(thr, pc, true, (void(*)())f, arg); +} + +TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { + if (cur_thread()->in_symbolizer) + return 0; + SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso); + if (dso) + return REAL(__cxa_atexit)(f, arg, dso); + return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg); } TSAN_INTERCEPTOR(void, longjmp, void *env, int val) { @@ -309,6 +338,8 @@ TSAN_INTERCEPTOR(void, siglongjmp, void *env, int val) { } TSAN_INTERCEPTOR(void*, malloc, uptr size) { + if (cur_thread()->in_symbolizer) + return __libc_malloc(size); void *p = 0; { SCOPED_INTERCEPTOR_RAW(malloc, size); @@ -324,6 +355,9 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { } TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { + if (cur_thread()->in_symbolizer) + return __libc_calloc(size, n); + if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) return 0; void *p = 0; { SCOPED_INTERCEPTOR_RAW(calloc, size, n); @@ -335,6 +369,8 @@ TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { } TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { + if (cur_thread()->in_symbolizer) + return __libc_realloc(p, size); if (p) invoke_free_hook(p); { @@ -348,6 +384,8 @@ TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { TSAN_INTERCEPTOR(void, free, void *p) { if (p == 0) return; + if (cur_thread()->in_symbolizer) + return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(free, p); user_free(thr, pc, p); @@ -356,12 +394,16 @@ TSAN_INTERCEPTOR(void, free, void *p) { TSAN_INTERCEPTOR(void, cfree, void *p) { if (p == 0) return; + if (cur_thread()->in_symbolizer) + return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(cfree, p); user_free(thr, pc, p); } #define OPERATOR_NEW_BODY(mangled_name) \ + if (cur_thread()->in_symbolizer) \ + return __libc_malloc(size); \ void *p = 0; \ { \ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ @@ -385,6 +427,8 @@ void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { #define OPERATOR_DELETE_BODY(mangled_name) \ if (ptr == 0) return; \ + if (cur_thread()->in_symbolizer) \ + return __libc_free(ptr); \ invoke_free_hook(ptr); \ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ user_free(thr, pc, ptr); @@ -549,6 +593,8 @@ TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot, return MAP_FAILED; void *res = REAL(mmap)(addr, sz, prot, flags, fd, off); if (res != MAP_FAILED) { + if (fd > 0) + FdAccess(thr, pc, fd); MemoryResetRange(thr, pc, (uptr)res, sz); } return res; @@ -561,6 +607,8 @@ TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot, return MAP_FAILED; void *res = REAL(mmap64)(addr, sz, prot, flags, fd, off); if (res != MAP_FAILED) { + if (fd > 0) + FdAccess(thr, pc, fd); MemoryResetRange(thr, pc, (uptr)res, sz); } return res; @@ -958,14 +1006,14 @@ TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count); - MemoryWrite1Byte(thr, pc, (uptr)b); + MemoryWrite(thr, pc, (uptr)b, kSizeLog1); int res = REAL(pthread_barrier_init)(b, a, count); return res; } TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_destroy, b); - MemoryWrite1Byte(thr, pc, (uptr)b); + MemoryWrite(thr, pc, (uptr)b, kSizeLog1); int res = REAL(pthread_barrier_destroy)(b); return res; } @@ -973,9 +1021,9 @@ TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) { TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_wait, b); Release(thr, pc, (uptr)b); - MemoryRead1Byte(thr, pc, (uptr)b); + MemoryRead(thr, pc, (uptr)b, kSizeLog1); int res = REAL(pthread_barrier_wait)(b); - MemoryRead1Byte(thr, pc, (uptr)b); + MemoryRead(thr, pc, (uptr)b, kSizeLog1); if (res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD) { Acquire(thr, pc, (uptr)b); } @@ -1062,6 +1110,74 @@ TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) { return res; } +TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf); + return REAL(__xstat)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf); + return REAL(__xstat)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf); + return REAL(__xstat64)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf); + return REAL(__xstat64)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf); + return REAL(__lxstat)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf); + return REAL(__lxstat)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf); + return REAL(__lxstat64)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf); + return REAL(__lxstat64)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat)(version, fd, buf); +} + +TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat)(0, fd, buf); +} + +TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat64)(version, fd, buf); +} + +TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat64)(0, fd, buf); +} + TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) { SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode); int fd = REAL(open)(name, flags, mode); @@ -1177,6 +1293,22 @@ TSAN_INTERCEPTOR(int, connect, int fd, void *addr, unsigned addrlen) { return res; } +TSAN_INTERCEPTOR(int, bind, int fd, void *addr, unsigned addrlen) { + SCOPED_TSAN_INTERCEPTOR(bind, fd, addr, addrlen); + int res = REAL(bind)(fd, addr, addrlen); + if (fd > 0 && res == 0) + FdAccess(thr, pc, fd); + return res; +} + +TSAN_INTERCEPTOR(int, listen, int fd, int backlog) { + SCOPED_TSAN_INTERCEPTOR(listen, fd, backlog); + int res = REAL(listen)(fd, backlog); + if (fd > 0 && res == 0) + FdAccess(thr, pc, fd); + return res; +} + TSAN_INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { SCOPED_TSAN_INTERCEPTOR(accept, fd, addr, addrlen); int fd2 = REAL(accept)(fd, addr, addrlen); @@ -1223,6 +1355,18 @@ TSAN_INTERCEPTOR(int, __close, int fd) { return REAL(__close)(fd); } +// glibc guts +TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) { + SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr); + int fds[64]; + int cnt = ExtractResolvFDs(state, fds, ARRAY_SIZE(fds)); + for (int i = 0; i < cnt; i++) { + if (fds[i] > 0) + FdClose(thr, pc, fds[i]); + } + REAL(__res_iclose)(state, free_addr); +} + TSAN_INTERCEPTOR(int, pipe, int *pipefd) { SCOPED_TSAN_INTERCEPTOR(pipe, pipefd); int res = REAL(pipe)(pipefd); @@ -1765,6 +1909,18 @@ void InitializeInterceptors() { TSAN_INTERCEPT(sem_post); TSAN_INTERCEPT(sem_getvalue); + TSAN_INTERCEPT(stat); + TSAN_INTERCEPT(__xstat); + TSAN_INTERCEPT(stat64); + TSAN_INTERCEPT(__xstat64); + TSAN_INTERCEPT(lstat); + TSAN_INTERCEPT(__lxstat); + TSAN_INTERCEPT(lstat64); + TSAN_INTERCEPT(__lxstat64); + TSAN_INTERCEPT(fstat); + TSAN_INTERCEPT(__fxstat); + TSAN_INTERCEPT(fstat64); + TSAN_INTERCEPT(__fxstat64); TSAN_INTERCEPT(open); TSAN_INTERCEPT(open64); TSAN_INTERCEPT(creat); @@ -1779,11 +1935,15 @@ void InitializeInterceptors() { TSAN_INTERCEPT(socket); TSAN_INTERCEPT(socketpair); TSAN_INTERCEPT(connect); + TSAN_INTERCEPT(bind); + TSAN_INTERCEPT(listen); TSAN_INTERCEPT(accept); TSAN_INTERCEPT(accept4); TSAN_INTERCEPT(epoll_create); TSAN_INTERCEPT(epoll_create1); TSAN_INTERCEPT(close); + TSAN_INTERCEPT(__close); + TSAN_INTERCEPT(__res_iclose); TSAN_INTERCEPT(pipe); TSAN_INTERCEPT(pipe2); @@ -1826,6 +1986,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(munlockall); TSAN_INTERCEPT(fork); + TSAN_INTERCEPT(on_exit); + TSAN_INTERCEPT(__cxa_atexit); // Need to setup it, because interceptors check that the function is resolved. // But atexit is emitted directly into the module, so can't be resolved. @@ -1833,7 +1995,7 @@ void InitializeInterceptors() { atexit_ctx = new(internal_alloc(MBlockAtExit, sizeof(AtExitContext))) AtExitContext(); - if (__cxa_atexit(&finalize, 0, 0)) { + if (REAL(__cxa_atexit)(&finalize, 0, 0)) { Printf("ThreadSanitizer: failed to setup atexit callback\n"); Die(); } diff --git a/libsanitizer/tsan/tsan_interface.cc b/libsanitizer/tsan/tsan_interface.cc index 08a51651114..992e3834aae 100644 --- a/libsanitizer/tsan/tsan_interface.cc +++ b/libsanitizer/tsan/tsan_interface.cc @@ -22,13 +22,13 @@ void __tsan_init() { } void __tsan_read16(void *addr) { - MemoryRead8Byte(cur_thread(), CALLERPC, (uptr)addr); - MemoryRead8Byte(cur_thread(), CALLERPC, (uptr)addr + 8); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } void __tsan_write16(void *addr) { - MemoryWrite8Byte(cur_thread(), CALLERPC, (uptr)addr); - MemoryWrite8Byte(cur_thread(), CALLERPC, (uptr)addr + 8); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } void __tsan_acquire(void *addr) { diff --git a/libsanitizer/tsan/tsan_interface.h b/libsanitizer/tsan/tsan_interface.h index 8f265ca27b5..2cfd7684183 100644 --- a/libsanitizer/tsan/tsan_interface.h +++ b/libsanitizer/tsan/tsan_interface.h @@ -14,7 +14,7 @@ #ifndef TSAN_INTERFACE_H #define TSAN_INTERFACE_H -#include <sanitizer/common_interface_defs.h> +#include <sanitizer_common/sanitizer_internal_defs.h> // This header should NOT include any other headers. // All functions in this header are extern "C" and start with __tsan_. diff --git a/libsanitizer/tsan/tsan_interface_ann.h b/libsanitizer/tsan/tsan_interface_ann.h index b10264cdd6f..b6500329428 100644 --- a/libsanitizer/tsan/tsan_interface_ann.h +++ b/libsanitizer/tsan/tsan_interface_ann.h @@ -12,7 +12,7 @@ #ifndef TSAN_INTERFACE_ANN_H #define TSAN_INTERFACE_ANN_H -#include <sanitizer/common_interface_defs.h> +#include <sanitizer_common/sanitizer_internal_defs.h> // This header should NOT include any other headers. // All functions in this header are extern "C" and start with __tsan_. diff --git a/libsanitizer/tsan/tsan_interface_atomic.cc b/libsanitizer/tsan/tsan_interface_atomic.cc index 770f8bd1014..2c8b2ab049c 100644 --- a/libsanitizer/tsan/tsan_interface_atomic.cc +++ b/libsanitizer/tsan/tsan_interface_atomic.cc @@ -18,25 +18,42 @@ // http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/ #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" #include "tsan_interface_atomic.h" #include "tsan_flags.h" #include "tsan_rtl.h" using namespace __tsan; // NOLINT +#define SCOPED_ATOMIC(func, ...) \ + const uptr callpc = (uptr)__builtin_return_address(0); \ + uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ + pc = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); \ + mo = ConvertOrder(mo); \ + mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \ + ThreadState *const thr = cur_thread(); \ + AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \ + ScopedAtomic sa(thr, callpc, __FUNCTION__); \ + return Atomic##func(thr, pc, __VA_ARGS__); \ +/**/ + class ScopedAtomic { public: ScopedAtomic(ThreadState *thr, uptr pc, const char *func) : thr_(thr) { - CHECK_EQ(thr_->in_rtl, 1); // 1 due to our own ScopedInRtl member. + CHECK_EQ(thr_->in_rtl, 0); + ProcessPendingSignals(thr); + FuncEntry(thr_, pc); DPrintf("#%d: %s\n", thr_->tid, func); + thr_->in_rtl++; } ~ScopedAtomic() { - CHECK_EQ(thr_->in_rtl, 1); + thr_->in_rtl--; + CHECK_EQ(thr_->in_rtl, 0); + FuncExit(thr_); } private: ThreadState *thr_; - ScopedInRtl in_rtl_; }; // Some shortcuts. @@ -210,16 +227,19 @@ a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) { } #endif -#define SCOPED_ATOMIC(func, ...) \ - mo = ConvertOrder(mo); \ - mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \ - ThreadState *const thr = cur_thread(); \ - ProcessPendingSignals(thr); \ - const uptr pc = (uptr)__builtin_return_address(0); \ - AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \ - ScopedAtomic sa(thr, pc, __FUNCTION__); \ - return Atomic##func(thr, pc, __VA_ARGS__); \ -/**/ +template<typename T> +static int SizeLog() { + if (sizeof(T) <= 1) + return kSizeLog1; + else if (sizeof(T) <= 2) + return kSizeLog2; + else if (sizeof(T) <= 4) + return kSizeLog4; + else + return kSizeLog8; + // For 16-byte atomics we also use 8-byte memory access, + // this leads to false negatives only in very obscure cases. +} template<typename T> static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, @@ -227,14 +247,17 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, CHECK(IsLoadOrder(mo)); // This fast-path is critical for performance. // Assume the access is atomic. - if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) + if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) { + MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>()); return *a; + } SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false); thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->clock.acquire(&s->clock); T v = *a; s->mtx.ReadUnlock(); __sync_synchronize(); + MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>()); return v; } @@ -242,6 +265,7 @@ template<typename T> static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { CHECK(IsStoreOrder(mo)); + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>()); // This fast-path is critical for performance. // Assume the access is atomic. // Strictly saying even relaxed store cuts off release sequence, @@ -263,6 +287,7 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, template<typename T, T (*F)(volatile T *v, T op)> static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>()); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); thr->clock.set(thr->tid, thr->fast_state.epoch()); if (IsAcqRelOrder(mo)) @@ -322,6 +347,7 @@ template<typename T> static bool AtomicCAS(ThreadState *thr, uptr pc, volatile T *a, T *c, T v, morder mo, morder fmo) { (void)fmo; // Unused because llvm does not pass it yet. + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>()); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); thr->clock.set(thr->tid, thr->fast_state.epoch()); if (IsAcqRelOrder(mo)) diff --git a/libsanitizer/tsan/tsan_interface_inl.h b/libsanitizer/tsan/tsan_interface_inl.h index 133348a942c..92796d1178f 100644 --- a/libsanitizer/tsan/tsan_interface_inl.h +++ b/libsanitizer/tsan/tsan_interface_inl.h @@ -17,41 +17,41 @@ using namespace __tsan; // NOLINT void __tsan_read1(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 0, 0); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog1); } void __tsan_read2(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 1, 0); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog2); } void __tsan_read4(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, 0); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog4); } void __tsan_read8(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 3, 0); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); } void __tsan_write1(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 0, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog1); } void __tsan_write2(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 1, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog2); } void __tsan_write4(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog4); } void __tsan_write8(void *addr) { - MemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 3, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); } void __tsan_vptr_update(void **vptr_p, void *new_val) { CHECK_EQ(sizeof(vptr_p), 8); if (*vptr_p != new_val) - MemoryAccess(cur_thread(), CALLERPC, (uptr)vptr_p, 3, 1); + MemoryWrite(cur_thread(), CALLERPC, (uptr)vptr_p, kSizeLog8); } void __tsan_func_entry(void *pc) { diff --git a/libsanitizer/tsan/tsan_interface_java.cc b/libsanitizer/tsan/tsan_interface_java.cc index d7325dcb2c4..f8c0b4eb635 100644 --- a/libsanitizer/tsan/tsan_interface_java.cc +++ b/libsanitizer/tsan/tsan_interface_java.cc @@ -150,7 +150,7 @@ SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) { return 0; } -} // namespace __tsan { +} // namespace __tsan #define SCOPED_JAVA_FUNC(func) \ ThreadState *thr = cur_thread(); \ diff --git a/libsanitizer/tsan/tsan_md5.cc b/libsanitizer/tsan/tsan_md5.cc index 6df823dae98..883239c2e71 100644 --- a/libsanitizer/tsan/tsan_md5.cc +++ b/libsanitizer/tsan/tsan_md5.cc @@ -240,4 +240,4 @@ MD5Hash md5_hash(const void *data, uptr size) { MD5_Final((unsigned char*)&res.hash[0], &ctx); return res; } -} +} // namespace __tsan diff --git a/libsanitizer/tsan/tsan_mman.cc b/libsanitizer/tsan/tsan_mman.cc index f4fafaf77f0..23c73c50cc1 100644 --- a/libsanitizer/tsan/tsan_mman.cc +++ b/libsanitizer/tsan/tsan_mman.cc @@ -36,8 +36,16 @@ void InitializeAllocator() { allocator()->Init(); } -void AlloctorThreadFinish(ThreadState *thr) { - allocator()->SwallowCache(&thr->alloc_cache); +void AllocatorThreadStart(ThreadState *thr) { + allocator()->InitCache(&thr->alloc_cache); +} + +void AllocatorThreadFinish(ThreadState *thr) { + allocator()->DestroyCache(&thr->alloc_cache); +} + +void AllocatorPrintStats() { + allocator()->PrintStats(); } static void SignalUnsafeCall(ThreadState *thr, uptr pc) { @@ -162,3 +170,49 @@ void internal_free(void *p) { } } // namespace __tsan + +using namespace __tsan; + +extern "C" { +uptr __tsan_get_current_allocated_bytes() { + u64 stats[AllocatorStatCount]; + allocator()->GetStats(stats); + u64 m = stats[AllocatorStatMalloced]; + u64 f = stats[AllocatorStatFreed]; + return m >= f ? m - f : 1; +} + +uptr __tsan_get_heap_size() { + u64 stats[AllocatorStatCount]; + allocator()->GetStats(stats); + u64 m = stats[AllocatorStatMmapped]; + u64 f = stats[AllocatorStatUnmapped]; + return m >= f ? m - f : 1; +} + +uptr __tsan_get_free_bytes() { + return 1; +} + +uptr __tsan_get_unmapped_bytes() { + return 1; +} + +uptr __tsan_get_estimated_allocated_size(uptr size) { + return size; +} + +bool __tsan_get_ownership(void *p) { + return allocator()->GetBlockBegin(p) != 0; +} + +uptr __tsan_get_allocated_size(void *p) { + if (p == 0) + return 0; + p = allocator()->GetBlockBegin(p); + if (p == 0) + return 0; + MBlock *b = (MBlock*)allocator()->GetMetaData(p); + return b->size; +} +} // extern "C" diff --git a/libsanitizer/tsan/tsan_mman.h b/libsanitizer/tsan/tsan_mman.h index 8697d228730..7a657e124bf 100644 --- a/libsanitizer/tsan/tsan_mman.h +++ b/libsanitizer/tsan/tsan_mman.h @@ -18,7 +18,9 @@ namespace __tsan { const uptr kDefaultAlignment = 16; void InitializeAllocator(); -void AlloctorThreadFinish(ThreadState *thr); +void AllocatorThreadStart(ThreadState *thr); +void AllocatorThreadFinish(ThreadState *thr); +void AllocatorPrintStats(); // For user allocations. void *user_alloc(ThreadState *thr, uptr pc, uptr sz, diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h index 9fdc4dd46e7..78c1a7d8195 100644 --- a/libsanitizer/tsan/tsan_platform.h +++ b/libsanitizer/tsan/tsan_platform.h @@ -149,6 +149,7 @@ bool IsGlobalVar(uptr addr); uptr GetTlsSize(); void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size); +int ExtractResolvFDs(void *state, int *fds, int nfd); } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_platform_linux.cc b/libsanitizer/tsan/tsan_platform_linux.cc index 2e7cd5138d6..def91559d5e 100644 --- a/libsanitizer/tsan/tsan_platform_linux.cc +++ b/libsanitizer/tsan/tsan_platform_linux.cc @@ -38,6 +38,8 @@ #include <errno.h> #include <sched.h> #include <dlfcn.h> +#define __need_res_state +#include <resolv.h> extern "C" int arch_prctl(int code, __sanitizer::uptr *addr); @@ -287,6 +289,19 @@ bool IsGlobalVar(uptr addr) { return g_data_start && addr >= g_data_start && addr < g_data_end; } +#ifndef TSAN_GO +int ExtractResolvFDs(void *state, int *fds, int nfd) { + int cnt = 0; + __res_state *statp = (__res_state*)state; + for (int i = 0; i < MAXNS && cnt < nfd; i++) { + if (statp->_u._ext.nsaddrs[i] && statp->_u._ext.nssocks[i] != -1) + fds[cnt++] = statp->_u._ext.nssocks[i]; + } + return cnt; +} +#endif + + } // namespace __tsan #endif // #ifdef __linux__ diff --git a/libsanitizer/tsan/tsan_report.cc b/libsanitizer/tsan/tsan_report.cc index f99fd2ea105..098d8262ba1 100644 --- a/libsanitizer/tsan/tsan_report.cc +++ b/libsanitizer/tsan/tsan_report.cc @@ -41,23 +41,20 @@ const char *thread_name(char *buf, int tid) { return buf; } -static void PrintHeader(ReportType typ) { - Printf("WARNING: ThreadSanitizer: "); - +static const char *ReportTypeString(ReportType typ) { if (typ == ReportTypeRace) - Printf("data race"); - else if (typ == ReportTypeUseAfterFree) - Printf("heap-use-after-free"); - else if (typ == ReportTypeThreadLeak) - Printf("thread leak"); - else if (typ == ReportTypeMutexDestroyLocked) - Printf("destroy of a locked mutex"); - else if (typ == ReportTypeSignalUnsafe) - Printf("signal-unsafe call inside of a signal"); - else if (typ == ReportTypeErrnoInSignal) - Printf("signal handler spoils errno"); - - Printf(" (pid=%d)\n", GetPid()); + return "data race"; + if (typ == ReportTypeUseAfterFree) + return "heap-use-after-free"; + if (typ == ReportTypeThreadLeak) + return "thread leak"; + if (typ == ReportTypeMutexDestroyLocked) + return "destroy of a locked mutex"; + if (typ == ReportTypeSignalUnsafe) + return "signal-unsafe call inside of a signal"; + if (typ == ReportTypeErrnoInSignal) + return "signal handler spoils errno"; + return ""; } void PrintStack(const ReportStack *ent) { @@ -87,11 +84,17 @@ static void PrintMutexSet(Vector<ReportMopMutex> const& mset) { } } +static const char *MopDesc(bool first, bool write, bool atomic) { + return atomic ? (first ? (write ? "Atomic write" : "Atomic read") + : (write ? "Previous atomic write" : "Previous atomic read")) + : (first ? (write ? "Write" : "Read") + : (write ? "Previous write" : "Previous read")); +} + static void PrintMop(const ReportMop *mop, bool first) { char thrbuf[kThreadBufSize]; Printf(" %s of size %d at %p by %s", - (first ? (mop->write ? "Write" : "Read") - : (mop->write ? "Previous write" : "Previous read")), + MopDesc(first, mop->write, mop->atomic), mop->size, (void*)mop->addr, thread_name(thrbuf, mop->tid)); PrintMutexSet(mop->mset); @@ -150,9 +153,28 @@ static void PrintSleep(const ReportStack *s) { PrintStack(s); } +static ReportStack *ChooseSummaryStack(const ReportDesc *rep) { + if (rep->mops.Size()) + return rep->mops[0]->stack; + if (rep->stacks.Size()) + return rep->stacks[0]; + if (rep->mutexes.Size()) + return rep->mutexes[0]->stack; + if (rep->threads.Size()) + return rep->threads[0]->stack; + return 0; +} + +ReportStack *SkipTsanInternalFrames(ReportStack *ent) { + while (FrameIsInternal(ent) && ent->next) + ent = ent->next; + return ent; +} + void PrintReport(const ReportDesc *rep) { Printf("==================\n"); - PrintHeader(rep->typ); + const char *rep_typ_str = ReportTypeString(rep->typ); + Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, GetPid()); for (uptr i = 0; i < rep->stacks.Size(); i++) { if (i) @@ -175,6 +197,9 @@ void PrintReport(const ReportDesc *rep) { for (uptr i = 0; i < rep->threads.Size(); i++) PrintThread(rep->threads[i]); + if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep))) + ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func); + Printf("==================\n"); } diff --git a/libsanitizer/tsan/tsan_report.h b/libsanitizer/tsan/tsan_report.h index 42f52af9e37..eae2b3c721f 100644 --- a/libsanitizer/tsan/tsan_report.h +++ b/libsanitizer/tsan/tsan_report.h @@ -46,6 +46,7 @@ struct ReportMop { uptr addr; int size; bool write; + bool atomic; Vector<ReportMopMutex> mset; ReportStack *stack; diff --git a/libsanitizer/tsan/tsan_rtl.cc b/libsanitizer/tsan/tsan_rtl.cc index 3615a7a9c2f..673a355f1dc 100644 --- a/libsanitizer/tsan/tsan_rtl.cc +++ b/libsanitizer/tsan/tsan_rtl.cc @@ -35,6 +35,11 @@ THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); #endif static char ctx_placeholder[sizeof(Context)] ALIGNED(64); +// Can be overriden by a front-end. +bool CPP_WEAK OnFinalize(bool failed) { + return failed; +} + static Context *ctx; Context *CTX() { return ctx; @@ -136,7 +141,7 @@ static void InitializeMemoryProfile() { InternalScopedBuffer<char> filename(4096); internal_snprintf(filename.data(), filename.size(), "%s.%d", flags()->profile_memory, GetPid()); - fd_t fd = internal_open(filename.data(), true); + fd_t fd = OpenFile(filename.data(), true); if (fd == kInvalidFd) { Printf("Failed to open memory profile file '%s'\n", &filename[0]); Die(); @@ -180,6 +185,7 @@ void Initialize(ThreadState *thr) { if (is_initialized) return; is_initialized = true; + SanitizerToolName = "ThreadSanitizer"; // Install tool-specific callbacks in sanitizer_common. SetCheckFailedCallback(TsanCheckFailed); @@ -237,7 +243,7 @@ void Initialize(ThreadState *thr) { Printf("ThreadSanitizer is suspended at startup (pid %d)." " Call __tsan_resume().\n", GetPid()); - while (__tsan_resumed == 0); + while (__tsan_resumed == 0) {} } } @@ -253,6 +259,11 @@ int Finalize(ThreadState *thr) { ctx->report_mtx.Lock(); ctx->report_mtx.Unlock(); +#ifndef TSAN_GO + if (ctx->flags.verbosity) + AllocatorPrintStats(); +#endif + ThreadFinalize(thr); if (ctx->nreported) { @@ -270,6 +281,8 @@ int Finalize(ThreadState *thr) { ctx->nmissed_expected); } + failed = OnFinalize(failed); + StatAggregate(ctx->stat, thr->stat); StatOutput(ctx->stat); return failed ? flags()->exitcode : 0; @@ -356,18 +369,6 @@ static inline void HandleRace(ThreadState *thr, u64 *shadow_mem, #endif } -static inline bool BothReads(Shadow s, int kAccessIsWrite) { - return !kAccessIsWrite && !s.is_write(); -} - -static inline bool OldIsRWNotWeaker(Shadow old, int kAccessIsWrite) { - return old.is_write() || !kAccessIsWrite; -} - -static inline bool OldIsRWWeakerOrEqual(Shadow old, int kAccessIsWrite) { - return !old.is_write() || kAccessIsWrite; -} - static inline bool OldIsInSameSynchEpoch(Shadow old, ThreadState *thr) { return old.epoch() >= thr->fast_synch_epoch; } @@ -378,7 +379,7 @@ static inline bool HappensBefore(Shadow old, ThreadState *thr) { ALWAYS_INLINE void MemoryAccessImpl(ThreadState *thr, uptr addr, - int kAccessSizeLog, bool kAccessIsWrite, + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, u64 *shadow_mem, Shadow cur) { StatInc(thr, StatMop); StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); @@ -452,7 +453,7 @@ void MemoryAccessImpl(ThreadState *thr, uptr addr, ALWAYS_INLINE void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, - int kAccessSizeLog, bool kAccessIsWrite) { + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic) { u64 *shadow_mem = (u64*)MemToShadow(addr); DPrintf2("#%d: MemoryAccess: @%p %p size=%d" " is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n", @@ -479,12 +480,13 @@ void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, Shadow cur(fast_state); cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog); cur.SetWrite(kAccessIsWrite); + cur.SetAtomic(kIsAtomic); // We must not store to the trace if we do not store to the shadow. // That is, this call must be moved somewhere below. TraceAddEvent(thr, fast_state, EventTypeMop, pc); - MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite, + MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic, shadow_mem, cur); } @@ -531,7 +533,10 @@ void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) { } void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) { + CHECK_EQ(thr->is_freeing, false); + thr->is_freeing = true; MemoryAccessRange(thr, pc, addr, size, true); + thr->is_freeing = false; Shadow s(thr->fast_state); s.ClearIgnoreBit(); s.MarkAsFreed(); diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h index bb2fe56bfe7..717f4599705 100644 --- a/libsanitizer/tsan/tsan_rtl.h +++ b/libsanitizer/tsan/tsan_rtl.h @@ -171,7 +171,8 @@ class FastState { // freed : 1 // tid : kTidBits // epoch : kClkBits -// is_write : 1 +// is_atomic : 1 +// is_read : 1 // size_log : 2 // addr0 : 3 class Shadow : public FastState { @@ -195,13 +196,26 @@ class Shadow : public FastState { } void SetWrite(unsigned kAccessIsWrite) { - DCHECK_EQ(x_ & 32, 0); - if (kAccessIsWrite) - x_ |= 32; - DCHECK_EQ(kAccessIsWrite, is_write()); + DCHECK_EQ(x_ & kReadBit, 0); + if (!kAccessIsWrite) + x_ |= kReadBit; + DCHECK_EQ(kAccessIsWrite, IsWrite()); } - bool IsZero() const { return x_ == 0; } + void SetAtomic(bool kIsAtomic) { + DCHECK(!IsAtomic()); + if (kIsAtomic) + x_ |= kAtomicBit; + DCHECK_EQ(IsAtomic(), kIsAtomic); + } + + bool IsAtomic() const { + return x_ & kAtomicBit; + } + + bool IsZero() const { + return x_ == 0; + } static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) { u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift; @@ -248,7 +262,8 @@ class Shadow : public FastState { } u64 addr0() const { return x_ & 7; } u64 size() const { return 1ull << size_log(); } - bool is_write() const { return x_ & 32; } + bool IsWrite() const { return !IsRead(); } + bool IsRead() const { return x_ & kReadBit; } // The idea behind the freed bit is as follows. // When the memory is freed (or otherwise unaccessible) we write to the shadow @@ -263,13 +278,46 @@ class Shadow : public FastState { x_ |= kFreedBit; } + bool IsFreed() const { + return x_ & kFreedBit; + } + bool GetFreedAndReset() { bool res = x_ & kFreedBit; x_ &= ~kFreedBit; return res; } + bool IsBothReadsOrAtomic(bool kIsWrite, bool kIsAtomic) const { + // analyzes 5-th bit (is_read) and 6-th bit (is_atomic) + bool v = x_ & u64(((kIsWrite ^ 1) << kReadShift) + | (kIsAtomic << kAtomicShift)); + DCHECK_EQ(v, (!IsWrite() && !kIsWrite) || (IsAtomic() && kIsAtomic)); + return v; + } + + bool IsRWNotWeaker(bool kIsWrite, bool kIsAtomic) const { + bool v = ((x_ >> kReadShift) & 3) + <= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); + DCHECK_EQ(v, (IsAtomic() < kIsAtomic) || + (IsAtomic() == kIsAtomic && !IsWrite() <= !kIsWrite)); + return v; + } + + bool IsRWWeakerOrEqual(bool kIsWrite, bool kIsAtomic) const { + bool v = ((x_ >> kReadShift) & 3) + >= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); + DCHECK_EQ(v, (IsAtomic() > kIsAtomic) || + (IsAtomic() == kIsAtomic && !IsWrite() >= !kIsWrite)); + return v; + } + private: + static const u64 kReadShift = 5; + static const u64 kReadBit = 1ull << kReadShift; + static const u64 kAtomicShift = 6; + static const u64 kAtomicBit = 1ull << kAtomicShift; + u64 size_log() const { return (x_ >> 3) & 3; } static bool TwoRangesIntersectSLOW(const Shadow s1, const Shadow s2) { @@ -324,7 +372,9 @@ struct ThreadState { const int tid; const int unique_id; int in_rtl; + bool in_symbolizer; bool is_alive; + bool is_freeing; const uptr stk_addr; const uptr stk_size; const uptr tls_addr; @@ -501,11 +551,14 @@ void InitializeDynamicAnnotations(); void ReportRace(ThreadState *thr); bool OutputReport(Context *ctx, const ScopedReport &srep, - const ReportStack *suppress_stack = 0); + const ReportStack *suppress_stack1 = 0, + const ReportStack *suppress_stack2 = 0); bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace); bool IsExpectedReport(uptr addr, uptr size); +bool FrameIsInternal(const ReportStack *frame); +ReportStack *SkipTsanInternalFrames(ReportStack *ent); #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1 # define DPrintf Printf @@ -521,6 +574,7 @@ bool IsExpectedReport(uptr addr, uptr size); u32 CurrentStackId(ThreadState *thr, uptr pc); void PrintCurrentStack(ThreadState *thr, uptr pc); +void PrintCurrentStackSlow(); // uses libunwind void Initialize(ThreadState *thr); int Finalize(ThreadState *thr); @@ -530,16 +584,38 @@ SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr, SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr); void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, - int kAccessSizeLog, bool kAccessIsWrite); + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic); void MemoryAccessImpl(ThreadState *thr, uptr addr, - int kAccessSizeLog, bool kAccessIsWrite, + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, u64 *shadow_mem, Shadow cur); -void MemoryRead1Byte(ThreadState *thr, uptr pc, uptr addr); -void MemoryWrite1Byte(ThreadState *thr, uptr pc, uptr addr); -void MemoryRead8Byte(ThreadState *thr, uptr pc, uptr addr); -void MemoryWrite8Byte(ThreadState *thr, uptr pc, uptr addr); void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, - uptr size, bool is_write); + uptr size, bool is_write); + +const int kSizeLog1 = 0; +const int kSizeLog2 = 1; +const int kSizeLog4 = 2; +const int kSizeLog8 = 3; + +void ALWAYS_INLINE INLINE MemoryRead(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, false, false); +} + +void ALWAYS_INLINE INLINE MemoryWrite(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, true, false); +} + +void ALWAYS_INLINE INLINE MemoryReadAtomic(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, false, true); +} + +void ALWAYS_INLINE INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, true, true); +} + void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cc b/libsanitizer/tsan/tsan_rtl_mutex.cc index db97e1d9853..22a71503c5c 100644 --- a/libsanitizer/tsan/tsan_rtl_mutex.cc +++ b/libsanitizer/tsan/tsan_rtl_mutex.cc @@ -24,8 +24,12 @@ void MutexCreate(ThreadState *thr, uptr pc, uptr addr, CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr); StatInc(thr, StatMutexCreate); - if (!linker_init && IsAppMem(addr)) - MemoryWrite1Byte(thr, pc, addr); + if (!linker_init && IsAppMem(addr)) { + CHECK(!thr->is_freeing); + thr->is_freeing = true; + MemoryWrite(thr, pc, addr, kSizeLog1); + thr->is_freeing = false; + } SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true); s->is_rw = rw; s->is_recursive = recursive; @@ -47,8 +51,12 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr); if (s == 0) return; - if (IsAppMem(addr)) - MemoryWrite1Byte(thr, pc, addr); + if (IsAppMem(addr)) { + CHECK(!thr->is_freeing); + thr->is_freeing = true; + MemoryWrite(thr, pc, addr, kSizeLog1); + thr->is_freeing = false; + } if (flags()->report_destroy_locked && s->owner_tid != SyncVar::kInvalidTid && !s->is_broken) { @@ -73,7 +81,7 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexLock %zx\n", thr->tid, addr); if (IsAppMem(addr)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId()); @@ -106,7 +114,7 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr); if (IsAppMem(addr)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId()); @@ -144,7 +152,7 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr); StatInc(thr, StatMutexReadLock); if (IsAppMem(addr)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); @@ -165,7 +173,7 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr); StatInc(thr, StatMutexReadUnlock); if (IsAppMem(addr)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); @@ -186,7 +194,7 @@ 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)) - MemoryRead1Byte(thr, pc, addr); + MemoryReadAtomic(thr, pc, addr, kSizeLog1); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); bool write = true; if (s->owner_tid == SyncVar::kInvalidTid) { diff --git a/libsanitizer/tsan/tsan_rtl_report.cc b/libsanitizer/tsan/tsan_rtl_report.cc index c4256da412a..ff1d43bc9e8 100644 --- a/libsanitizer/tsan/tsan_rtl_report.cc +++ b/libsanitizer/tsan/tsan_rtl_report.cc @@ -13,6 +13,7 @@ #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_stacktrace.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_suppressions.h" @@ -27,12 +28,15 @@ namespace __tsan { using namespace __sanitizer; // NOLINT +static ReportStack *SymbolizeStack(const StackTrace& trace); + void TsanCheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { ScopedInRtl in_rtl; Printf("FATAL: ThreadSanitizer CHECK failed: " "%s:%d \"%s\" (0x%zx, 0x%zx)\n", file, line, cond, (uptr)v1, (uptr)v2); + PrintCurrentStackSlow(); Die(); } @@ -144,7 +148,8 @@ void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, mop->tid = s.tid(); mop->addr = addr + s.addr0(); mop->size = s.size(); - mop->write = s.is_write(); + mop->write = s.IsWrite(); + mop->atomic = s.IsAtomic(); mop->stack = SymbolizeStack(*stack); for (uptr i = 0; i < mset->Size(); i++) { MutexSet::Desc d = mset->Get(i); @@ -458,9 +463,12 @@ static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], bool OutputReport(Context *ctx, const ScopedReport &srep, - const ReportStack *suppress_stack) { + const ReportStack *suppress_stack1, + const ReportStack *suppress_stack2) { const ReportDesc *rep = srep.GetReport(); - const uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack); + uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1); + if (suppress_pc == 0) + suppress_pc = IsSuppressed(rep->typ, suppress_stack2); if (suppress_pc != 0) { FiredSuppression supp = {srep.GetReport()->typ, suppress_pc}; ctx->fired_suppressions.PushBack(supp); @@ -486,6 +494,13 @@ bool IsFiredSuppression(Context *ctx, return false; } +bool FrameIsInternal(const ReportStack *frame) { + return frame != 0 && frame->file != 0 + && (internal_strstr(frame->file, "tsan_interceptors.cc") || + internal_strstr(frame->file, "sanitizer_common_interceptors.inc") || + internal_strstr(frame->file, "tsan_interface_")); +} + // On programs that use Java we see weird reports like: // WARNING: ThreadSanitizer: data race (pid=22512) // Read of size 8 at 0x7d2b00084318 by thread 100: @@ -495,22 +510,20 @@ bool IsFiredSuppression(Context *ctx, // #0 strncpy tsan_interceptors.cc:501 (foo+0x00000d8e0919) // #1 <null> <null>:0 (0x7f7ad9b42707) static bool IsJavaNonsense(const ReportDesc *rep) { +#ifndef TSAN_GO for (uptr i = 0; i < rep->mops.Size(); i++) { ReportMop *mop = rep->mops[i]; ReportStack *frame = mop->stack; - if (frame != 0 && frame->func != 0 - && (internal_strcmp(frame->func, "memset") == 0 - || internal_strcmp(frame->func, "memcpy") == 0 - || internal_strcmp(frame->func, "memmove") == 0 - || internal_strcmp(frame->func, "strcmp") == 0 - || internal_strcmp(frame->func, "strncpy") == 0 - || internal_strcmp(frame->func, "strlen") == 0 - || internal_strcmp(frame->func, "free") == 0 - || internal_strcmp(frame->func, "pthread_mutex_lock") == 0)) { + if (frame == 0 + || (frame->func == 0 && frame->file == 0 && frame->line == 0 + && frame->module == 0)) { + return true; + } + if (FrameIsInternal(frame)) { frame = frame->next; if (frame == 0 || (frame->func == 0 && frame->file == 0 && frame->line == 0 - && frame->module == 0)) { + && frame->module == 0)) { if (frame) { FiredSuppression supp = {rep->typ, frame->pc}; CTX()->fired_suppressions.PushBack(supp); @@ -519,6 +532,20 @@ static bool IsJavaNonsense(const ReportDesc *rep) { } } } +#endif + return false; +} + +static bool RaceBetweenAtomicAndFree(ThreadState *thr) { + Shadow s0(thr->racy_state[0]); + Shadow s1(thr->racy_state[1]); + CHECK(!(s0.IsAtomic() && s1.IsAtomic())); + if (!s0.IsAtomic() && !s1.IsAtomic()) + return true; + if (s0.IsAtomic() && s1.IsFreed()) + return true; + if (s1.IsAtomic() && thr->is_freeing) + return true; return false; } @@ -527,6 +554,9 @@ void ReportRace(ThreadState *thr) { return; ScopedInRtl in_rtl; + if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr)) + return; + if (thr->in_signal_handler) Printf("ThreadSanitizer: printing report from signal handler." " Can crash or hang.\n"); @@ -597,7 +627,8 @@ void ReportRace(ThreadState *thr) { } #endif - if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack)) + if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack, + rep.GetReport()->mops[1]->stack)) return; AddRacyStacks(thr, traces, addr_min, addr_max); @@ -609,4 +640,16 @@ void PrintCurrentStack(ThreadState *thr, uptr pc) { PrintStack(SymbolizeStack(trace)); } +void PrintCurrentStackSlow() { +#ifndef TSAN_GO + __sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace, + sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace; + ptrace->SlowUnwindStack(__sanitizer::StackTrace::GetCurrentPc(), + kStackTraceMax); + StackTrace trace; + trace.Init(ptrace->trace, ptrace->size); + PrintStack(SymbolizeStack(trace)); +#endif +} + } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_rtl_thread.cc b/libsanitizer/tsan/tsan_rtl_thread.cc index d5b3444be6d..9c89417ad68 100644 --- a/libsanitizer/tsan/tsan_rtl_thread.cc +++ b/libsanitizer/tsan/tsan_rtl_thread.cc @@ -207,6 +207,9 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) { thr->shadow_stack_pos = thr->shadow_stack; thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; #endif +#ifndef TSAN_GO + AllocatorThreadStart(thr); +#endif tctx->thr = thr; thr->fast_synch_epoch = tctx->epoch0; thr->clock.set(tid, tctx->epoch0); @@ -267,7 +270,7 @@ void ThreadFinish(ThreadState *thr) { tctx->epoch1 = thr->fast_state.epoch(); #ifndef TSAN_GO - AlloctorThreadFinish(thr); + AllocatorThreadFinish(thr); #endif thr->~ThreadState(); StatAggregate(ctx->stat, thr->stat); @@ -392,7 +395,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); - MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, + MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); } if (unaligned) @@ -403,7 +406,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(0, kAccessSizeLog); - MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, + MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); shadow_mem += kShadowCnt; } @@ -413,24 +416,8 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); - MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, + MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); } } - -void MemoryRead1Byte(ThreadState *thr, uptr pc, uptr addr) { - MemoryAccess(thr, pc, addr, 0, 0); -} - -void MemoryWrite1Byte(ThreadState *thr, uptr pc, uptr addr) { - MemoryAccess(thr, pc, addr, 0, 1); -} - -void MemoryRead8Byte(ThreadState *thr, uptr pc, uptr addr) { - MemoryAccess(thr, pc, addr, 3, 0); -} - -void MemoryWrite8Byte(ThreadState *thr, uptr pc, uptr addr) { - MemoryAccess(thr, pc, addr, 3, 1); -} } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_stat.cc b/libsanitizer/tsan/tsan_stat.cc index 105d0bc2375..cd88d2df928 100644 --- a/libsanitizer/tsan/tsan_stat.cc +++ b/libsanitizer/tsan/tsan_stat.cc @@ -179,6 +179,18 @@ void StatOutput(u64 *stat) { name[StatInt_sem_timedwait] = " sem_timedwait "; name[StatInt_sem_post] = " sem_post "; name[StatInt_sem_getvalue] = " sem_getvalue "; + name[StatInt_stat] = " stat "; + name[StatInt___xstat] = " __xstat "; + name[StatInt_stat64] = " stat64 "; + name[StatInt___xstat64] = " __xstat64 "; + name[StatInt_lstat] = " lstat "; + name[StatInt___lxstat] = " __lxstat "; + name[StatInt_lstat64] = " lstat64 "; + name[StatInt___lxstat64] = " __lxstat64 "; + name[StatInt_fstat] = " fstat "; + name[StatInt___fxstat] = " __fxstat "; + name[StatInt_fstat64] = " fstat64 "; + name[StatInt___fxstat64] = " __fxstat64 "; name[StatInt_open] = " open "; name[StatInt_open64] = " open64 "; name[StatInt_creat] = " creat "; @@ -193,12 +205,15 @@ void StatOutput(u64 *stat) { name[StatInt_socket] = " socket "; name[StatInt_socketpair] = " socketpair "; name[StatInt_connect] = " connect "; + name[StatInt_bind] = " bind "; + name[StatInt_listen] = " listen "; name[StatInt_accept] = " accept "; name[StatInt_accept4] = " accept4 "; name[StatInt_epoll_create] = " epoll_create "; name[StatInt_epoll_create1] = " epoll_create1 "; name[StatInt_close] = " close "; name[StatInt___close] = " __close "; + name[StatInt___res_iclose] = " __res_iclose "; name[StatInt_pipe] = " pipe "; name[StatInt_pipe2] = " pipe2 "; name[StatInt_read] = " read "; @@ -240,6 +255,14 @@ void StatOutput(u64 *stat) { name[StatInt_scanf] = " scanf "; name[StatInt_sscanf] = " sscanf "; name[StatInt_fscanf] = " fscanf "; + name[StatInt___isoc99_vscanf] = " vscanf "; + name[StatInt___isoc99_vsscanf] = " vsscanf "; + name[StatInt___isoc99_vfscanf] = " vfscanf "; + name[StatInt___isoc99_scanf] = " scanf "; + name[StatInt___isoc99_sscanf] = " sscanf "; + name[StatInt___isoc99_fscanf] = " fscanf "; + name[StatInt_on_exit] = " on_exit "; + name[StatInt___cxa_atexit] = " __cxa_atexit "; name[StatAnnotation] = "Dynamic annotations "; name[StatAnnotateHappensBefore] = " HappensBefore "; @@ -285,6 +308,7 @@ void StatOutput(u64 *stat) { name[StatMtxAnnotations] = " Annotations "; name[StatMtxMBlock] = " MBlock "; name[StatMtxJavaMBlock] = " JavaMBlock "; + name[StatMtxFD] = " FD "; Printf("Statistics:\n"); for (int i = 0; i < StatCnt; i++) diff --git a/libsanitizer/tsan/tsan_stat.h b/libsanitizer/tsan/tsan_stat.h index f40d3a2ac5e..1d6c54cdd4f 100644 --- a/libsanitizer/tsan/tsan_stat.h +++ b/libsanitizer/tsan/tsan_stat.h @@ -174,6 +174,18 @@ enum StatType { StatInt_sem_timedwait, StatInt_sem_post, StatInt_sem_getvalue, + StatInt_stat, + StatInt___xstat, + StatInt_stat64, + StatInt___xstat64, + StatInt_lstat, + StatInt___lxstat, + StatInt_lstat64, + StatInt___lxstat64, + StatInt_fstat, + StatInt___fxstat, + StatInt_fstat64, + StatInt___fxstat64, StatInt_open, StatInt_open64, StatInt_creat, @@ -188,12 +200,15 @@ enum StatType { StatInt_socket, StatInt_socketpair, StatInt_connect, + StatInt_bind, + StatInt_listen, StatInt_accept, StatInt_accept4, StatInt_epoll_create, StatInt_epoll_create1, StatInt_close, StatInt___close, + StatInt___res_iclose, StatInt_pipe, StatInt_pipe2, StatInt_read, @@ -239,6 +254,14 @@ enum StatType { StatInt_scanf, StatInt_sscanf, StatInt_fscanf, + StatInt___isoc99_vscanf, + StatInt___isoc99_vsscanf, + StatInt___isoc99_vfscanf, + StatInt___isoc99_scanf, + StatInt___isoc99_sscanf, + StatInt___isoc99_fscanf, + StatInt_on_exit, + StatInt___cxa_atexit, // Dynamic annotations. StatAnnotation, @@ -287,6 +310,7 @@ enum StatType { StatMtxAtExit, StatMtxMBlock, StatMtxJavaMBlock, + StatMtxFD, // This must be the last. StatCnt diff --git a/libsanitizer/tsan/tsan_suppressions.cc b/libsanitizer/tsan/tsan_suppressions.cc index 42ba9b509eb..b6c54db2c51 100644 --- a/libsanitizer/tsan/tsan_suppressions.cc +++ b/libsanitizer/tsan/tsan_suppressions.cc @@ -17,6 +17,13 @@ #include "tsan_mman.h" #include "tsan_platform.h" +// Can be overriden in frontend. +#ifndef TSAN_GO +extern "C" const char *WEAK __tsan_default_suppressions() { + return 0; +} +#endif + namespace __tsan { static Suppression *g_suppressions; @@ -29,7 +36,7 @@ static char *ReadFile(const char *filename) { internal_snprintf(tmp.data(), tmp.size(), "%s", filename); else internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename); - fd_t fd = internal_open(tmp.data(), false); + fd_t fd = OpenFile(tmp.data(), false); if (fd == kInvalidFd) { Printf("ThreadSanitizer: failed to open suppressions file '%s'\n", tmp.data()); @@ -78,8 +85,7 @@ bool SuppressionMatch(char *templ, const char *str) { return true; } -Suppression *SuppressionParse(const char* supp) { - Suppression *head = 0; +Suppression *SuppressionParse(Suppression *head, const char* supp) { const char *line = supp; while (line) { while (line[0] == ' ' || line[0] == '\t') @@ -128,8 +134,12 @@ Suppression *SuppressionParse(const char* supp) { } void InitializeSuppressions() { - char *supp = ReadFile(flags()->suppressions); - g_suppressions = SuppressionParse(supp); + const char *supp = ReadFile(flags()->suppressions); + g_suppressions = SuppressionParse(0, supp); +#ifndef TSAN_GO + supp = __tsan_default_suppressions(); + g_suppressions = SuppressionParse(0, supp); +#endif } uptr IsSuppressed(ReportType typ, const ReportStack *stack) { @@ -150,7 +160,8 @@ uptr IsSuppressed(ReportType typ, const ReportStack *stack) { for (Suppression *supp = g_suppressions; supp; supp = supp->next) { if (stype == supp->type && (SuppressionMatch(supp->templ, frame->func) || - SuppressionMatch(supp->templ, frame->file))) { + SuppressionMatch(supp->templ, frame->file) || + SuppressionMatch(supp->templ, frame->module))) { DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ); return frame->pc; } diff --git a/libsanitizer/tsan/tsan_suppressions.h b/libsanitizer/tsan/tsan_suppressions.h index 4761eaa0cee..d44d8dd3529 100644 --- a/libsanitizer/tsan/tsan_suppressions.h +++ b/libsanitizer/tsan/tsan_suppressions.h @@ -33,7 +33,7 @@ struct Suppression { char *templ; }; -Suppression *SuppressionParse(const char* supp); +Suppression *SuppressionParse(Suppression *head, const char* supp); bool SuppressionMatch(char *templ, const char *str); } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_symbolize.cc b/libsanitizer/tsan/tsan_symbolize.cc index 015b98717f1..65a994670b5 100644 --- a/libsanitizer/tsan/tsan_symbolize.cc +++ b/libsanitizer/tsan/tsan_symbolize.cc @@ -16,9 +16,24 @@ #include "sanitizer_common/sanitizer_symbolizer.h" #include "tsan_flags.h" #include "tsan_report.h" +#include "tsan_rtl.h" namespace __tsan { +struct ScopedInSymbolizer { + ScopedInSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(!thr->in_symbolizer); + thr->in_symbolizer = true; + } + + ~ScopedInSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(thr->in_symbolizer); + thr->in_symbolizer = false; + } +}; + ReportStack *NewReportStackEntry(uptr addr) { ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack, sizeof(ReportStack)); @@ -53,35 +68,36 @@ static ReportStack *NewReportStackEntry(const AddressInfo &info) { } ReportStack *SymbolizeCode(uptr addr) { - if (flags()->external_symbolizer_path[0]) { - static const uptr kMaxAddrFrames = 16; - InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); - for (uptr i = 0; i < kMaxAddrFrames; i++) - new(&addr_frames[i]) AddressInfo(); - uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(), - kMaxAddrFrames); - if (addr_frames_num == 0) - return NewReportStackEntry(addr); - ReportStack *top = 0; - ReportStack *bottom = 0; - for (uptr i = 0; i < addr_frames_num; i++) { - ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]); - CHECK(cur_entry); - addr_frames[i].Clear(); - if (i == 0) - top = cur_entry; - else - bottom->next = cur_entry; - bottom = cur_entry; - } - return top; + if (!IsSymbolizerAvailable()) + return SymbolizeCodeAddr2Line(addr); + ScopedInSymbolizer in_symbolizer; + static const uptr kMaxAddrFrames = 16; + InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); + for (uptr i = 0; i < kMaxAddrFrames; i++) + new(&addr_frames[i]) AddressInfo(); + uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(), + kMaxAddrFrames); + if (addr_frames_num == 0) + return NewReportStackEntry(addr); + ReportStack *top = 0; + ReportStack *bottom = 0; + for (uptr i = 0; i < addr_frames_num; i++) { + ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]); + CHECK(cur_entry); + addr_frames[i].Clear(); + if (i == 0) + top = cur_entry; + else + bottom->next = cur_entry; + bottom = cur_entry; } - return SymbolizeCodeAddr2Line(addr); + return top; } ReportLocation *SymbolizeData(uptr addr) { - if (flags()->external_symbolizer_path[0] == 0) + if (!IsSymbolizerAvailable()) return 0; + ScopedInSymbolizer in_symbolizer; DataInfo info; if (!__sanitizer::SymbolizeData(addr, &info)) return 0; diff --git a/libsanitizer/tsan/tsan_update_shadow_word_inl.h b/libsanitizer/tsan/tsan_update_shadow_word_inl.h index e22859c1dde..b9aa51c7957 100644 --- a/libsanitizer/tsan/tsan_update_shadow_word_inl.h +++ b/libsanitizer/tsan/tsan_update_shadow_word_inl.h @@ -32,7 +32,7 @@ do { if (Shadow::TidsAreEqual(old, cur)) { StatInc(thr, StatShadowSameThread); if (OldIsInSameSynchEpoch(old, thr)) { - if (OldIsRWNotWeaker(old, kAccessIsWrite)) { + if (old.IsRWNotWeaker(kAccessIsWrite, kIsAtomic)) { // found a slot that holds effectively the same info // (that is, same tid, same sync epoch and same size) StatInc(thr, StatMopSame); @@ -41,7 +41,7 @@ do { StoreIfNotYetStored(sp, &store_word); break; } - if (OldIsRWWeakerOrEqual(old, kAccessIsWrite)) + if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic)) StoreIfNotYetStored(sp, &store_word); break; } @@ -50,25 +50,23 @@ do { StoreIfNotYetStored(sp, &store_word); break; } - if (BothReads(old, kAccessIsWrite)) + if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)) break; goto RACE; } - // Do the memory access intersect? - if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { + // In Go all memory accesses are 1 byte, so there can be no intersections. + if (kCppMode && Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { StatInc(thr, StatShadowIntersect); if (Shadow::TidsAreEqual(old, cur)) { StatInc(thr, StatShadowSameThread); break; } StatInc(thr, StatShadowAnotherThread); - if (HappensBefore(old, thr)) + if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)) break; - - if (BothReads(old, kAccessIsWrite)) + if (HappensBefore(old, thr)) break; - goto RACE; } // The accesses do not intersect. diff --git a/libsanitizer/tsan/tsan_vector.h b/libsanitizer/tsan/tsan_vector.h index d6bb7076a45..99e9f792c20 100644 --- a/libsanitizer/tsan/tsan_vector.h +++ b/libsanitizer/tsan/tsan_vector.h @@ -103,6 +103,6 @@ class Vector { Vector(const Vector&); void operator=(const Vector&); }; -} +} // namespace __tsan #endif // #ifndef TSAN_VECTOR_H |