diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp | 160 |
1 files changed, 123 insertions, 37 deletions
diff --git a/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp index 479d9a3..6e0e173 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp +++ b/contrib/llvm/tools/clang/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); |