diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-02-03 04:45:26 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-02-03 04:45:26 +0000 |
commit | 2e5156274b8051217565b557bfa14c80f7990e9c (patch) | |
tree | 300453996c9eabf553db8bd94a4bf611f8cd4b9f /lib | |
parent | aacde7174af6c5759b52dc0ceb7b167d323afb6a (diff) |
Thread safety analysis:
* When we detect that a CFG block has inconsistent lock sets, point the
diagnostic at the location where we found the inconsistency, and point a note
at somewhere the inconsistently-locked mutex was locked.
* Fix the wording of the normal (non-loop, non-end-of-function) case of this
diagnostic to not suggest that the mutex is going out of scope.
* Fix the diagnostic emission code to keep a warning and its note together when
sorting the diagnostics into source location order.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149669 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Analysis/ThreadSafety.cpp | 96 | ||||
-rw-r--r-- | lib/Sema/AnalysisBasedWarnings.cpp | 74 |
2 files changed, 126 insertions, 44 deletions
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp index 69ea830bdd..f4386538d7 100644 --- a/lib/Analysis/ThreadSafety.cpp +++ b/lib/Analysis/ThreadSafety.cpp @@ -290,6 +290,8 @@ typedef llvm::ImmutableMap<NamedDecl*, unsigned> LocalVarContext; class LocalVariableMap; +/// A side (entry or exit) of a CFG node. +enum CFGBlockSide { CBS_Entry, CBS_Exit }; /// CFGBlockInfo is a struct which contains all the information that is /// maintained for each block in the CFG. See LocalVariableMap for more @@ -299,8 +301,17 @@ struct CFGBlockInfo { Lockset ExitSet; // Lockset held at exit from block LocalVarContext EntryContext; // Context held at entry to block LocalVarContext ExitContext; // Context held at exit from block + SourceLocation EntryLoc; // Location of first statement in block + SourceLocation ExitLoc; // Location of last statement in block. unsigned EntryIndex; // Used to replay contexts later + const Lockset &getSet(CFGBlockSide Side) const { + return Side == CBS_Entry ? EntrySet : ExitSet; + } + SourceLocation getLocation(CFGBlockSide Side) const { + return Side == CBS_Entry ? EntryLoc : ExitLoc; + } + private: CFGBlockInfo(Lockset EmptySet, LocalVarContext EmptyCtx) : EntrySet(EmptySet), ExitSet(EmptySet), @@ -760,6 +771,51 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph, saveContext(0, BlockInfo[exitID].ExitContext); } +/// Find the appropriate source locations to use when producing diagnostics for +/// each block in the CFG. +static void findBlockLocations(CFG *CFGraph, + PostOrderCFGView *SortedGraph, + std::vector<CFGBlockInfo> &BlockInfo) { + for (PostOrderCFGView::iterator I = SortedGraph->begin(), + E = SortedGraph->end(); I!= E; ++I) { + const CFGBlock *CurrBlock = *I; + CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()]; + + // Find the source location of the last statement in the block, if the + // block is not empty. + if (const Stmt *S = CurrBlock->getTerminator()) { + CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getLocStart(); + } else { + for (CFGBlock::const_reverse_iterator BI = CurrBlock->rbegin(), + BE = CurrBlock->rend(); BI != BE; ++BI) { + // FIXME: Handle other CFGElement kinds. + if (const CFGStmt *CS = dyn_cast<CFGStmt>(&*BI)) { + CurrBlockInfo->ExitLoc = CS->getStmt()->getLocStart(); + break; + } + } + } + + if (!CurrBlockInfo->ExitLoc.isInvalid()) { + // This block contains at least one statement. Find the source location + // of the first statement in the block. + for (CFGBlock::const_iterator BI = CurrBlock->begin(), + BE = CurrBlock->end(); BI != BE; ++BI) { + // FIXME: Handle other CFGElement kinds. + if (const CFGStmt *CS = dyn_cast<CFGStmt>(&*BI)) { + CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart(); + break; + } + } + } else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() && + CurrBlock != &CFGraph->getExit()) { + // The block is empty, and has a single predecessor. Use its exit + // location. + CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = + BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc; + } + } +} /// \brief Class which implements the core thread safety analysis routines. class ThreadSafetyAnalyzer { @@ -772,7 +828,8 @@ class ThreadSafetyAnalyzer { public: ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {} - Lockset intersectAndWarn(const Lockset LSet1, const Lockset LSet2, + Lockset intersectAndWarn(const CFGBlockInfo &Block1, CFGBlockSide Side1, + const CFGBlockInfo &Block2, CFGBlockSide Side2, LockErrorKind LEK); Lockset addLock(Lockset &LSet, Expr *MutexExp, const NamedDecl *D, @@ -1304,9 +1361,14 @@ void BuildLockset::VisitDeclStmt(DeclStmt *S) { /// 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. -Lockset ThreadSafetyAnalyzer::intersectAndWarn(const Lockset LSet1, - const Lockset LSet2, +Lockset ThreadSafetyAnalyzer::intersectAndWarn(const CFGBlockInfo &Block1, + CFGBlockSide Side1, + const CFGBlockInfo &Block2, + CFGBlockSide Side2, LockErrorKind LEK) { + Lockset LSet1 = Block1.getSet(Side1); + Lockset LSet2 = Block2.getSet(Side2); + Lockset Intersection = LSet1; for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) { const MutexID &LSet2Mutex = I.getKey(); @@ -1322,7 +1384,8 @@ Lockset ThreadSafetyAnalyzer::intersectAndWarn(const Lockset LSet1, } } else { Handler.handleMutexHeldEndOfScope(LSet2Mutex.getName(), - LSet2LockData.AcquireLoc, LEK); + LSet2LockData.AcquireLoc, + Block1.getLocation(Side1), LEK); } } @@ -1331,7 +1394,8 @@ Lockset ThreadSafetyAnalyzer::intersectAndWarn(const Lockset LSet1, const MutexID &Mutex = I.getKey(); const LockData &MissingLock = I.getData(); Handler.handleMutexHeldEndOfScope(Mutex.getName(), - MissingLock.AcquireLoc, LEK); + MissingLock.AcquireLoc, + Block2.getLocation(Side2), LEK); Intersection = LocksetFactory.remove(Intersection, Mutex); } } @@ -1377,6 +1441,9 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { // Compute SSA names for local variables LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo); + // Fill in source locations for all CFGBlocks. + findBlockLocations(CFGraph, SortedGraph, BlockInfo); + // Add locks from exclusive_locks_required and shared_locks_required // to initial lockset. if (!SortedGraph->empty() && D->hasAttrs()) { @@ -1456,7 +1523,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { LocksetInitialized = true; } else { CurrBlockInfo->EntrySet = - intersectAndWarn(CurrBlockInfo->EntrySet, PrevBlockInfo->ExitSet, + intersectAndWarn(*CurrBlockInfo, CBS_Entry, + *PrevBlockInfo, CBS_Exit, LEK_LockedSomePredecessors); } } @@ -1482,7 +1550,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { bool IsLoop = Terminator && isa<ContinueStmt>(Terminator); // Do not update EntrySet. - intersectAndWarn(CurrBlockInfo->EntrySet, PrevBlockInfo->ExitSet, + intersectAndWarn(*CurrBlockInfo, CBS_Entry, *PrevBlockInfo, CBS_Exit, IsLoop ? LEK_LockedSomeLoopIterations : LEK_LockedSomePredecessors); } @@ -1541,17 +1609,19 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { continue; CFGBlock *FirstLoopBlock = *SI; - Lockset PreLoop = BlockInfo[FirstLoopBlock->getBlockID()].EntrySet; - Lockset LoopEnd = BlockInfo[CurrBlockID].ExitSet; - intersectAndWarn(LoopEnd, PreLoop, LEK_LockedSomeLoopIterations); + CFGBlockInfo &PreLoop = BlockInfo[FirstLoopBlock->getBlockID()]; + CFGBlockInfo &LoopEnd = BlockInfo[CurrBlockID]; + intersectAndWarn(LoopEnd, CBS_Exit, PreLoop, CBS_Entry, + LEK_LockedSomeLoopIterations); } } - Lockset InitialLockset = BlockInfo[CFGraph->getEntry().getBlockID()].EntrySet; - Lockset FinalLockset = BlockInfo[CFGraph->getExit().getBlockID()].ExitSet; + CFGBlockInfo &Initial = BlockInfo[CFGraph->getEntry().getBlockID()]; + CFGBlockInfo &Final = BlockInfo[CFGraph->getExit().getBlockID()]; // FIXME: Should we call this function for all blocks which exit the function? - intersectAndWarn(InitialLockset, FinalLockset, LEK_LockedAtEndOfFunction); + intersectAndWarn(Initial, CBS_Entry, Final, CBS_Exit, + LEK_LockedAtEndOfFunction); } } // end anonymous namespace diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index e2d1e8c331..71bf3359ea 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -592,7 +592,8 @@ private: //===----------------------------------------------------------------------===// namespace clang { namespace thread_safety { -typedef std::pair<SourceLocation, PartialDiagnostic> DelayedDiag; +typedef llvm::SmallVector<PartialDiagnosticAt, 1> OptionalNotes; +typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag; typedef llvm::SmallVector<DelayedDiag, 4> DiagList; struct SortDiagBySourceLocation { @@ -602,8 +603,8 @@ struct SortDiagBySourceLocation { 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); + return S.getSourceManager().isBeforeInTranslationUnit(left.first.first, + right.first.first); } }; @@ -611,7 +612,7 @@ namespace { class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { Sema &S; DiagList Warnings; - SourceLocation FunLocation; + SourceLocation FunLocation, FunEndLocation; // Helper functions void warnLockMismatch(unsigned DiagID, Name LockName, SourceLocation Loc) { @@ -619,13 +620,13 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { // precise source location. if (!Loc.isValid()) Loc = FunLocation; - PartialDiagnostic Warning = S.PDiag(DiagID) << LockName; - Warnings.push_back(DelayedDiag(Loc, Warning)); + PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << LockName); + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); } public: - ThreadSafetyReporter(Sema &S, SourceLocation FL) - : S(S), FunLocation(FL) {} + ThreadSafetyReporter(Sema &S, SourceLocation FL, SourceLocation FEL) + : S(S), FunLocation(FL), FunEndLocation(FEL) {} /// \brief Emit all buffered diagnostics in order of sourcelocation. /// We need to output diagnostics produced while iterating through @@ -635,13 +636,18 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { SortDiagBySourceLocation SortDiagBySL(S); sort(Warnings.begin(), Warnings.end(), SortDiagBySL); for (DiagList::iterator I = Warnings.begin(), E = Warnings.end(); - I != E; ++I) - S.Diag(I->first, I->second); + I != E; ++I) { + S.Diag(I->first.first, I->first.second); + const OptionalNotes &Notes = I->second; + for (unsigned NoteI = 0, NoteN = Notes.size(); NoteI != NoteN; ++NoteI) + S.Diag(Notes[NoteI].first, Notes[NoteI].second); + } } void handleInvalidLockExp(SourceLocation Loc) { - PartialDiagnostic Warning = S.PDiag(diag::warn_cannot_resolve_lock) << Loc; - Warnings.push_back(DelayedDiag(Loc, Warning)); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_cannot_resolve_lock) << Loc); + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); } void handleUnmatchedUnlock(Name LockName, SourceLocation Loc) { warnLockMismatch(diag::warn_unlock_but_no_lock, LockName, Loc); @@ -651,12 +657,13 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { warnLockMismatch(diag::warn_double_lock, LockName, Loc); } - void handleMutexHeldEndOfScope(Name LockName, SourceLocation Loc, + void handleMutexHeldEndOfScope(Name LockName, SourceLocation LocLocked, + SourceLocation LocEndOfScope, LockErrorKind LEK){ unsigned DiagID = 0; switch (LEK) { case LEK_LockedSomePredecessors: - DiagID = diag::warn_lock_at_end_of_scope; + DiagID = diag::warn_lock_some_predecessors; break; case LEK_LockedSomeLoopIterations: DiagID = diag::warn_expecting_lock_held_on_loop; @@ -665,18 +672,22 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { DiagID = diag::warn_no_unlock; break; } - warnLockMismatch(DiagID, LockName, Loc); + if (LocEndOfScope.isInvalid()) + LocEndOfScope = FunEndLocation; + + PartialDiagnosticAt Warning(LocEndOfScope, S.PDiag(DiagID) << LockName); + PartialDiagnosticAt Note(LocLocked, S.PDiag(diag::note_locked_here)); + Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note))); } void handleExclusiveAndShared(Name LockName, SourceLocation Loc1, SourceLocation Loc2) { - PartialDiagnostic Warning = - S.PDiag(diag::warn_lock_exclusive_and_shared) << LockName; - PartialDiagnostic Note = - S.PDiag(diag::note_lock_exclusive_and_shared) << LockName; - Warnings.push_back(DelayedDiag(Loc1, Warning)); - Warnings.push_back(DelayedDiag(Loc2, Note)); + PartialDiagnosticAt Warning( + Loc1, S.PDiag(diag::warn_lock_exclusive_and_shared) << LockName); + PartialDiagnosticAt Note( + Loc2, S.PDiag(diag::note_lock_exclusive_and_shared) << LockName); + Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note))); } void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK, @@ -686,9 +697,9 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { unsigned DiagID = POK == POK_VarAccess? diag::warn_variable_requires_any_lock: diag::warn_var_deref_requires_any_lock; - PartialDiagnostic Warning = S.PDiag(DiagID) - << D->getName() << getLockKindFromAccessKind(AK); - Warnings.push_back(DelayedDiag(Loc, Warning)); + PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) + << D->getName() << getLockKindFromAccessKind(AK)); + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); } void handleMutexNotHeld(const NamedDecl *D, ProtectedOperationKind POK, @@ -705,15 +716,15 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler { DiagID = diag::warn_fun_requires_lock; break; } - PartialDiagnostic Warning = S.PDiag(DiagID) - << D->getName() << LockName << LK; - Warnings.push_back(DelayedDiag(Loc, Warning)); + PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) + << D->getName() << LockName << LK); + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); } void handleFunExcludesLock(Name FunName, Name LockName, SourceLocation Loc) { - PartialDiagnostic Warning = - S.PDiag(diag::warn_fun_excludes_mutex) << FunName << LockName; - Warnings.push_back(DelayedDiag(Loc, Warning)); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_fun_excludes_mutex) << FunName << LockName); + Warnings.push_back(DelayedDiag(Warning, OptionalNotes())); } }; } @@ -897,7 +908,8 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, // Check for thread safety violations if (P.enableThreadSafetyAnalysis) { SourceLocation FL = AC.getDecl()->getLocation(); - thread_safety::ThreadSafetyReporter Reporter(S, FL); + SourceLocation FEL = AC.getDecl()->getLocEnd(); + thread_safety::ThreadSafetyReporter Reporter(S, FL, FEL); thread_safety::runThreadSafetyAnalysis(AC, Reporter); Reporter.emitDiagnostics(); } |