[sanitizer] add support for try_lock in deadlock detector

llvm-svn: 202120
This commit is contained in:
Kostya Serebryany 2014-02-25 08:24:15 +00:00
parent 26af6f7f1b
commit 11f4f30fa7
6 changed files with 95 additions and 12 deletions

View File

@ -143,6 +143,18 @@ class DeadlockDetector {
return is_reachable;
}
// Handles the try_lock event, returns false.
// When a try_lock event happens (i.e. a try_lock call succeeds) we need
// to add this lock to the currently held locks, but we should not try to
// change the lock graph or to detect a cycle. We may want to investigate
// whether a more aggressive strategy is possible for try_lock.
bool onTryLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node) {
ensureCurrentEpoch(dtls);
uptr cur_idx = nodeToIndex(cur_node);
dtls->addLock(cur_idx, current_epoch_);
return false;
}
// Finds a path between the lock 'cur_node' (which is currently held in dtls)
// and some other currently held lock, returns the length of the path
// or 0 on failure.
@ -170,8 +182,13 @@ class DeadlockDetector {
}
uptr testOnlyGetEpoch() const { return current_epoch_; }
bool testOnlyHasEdge(uptr l1, uptr l2) {
return g_.hasEdge(nodeToIndex(l1), nodeToIndex(l2));
}
// idx1 and idx2 are raw indices to g_, not lock IDs.
bool testOnlyHasEdge(uptr idx1, uptr idx2) { return g_.hasEdge(idx1, idx2); }
bool testOnlyHasEdgeRaw(uptr idx1, uptr idx2) {
return g_.hasEdge(idx1, idx2);
}
void Print() {
for (uptr from = 0; from < size(); from++)

View File

@ -301,14 +301,41 @@ void RunCorrectEpochFlush() {
EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2);
d.onLock(&dtls, l0);
d.onLock(&dtls, l1);
EXPECT_TRUE(d.testOnlyHasEdge(0, 1));
EXPECT_FALSE(d.testOnlyHasEdge(1, 0));
EXPECT_FALSE(d.testOnlyHasEdge(3, 0));
EXPECT_FALSE(d.testOnlyHasEdge(4, 0));
EXPECT_FALSE(d.testOnlyHasEdge(5, 0));
EXPECT_TRUE(d.testOnlyHasEdgeRaw(0, 1));
EXPECT_FALSE(d.testOnlyHasEdgeRaw(1, 0));
EXPECT_FALSE(d.testOnlyHasEdgeRaw(3, 0));
EXPECT_FALSE(d.testOnlyHasEdgeRaw(4, 0));
EXPECT_FALSE(d.testOnlyHasEdgeRaw(5, 0));
}
TEST(DeadlockDetector, CorrectEpochFlush) {
RunCorrectEpochFlush<BV1>();
RunCorrectEpochFlush<BV2>();
}
template <class BV>
void RunTryLockTest() {
ScopedDD<BV> sdd;
DeadlockDetector<BV> &d = *sdd.dp;
DeadlockDetectorTLS<BV> &dtls = sdd.dtls;
uptr l0 = d.newNode(0);
uptr l1 = d.newNode(0);
uptr l2 = d.newNode(0);
EXPECT_FALSE(d.onLock(&dtls, l0));
EXPECT_FALSE(d.onTryLock(&dtls, l1));
EXPECT_FALSE(d.onLock(&dtls, l2));
EXPECT_TRUE(d.isHeld(&dtls, l0));
EXPECT_TRUE(d.isHeld(&dtls, l1));
EXPECT_TRUE(d.isHeld(&dtls, l2));
EXPECT_FALSE(d.testOnlyHasEdge(l0, l1));
EXPECT_TRUE(d.testOnlyHasEdge(l1, l2));
d.onUnlock(&dtls, l0);
d.onUnlock(&dtls, l1);
d.onUnlock(&dtls, l2);
}
TEST(DeadlockDetector, TryLockTest) {
RunTryLockTest<BV1>();
RunTryLockTest<BV2>();
}

View File

@ -960,7 +960,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
if (res == EOWNERDEAD)
MutexRepair(thr, pc, (uptr)m);
if (res == 0 || res == EOWNERDEAD)
MutexLock(thr, pc, (uptr)m);
MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true);
return res;
}
@ -1004,7 +1004,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m);
int res = REAL(pthread_spin_trylock)(m);
if (res == 0) {
MutexLock(thr, pc, (uptr)m);
MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true);
}
return res;
}

View File

@ -728,7 +728,8 @@ void ProcessPendingSignals(ThreadState *thr);
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
bool rw, bool recursive, bool linker_init);
void MutexDestroy(ThreadState *thr, uptr pc, uptr addr);
void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec = 1);
void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec = 1,
bool try_lock = false);
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);

View File

@ -92,7 +92,7 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
DestroyAndFree(s);
}
void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) {
void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec, bool try_lock) {
Context *ctx = CTX();
DPrintf("#%d: MutexLock %zx rec=%d\n", thr->tid, addr, rec);
CHECK_GT(rec, 0);
@ -128,8 +128,11 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) {
Printf("ThreadSanitizer: reursive-lock\n");
}
// Printf("MutexLock: %zx\n", s->deadlock_detector_id);
bool has_deadlock =
ctx->dd.onLock(&thr->deadlock_detector_tls, s->deadlock_detector_id);
bool has_deadlock = try_lock
? ctx->dd.onTryLock(&thr->deadlock_detector_tls,
s->deadlock_detector_id)
: ctx->dd.onLock(&thr->deadlock_detector_tls,
s->deadlock_detector_id);
if (has_deadlock) {
uptr path[10];
uptr len = ctx->dd.findPathToHeldLock(&thr->deadlock_detector_tls,

View File

@ -14,6 +14,7 @@ class PaddedLock {
}
void lock() { assert(0 == pthread_mutex_lock(&mu_)); }
void unlock() { assert(0 == pthread_mutex_unlock(&mu_)); }
bool try_lock() { return 0 == pthread_mutex_trylock(&mu_); }
private:
pthread_mutex_t mu_;
@ -38,6 +39,11 @@ class LockTest {
return &locks_[i];
}
bool T(size_t i) {
assert(i < n_);
return locks_[i].try_lock();
}
// Simple lock order onversion.
void Test1() {
fprintf(stderr, "Starting Test1\n");
@ -114,6 +120,34 @@ class LockTest {
&LockTest::Lock1_Loop_2);
}
void Test7() {
fprintf(stderr, "Starting Test7\n");
// CHECK: Starting Test7
L(0); T(1); U(1); U(0);
T(1); L(0); U(1); U(0);
// CHECK-NOT: WARNING: ThreadSanitizer:
fprintf(stderr, "No cycle: 0=>1\n");
// CHECK: No cycle: 0=>1
T(2); L(3); U(3); U(2);
L(3); T(2); U(3); U(2);
// CHECK-NOT: WARNING: ThreadSanitizer:
fprintf(stderr, "No cycle: 2=>3\n");
// CHECK: No cycle: 2=>3
T(4); L(5); U(4); U(5);
L(5); L(4); U(4); U(5);
// CHECK: WARNING: ThreadSanitizer: lock-order-inversion
fprintf(stderr, "Have cycle: 4=>5\n");
// CHECK: Have cycle: 4=>5
L(7); L(6); U(6); U(7);
T(6); L(7); U(6); U(7);
// CHECK: WARNING: ThreadSanitizer: lock-order-inversion
fprintf(stderr, "Have cycle: 6=>7\n");
// CHECK: Have cycle: 6=>7
}
private:
void Lock2(size_t l1, size_t l2) { L(l1); L(l2); U(l2); U(l1); }
void Lock_0_1() { Lock2(0, 1); }
@ -177,6 +211,7 @@ int main() {
{ LockTest t(5); t.Test4(); }
{ LockTest t(5); t.Test5(); }
{ LockTest t(5); t.Test6(); }
{ LockTest t(10); t.Test7(); }
fprintf(stderr, "DONE\n");
// CHECK: DONE
}