Home | History | Annotate | Download | only in sanitizer_common
      1 //===-- sanitizer_deadlock_detector.h ---------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is a part of Sanitizer runtime.
     11 // The deadlock detector maintains a directed graph of lock acquisitions.
     12 // When a lock event happens, the detector checks if the locks already held by
     13 // the current thread are reachable from the newly acquired lock.
     14 //
     15 // The detector can handle only a fixed amount of simultaneously live locks
     16 // (a lock is alive if it has been locked at least once and has not been
     17 // destroyed). When the maximal number of locks is reached the entire graph
     18 // is flushed and the new lock epoch is started. The node ids from the old
     19 // epochs can not be used with any of the detector methods except for
     20 // nodeBelongsToCurrentEpoch().
     21 //
     22 // FIXME: this is work in progress, nothing really works yet.
     23 //
     24 //===----------------------------------------------------------------------===//
     25 
     26 #ifndef SANITIZER_DEADLOCK_DETECTOR_H
     27 #define SANITIZER_DEADLOCK_DETECTOR_H
     28 
     29 #include "sanitizer_common.h"
     30 #include "sanitizer_bvgraph.h"
     31 
     32 namespace __sanitizer {
     33 
     34 // Thread-local state for DeadlockDetector.
     35 // It contains the locks currently held by the owning thread.
     36 template <class BV>
     37 class DeadlockDetectorTLS {
     38  public:
     39   // No CTOR.
     40   void clear() {
     41     bv_.clear();
     42     epoch_ = 0;
     43     n_recursive_locks = 0;
     44     n_all_locks_ = 0;
     45   }
     46 
     47   bool empty() const { return bv_.empty(); }
     48 
     49   void ensureCurrentEpoch(uptr current_epoch) {
     50     if (epoch_ == current_epoch) return;
     51     bv_.clear();
     52     epoch_ = current_epoch;
     53     n_recursive_locks = 0;
     54     n_all_locks_ = 0;
     55   }
     56 
     57   uptr getEpoch() const { return epoch_; }
     58 
     59   // Returns true if this is the first (non-recursive) acquisition of this lock.
     60   bool addLock(uptr lock_id, uptr current_epoch, u32 stk) {
     61     // Printf("addLock: %zx %zx stk %u\n", lock_id, current_epoch, stk);
     62     CHECK_EQ(epoch_, current_epoch);
     63     if (!bv_.setBit(lock_id)) {
     64       // The lock is already held by this thread, it must be recursive.
     65       CHECK_LT(n_recursive_locks, ARRAY_SIZE(recursive_locks));
     66       recursive_locks[n_recursive_locks++] = lock_id;
     67       return false;
     68     }
     69     CHECK_LT(n_all_locks_, ARRAY_SIZE(all_locks_with_contexts_));
     70     // lock_id < BV::kSize, can cast to a smaller int.
     71     u32 lock_id_short = static_cast<u32>(lock_id);
     72     LockWithContext l = {lock_id_short, stk};
     73     all_locks_with_contexts_[n_all_locks_++] = l;
     74     return true;
     75   }
     76 
     77   void removeLock(uptr lock_id) {
     78     if (n_recursive_locks) {
     79       for (sptr i = n_recursive_locks - 1; i >= 0; i--) {
     80         if (recursive_locks[i] == lock_id) {
     81           n_recursive_locks--;
     82           Swap(recursive_locks[i], recursive_locks[n_recursive_locks]);
     83           return;
     84         }
     85       }
     86     }
     87     // Printf("remLock: %zx %zx\n", lock_id, epoch_);
     88     if (!bv_.clearBit(lock_id))
     89       return;  // probably addLock happened before flush
     90     if (n_all_locks_) {
     91       for (sptr i = n_all_locks_ - 1; i >= 0; i--) {
     92         if (all_locks_with_contexts_[i].lock == static_cast<u32>(lock_id)) {
     93           Swap(all_locks_with_contexts_[i],
     94                all_locks_with_contexts_[n_all_locks_ - 1]);
     95           n_all_locks_--;
     96           break;
     97         }
     98       }
     99     }
    100   }
    101 
    102   u32 findLockContext(uptr lock_id) {
    103     for (uptr i = 0; i < n_all_locks_; i++)
    104       if (all_locks_with_contexts_[i].lock == static_cast<u32>(lock_id))
    105         return all_locks_with_contexts_[i].stk;
    106     return 0;
    107   }
    108 
    109   const BV &getLocks(uptr current_epoch) const {
    110     CHECK_EQ(epoch_, current_epoch);
    111     return bv_;
    112   }
    113 
    114   uptr getNumLocks() const { return n_all_locks_; }
    115   uptr getLock(uptr idx) const { return all_locks_with_contexts_[idx].lock; }
    116 
    117  private:
    118   BV bv_;
    119   uptr epoch_;
    120   uptr recursive_locks[64];
    121   uptr n_recursive_locks;
    122   struct LockWithContext {
    123     u32 lock;
    124     u32 stk;
    125   };
    126   LockWithContext all_locks_with_contexts_[64];
    127   uptr n_all_locks_;
    128 };
    129 
    130 // DeadlockDetector.
    131 // For deadlock detection to work we need one global DeadlockDetector object
    132 // and one DeadlockDetectorTLS object per evey thread.
    133 // This class is not thread safe, all concurrent accesses should be guarded
    134 // by an external lock.
    135 // Most of the methods of this class are not thread-safe (i.e. should
    136 // be protected by an external lock) unless explicitly told otherwise.
    137 template <class BV>
    138 class DeadlockDetector {
    139  public:
    140   typedef BV BitVector;
    141 
    142   uptr size() const { return g_.size(); }
    143 
    144   // No CTOR.
    145   void clear() {
    146     current_epoch_ = 0;
    147     available_nodes_.clear();
    148     recycled_nodes_.clear();
    149     g_.clear();
    150     n_edges_ = 0;
    151   }
    152 
    153   // Allocate new deadlock detector node.
    154   // If we are out of available nodes first try to recycle some.
    155   // If there is nothing to recycle, flush the graph and increment the epoch.
    156   // Associate 'data' (opaque user's object) with the new node.
    157   uptr newNode(uptr data) {
    158     if (!available_nodes_.empty())
    159       return getAvailableNode(data);
    160     if (!recycled_nodes_.empty()) {
    161       // Printf("recycling: n_edges_ %zd\n", n_edges_);
    162       for (sptr i = n_edges_ - 1; i >= 0; i--) {
    163         if (recycled_nodes_.getBit(edges_[i].from) ||
    164             recycled_nodes_.getBit(edges_[i].to)) {
    165           Swap(edges_[i], edges_[n_edges_ - 1]);
    166           n_edges_--;
    167         }
    168       }
    169       CHECK(available_nodes_.empty());
    170       // removeEdgesFrom was called in removeNode.
    171       g_.removeEdgesTo(recycled_nodes_);
    172       available_nodes_.setUnion(recycled_nodes_);
    173       recycled_nodes_.clear();
    174       return getAvailableNode(data);
    175     }
    176     // We are out of vacant nodes. Flush and increment the current_epoch_.
    177     current_epoch_ += size();
    178     recycled_nodes_.clear();
    179     available_nodes_.setAll();
    180     g_.clear();
    181     n_edges_ = 0;
    182     return getAvailableNode(data);
    183   }
    184 
    185   // Get data associated with the node created by newNode().
    186   uptr getData(uptr node) const { return data_[nodeToIndex(node)]; }
    187 
    188   bool nodeBelongsToCurrentEpoch(uptr node) {
    189     return node && (node / size() * size()) == current_epoch_;
    190   }
    191 
    192   void removeNode(uptr node) {
    193     uptr idx = nodeToIndex(node);
    194     CHECK(!available_nodes_.getBit(idx));
    195     CHECK(recycled_nodes_.setBit(idx));
    196     g_.removeEdgesFrom(idx);
    197   }
    198 
    199   void ensureCurrentEpoch(DeadlockDetectorTLS<BV> *dtls) {
    200     dtls->ensureCurrentEpoch(current_epoch_);
    201   }
    202 
    203   // Returns true if there is a cycle in the graph after this lock event.
    204   // Ideally should be called before the lock is acquired so that we can
    205   // report a deadlock before a real deadlock happens.
    206   bool onLockBefore(DeadlockDetectorTLS<BV> *dtls, uptr cur_node) {
    207     ensureCurrentEpoch(dtls);
    208     uptr cur_idx = nodeToIndex(cur_node);
    209     return g_.isReachable(cur_idx, dtls->getLocks(current_epoch_));
    210   }
    211 
    212   u32 findLockContext(DeadlockDetectorTLS<BV> *dtls, uptr node) {
    213     return dtls->findLockContext(nodeToIndex(node));
    214   }
    215 
    216   // Add cur_node to the set of locks held currently by dtls.
    217   void onLockAfter(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
    218     ensureCurrentEpoch(dtls);
    219     uptr cur_idx = nodeToIndex(cur_node);
    220     dtls->addLock(cur_idx, current_epoch_, stk);
    221   }
    222 
    223   // Experimental *racy* fast path function.
    224   // Returns true if all edges from the currently held locks to cur_node exist.
    225   bool hasAllEdges(DeadlockDetectorTLS<BV> *dtls, uptr cur_node) {
    226     uptr local_epoch = dtls->getEpoch();
    227     // Read from current_epoch_ is racy.
    228     if (cur_node && local_epoch == current_epoch_ &&
    229         local_epoch == nodeToEpoch(cur_node)) {
    230       uptr cur_idx = nodeToIndexUnchecked(cur_node);
    231       for (uptr i = 0, n = dtls->getNumLocks(); i < n; i++) {
    232         if (!g_.hasEdge(dtls->getLock(i), cur_idx))
    233           return false;
    234       }
    235       return true;
    236     }
    237     return false;
    238   }
    239 
    240   // Adds edges from currently held locks to cur_node,
    241   // returns the number of added edges, and puts the sources of added edges
    242   // into added_edges[].
    243   // Should be called before onLockAfter.
    244   uptr addEdges(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk,
    245                 int unique_tid) {
    246     ensureCurrentEpoch(dtls);
    247     uptr cur_idx = nodeToIndex(cur_node);
    248     uptr added_edges[40];
    249     uptr n_added_edges = g_.addEdges(dtls->getLocks(current_epoch_), cur_idx,
    250                                      added_edges, ARRAY_SIZE(added_edges));
    251     for (uptr i = 0; i < n_added_edges; i++) {
    252       if (n_edges_ < ARRAY_SIZE(edges_)) {
    253         Edge e = {(u16)added_edges[i], (u16)cur_idx,
    254                   dtls->findLockContext(added_edges[i]), stk,
    255                   unique_tid};
    256         edges_[n_edges_++] = e;
    257       }
    258       // Printf("Edge%zd: %u %zd=>%zd in T%d\n",
    259       //        n_edges_, stk, added_edges[i], cur_idx, unique_tid);
    260     }
    261     return n_added_edges;
    262   }
    263 
    264   bool findEdge(uptr from_node, uptr to_node, u32 *stk_from, u32 *stk_to,
    265                 int *unique_tid) {
    266     uptr from_idx = nodeToIndex(from_node);
    267     uptr to_idx = nodeToIndex(to_node);
    268     for (uptr i = 0; i < n_edges_; i++) {
    269       if (edges_[i].from == from_idx && edges_[i].to == to_idx) {
    270         *stk_from = edges_[i].stk_from;
    271         *stk_to = edges_[i].stk_to;
    272         *unique_tid = edges_[i].unique_tid;
    273         return true;
    274       }
    275     }
    276     return false;
    277   }
    278 
    279   // Test-only function. Handles the before/after lock events,
    280   // returns true if there is a cycle.
    281   bool onLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
    282     ensureCurrentEpoch(dtls);
    283     bool is_reachable = !isHeld(dtls, cur_node) && onLockBefore(dtls, cur_node);
    284     addEdges(dtls, cur_node, stk, 0);
    285     onLockAfter(dtls, cur_node, stk);
    286     return is_reachable;
    287   }
    288 
    289   // Handles the try_lock event, returns false.
    290   // When a try_lock event happens (i.e. a try_lock call succeeds) we need
    291   // to add this lock to the currently held locks, but we should not try to
    292   // change the lock graph or to detect a cycle.  We may want to investigate
    293   // whether a more aggressive strategy is possible for try_lock.
    294   bool onTryLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
    295     ensureCurrentEpoch(dtls);
    296     uptr cur_idx = nodeToIndex(cur_node);
    297     dtls->addLock(cur_idx, current_epoch_, stk);
    298     return false;
    299   }
    300 
    301   // Returns true iff dtls is empty (no locks are currently held) and we can
    302   // add the node to the currently held locks w/o chanding the global state.
    303   // This operation is thread-safe as it only touches the dtls.
    304   bool onFirstLock(DeadlockDetectorTLS<BV> *dtls, uptr node, u32 stk = 0) {
    305     if (!dtls->empty()) return false;
    306     if (dtls->getEpoch() && dtls->getEpoch() == nodeToEpoch(node)) {
    307       dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk);
    308       return true;
    309     }
    310     return false;
    311   }
    312 
    313   // Finds a path between the lock 'cur_node' (currently not held in dtls)
    314   // and some currently held lock, returns the length of the path
    315   // or 0 on failure.
    316   uptr findPathToLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, uptr *path,
    317                       uptr path_size) {
    318     tmp_bv_.copyFrom(dtls->getLocks(current_epoch_));
    319     uptr idx = nodeToIndex(cur_node);
    320     CHECK(!tmp_bv_.getBit(idx));
    321     uptr res = g_.findShortestPath(idx, tmp_bv_, path, path_size);
    322     for (uptr i = 0; i < res; i++)
    323       path[i] = indexToNode(path[i]);
    324     if (res)
    325       CHECK_EQ(path[0], cur_node);
    326     return res;
    327   }
    328 
    329   // Handle the unlock event.
    330   // This operation is thread-safe as it only touches the dtls.
    331   void onUnlock(DeadlockDetectorTLS<BV> *dtls, uptr node) {
    332     if (dtls->getEpoch() == nodeToEpoch(node))
    333       dtls->removeLock(nodeToIndexUnchecked(node));
    334   }
    335 
    336   // Tries to handle the lock event w/o writing to global state.
    337   // Returns true on success.
    338   // This operation is thread-safe as it only touches the dtls
    339   // (modulo racy nature of hasAllEdges).
    340   bool onLockFast(DeadlockDetectorTLS<BV> *dtls, uptr node, u32 stk = 0) {
    341     if (hasAllEdges(dtls, node)) {
    342       dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk);
    343       return true;
    344     }
    345     return false;
    346   }
    347 
    348   bool isHeld(DeadlockDetectorTLS<BV> *dtls, uptr node) const {
    349     return dtls->getLocks(current_epoch_).getBit(nodeToIndex(node));
    350   }
    351 
    352   uptr testOnlyGetEpoch() const { return current_epoch_; }
    353   bool testOnlyHasEdge(uptr l1, uptr l2) {
    354     return g_.hasEdge(nodeToIndex(l1), nodeToIndex(l2));
    355   }
    356   // idx1 and idx2 are raw indices to g_, not lock IDs.
    357   bool testOnlyHasEdgeRaw(uptr idx1, uptr idx2) {
    358     return g_.hasEdge(idx1, idx2);
    359   }
    360 
    361   void Print() {
    362     for (uptr from = 0; from < size(); from++)
    363       for (uptr to = 0; to < size(); to++)
    364         if (g_.hasEdge(from, to))
    365           Printf("  %zx => %zx\n", from, to);
    366   }
    367 
    368  private:
    369   void check_idx(uptr idx) const { CHECK_LT(idx, size()); }
    370 
    371   void check_node(uptr node) const {
    372     CHECK_GE(node, size());
    373     CHECK_EQ(current_epoch_, nodeToEpoch(node));
    374   }
    375 
    376   uptr indexToNode(uptr idx) const {
    377     check_idx(idx);
    378     return idx + current_epoch_;
    379   }
    380 
    381   uptr nodeToIndexUnchecked(uptr node) const { return node % size(); }
    382 
    383   uptr nodeToIndex(uptr node) const {
    384     check_node(node);
    385     return nodeToIndexUnchecked(node);
    386   }
    387 
    388   uptr nodeToEpoch(uptr node) const { return node / size() * size(); }
    389 
    390   uptr getAvailableNode(uptr data) {
    391     uptr idx = available_nodes_.getAndClearFirstOne();
    392     data_[idx] = data;
    393     return indexToNode(idx);
    394   }
    395 
    396   struct Edge {
    397     u16 from;
    398     u16 to;
    399     u32 stk_from;
    400     u32 stk_to;
    401     int unique_tid;
    402   };
    403 
    404   uptr current_epoch_;
    405   BV available_nodes_;
    406   BV recycled_nodes_;
    407   BV tmp_bv_;
    408   BVGraph<BV> g_;
    409   uptr data_[BV::kSize];
    410   Edge edges_[BV::kSize * 32];
    411   uptr n_edges_;
    412 };
    413 
    414 } // namespace __sanitizer
    415 
    416 #endif // SANITIZER_DEADLOCK_DETECTOR_H
    417