tsan: add support for robust mutexes
llvm-svn: 194823
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
// COMMON_INTERCEPTOR_ON_EXIT
|
||||
// COMMON_INTERCEPTOR_MUTEX_LOCK
|
||||
// COMMON_INTERCEPTOR_MUTEX_UNLOCK
|
||||
// COMMON_INTERCEPTOR_MUTEX_REPAIR
|
||||
// COMMON_INTERCEPTOR_SET_PTHREAD_NAME
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "interception/interception.h"
|
||||
@@ -50,6 +51,10 @@
|
||||
#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) {}
|
||||
#endif
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_MUTEX_REPAIR
|
||||
#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {}
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRCMP
|
||||
static inline int CharCmpX(unsigned char c1, unsigned char c2) {
|
||||
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
|
||||
@@ -2179,7 +2184,10 @@ INTERCEPTOR(int, pthread_mutex_lock, void *m) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_lock, m);
|
||||
int res = REAL(pthread_mutex_lock)(m);
|
||||
if (res == 0) COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
|
||||
if (res == errno_EOWNERDEAD)
|
||||
COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m);
|
||||
if (res == 0 || res == errno_EOWNERDEAD)
|
||||
COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <limits.h>
|
||||
#include <net/if.h>
|
||||
@@ -762,6 +763,8 @@ namespace __sanitizer {
|
||||
unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI;
|
||||
unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL;
|
||||
#endif
|
||||
|
||||
extern const int errno_EOWNERDEAD = EOWNERDEAD;
|
||||
} // namespace __sanitizer
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
|
||||
|
||||
@@ -921,6 +921,7 @@ namespace __sanitizer {
|
||||
extern unsigned IOCTL_TIOCSSERIAL;
|
||||
#endif
|
||||
|
||||
extern const int errno_EOWNERDEAD;
|
||||
} // namespace __sanitizer
|
||||
|
||||
#define CHECK_TYPE_SIZE(TYPE) \
|
||||
|
||||
36
compiler-rt/lib/tsan/lit_tests/mutex_robust.cc
Normal file
36
compiler-rt/lib/tsan/lit_tests/mutex_robust.cc
Normal file
@@ -0,0 +1,36 @@
|
||||
// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
pthread_mutex_t m;
|
||||
|
||||
void *thr(void *p) {
|
||||
pthread_mutex_lock(&m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_mutexattr_t a;
|
||||
pthread_mutexattr_init(&a);
|
||||
pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST);
|
||||
pthread_mutex_init(&m, &a);
|
||||
pthread_t th;
|
||||
pthread_create(&th, 0, thr, 0);
|
||||
sleep(1);
|
||||
if (pthread_mutex_lock(&m) != EOWNERDEAD) {
|
||||
fprintf(stderr, "not EOWNERDEAD\n");
|
||||
exit(1);
|
||||
}
|
||||
pthread_join(th, 0);
|
||||
fprintf(stderr, "DONE\n");
|
||||
}
|
||||
|
||||
// This is a correct code, and tsan must not bark.
|
||||
// CHECK-NOT: WARNING: ThreadSanitizer
|
||||
// CHECK-NOT: EOWNERDEAD
|
||||
// CHECK: DONE
|
||||
// CHECK-NOT: WARNING: ThreadSanitizer
|
||||
|
||||
41
compiler-rt/lib/tsan/lit_tests/mutex_robust2.cc
Normal file
41
compiler-rt/lib/tsan/lit_tests/mutex_robust2.cc
Normal file
@@ -0,0 +1,41 @@
|
||||
// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
pthread_mutex_t m;
|
||||
int x;
|
||||
|
||||
void *thr(void *p) {
|
||||
pthread_mutex_lock(&m);
|
||||
x = 42;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_mutexattr_t a;
|
||||
pthread_mutexattr_init(&a);
|
||||
pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST);
|
||||
pthread_mutex_init(&m, &a);
|
||||
pthread_t th;
|
||||
pthread_create(&th, 0, thr, 0);
|
||||
sleep(1);
|
||||
if (pthread_mutex_trylock(&m) != EOWNERDEAD) {
|
||||
fprintf(stderr, "not EOWNERDEAD\n");
|
||||
exit(1);
|
||||
}
|
||||
x = 43;
|
||||
pthread_join(th, 0);
|
||||
fprintf(stderr, "DONE\n");
|
||||
}
|
||||
|
||||
// This is a false positive, tsan must not bark at the data race.
|
||||
// But currently it does.
|
||||
// CHECK-NOT: WARNING: ThreadSanitizer WARNING: double lock of mutex
|
||||
// CHECK: WARNING: ThreadSanitizer: data race
|
||||
// CHECK-NOT: EOWNERDEAD
|
||||
// CHECK: DONE
|
||||
// CHECK-NOT: WARNING: ThreadSanitizer
|
||||
|
||||
@@ -66,6 +66,7 @@ const int PTHREAD_MUTEX_RECURSIVE = 1;
|
||||
const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
|
||||
const int EINVAL = 22;
|
||||
const int EBUSY = 16;
|
||||
const int EOWNERDEAD = 130;
|
||||
const int EPOLL_CTL_ADD = 1;
|
||||
const int SIGILL = 4;
|
||||
const int SIGABRT = 6;
|
||||
@@ -948,9 +949,10 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) {
|
||||
TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
|
||||
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m);
|
||||
int res = REAL(pthread_mutex_trylock)(m);
|
||||
if (res == 0) {
|
||||
if (res == EOWNERDEAD)
|
||||
MutexRepair(thr, pc, (uptr)m);
|
||||
if (res == 0 || res == EOWNERDEAD)
|
||||
MutexLock(thr, pc, (uptr)m);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1892,6 +1894,10 @@ struct TsanInterceptorContext {
|
||||
MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \
|
||||
((TsanInterceptorContext *)ctx)->pc, (uptr)m)
|
||||
|
||||
#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \
|
||||
MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
|
||||
((TsanInterceptorContext *)ctx)->pc, (uptr)m)
|
||||
|
||||
#include "sanitizer_common/sanitizer_common_interceptors.inc"
|
||||
|
||||
#define TSAN_SYSCALL() \
|
||||
|
||||
@@ -705,6 +705,7 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false);
|
||||
void MutexReadLock(ThreadState *thr, uptr pc, uptr addr);
|
||||
void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr);
|
||||
void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr);
|
||||
void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD
|
||||
|
||||
void Acquire(ThreadState *thr, uptr pc, uptr addr);
|
||||
void AcquireGlobal(ThreadState *thr, uptr pc);
|
||||
|
||||
@@ -224,6 +224,16 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
|
||||
s->mtx.Unlock();
|
||||
}
|
||||
|
||||
void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
|
||||
Context *ctx = CTX();
|
||||
CHECK_GT(thr->in_rtl, 0);
|
||||
DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr);
|
||||
SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
|
||||
s->owner_tid = SyncVar::kInvalidTid;
|
||||
s->recursion = 0;
|
||||
s->mtx.Unlock();
|
||||
}
|
||||
|
||||
void Acquire(ThreadState *thr, uptr pc, uptr addr) {
|
||||
CHECK_GT(thr->in_rtl, 0);
|
||||
DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
|
||||
|
||||
Reference in New Issue
Block a user