diff options
Diffstat (limited to 'lib/Analysis/ThreadSafety.cpp')
-rw-r--r-- | lib/Analysis/ThreadSafety.cpp | 150 |
1 files changed, 91 insertions, 59 deletions
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp index 7bb54d6ad3..479d9a301f 100644 --- a/lib/Analysis/ThreadSafety.cpp +++ b/lib/Analysis/ThreadSafety.cpp @@ -784,7 +784,7 @@ struct LockData { /// \brief A FactEntry stores a single fact that is known at a particular point /// in the program execution. Currently, this is information regarding a lock -/// that is held at that point. +/// that is held at that point. struct FactEntry { SExpr MutID; LockData LDat; @@ -797,7 +797,7 @@ struct FactEntry { typedef unsigned short FactID; -/// \brief FactManager manages the memory for all facts that are created during +/// \brief FactManager manages the memory for all facts that are created during /// the analysis of a single routine. class FactManager { private: @@ -815,9 +815,9 @@ public: /// \brief A FactSet is the set of facts that are known to be true at a -/// particular program point. FactSets must be small, because they are +/// particular program point. FactSets must be small, because they are /// frequently copied, and are thus implemented as a set of indices into a -/// table maintained by a FactManager. A typical FactSet only holds 1 or 2 +/// table maintained by a FactManager. A typical FactSet only holds 1 or 2 /// locks, so we can get away with doing a linear search for lookup. Note /// that a hashtable or map is inappropriate in this case, because lookups /// may involve partial pattern matches, rather than exact matches. @@ -1858,13 +1858,11 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) { return; } - if (Analyzer->Handler.issueBetaWarnings()) { - if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) { - if (ME->isArrow()) - checkPtAccess(ME->getBase(), AK); - else - checkAccess(ME->getBase(), AK); - } + if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) { + if (ME->isArrow()) + checkPtAccess(ME->getBase(), AK); + else + checkAccess(ME->getBase(), AK); } const ValueDecl *D = getValueDecl(Exp); @@ -2065,40 +2063,38 @@ void BuildLockset::VisitCastExpr(CastExpr *CE) { void BuildLockset::VisitCallExpr(CallExpr *Exp) { - if (Analyzer->Handler.issueBetaWarnings()) { - if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp)) { - MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee()); - // ME can be null when calling a method pointer - CXXMethodDecl *MD = CE->getMethodDecl(); - - if (ME && MD) { - if (ME->isArrow()) { - if (MD->isConst()) { - checkPtAccess(CE->getImplicitObjectArgument(), AK_Read); - } else { // FIXME -- should be AK_Written - checkPtAccess(CE->getImplicitObjectArgument(), AK_Read); - } - } else { - if (MD->isConst()) - checkAccess(CE->getImplicitObjectArgument(), AK_Read); - else // FIXME -- should be AK_Written - checkAccess(CE->getImplicitObjectArgument(), AK_Read); + if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp)) { + MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee()); + // ME can be null when calling a method pointer + CXXMethodDecl *MD = CE->getMethodDecl(); + + if (ME && MD) { + if (ME->isArrow()) { + if (MD->isConst()) { + checkPtAccess(CE->getImplicitObjectArgument(), AK_Read); + } else { // FIXME -- should be AK_Written + checkPtAccess(CE->getImplicitObjectArgument(), AK_Read); } + } else { + if (MD->isConst()) + checkAccess(CE->getImplicitObjectArgument(), AK_Read); + else // FIXME -- should be AK_Written + checkAccess(CE->getImplicitObjectArgument(), AK_Read); } - } else if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) { - switch (OE->getOperator()) { - case OO_Equal: { - const Expr *Target = OE->getArg(0); - const Expr *Source = OE->getArg(1); - checkAccess(Target, AK_Written); - checkAccess(Source, AK_Read); - break; - } - default: { - const Expr *Source = OE->getArg(0); - checkAccess(Source, AK_Read); - break; - } + } + } else if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) { + switch (OE->getOperator()) { + case OO_Equal: { + const Expr *Target = OE->getArg(0); + const Expr *Source = OE->getArg(1); + checkAccess(Target, AK_Written); + checkAccess(Source, AK_Read); + break; + } + default: { + const Expr *Source = OE->getArg(0); + checkAccess(Source, AK_Read); + break; } } } @@ -2109,12 +2105,10 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) { } void BuildLockset::VisitCXXConstructExpr(CXXConstructExpr *Exp) { - if (Analyzer->Handler.issueBetaWarnings()) { - const CXXConstructorDecl *D = Exp->getConstructor(); - if (D && D->isCopyConstructor()) { - const Expr* Source = Exp->getArg(0); - checkAccess(Source, AK_Read); - } + const CXXConstructorDecl *D = Exp->getConstructor(); + if (D && D->isCopyConstructor()) { + const Expr* Source = Exp->getArg(0); + checkAccess(Source, AK_Read); } // FIXME -- only handles constructors in DeclStmt below. } @@ -2285,6 +2279,10 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { // Fill in source locations for all CFGBlocks. findBlockLocations(CFGraph, SortedGraph, BlockInfo); + MutexIDList ExclusiveLocksAcquired; + MutexIDList SharedLocksAcquired; + MutexIDList LocksReleased; + // Add locks from exclusive_locks_required and shared_locks_required // to initial lockset. Also turn off checking for lock and unlock functions. // FIXME: is there a more intelligent way to check lock/unlock functions? @@ -2306,15 +2304,30 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { } else if (SharedLocksRequiredAttr *A = dyn_cast<SharedLocksRequiredAttr>(Attr)) { getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D); - } else if (isa<UnlockFunctionAttr>(Attr)) { - // Don't try to check unlock functions for now - return; - } else if (isa<ExclusiveLockFunctionAttr>(Attr)) { - // Don't try to check lock functions for now - return; - } else if (isa<SharedLockFunctionAttr>(Attr)) { - // Don't try to check lock functions for now - return; + } 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) + return; + // FIXME -- deal with exclusive vs. shared unlock functions? + getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D); + 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); } else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) { // Don't try to check trylock functions for now return; @@ -2497,8 +2510,27 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { if (!Final->Reachable) return; + // By default, we expect all locks held on entry to be held on exit. + FactSet ExpectedExitSet = Initial->EntrySet; + + // Adjust the expected exit set by adding or removing locks, as declared + // by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then + // issue the appropriate warning. + // FIXME: the location here is not quite right. + for (unsigned i=0,n=ExclusiveLocksAcquired.size(); i<n; ++i) { + ExpectedExitSet.addLock(FactMan, ExclusiveLocksAcquired[i], + LockData(D->getLocation(), LK_Exclusive)); + } + for (unsigned i=0,n=SharedLocksAcquired.size(); i<n; ++i) { + ExpectedExitSet.addLock(FactMan, SharedLocksAcquired[i], + LockData(D->getLocation(), LK_Shared)); + } + for (unsigned i=0,n=LocksReleased.size(); i<n; ++i) { + ExpectedExitSet.removeLock(FactMan, LocksReleased[i]); + } + // FIXME: Should we call this function for all blocks which exit the function? - intersectAndWarn(Initial->EntrySet, Final->ExitSet, + intersectAndWarn(ExpectedExitSet, Final->ExitSet, Final->ExitLoc, LEK_LockedAtEndOfFunction, LEK_NotLockedAtEndOfFunction, |