forked from OSchip/llvm-project
[sanitizer] more code for deadlock detector, nothing really works yet (except for small unit tests).
llvm-svn: 201302
This commit is contained in:
parent
7aaa0c2ddb
commit
5e52d48e3a
|
@ -51,6 +51,7 @@ set(SANITIZER_HEADERS
|
|||
sanitizer_common_interceptors_ioctl.inc
|
||||
sanitizer_common_interceptors_format.inc
|
||||
sanitizer_common_syscalls.inc
|
||||
sanitizer_deadlock_detector.h
|
||||
sanitizer_flags.h
|
||||
sanitizer_internal_defs.h
|
||||
sanitizer_lfstack.h
|
||||
|
|
|
@ -26,6 +26,7 @@ class BasicBitVector {
|
|||
uptr size() const { return kSize; }
|
||||
// No CTOR.
|
||||
void clear() { bits_ = 0; }
|
||||
void setAll() { bits_ = ~(basic_int_t)0; }
|
||||
bool empty() const { return bits_ == 0; }
|
||||
// Returns true if the bit has changed from 0 to 1.
|
||||
bool setBit(uptr idx) {
|
||||
|
@ -85,6 +86,13 @@ class TwoLevelBitVector {
|
|||
for (uptr i = 0; i < kLevel1Size; i++)
|
||||
l1_[i].clear();
|
||||
}
|
||||
void setAll() {
|
||||
for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
|
||||
l1_[i0].setAll();
|
||||
for (uptr i1 = 0; i1 < BV::kSize; i1++)
|
||||
l2_[i0][i1].setAll();
|
||||
}
|
||||
}
|
||||
bool empty() {
|
||||
for (uptr i = 0; i < kLevel1Size; i++)
|
||||
if (!l1_[i].empty())
|
||||
|
|
|
@ -44,8 +44,8 @@ class BVGraph {
|
|||
}
|
||||
|
||||
// Returns true if there is a path from the node 'from'
|
||||
// to any of the nodes in 'target'.
|
||||
bool isReachable(uptr from, const BV &target) {
|
||||
// to any of the nodes in 'targets'.
|
||||
bool isReachable(uptr from, const BV &targets) {
|
||||
BV to_visit, visited;
|
||||
to_visit.clear();
|
||||
to_visit.setUnion(v[from]);
|
||||
|
@ -56,7 +56,7 @@ class BVGraph {
|
|||
if (visited.setBit(idx))
|
||||
to_visit.setUnion(v[idx]);
|
||||
}
|
||||
return target.intersectsWith(visited);
|
||||
return targets.intersectsWith(visited);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
//===-- sanitizer_deadlock_detector.h ---------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer runtime.
|
||||
// The deadlock detector maintains a directed graph of lock acquisitions.
|
||||
// When a lock event happens, the detector checks if the locks already held by
|
||||
// the current thread are reachable from the newly acquired lock.
|
||||
//
|
||||
// FIXME: this is work in progress, nothing really works yet.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_DEADLOCK_DETECTOR_H
|
||||
#define SANITIZER_DEADLOCK_DETECTOR_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_bvgraph.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Thread-local state for DeadlockDetector.
|
||||
// It contains the locks currently held by the owning thread.
|
||||
class DeadlockDetectorTLS {
|
||||
public:
|
||||
// No CTOR.
|
||||
void clear() { n_locks_ = 0; }
|
||||
void addLock(uptr node) {
|
||||
CHECK_LT(n_locks_, ARRAY_SIZE(locks_));
|
||||
locks_[n_locks_++] = node;
|
||||
}
|
||||
void removeLock(uptr node) {
|
||||
CHECK_NE(n_locks_, 0U);
|
||||
for (sptr i = n_locks_ - 1; i >= 0; i--) {
|
||||
if (locks_[i] == node) {
|
||||
locks_[i] = locks_[n_locks_ - 1];
|
||||
n_locks_--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
CHECK(0);
|
||||
}
|
||||
uptr numLocks() const { return n_locks_; }
|
||||
uptr getLock(uptr idx) const {
|
||||
CHECK_LT(idx, n_locks_);
|
||||
return locks_[idx];
|
||||
}
|
||||
|
||||
private:
|
||||
uptr n_locks_;
|
||||
uptr locks_[64];
|
||||
};
|
||||
|
||||
// DeadlockDetector.
|
||||
// For deadlock detection to work we need one global DeadlockDetector object
|
||||
// and one DeadlockDetectorTLS object per evey thread.
|
||||
template <class BV>
|
||||
class DeadlockDetector {
|
||||
public:
|
||||
typedef BV BitVector;
|
||||
|
||||
uptr size() const { return g_.size(); }
|
||||
// No CTOR.
|
||||
void clear() {
|
||||
current_epoch_ = 0;
|
||||
available_nodes_.clear();
|
||||
recycled_nodes_.clear();
|
||||
g_.clear();
|
||||
}
|
||||
|
||||
// Allocate new deadlock detector node.
|
||||
// If we are out of available nodes first try to recycle some.
|
||||
// If there is nothing to recycle, flush the graph and increment the epoch.
|
||||
// Associate 'data' (opaque user's object) with the new node.
|
||||
uptr newNode(uptr data) {
|
||||
if (!available_nodes_.empty())
|
||||
return getAvailableNode(data);
|
||||
if (!recycled_nodes_.empty()) {
|
||||
CHECK(available_nodes_.empty());
|
||||
available_nodes_.setUnion(recycled_nodes_);
|
||||
recycled_nodes_.clear();
|
||||
// FIXME: actually recycle nodes in the graph.
|
||||
return getAvailableNode(data);
|
||||
}
|
||||
// We are out of vacant nodes. Flush and increment the current_epoch_.
|
||||
uptr new_epoch = current_epoch_ + BV::kSize;
|
||||
clear();
|
||||
current_epoch_ = new_epoch;
|
||||
available_nodes_.setAll();
|
||||
return getAvailableNode(data);
|
||||
}
|
||||
|
||||
// Get data associated with the node created by newNode().
|
||||
uptr getData(uptr node) const { return data_[nodeToIndex(node)]; }
|
||||
|
||||
void removeNode(uptr node) {
|
||||
uptr idx = nodeToIndex(node);
|
||||
CHECK(!available_nodes_.getBit(idx));
|
||||
CHECK(recycled_nodes_.setBit(idx));
|
||||
// FIXME: also remove from the graph.
|
||||
}
|
||||
|
||||
// Handle the lock event, return true if there is a cycle.
|
||||
// FIXME: handle RW locks, recusive locks, etc.
|
||||
bool onLock(DeadlockDetectorTLS *dtls, uptr cur_node) {
|
||||
BV cur_locks;
|
||||
cur_locks.clear();
|
||||
uptr cur_idx = nodeToIndex(cur_node);
|
||||
for (uptr i = 0, n = dtls->numLocks(); i < n; i++) {
|
||||
uptr prev_node = dtls->getLock(i);
|
||||
uptr prev_idx = nodeToIndex(prev_node);
|
||||
g_.addEdge(prev_idx, cur_idx);
|
||||
cur_locks.setBit(prev_idx);
|
||||
// Printf("OnLock %zx; prev %zx\n", cur_node, dtls->getLock(i));
|
||||
}
|
||||
dtls->addLock(cur_node);
|
||||
return g_.isReachable(cur_idx, cur_locks);
|
||||
}
|
||||
|
||||
// Handle the unlock event.
|
||||
void onUnlock(DeadlockDetectorTLS *dtls, uptr node) {
|
||||
dtls->removeLock(node);
|
||||
}
|
||||
|
||||
private:
|
||||
void check_idx(uptr idx) const { CHECK_LT(idx, size()); }
|
||||
void check_node(uptr node) const {
|
||||
CHECK_GE(node, size());
|
||||
CHECK_EQ(current_epoch_, node / size() * size());
|
||||
}
|
||||
uptr indexToNode(uptr idx) {
|
||||
check_idx(idx);
|
||||
return idx | current_epoch_;
|
||||
}
|
||||
uptr nodeToIndex(uptr node) {
|
||||
check_node(node);
|
||||
return node % size();
|
||||
}
|
||||
uptr getAvailableNode(uptr data) {
|
||||
uptr idx = available_nodes_.getAndClearFirstOne();
|
||||
data_[idx] = data;
|
||||
return indexToNode(idx);
|
||||
}
|
||||
uptr current_epoch_;
|
||||
BV available_nodes_;
|
||||
BV recycled_nodes_;
|
||||
BVGraph<BV> g_;
|
||||
uptr data_[BV::kSize];
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_DEADLOCK_DETECTOR_H
|
|
@ -6,6 +6,7 @@ set(SANITIZER_UNITTESTS
|
|||
sanitizer_bitvector_test.cc
|
||||
sanitizer_bvgraph_test.cc
|
||||
sanitizer_common_test.cc
|
||||
sanitizer_deadlock_detector_test.cc
|
||||
sanitizer_flags_test.cc
|
||||
sanitizer_format_interceptor_test.cc
|
||||
sanitizer_ioctl_test.cc
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
//===-- sanitizer_deadlock_detector_test.cc -------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer runtime.
|
||||
// Tests for sanitizer_deadlock_detector.h
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "sanitizer_common/sanitizer_deadlock_detector.h"
|
||||
|
||||
#include "sanitizer_test_utils.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
using namespace __sanitizer;
|
||||
using namespace std;
|
||||
|
||||
template <class BV>
|
||||
void TestDeadlockDetector() {
|
||||
// Can't allocate on stack.
|
||||
DeadlockDetector<BV> *dp = new DeadlockDetector<BV>;
|
||||
DeadlockDetector<BV> &d = *dp;
|
||||
DeadlockDetectorTLS dtls;
|
||||
d.clear();
|
||||
set<uptr> s;
|
||||
for (size_t i = 0; i < d.size() * 3; i++) {
|
||||
uptr node = d.newNode(0);
|
||||
EXPECT_TRUE(s.insert(node).second);
|
||||
}
|
||||
|
||||
d.clear();
|
||||
s.clear();
|
||||
// Add size() nodes.
|
||||
for (size_t i = 0; i < d.size(); i++) {
|
||||
uptr node = d.newNode(0);
|
||||
EXPECT_TRUE(s.insert(node).second);
|
||||
}
|
||||
// Remove all nodes.
|
||||
for (set<uptr>::iterator it = s.begin(); it != s.end(); ++it)
|
||||
d.removeNode(*it);
|
||||
// The nodes should be reused.
|
||||
for (size_t i = 0; i < d.size(); i++) {
|
||||
uptr node = d.newNode(0);
|
||||
EXPECT_FALSE(s.insert(node).second);
|
||||
}
|
||||
|
||||
// Cycle: n1->n2->n1
|
||||
{
|
||||
d.clear();
|
||||
dtls.clear();
|
||||
uptr n1 = d.newNode(1);
|
||||
uptr n2 = d.newNode(2);
|
||||
EXPECT_FALSE(d.onLock(&dtls, n1));
|
||||
EXPECT_FALSE(d.onLock(&dtls, n2));
|
||||
d.onUnlock(&dtls, n2);
|
||||
d.onUnlock(&dtls, n1);
|
||||
|
||||
EXPECT_FALSE(d.onLock(&dtls, n2));
|
||||
EXPECT_TRUE(d.onLock(&dtls, n1));
|
||||
d.onUnlock(&dtls, n1);
|
||||
d.onUnlock(&dtls, n2);
|
||||
}
|
||||
|
||||
// Cycle: n1->n2->n3->n1
|
||||
{
|
||||
d.clear();
|
||||
dtls.clear();
|
||||
uptr n1 = d.newNode(1);
|
||||
uptr n2 = d.newNode(2);
|
||||
uptr n3 = d.newNode(3);
|
||||
|
||||
EXPECT_FALSE(d.onLock(&dtls, n1));
|
||||
EXPECT_FALSE(d.onLock(&dtls, n2));
|
||||
d.onUnlock(&dtls, n2);
|
||||
d.onUnlock(&dtls, n1);
|
||||
|
||||
EXPECT_FALSE(d.onLock(&dtls, n2));
|
||||
EXPECT_FALSE(d.onLock(&dtls, n3));
|
||||
d.onUnlock(&dtls, n3);
|
||||
d.onUnlock(&dtls, n2);
|
||||
|
||||
EXPECT_FALSE(d.onLock(&dtls, n3));
|
||||
EXPECT_TRUE(d.onLock(&dtls, n1));
|
||||
d.onUnlock(&dtls, n1);
|
||||
d.onUnlock(&dtls, n3);
|
||||
}
|
||||
|
||||
delete dp;
|
||||
}
|
||||
|
||||
TEST(SanitizerCommon, DeadlockDetector) {
|
||||
TestDeadlockDetector<BasicBitVector<> >();
|
||||
TestDeadlockDetector<TwoLevelBitVector<2> >();
|
||||
}
|
Loading…
Reference in New Issue