diff options
author | Ted Kremenek <kremenek@apple.com> | 2010-01-25 04:41:41 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2010-01-25 04:41:41 +0000 |
commit | 1309f9a3b225ea846e5822691c39a77423125505 (patch) | |
tree | 1ba6d1976da4f426292619af026dbf9d9314c573 /lib/Checker/PthreadLockChecker.cpp | |
parent | 3db9eb1fbe5771d3d64db01af46b4eee9aca8ed0 (diff) |
Split libAnalysis into two libraries: libAnalysis and libChecker.
(1) libAnalysis is a generic analysis library that can be used by
Sema. It defines the CFG, basic dataflow analysis primitives, and
inexpensive flow-sensitive analyses (e.g. LiveVariables).
(2) libChecker contains the guts of the static analyzer, incuding the
path-sensitive analysis engine and domain-specific checks.
Now any clients that want to use the frontend to build their own tools
don't need to link in the entire static analyzer.
This change exposes various obvious cleanups that can be made to the
layout of files and headers in libChecker. More changes pending. :)
This change also exposed a layering violation between AnalysisContext
and MemRegion. BlockInvocationContext shouldn't explicitly know about
BlockDataRegions. For now I've removed the BlockDataRegion* from
BlockInvocationContext (removing context-sensitivity; although this
wasn't used yet). We need to have a better way to extend
BlockInvocationContext (and any LocationContext) to add
context-sensitivty.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94406 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Checker/PthreadLockChecker.cpp')
-rw-r--r-- | lib/Checker/PthreadLockChecker.cpp | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/lib/Checker/PthreadLockChecker.cpp b/lib/Checker/PthreadLockChecker.cpp new file mode 100644 index 0000000000..da1f7f30f2 --- /dev/null +++ b/lib/Checker/PthreadLockChecker.cpp @@ -0,0 +1,141 @@ +//===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually +// this shouldn't be registered with GRExprEngineInternalChecks. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/PathSensitive/BugReporter.h" +#include "clang/Checker/PathSensitive/GRStateTrait.h" +#include "GRExprEngineExperimentalChecks.h" +#include "llvm/ADT/ImmutableSet.h" + +using namespace clang; + +namespace { +class PthreadLockChecker + : public CheckerVisitor<PthreadLockChecker> { + BugType *BT; +public: + PthreadLockChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + void AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock); + + void ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock); + +}; +} // end anonymous namespace + +// GDM Entry for tracking lock state. +namespace { class LockSet {}; } +namespace clang { +template <> struct GRStateTrait<LockSet> : + public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { + static void* GDMIndex() { return PthreadLockChecker::getTag(); } +}; +} // end clang namespace + +void clang::RegisterPthreadLockChecker(GRExprEngine &Eng) { + Eng.registerCheck(new PthreadLockChecker()); +} + + +void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionTextRegion *R = + dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); + + if (!R) + return; + + llvm::StringRef FName = R->getDecl()->getName(); + + if (FName == "pthread_mutex_lock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); + } + else if (FName == "pthread_mutex_trylock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); + } + else if (FName == "pthread_mutex_unlock") { + if (CE->getNumArgs() != 1) + return; + ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); + } +} + +void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + SVal X = state->getSVal(CE); + if (X.isUnknownOrUndef()) + return; + + DefinedSVal retVal = cast<DefinedSVal>(X); + const GRState *lockSucc = state; + + if (isTryLock) { + // Bifurcate the state, and allow a mode where the lock acquisition fails. + const GRState *lockFail; + llvm::tie(lockFail, lockSucc) = state->Assume(retVal); + assert(lockFail && lockSucc); + C.addTransition(C.GenerateNode(CE, lockFail)); + } + else { + // Assume that the return value was 0. + lockSucc = state->Assume(retVal, false); + assert(lockSucc); + } + + // Record that the lock was acquired. + lockSucc = lockSucc->add<LockSet>(lockR); + + C.addTransition(lockSucc != state ? C.GenerateNode(CE, lockSucc) : + C.getPredecessor()); +} + +void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + // Record that the lock was released. + // FIXME: Handle unlocking locks that were never acquired. This may + // require IPA for wrappers. + const GRState *unlockState = state->remove<LockSet>(lockR); + + if (state == unlockState) + return; + + C.addTransition(C.GenerateNode(CE, unlockState)); +} |