aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Analysis/ThreadSafety.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Analysis/ThreadSafety.cpp')
-rw-r--r--lib/Analysis/ThreadSafety.cpp160
1 files changed, 123 insertions, 37 deletions
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 479d9a301f4b..6e0e1732bae9 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -10,8 +10,8 @@
// A intra-procedural analysis for thread safety (e.g. deadlocks and race
// conditions), based off of an annotation system.
//
-// See http://clang.llvm.org/docs/LanguageExtensions.html#threadsafety for more
-// information.
+// See http://clang.llvm.org/docs/LanguageExtensions.html#thread-safety-annotation-checking
+// for more information.
//
//===----------------------------------------------------------------------===//
@@ -266,6 +266,11 @@ private:
return NodeVec.size()-1;
}
+ inline bool isCalleeArrow(const Expr *E) {
+ const MemberExpr *ME = dyn_cast<MemberExpr>(E->IgnoreParenCasts());
+ return ME ? ME->isArrow() : false;
+ }
+
/// Build an SExpr from the given C++ expression.
/// Recursive function that terminates on DeclRefExpr.
/// Note: this function merely creates a SExpr; it does not check to
@@ -323,13 +328,11 @@ private:
} else if (const CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) {
// When calling a function with a lock_returned attribute, replace
// the function call with the expression in lock_returned.
- const CXXMethodDecl* MD =
- cast<CXXMethodDecl>(CMCE->getMethodDecl()->getMostRecentDecl());
+ const CXXMethodDecl *MD = CMCE->getMethodDecl()->getMostRecentDecl();
if (LockReturnedAttr* At = MD->getAttr<LockReturnedAttr>()) {
CallingContext LRCallCtx(CMCE->getMethodDecl());
LRCallCtx.SelfArg = CMCE->getImplicitObjectArgument();
- LRCallCtx.SelfArrow =
- dyn_cast<MemberExpr>(CMCE->getCallee())->isArrow();
+ LRCallCtx.SelfArrow = isCalleeArrow(CMCE->getCallee());
LRCallCtx.NumArgs = CMCE->getNumArgs();
LRCallCtx.FunArgs = CMCE->getArgs();
LRCallCtx.PrevCtx = CallCtx;
@@ -339,7 +342,7 @@ private:
// ignore any method named get().
if (CMCE->getMethodDecl()->getNameAsString() == "get" &&
CMCE->getNumArgs() == 0) {
- if (NDeref && dyn_cast<MemberExpr>(CMCE->getCallee())->isArrow())
+ if (NDeref && isCalleeArrow(CMCE->getCallee()))
++(*NDeref);
return buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx, NDeref);
}
@@ -353,8 +356,7 @@ private:
NodeVec[Root].setSize(Sz + 1);
return Sz + 1;
} else if (const CallExpr *CE = dyn_cast<CallExpr>(Exp)) {
- const FunctionDecl* FD =
- cast<FunctionDecl>(CE->getDirectCallee()->getMostRecentDecl());
+ const FunctionDecl *FD = CE->getDirectCallee()->getMostRecentDecl();
if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) {
CallingContext LRCallCtx(CE->getDirectCallee());
LRCallCtx.NumArgs = CE->getNumArgs();
@@ -498,11 +500,10 @@ private:
} else if (const CXXMemberCallExpr *CE =
dyn_cast<CXXMemberCallExpr>(DeclExp)) {
CallCtx.SelfArg = CE->getImplicitObjectArgument();
- CallCtx.SelfArrow = dyn_cast<MemberExpr>(CE->getCallee())->isArrow();
+ CallCtx.SelfArrow = isCalleeArrow(CE->getCallee());
CallCtx.NumArgs = CE->getNumArgs();
CallCtx.FunArgs = CE->getArgs();
- } else if (const CallExpr *CE =
- dyn_cast<CallExpr>(DeclExp)) {
+ } else if (const CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) {
CallCtx.NumArgs = CE->getNumArgs();
CallCtx.FunArgs = CE->getArgs();
} else if (const CXXConstructExpr *CE =
@@ -750,16 +751,18 @@ struct LockData {
///
/// FIXME: add support for re-entrant locking and lock up/downgrading
LockKind LKind;
+ bool Asserted; // for asserted locks
bool Managed; // for ScopedLockable objects
SExpr UnderlyingMutex; // for ScopedLockable objects
- LockData(SourceLocation AcquireLoc, LockKind LKind, bool M = false)
- : AcquireLoc(AcquireLoc), LKind(LKind), Managed(M),
+ LockData(SourceLocation AcquireLoc, LockKind LKind, bool M=false,
+ bool Asrt=false)
+ : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(Asrt), Managed(M),
UnderlyingMutex(Decl::EmptyShell())
{}
LockData(SourceLocation AcquireLoc, LockKind LKind, const SExpr &Mu)
- : AcquireLoc(AcquireLoc), LKind(LKind), Managed(false),
+ : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(false), Managed(false),
UnderlyingMutex(Mu)
{}
@@ -864,6 +867,16 @@ public:
return false;
}
+ // Returns an iterator
+ iterator findLockIter(FactManager &FM, const SExpr &M) {
+ for (iterator I = begin(), E = end(); I != E; ++I) {
+ const SExpr &Exp = FM[*I].MutID;
+ if (Exp.matches(M))
+ return I;
+ }
+ return end();
+ }
+
LockData* findLock(FactManager &FM, const SExpr &M) const {
for (const_iterator I = begin(), E = end(); I != E; ++I) {
const SExpr &Exp = FM[*I].MutID;
@@ -1484,7 +1497,8 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,
return;
if (FSet.findLock(FactMan, Mutex)) {
- Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc);
+ if (!LDat.Asserted)
+ Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc);
} else {
FSet.addLock(FactMan, Mutex, LDat);
}
@@ -1647,15 +1661,22 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,
if (!TCond) Negate = !Negate;
return getTrylockCallExpr(BOP->getLHS(), C, Negate);
}
- else if (getStaticBooleanValue(BOP->getLHS(), TCond)) {
+ TCond = false;
+ if (getStaticBooleanValue(BOP->getLHS(), TCond)) {
if (!TCond) Negate = !Negate;
return getTrylockCallExpr(BOP->getRHS(), C, Negate);
}
return 0;
}
+ if (BOP->getOpcode() == BO_LAnd) {
+ // LHS must have been evaluated in a different block.
+ return getTrylockCallExpr(BOP->getRHS(), C, Negate);
+ }
+ if (BOP->getOpcode() == BO_LOr) {
+ return getTrylockCallExpr(BOP->getRHS(), C, Negate);
+ }
return 0;
}
- // FIXME -- handle && and || as well.
return 0;
}
@@ -1669,11 +1690,11 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
const CFGBlock *CurrBlock) {
Result = ExitSet;
- if (!PredBlock->getTerminatorCondition())
+ const Stmt *Cond = PredBlock->getTerminatorCondition();
+ if (!Cond)
return;
bool Negate = false;
- const Stmt *Cond = PredBlock->getTerminatorCondition();
const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];
const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
@@ -1686,7 +1707,6 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
if(!FunDecl || !FunDecl->hasAttrs())
return;
-
MutexIDList ExclusiveLocksToAdd;
MutexIDList SharedLocksToAdd;
@@ -1858,6 +1878,13 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) {
return;
}
+ if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
+ if (Analyzer->Handler.issueBetaWarnings()) {
+ checkPtAccess(AE->getLHS(), AK);
+ return;
+ }
+ }
+
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
if (ME->isArrow())
checkPtAccess(ME->getBase(), AK);
@@ -1881,7 +1908,27 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) {
/// \brief Checks pt_guarded_by and pt_guarded_var attributes.
void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) {
- Exp = Exp->IgnoreParenCasts();
+ if (Analyzer->Handler.issueBetaWarnings()) {
+ while (true) {
+ if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
+ Exp = PE->getSubExpr();
+ continue;
+ }
+ if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
+ if (CE->getCastKind() == CK_ArrayToPointerDecay) {
+ // If it's an actual array, and not a pointer, then it's elements
+ // are protected by GUARDED_BY, not PT_GUARDED_BY;
+ checkAccess(CE->getSubExpr(), AK);
+ return;
+ }
+ Exp = CE->getSubExpr();
+ continue;
+ }
+ break;
+ }
+ }
+ else
+ Exp = Exp->IgnoreParenCasts();
const ValueDecl *D = getValueDecl(Exp);
if (!D || !D->hasAttrs())
@@ -1909,6 +1956,7 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) {
/// the same signature as const method calls can be also treated as reads.
///
void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
+ SourceLocation Loc = Exp->getExprLoc();
const AttrVec &ArgAttrs = D->getAttrs();
MutexIDList ExclusiveLocksToAdd;
MutexIDList SharedLocksToAdd;
@@ -1933,6 +1981,32 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
break;
}
+ // An assert will add a lock to the lockset, but will not generate
+ // a warning if it is already there, and will not generate a warning
+ // if it is not removed.
+ case attr::AssertExclusiveLock: {
+ AssertExclusiveLockAttr *A = cast<AssertExclusiveLockAttr>(At);
+
+ MutexIDList AssertLocks;
+ Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
+ for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) {
+ Analyzer->addLock(FSet, AssertLocks[i],
+ LockData(Loc, LK_Exclusive, false, true));
+ }
+ break;
+ }
+ case attr::AssertSharedLock: {
+ AssertSharedLockAttr *A = cast<AssertSharedLockAttr>(At);
+
+ MutexIDList AssertLocks;
+ Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
+ for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) {
+ Analyzer->addLock(FSet, AssertLocks[i],
+ LockData(Loc, LK_Shared, false, true));
+ }
+ break;
+ }
+
// When we encounter an unlock function, we need to remove unlocked
// mutexes from the lockset, and flag a warning if they are not there.
case attr::UnlockFunction: {
@@ -1986,7 +2060,6 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
}
// Add locks.
- SourceLocation Loc = Exp->getExprLoc();
for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
Analyzer->addLock(FSet, ExclusiveLocksToAdd[i],
LockData(Loc, LK_Exclusive, isScopedVar));
@@ -2052,6 +2125,7 @@ void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
checkAccess(BO->getLHS(), AK_Written);
}
+
/// Whenever we do an LValue to Rvalue cast, we are reading a variable and
/// need to ensure we hold any required mutexes.
/// FIXME: Deal with non-primitive types.
@@ -2091,9 +2165,19 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) {
checkAccess(Source, AK_Read);
break;
}
+ case OO_Star:
+ case OO_Arrow:
+ case OO_Subscript: {
+ if (Analyzer->Handler.issueBetaWarnings()) {
+ const Expr *Obj = OE->getArg(0);
+ checkAccess(Obj, AK_Read);
+ checkPtAccess(Obj, AK_Read);
+ }
+ break;
+ }
default: {
- const Expr *Source = OE->getArg(0);
- checkAccess(Source, AK_Read);
+ const Expr *Obj = OE->getArg(0);
+ checkAccess(Obj, AK_Read);
break;
}
}
@@ -2160,21 +2244,28 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
bool Modify) {
FactSet FSet1Orig = FSet1;
+ // Find locks in FSet2 that conflict or are not in FSet1, and warn.
for (FactSet::const_iterator I = FSet2.begin(), E = FSet2.end();
I != E; ++I) {
const SExpr &FSet2Mutex = FactMan[*I].MutID;
const LockData &LDat2 = FactMan[*I].LDat;
+ FactSet::iterator I1 = FSet1.findLockIter(FactMan, FSet2Mutex);
- if (const LockData *LDat1 = FSet1.findLock(FactMan, FSet2Mutex)) {
+ if (I1 != FSet1.end()) {
+ const LockData* LDat1 = &FactMan[*I1].LDat;
if (LDat1->LKind != LDat2.LKind) {
Handler.handleExclusiveAndShared(FSet2Mutex.toString(),
LDat2.AcquireLoc,
LDat1->AcquireLoc);
if (Modify && LDat1->LKind != LK_Exclusive) {
- FSet1.removeLock(FactMan, FSet2Mutex);
- FSet1.addLock(FactMan, FSet2Mutex, LDat2);
+ // Take the exclusive lock, which is the one in FSet2.
+ *I1 = *I;
}
}
+ else if (LDat1->Asserted && !LDat2.Asserted) {
+ // The non-asserted lock in FSet2 is the one we want to track.
+ *I1 = *I;
+ }
} else {
if (LDat2.UnderlyingMutex.isValid()) {
if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) {
@@ -2186,14 +2277,15 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
JoinLoc, LEK1);
}
}
- else if (!LDat2.Managed && !FSet2Mutex.isUniversal())
+ else if (!LDat2.Managed && !FSet2Mutex.isUniversal() && !LDat2.Asserted)
Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(),
LDat2.AcquireLoc,
JoinLoc, LEK1);
}
}
- for (FactSet::const_iterator I = FSet1.begin(), E = FSet1.end();
+ // Find locks in FSet1 that are not in FSet2, and remove them.
+ for (FactSet::const_iterator I = FSet1Orig.begin(), E = FSet1Orig.end();
I != E; ++I) {
const SExpr &FSet1Mutex = FactMan[*I].MutID;
const LockData &LDat1 = FactMan[*I].LDat;
@@ -2209,7 +2301,7 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
JoinLoc, LEK1);
}
}
- else if (!LDat1.Managed && !FSet1Mutex.isUniversal())
+ else if (!LDat1.Managed && !FSet1Mutex.isUniversal() && !LDat1.Asserted)
Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(),
LDat1.AcquireLoc,
JoinLoc, LEK2);
@@ -2305,8 +2397,6 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
= dyn_cast<SharedLocksRequiredAttr>(Attr)) {
getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D);
} else if (UnlockFunctionAttr *A = dyn_cast<UnlockFunctionAttr>(Attr)) {
- if (!Handler.issueBetaWarnings())
- return;
// UNLOCK_FUNCTION() is used to hide the underlying lock implementation.
// We must ignore such methods.
if (A->args_size() == 0)
@@ -2316,15 +2406,11 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
getMutexIDs(LocksReleased, A, (Expr*) 0, D);
} else if (ExclusiveLockFunctionAttr *A
= dyn_cast<ExclusiveLockFunctionAttr>(Attr)) {
- if (!Handler.issueBetaWarnings())
- return;
if (A->args_size() == 0)
return;
getMutexIDs(ExclusiveLocksAcquired, A, (Expr*) 0, D);
} else if (SharedLockFunctionAttr *A
= dyn_cast<SharedLockFunctionAttr>(Attr)) {
- if (!Handler.issueBetaWarnings())
- return;
if (A->args_size() == 0)
return;
getMutexIDs(SharedLocksAcquired, A, (Expr*) 0, D);