]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
Copy head to stable/9 as part of 9.0-RELEASE release cycle.
[FreeBSD/stable/9.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / PthreadLockChecker.cpp
1 //===--- PthreadLockChecker.h - Undefined arguments checker ----*- 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 defines PthreadLockChecker, a simple lock -> unlock checker.  Eventually
11 // this shouldn't be registered with ExprEngineInternalChecks.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "ClangSACheckers.h"
16 #include "clang/StaticAnalyzer/Core/Checker.h"
17 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
21 #include "llvm/ADT/ImmutableSet.h"
22
23 using namespace clang;
24 using namespace ento;
25
26 namespace {
27 class PthreadLockChecker
28   : public Checker< check::PostStmt<CallExpr> > {
29 public:
30   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
31     
32   void AcquireLock(CheckerContext &C, const CallExpr *CE,
33                    SVal lock, bool isTryLock) const;
34     
35   void ReleaseLock(CheckerContext &C, const CallExpr *CE,
36                     SVal lock) const;
37
38 };
39 } // end anonymous namespace
40
41 // GDM Entry for tracking lock state.
42 namespace { class LockSet {}; }
43 namespace clang {
44 namespace ento {
45 template <> struct GRStateTrait<LockSet> :
46   public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > {
47     static void* GDMIndex() { static int x = 0; return &x; }
48 };
49 } // end GR namespace
50 } // end clang namespace
51
52
53 void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
54                                        CheckerContext &C) const {
55   const GRState *state = C.getState();
56   const Expr *Callee = CE->getCallee();
57   const FunctionTextRegion *R =
58     dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion());
59   
60   if (!R)
61     return;
62   
63   IdentifierInfo *II = R->getDecl()->getIdentifier();
64   if (!II)   // if no identifier, not a simple C function
65     return;
66   llvm::StringRef FName = II->getName();
67   
68   if (FName == "pthread_mutex_lock") {
69     if (CE->getNumArgs() != 1)
70       return;
71     AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false);
72   }
73   else if (FName == "pthread_mutex_trylock") {
74     if (CE->getNumArgs() != 1)
75       return;
76     AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true);
77   }  
78   else if (FName == "pthread_mutex_unlock") {
79     if (CE->getNumArgs() != 1)
80       return;
81     ReleaseLock(C, CE, state->getSVal(CE->getArg(0)));
82   }
83 }
84
85 void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
86                                      SVal lock, bool isTryLock) const {
87   
88   const MemRegion *lockR = lock.getAsRegion();
89   if (!lockR)
90     return;
91   
92   const GRState *state = C.getState();
93   
94   SVal X = state->getSVal(CE);
95   if (X.isUnknownOrUndef())
96     return;
97   
98   DefinedSVal retVal = cast<DefinedSVal>(X);
99   const GRState *lockSucc = state;
100   
101   if (isTryLock) {
102       // Bifurcate the state, and allow a mode where the lock acquisition fails.
103     const GRState *lockFail;
104     llvm::tie(lockFail, lockSucc) = state->assume(retVal);    
105     assert(lockFail && lockSucc);
106     C.addTransition(C.generateNode(CE, lockFail));
107   }
108   else {
109       // Assume that the return value was 0.
110     lockSucc = state->assume(retVal, false);
111     assert(lockSucc);
112   }
113   
114     // Record that the lock was acquired.  
115   lockSucc = lockSucc->add<LockSet>(lockR);
116   
117   C.addTransition(lockSucc != state ? C.generateNode(CE, lockSucc) :
118                   C.getPredecessor());
119 }
120
121 void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
122                                      SVal lock) const {
123
124   const MemRegion *lockR = lock.getAsRegion();
125   if (!lockR)
126     return;
127   
128   const GRState *state = C.getState();
129
130   // Record that the lock was released.  
131   // FIXME: Handle unlocking locks that were never acquired.  This may
132   // require IPA for wrappers.
133   const GRState *unlockState = state->remove<LockSet>(lockR);
134   
135   if (state == unlockState)
136     return;
137   
138   C.addTransition(C.generateNode(CE, unlockState));  
139 }
140
141 void ento::registerPthreadLockChecker(CheckerManager &mgr) {
142   mgr.registerChecker<PthreadLockChecker>();
143 }