aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/AnalysisBasedWarnings.cpp
diff options
context:
space:
mode:
authorCaitlin Sadowski <supertri@google.com>2011-08-23 18:46:34 +0000
committerCaitlin Sadowski <supertri@google.com>2011-08-23 18:46:34 +0000
commit3ac1fbc303d22af2e11a14023ecee7bd7b7d0bfd (patch)
tree568b56519f7184f527aa21521573b65d31d82f28 /lib/Sema/AnalysisBasedWarnings.cpp
parentf857186fd1995b34185d063a29b11ad4f948519f (diff)
Thread-safety analysis: adding in a basic lockset tracking system. This
system flags an error when unlocking a lock which was not held, locking the same lock twice, having a different lockset on each iteration of a loop, or going out of scope while still holding a lock. In order to successfully use the lockset, this patch also makes sure that attribute arguments are attached correctly for later parsing. This patch was also worked on by DeLesley Hutchins. Note: This patch has been reviewed by Chandler Carruth and Jeffrey Yasskin. Feel free to provide post-commit review comments for a subsequent patch. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@138350 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/AnalysisBasedWarnings.cpp')
-rw-r--r--lib/Sema/AnalysisBasedWarnings.cpp561
1 files changed, 561 insertions, 0 deletions
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp
index 9f5ab50266..99525bae9f 100644
--- a/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/lib/Sema/AnalysisBasedWarnings.cpp
@@ -25,6 +25,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/EvaluatedExprVisitor.h"
+#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/Analyses/ReachableCode.h"
@@ -32,7 +33,13 @@
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/Analyses/UninitializedValues.h"
#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <vector>
using namespace clang;
@@ -585,6 +592,551 @@ public:
};
}
+
+//===----------------------------------------------------------------------===//
+// -Wthread-safety
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// \brief Implements a set of CFGBlocks using a BitVector.
+///
+/// This class contains a minimal interface, primarily dictated by the SetType
+/// template parameter of the llvm::po_iterator template, as used with external
+/// storage. We also use this set to keep track of which CFGBlocks we visit
+/// during the analysis.
+class CFGBlockSet {
+ llvm::BitVector VisitedBlockIDs;
+
+public:
+ // po_iterator requires this iterator, but the only interface needed is the
+ // value_type typedef.
+ struct iterator {
+ typedef const CFGBlock *value_type;
+ };
+
+ CFGBlockSet() {}
+ CFGBlockSet(const CFG *G) : VisitedBlockIDs(G->getNumBlockIDs(), false) {}
+
+ /// \brief Set the bit associated with a particular CFGBlock.
+ /// This is the important method for the SetType template parameter.
+ bool insert(const CFGBlock *Block) {
+ if (VisitedBlockIDs.test(Block->getBlockID()))
+ return false;
+ VisitedBlockIDs.set(Block->getBlockID());
+ return true;
+ }
+
+ /// \brief Check if the bit for a CFGBlock has been already set.
+ /// This mehtod is for tracking visited blocks in the main threadsafety loop.
+ bool alreadySet(const CFGBlock *Block) {
+ return VisitedBlockIDs.test(Block->getBlockID());
+ }
+};
+
+/// \brief We create a helper class which we use to iterate through CFGBlocks in
+/// the topological order.
+class TopologicallySortedCFG {
+ typedef llvm::po_iterator<const CFG*, CFGBlockSet, true> po_iterator;
+
+ std::vector<const CFGBlock*> Blocks;
+
+public:
+ typedef std::vector<const CFGBlock*>::reverse_iterator iterator;
+
+ TopologicallySortedCFG(const CFG *CFGraph) {
+ Blocks.reserve(CFGraph->getNumBlockIDs());
+ CFGBlockSet BSet(CFGraph);
+
+ for (po_iterator I = po_iterator::begin(CFGraph, BSet),
+ E = po_iterator::end(CFGraph, BSet); I != E; ++I) {
+ Blocks.push_back(*I);
+ }
+ }
+
+ iterator begin() {
+ return Blocks.rbegin();
+ }
+
+ iterator end() {
+ return Blocks.rend();
+ }
+};
+
+/// \brief A Lock object uniquely identifies a particular lock acquired, and is
+/// built from an Expr* (i.e. calling a lock function).
+///
+/// Thread-safety analysis works by comparing lock expressions. Within the
+/// body of a function, an expression such as "x->foo->bar.mu" will resolve to
+/// a particular lock object at run-time. Subsequent occurrences of the same
+/// expression (where "same" means syntactic equality) will refer to the same
+/// run-time object if three conditions hold:
+/// (1) Local variables in the expression, such as "x" have not changed.
+/// (2) Values on the heap that affect the expression have not changed.
+/// (3) The expression involves only pure function calls.
+/// The current implementation assumes, but does not verify, that multiple uses
+/// of the same lock expression satisfies these criteria.
+///
+/// Clang introduces an additional wrinkle, which is that it is difficult to
+/// derive canonical expressions, or compare expressions directly for equality.
+/// Thus, we identify a lock not by an Expr, but by the set of named
+/// declarations that are referenced by the Expr. In other words,
+/// x->foo->bar.mu will be a four element vector with the Decls for
+/// mu, bar, and foo, and x. The vector will uniquely identify the expression
+/// for all practical purposes.
+///
+/// Note we will need to perform substitution on "this" and function parameter
+/// names when constructing a lock expression.
+///
+/// For example:
+/// class C { Mutex Mu; void lock() EXCLUSIVE_LOCK_FUNCTION(this->Mu); };
+/// void myFunc(C *X) { ... X->lock() ... }
+/// The original expression for the lock acquired by myFunc is "this->Mu", but
+/// "X" is substituted for "this" so we get X->Mu();
+///
+/// For another example:
+/// foo(MyList *L) EXCLUSIVE_LOCKS_REQUIRED(L->Mu) { ... }
+/// MyList *MyL;
+/// foo(MyL); // requires lock MyL->Mu to be held
+///
+/// FIXME: In C++0x Mutexes are the objects that control access to shared
+/// variables, while Locks are the objects that acquire and release Mutexes. We
+/// may want to switch to this new terminology soon, in which case we should
+/// rename this class "Mutex" and rename "LockId" to "MutexId", as well as
+/// making sure that the terms Lock and Mutex throughout this code are
+/// consistent with C++0x
+///
+/// FIXME: We should also pick one and canonicalize all usage of lock vs acquire
+/// and unlock vs release as verbs.
+class LockID {
+ SmallVector<NamedDecl*, 2> DeclSeq;
+
+ /// Build a Decl sequence representing the lock from the given expression.
+ /// Recursive function that bottoms out when the final DeclRefExpr is reached.
+ void buildLock(Expr *Exp) {
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
+ NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
+ DeclSeq.push_back(ND);
+ } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
+ NamedDecl *ND = ME->getMemberDecl();
+ DeclSeq.push_back(ND);
+ buildLock(ME->getBase());
+ } else {
+ // FIXME: add diagnostic
+ llvm::report_fatal_error("Expected lock expression!");
+ }
+ }
+
+public:
+ LockID(Expr *LExpr) {
+ buildLock(LExpr);
+ assert(!DeclSeq.empty());
+ }
+
+ bool operator==(const LockID &other) const {
+ return DeclSeq == other.DeclSeq;
+ }
+
+ bool operator!=(const LockID &other) const {
+ return !(*this == other);
+ }
+
+ // SmallVector overloads Operator< to do lexicographic ordering. Note that
+ // we use pointer equality (and <) to compare NamedDecls. This means the order
+ // of locks in a lockset is nondeterministic. In order to output
+ // diagnostics in a deterministic ordering, we must order all diagnostics to
+ // output by SourceLocation when iterating through this lockset.
+ bool operator<(const LockID &other) const {
+ return DeclSeq < other.DeclSeq;
+ }
+
+ /// \brief Returns the name of the first Decl in the list for a given LockId;
+ /// e.g. the lock expression foo.bar() has name "bar".
+ /// The caret will point unambiguously to the lock expression, so using this
+ /// name in diagnostics is a way to get simple, and consistent, lock names.
+ /// We do not want to output the entire expression text for security reasons.
+ StringRef getName() const {
+ return DeclSeq.front()->getName();
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ for (SmallVectorImpl<NamedDecl*>::const_iterator I = DeclSeq.begin(),
+ E = DeclSeq.end(); I != E; ++I) {
+ ID.AddPointer(*I);
+ }
+ }
+};
+
+/// \brief This is a helper class that stores info about the most recent
+/// accquire of a Lock.
+///
+/// The main body of the analysis maps Locks to LockDatas.
+struct LockData {
+ SourceLocation AcquireLoc;
+
+ LockData(SourceLocation Loc) : AcquireLoc(Loc) {}
+
+ bool operator==(const LockData &other) const {
+ return AcquireLoc == other.AcquireLoc;
+ }
+
+ bool operator!=(const LockData &other) const {
+ return !(*this == other);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(AcquireLoc.getRawEncoding());
+ }
+};
+
+ /// A Lockset maps each lock (defined above) to information about how it has
+/// been locked.
+typedef llvm::ImmutableMap<LockID, LockData> Lockset;
+
+/// \brief We use this class to visit different types of expressions in
+/// CFGBlocks, and build up the lockset.
+/// An expression may cause us to add or remove locks from the lockset, or else
+/// output error messages related to missing locks.
+/// FIXME: In future, we may be able to not inherit from a visitor.
+class BuildLockset : public StmtVisitor<BuildLockset> {
+ Sema &S;
+ Lockset LSet;
+ Lockset::Factory &LocksetFactory;
+
+ // Helper functions
+ void RemoveLock(SourceLocation UnlockLoc, Expr *LockExp);
+ void AddLock(SourceLocation LockLoc, Expr *LockExp);
+
+public:
+ BuildLockset(Sema &S, Lockset LS, Lockset::Factory &F)
+ : StmtVisitor<BuildLockset>(), S(S), LSet(LS),
+ LocksetFactory(F) {}
+
+ Lockset getLockset() {
+ return LSet;
+ }
+
+ void VisitDeclRefExpr(DeclRefExpr *Exp);
+ void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp);
+};
+
+/// \brief Add a new lock to the lockset, warning if the lock is already there.
+/// \param LockExp The lock expression corresponding to the lock to be added
+/// \param LockLoc The source location of the acquire
+void BuildLockset::AddLock(SourceLocation LockLoc, Expr *LockExp) {
+ LockID Lock(LockExp);
+ LockData NewLockData(LockLoc);
+
+ if (LSet.contains(Lock))
+ S.Diag(LockLoc, diag::warn_double_lock) << Lock.getName();
+
+ LSet = LocksetFactory.add(LSet, Lock, NewLockData);
+}
+
+/// \brief Remove a lock from the lockset, warning if the lock is not there.
+/// \param LockExp The lock expression corresponding to the lock to be removed
+/// \param UnlockLoc The source location of the unlock (only used in error msg)
+void BuildLockset::RemoveLock(SourceLocation UnlockLoc, Expr *LockExp) {
+ LockID Lock(LockExp);
+
+ Lockset NewLSet = LocksetFactory.remove(LSet, Lock);
+ if(NewLSet == LSet)
+ S.Diag(UnlockLoc, diag::warn_unlock_but_no_acquire) << Lock.getName();
+
+ LSet = NewLSet;
+}
+
+void BuildLockset::VisitDeclRefExpr(DeclRefExpr *Exp) {
+ // FIXME: checking for guarded_by/var and pt_guarded_by/var
+}
+
+/// \brief When visiting CXXMemberCallExprs we need to examine the attributes on
+/// the method that is being called and add, remove or check locks in the
+/// lockset accordingly.
+void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
+ NamedDecl *D = dyn_cast<NamedDecl>(Exp->getCalleeDecl());
+ SourceLocation ExpLocation = Exp->getExprLoc();
+ Expr *Parent = Exp->getImplicitObjectArgument();
+
+ if(!D || !D->hasAttrs())
+ return;
+
+ AttrVec &ArgAttrs = D->getAttrs();
+ for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
+ Attr *Attr = ArgAttrs[i];
+ switch (Attr->getKind()) {
+ // When we encounter an exclusive lock function, we need to add the lock
+ // to our lockset.
+ case attr::ExclusiveLockFunction: {
+ ExclusiveLockFunctionAttr *ELFAttr =
+ cast<ExclusiveLockFunctionAttr>(Attr);
+
+ if (ELFAttr->args_size() == 0) {// The lock held is the "this" object.
+ AddLock(ExpLocation, Parent);
+ break;
+ }
+
+ for (ExclusiveLockFunctionAttr::args_iterator I = ELFAttr->args_begin(),
+ E = ELFAttr->args_end(); I != E; ++I)
+ AddLock(ExpLocation, *I);
+ // FIXME: acquired_after/acquired_before annotations
+ break;
+ }
+
+ // When we encounter an unlock function, we need to remove unlocked locks
+ // from the lockset, and flag a warning if they are not there.
+ case attr::UnlockFunction: {
+ UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr);
+
+ if (UFAttr->args_size() == 0) { // The lock held is the "this" object.
+ RemoveLock(ExpLocation, Parent);
+ break;
+ }
+
+ for (UnlockFunctionAttr::args_iterator I = UFAttr->args_begin(),
+ E = UFAttr->args_end(); I != E; ++I)
+ RemoveLock(ExpLocation, *I);
+ break;
+ }
+
+ // Ignore other (non thread-safety) attributes
+ default:
+ break;
+ }
+ }
+}
+
+typedef std::pair<SourceLocation, PartialDiagnostic> DelayedDiag;
+typedef llvm::SmallVector<DelayedDiag, 4> DiagList;
+
+struct SortDiagBySourceLocation {
+ Sema &S;
+
+ SortDiagBySourceLocation(Sema &S) : S(S) {}
+
+ bool operator()(const DelayedDiag &left, const DelayedDiag &right) {
+ // Although this call will be slow, this is only called when outputting
+ // multiple warnings.
+ return S.getSourceManager().isBeforeInTranslationUnit(left.first,
+ right.first);
+ }
+};
+} // end anonymous namespace
+
+/// \brief Emit all buffered diagnostics in order of sourcelocation.
+/// We need to output diagnostics produced while iterating through
+/// the lockset in deterministic order, so this function orders diagnostics
+/// and outputs them.
+static void EmitDiagnostics(Sema &S, DiagList &D) {
+ SortDiagBySourceLocation SortDiagBySL(S);
+ sort(D.begin(), D.end(), SortDiagBySL);
+ for (DiagList::iterator I = D.begin(), E = D.end(); I != E; ++I)
+ S.Diag(I->first, I->second);
+}
+
+/// \brief Compute the intersection of two locksets and issue warnings for any
+/// locks in the symmetric difference.
+///
+/// This function is used at a merge point in the CFG when comparing the lockset
+/// of each branch being merged. For example, given the following sequence:
+/// A; if () then B; else C; D; we need to check that the lockset after B and C
+/// are the same. In the event of a difference, we use the intersection of these
+/// two locksets at the start of D.
+static Lockset intersectAndWarn(Sema &S, Lockset LSet1, Lockset LSet2,
+ Lockset::Factory &Fact) {
+ Lockset Intersection = LSet1;
+ DiagList Warnings;
+
+ for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) {
+ if (!LSet1.contains(I.getKey())) {
+ const LockID &MissingLock = I.getKey();
+ const LockData &MissingLockData = I.getData();
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
+ Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+ }
+ }
+
+ for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I) {
+ if (!LSet2.contains(I.getKey())) {
+ const LockID &MissingLock = I.getKey();
+ const LockData &MissingLockData = I.getData();
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
+ Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+ Intersection = Fact.remove(Intersection, MissingLock);
+ }
+ }
+
+ EmitDiagnostics(S, Warnings);
+ return Intersection;
+}
+
+/// \brief Returns the location of the first Stmt in a Block.
+static SourceLocation getFirstStmtLocation(CFGBlock *Block) {
+ for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
+ BI != BE; ++BI) {
+ if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&(*BI)))
+ return CfgStmt->getStmt()->getLocStart();
+ }
+ return SourceLocation();
+}
+
+/// \brief Warn about different locksets along backedges of loops.
+/// This function is called when we encounter a back edge. At that point,
+/// we need to verify that the lockset before taking the backedge is the
+/// same as the lockset before entering the loop.
+///
+/// \param LoopEntrySet Locks held before starting the loop
+/// \param LoopReentrySet Locks held in the last CFG block of the loop
+static void warnBackEdgeUnequalLocksets(Sema &S, const Lockset LoopReentrySet,
+ const Lockset LoopEntrySet,
+ SourceLocation FirstLocInLoop) {
+ assert(FirstLocInLoop.isValid());
+ DiagList Warnings;
+
+ // Warn for locks held at the start of the loop, but not the end.
+ for (Lockset::iterator I = LoopEntrySet.begin(), E = LoopEntrySet.end();
+ I != E; ++I) {
+ if (!LoopReentrySet.contains(I.getKey())) {
+ const LockID &MissingLock = I.getKey();
+ // We report this error at the location of the first statement in a loop
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_expecting_lock_held_on_loop)
+ << MissingLock.getName();
+ Warnings.push_back(DelayedDiag(FirstLocInLoop, Warning));
+ }
+ }
+
+ // Warn for locks held at the end of the loop, but not at the start.
+ for (Lockset::iterator I = LoopReentrySet.begin(), E = LoopReentrySet.end();
+ I != E; ++I) {
+ if (!LoopEntrySet.contains(I.getKey())) {
+ const LockID &MissingLock = I.getKey();
+ const LockData &MissingLockData = I.getData();
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
+ Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+ }
+ }
+
+ EmitDiagnostics(S, Warnings);
+}
+
+/// \brief Check a function's CFG for thread-safety violations.
+///
+/// We traverse the blocks in the CFG, compute the set of locks that are held
+/// at the end of each block, and issue warnings for thread safety violations.
+/// Each block in the CFG is traversed exactly once.
+static void checkThreadSafety(Sema &S, AnalysisContext &AC) {
+ CFG *CFGraph = AC.getCFG();
+ if (!CFGraph) return;
+
+ StringRef FunName;
+ if (const NamedDecl *ContextDecl = dyn_cast<NamedDecl>(AC.getDecl()))
+ FunName = ContextDecl->getName();
+
+ Lockset::Factory LocksetFactory;
+
+ // FIXME: Swith to SmallVector? Otherwise improve performance impact?
+ std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(),
+ LocksetFactory.getEmptyMap());
+ std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(),
+ LocksetFactory.getEmptyMap());
+
+ // We need to explore the CFG via a "topological" ordering.
+ // That way, we will be guaranteed to have information about required
+ // predecessor locksets when exploring a new block.
+ TopologicallySortedCFG SortedGraph(CFGraph);
+ CFGBlockSet VisitedBlocks(CFGraph);
+
+ for (TopologicallySortedCFG::iterator I = SortedGraph.begin(),
+ E = SortedGraph.end(); I!= E; ++I) {
+ const CFGBlock *CurrBlock = *I;
+ int CurrBlockID = CurrBlock->getBlockID();
+
+ VisitedBlocks.insert(CurrBlock);
+
+ // Use the default initial lockset in case there are no predecessors.
+ Lockset &Entryset = EntryLocksets[CurrBlockID];
+ Lockset &Exitset = ExitLocksets[CurrBlockID];
+
+ // Iterate through the predecessor blocks and warn if the lockset for all
+ // predecessors is not the same. We take the entry lockset of the current
+ // block to be the intersection of all previous locksets.
+ // FIXME: By keeping the intersection, we may output more errors in future
+ // for a lock which is not in the intersection, but was in the union. We
+ // may want to also keep the union in future. As an example, let's say
+ // the intersection contains Lock L, and the union contains L and M.
+ // Later we unlock M. At this point, we would output an error because we
+ // never locked M; although the real error is probably that we forgot to
+ // lock M on all code paths. Conversely, let's say that later we lock M.
+ // In this case, we should compare against the intersection instead of the
+ // union because the real error is probably that we forgot to unlock M on
+ // all code paths.
+ bool LocksetInitialized = false;
+ for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
+ PE = CurrBlock->pred_end(); PI != PE; ++PI) {
+
+ // if *PI -> CurrBlock is a back edge
+ if (!VisitedBlocks.alreadySet(*PI))
+ continue;
+
+ int PrevBlockID = (*PI)->getBlockID();
+ if (!LocksetInitialized) {
+ Entryset = ExitLocksets[PrevBlockID];
+ LocksetInitialized = true;
+ } else {
+ Entryset = intersectAndWarn(S, Entryset, ExitLocksets[PrevBlockID],
+ LocksetFactory);
+ }
+ }
+
+ BuildLockset LocksetBuilder(S, Entryset, LocksetFactory);
+ for (CFGBlock::const_iterator BI = CurrBlock->begin(),
+ BE = CurrBlock->end(); BI != BE; ++BI) {
+ if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*BI)) {
+ LocksetBuilder.Visit(CfgStmt->getStmt());
+ }
+ }
+ Exitset = LocksetBuilder.getLockset();
+
+ // For every back edge from CurrBlock (the end of the loop) to another block
+ // (FirstLoopBlock) we need to check that the Lockset of Block is equal to
+ // the one held at the beginning of FirstLoopBlock. We can look up the
+ // Lockset held at the beginning of FirstLoopBlock in the EntryLockSets map.
+ for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
+ SE = CurrBlock->succ_end(); SI != SE; ++SI) {
+
+ // if CurrBlock -> *SI is *not* a back edge
+ if (!VisitedBlocks.alreadySet(*SI))
+ continue;
+
+ CFGBlock *FirstLoopBlock = *SI;
+ SourceLocation FirstLoopLocation = getFirstStmtLocation(FirstLoopBlock);
+
+ Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
+ Lockset LoopEnd = ExitLocksets[CurrBlockID];
+ warnBackEdgeUnequalLocksets(S, LoopEnd, PreLoop, FirstLoopLocation);
+ }
+ }
+
+ Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
+ if (!FinalLockset.isEmpty()) {
+ DiagList Warnings;
+ for (Lockset::iterator I=FinalLockset.begin(), E=FinalLockset.end();
+ I != E; ++I) {
+ const LockID &MissingLock = I.getKey();
+ const LockData &MissingLockData = I.getData();
+ PartialDiagnostic Warning =
+ S.PDiag(diag::warn_locks_not_released)
+ << MissingLock.getName() << FunName;
+ Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
+ }
+ EmitDiagnostics(S, Warnings);
+ }
+}
+
+
//===----------------------------------------------------------------------===//
// AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
// warnings on a function, method, or block.
@@ -593,6 +1145,7 @@ public:
clang::sema::AnalysisBasedWarnings::Policy::Policy() {
enableCheckFallThrough = 1;
enableCheckUnreachable = 0;
+ enableThreadSafetyAnalysis = 0;
}
clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
@@ -610,6 +1163,10 @@ clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
DefaultPolicy.enableCheckUnreachable = (unsigned)
(D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) !=
Diagnostic::Ignored);
+ DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
+ (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
+ Diagnostic::Ignored);
+
}
static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) {
@@ -735,6 +1292,10 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
if (P.enableCheckUnreachable)
CheckUnreachable(S, AC);
+ // Check for thread safety violations
+ if (P.enableThreadSafetyAnalysis)
+ checkThreadSafety(S, AC);
+
if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
!= Diagnostic::Ignored ||
Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart())