[cfe-commits] r139369 - in /cfe/trunk: include/clang/Analysis/Analyses/ThreadSafety.h lib/Analysis/CMakeLists.txt lib/Analysis/ThreadSafety.cpp lib/Sema/AnalysisBasedWarnings.cpp
Caitlin Sadowski
supertri at google.com
Mon Sep 12 14:08:14 PDT 2011
How does this look?
Cheers,
Caitlin
On Fri, Sep 9, 2011 at 9:41 AM, Chandler Carruth <chandlerc at google.com> wrote:
> On Fri, Sep 9, 2011 at 9:11 AM, Caitlin Sadowski <supertri at google.com>
> wrote:
>>
>> Author: supertri
>> Date: Fri Sep 9 11:11:56 2011
>> New Revision: 139369
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=139369&view=rev
>> Log:
>> Thread Safety: Moving the analysis to a new file
>>
>> Added:
>> cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h
>> cfe/trunk/lib/Analysis/ThreadSafety.cpp
>> Modified:
>> cfe/trunk/lib/Analysis/CMakeLists.txt
>> cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
>>
>> Added: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h?rev=139369&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h (added)
>> +++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h Fri Sep 9
>> 11:11:56 2011
>> @@ -0,0 +1,71 @@
>> +//===- ThreadSafety.h ------------------------------------------*- C++
>> --*-===//
>> +//
>> +// The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +//
>> +// A intra-procedural analysis for thread safety (e.g. deadlocks and race
>> +// conditions), based off of an annotation system.
>> +//
>> +// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation for the gcc
>> version.
>
> Can you add a FIXME to write documentation for the Clang version? It seems
> poor form to just refer to the GCC documentation, especially as we already
> have small divergences from it and are likely to have ever more as time
> passes.
>
>>
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#ifndef LLVM_CLANG_THREADSAFETY_H
>> +#define LLVM_CLANG_THREADSAFETY_H
>> +
>> +#include "clang/Basic/SourceLocation.h"
>> +#include "llvm/ADT/StringRef.h"
>> +#include "clang/Analysis/AnalysisContext.h"
>> +#include "clang/Sema/SemaInternal.h"
>> +
>> +namespace clang {
>> +namespace thread_safety {
>> +
>> +enum ProtectedOperationKind {
>> + POK_VarDereference,
>> + POK_VarAccess,
>> + POK_FunctionCall
>> +};
>> +
>> +enum LockKind {
>> + LK_Shared,
>> + LK_Exclusive
>> +};
>> +
>> +enum AccessKind {
>> + AK_Read,
>> + AK_Written
>> +};
>> +
>
> Where did all the doxygen comments go?
> Because this is an interface, you should document it here in the header
> really carefully.
>>
>> +class ThreadSafetyHandler {
>> +public:
>> + typedef llvm::StringRef Name;
>> + ThreadSafetyHandler() {}
>
> Why is this needed?
>
>>
>> + virtual ~ThreadSafetyHandler() {}
>
> Why is this implemented? Implementing the others as no-ops is nice because
> the subclass can choose which to implement, but I don't see a reason to
> support creating a bare ThreadSafetyHandler, there should always be *some*
> subclass used...
>
>>
>> + virtual void handleUnmatchedUnlock(Name LockName, SourceLocation Loc)
>> {}
>> + virtual void handleDoubleLock(Name LockName, SourceLocation Loc) {}
>> + virtual void handleMutexHeldEndOfScope(Name LockName, SourceLocation
>> Loc){}
>> + virtual void handleNoLockLoopEntry(Name LockName, SourceLocation Loc)
>> {}
>> + virtual void handleNoUnlock(Name LockName, Name FunName,
>> + SourceLocation Loc) {}
>> + virtual void handleExclusiveAndShared(Name LockName, SourceLocation
>> Loc1,
>> + SourceLocation Loc2) {}
>> + virtual void handleNoMutexHeld(const NamedDecl *D,
>> ProtectedOperationKind POK,
>> + AccessKind AK, SourceLocation Loc) {}
>> + virtual void handleMutexNotHeld(const NamedDecl *D,
>> + ProtectedOperationKind POK, Name
>> LockName,
>> + LockKind LK, SourceLocation Loc) {}
>> + virtual void handleFunExcludesLock(Name FunName, Name LockName,
>> + SourceLocation Loc) {}
>> +};
>> +
>> +void runThreadSafetyAnalysis(AnalysisContext &AC, ThreadSafetyHandler
>> &Handler);
>> +LockKind getLockKindFromAccessKind(AccessKind AK);
>> +
>> +}} // end namespace clang::thread_safety
>> +#endif
>>
>> Modified: cfe/trunk/lib/Analysis/CMakeLists.txt
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CMakeLists.txt?rev=139369&r1=139368&r2=139369&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Analysis/CMakeLists.txt (original)
>> +++ cfe/trunk/lib/Analysis/CMakeLists.txt Fri Sep 9 11:11:56 2011
>> @@ -13,6 +13,7 @@
>> PseudoConstantAnalysis.cpp
>> ReachableCode.cpp
>> ScanfFormatString.cpp
>> + ThreadSafety.cpp
>> UninitializedValues.cpp
>> )
>>
>>
>> Added: cfe/trunk/lib/Analysis/ThreadSafety.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=139369&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Analysis/ThreadSafety.cpp (added)
>> +++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Fri Sep 9 11:11:56 2011
>> @@ -0,0 +1,794 @@
>> +//===- ThreadSafety.cpp ----------------------------------------*- C++
>> --*-===//
>> +//
>> +// The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// A intra-procedural analysis for thread safety (e.g. deadlocks and race
>> +// conditions), based off of an annotation system.
>> +//
>> +// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation for the gcc
>> version.
>> +//
>>
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "clang/Analysis/Analyses/ThreadSafety.h"
>> +#include "clang/Sema/SemaInternal.h"
>> +#include "clang/Basic/SourceManager.h"
>> +#include "clang/Basic/SourceLocation.h"
>> +#include "clang/AST/DeclCXX.h"
>> +#include "clang/AST/ExprCXX.h"
>> +#include "clang/AST/StmtCXX.h"
>> +#include "clang/AST/StmtVisitor.h"
>> +#include "clang/Analysis/AnalysisContext.h"
>> +#include "clang/Analysis/CFG.h"
>> +#include "clang/Analysis/CFGStmtMap.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/ADT/StringRef.h"
>> +#include <algorithm>
>> +#include <vector>
>> +
>> +using namespace clang;
>> +using namespace thread_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) {
>> + // Note that insert() is called by po_iterator, which doesn't check
>> to make
>> + // sure that Block is non-null. Moreover, the CFGBlock iterator will
>> + // occasionally hand out null pointers for pruned edges, so we catch
>> those
>> + // here.
>> + if (Block == 0)
>> + return false; // if an edge is trivially false.
>> + 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 method is for tracking visited blocks in the main threadsafety
>> loop.
>> + /// Block must not be null.
>> + 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 MutexID object uniquely identifies a particular mutex, 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 mutex 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 mutex 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 mutex 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
>> +class MutexID {
>> + 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 buildMutexID(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);
>> + buildMutexID(ME->getBase());
>> + } else if (isa<CXXThisExpr>(Exp)) {
>> + return;
>> + } else {
>> + // FIXME: add diagnostic
>> + llvm::report_fatal_error("Expected lock expression!");
>> + }
>> + }
>> +
>> +public:
>> + MutexID(Expr *LExpr) {
>> + buildMutexID(LExpr);
>> + assert(!DeclSeq.empty());
>> + }
>> +
>> + bool operator==(const MutexID &other) const {
>> + return DeclSeq == other.DeclSeq;
>> + }
>> +
>> + bool operator!=(const MutexID &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 MutexIDs 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 MutexID &other) const {
>> + return DeclSeq < other.DeclSeq;
>> + }
>> +
>> + /// \brief Returns the name of the first Decl in the list for a given
>> MutexID;
>> + /// 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, mutex
>> 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 MutexIDs to LockDatas.
>> +struct LockData {
>> + SourceLocation AcquireLoc;
>> +
>> + /// \brief LKind stores whether a lock is held shared or exclusively.
>> + /// Note that this analysis does not currently support either
>> re-entrant
>> + /// locking or lock "upgrading" and "downgrading" between exclusive and
>> + /// shared.
>> + ///
>> + /// FIXME: add support for re-entrant locking and lock up/downgrading
>> + LockKind LKind;
>> +
>> + LockData(SourceLocation AcquireLoc, LockKind LKind)
>> + : AcquireLoc(AcquireLoc), LKind(LKind) {}
>> +
>> + bool operator==(const LockData &other) const {
>> + return AcquireLoc == other.AcquireLoc && LKind == other.LKind;
>> + }
>> +
>> + bool operator!=(const LockData &other) const {
>> + return !(*this == other);
>> + }
>> +
>> + void Profile(llvm::FoldingSetNodeID &ID) const {
>> + ID.AddInteger(AcquireLoc.getRawEncoding());
>> + ID.AddInteger(LKind);
>> + }
>> +};
>> +
>> +/// A Lockset maps each MutexID (defined above) to information about how
>> it has
>> +/// been locked.
>> +typedef llvm::ImmutableMap<MutexID, 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> {
>> + ThreadSafetyHandler &Handler;
>> + Lockset LSet;
>> + Lockset::Factory &LocksetFactory;
>> +
>> + // Helper functions
>> + void removeLock(SourceLocation UnlockLoc, Expr *LockExp);
>> + void addLock(SourceLocation LockLoc, Expr *LockExp, LockKind LK);
>> + const ValueDecl *getValueDecl(Expr *Exp);
>> + void warnIfMutexNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK,
>> + Expr *MutexExp, ProtectedOperationKind POK);
>> + void checkAccess(Expr *Exp, AccessKind AK);
>> + void checkDereference(Expr *Exp, AccessKind AK);
>> +
>> + template <class AttrType>
>> + void addLocksToSet(LockKind LK, Attr *Attr, CXXMemberCallExpr *Exp);
>> +
>> + /// \brief Returns true if the lockset contains a lock, regardless of
>> whether
>> + /// the lock is held exclusively or shared.
>> + bool locksetContains(MutexID Lock) const {
>> + return LSet.lookup(Lock);
>> + }
>> +
>> + /// \brief Returns true if the lockset contains a lock with the passed
>> in
>> + /// locktype.
>> + bool locksetContains(MutexID Lock, LockKind KindRequested) const {
>> + const LockData *LockHeld = LSet.lookup(Lock);
>> + return (LockHeld && KindRequested == LockHeld->LKind);
>> + }
>> +
>> + /// \brief Returns true if the lockset contains a lock with at least
>> the
>> + /// passed in locktype. So for example, if we pass in LK_Shared, this
>> function
>> + /// returns true if the lock is held LK_Shared or LK_Exclusive. If we
>> pass in
>> + /// LK_Exclusive, this function returns true if the lock is held
>> LK_Exclusive.
>> + bool locksetContainsAtLeast(MutexID Lock, LockKind KindRequested) const
>> {
>> + switch (KindRequested) {
>> + case LK_Shared:
>> + return locksetContains(Lock);
>> + case LK_Exclusive:
>> + return locksetContains(Lock, KindRequested);
>> + }
>> + }
>> +
>> +public:
>> + BuildLockset(ThreadSafetyHandler &Handler, Lockset LS, Lockset::Factory
>> &F)
>> + : StmtVisitor<BuildLockset>(), Handler(Handler), LSet(LS),
>> + LocksetFactory(F) {}
>> +
>> + Lockset getLockset() {
>> + return LSet;
>> + }
>> +
>> + void VisitUnaryOperator(UnaryOperator *UO);
>> + void VisitBinaryOperator(BinaryOperator *BO);
>> + void VisitCastExpr(CastExpr *CE);
>> + void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp);
>> +};
>> +
>> +/// \brief Add a new lock to the lockset, warning if the lock is already
>> there.
>> +/// \param LockLoc The source location of the acquire
>> +/// \param LockExp The lock expression corresponding to the lock to be
>> added
>> +void BuildLockset::addLock(SourceLocation LockLoc, Expr *LockExp,
>> + LockKind LK) {
>> + // FIXME: deal with acquired before/after annotations
>> + MutexID Mutex(LockExp);
>> + LockData NewLock(LockLoc, LK);
>> +
>> + // FIXME: Don't always warn when we have support for reentrant locks.
>> + if (locksetContains(Mutex))
>> + Handler.handleDoubleLock(Mutex.getName(), LockLoc);
>> + LSet = LocksetFactory.add(LSet, Mutex, NewLock);
>> +}
>> +
>> +/// \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) {
>> + MutexID Mutex(LockExp);
>> +
>> + Lockset NewLSet = LocksetFactory.remove(LSet, Mutex);
>> + if(NewLSet == LSet)
>> + Handler.handleUnmatchedUnlock(Mutex.getName(), UnlockLoc);
>> +
>> + LSet = NewLSet;
>> +}
>> +
>> +/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
>> +const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
>> + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
>> + return DR->getDecl();
>> +
>> + if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
>> + return ME->getMemberDecl();
>> +
>> + return 0;
>> +}
>> +
>> +/// \brief Warn if the LSet does not contain a lock sufficient to protect
>> access
>> +/// of at least the passed in AccessType.
>> +void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp,
>> + AccessKind AK, Expr *MutexExp,
>> + ProtectedOperationKind POK) {
>> + LockKind LK = getLockKindFromAccessKind(AK);
>> + MutexID Mutex(MutexExp);
>> + if (!locksetContainsAtLeast(Mutex, LK))
>> + Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK,
>> Exp->getExprLoc());
>> +}
>> +
>> +
>> +/// \brief This method identifies variable dereferences and checks
>> pt_guarded_by
>> +/// and pt_guarded_var annotations. Note that we only check these
>> annotations
>> +/// at the time a pointer is dereferenced.
>> +/// FIXME: We need to check for other types of pointer dereferences
>> +/// (e.g. [], ->) and deal with them here.
>> +/// \param Exp An expression that has been read or written.
>> +void BuildLockset::checkDereference(Expr *Exp, AccessKind AK) {
>> + UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp);
>> + if (!UO || UO->getOpcode() != clang::UO_Deref)
>> + return;
>> + Exp = UO->getSubExpr()->IgnoreParenCasts();
>> +
>> + const ValueDecl *D = getValueDecl(Exp);
>> + if(!D || !D->hasAttrs())
>> + return;
>> +
>> + if (D->getAttr<PtGuardedVarAttr>() && LSet.isEmpty())
>> + Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
>> Exp->getExprLoc());
>> +
>> + const AttrVec &ArgAttrs = D->getAttrs();
>> + for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
>> + if (PtGuardedByAttr *PGBAttr =
>> dyn_cast<PtGuardedByAttr>(ArgAttrs[i]))
>> + warnIfMutexNotHeld(D, Exp, AK, PGBAttr->getArg(),
>> POK_VarDereference);
>> +}
>> +
>> +/// \brief Checks guarded_by and guarded_var attributes.
>> +/// Whenever we identify an access (read or write) of a DeclRefExpr or
>> +/// MemberExpr, we need to check whether there are any guarded_by or
>> +/// guarded_var attributes, and make sure we hold the appropriate
>> mutexes.
>> +void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) {
>> + const ValueDecl *D = getValueDecl(Exp);
>> + if(!D || !D->hasAttrs())
>> + return;
>> +
>> + if (D->getAttr<GuardedVarAttr>() && LSet.isEmpty())
>> + Handler.handleNoMutexHeld(D, POK_VarAccess, AK, Exp->getExprLoc());
>> +
>> + const AttrVec &ArgAttrs = D->getAttrs();
>> + for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
>> + if (GuardedByAttr *GBAttr = dyn_cast<GuardedByAttr>(ArgAttrs[i]))
>> + warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarAccess);
>> +}
>> +
>> +/// \brief For unary operations which read and write a variable, we need
>> to
>> +/// check whether we hold any required mutexes. Reads are checked in
>> +/// VisitCastExpr.
>> +void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
>> + switch (UO->getOpcode()) {
>> + case clang::UO_PostDec:
>> + case clang::UO_PostInc:
>> + case clang::UO_PreDec:
>> + case clang::UO_PreInc: {
>> + Expr *SubExp = UO->getSubExpr()->IgnoreParenCasts();
>> + checkAccess(SubExp, AK_Written);
>> + checkDereference(SubExp, AK_Written);
>> + break;
>> + }
>> + default:
>> + break;
>> + }
>> +}
>> +
>> +/// For binary operations which assign to a variable (writes), we need to
>> check
>> +/// whether we hold any required mutexes.
>> +/// FIXME: Deal with non-primitive types.
>> +void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
>> + if (!BO->isAssignmentOp())
>> + return;
>> + Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
>> + checkAccess(LHSExp, AK_Written);
>> + checkDereference(LHSExp, 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.
>> +void BuildLockset::VisitCastExpr(CastExpr *CE) {
>> + if (CE->getCastKind() != CK_LValueToRValue)
>> + return;
>> + Expr *SubExp = CE->getSubExpr()->IgnoreParenCasts();
>> + checkAccess(SubExp, AK_Read);
>> + checkDereference(SubExp, AK_Read);
>> +}
>> +
>> +/// \brief This function, parameterized by an attribute type, is used to
>> add a
>> +/// set of locks specified as attribute arguments to the lockset.
>> +template <typename AttrType>
>> +void BuildLockset::addLocksToSet(LockKind LK, Attr *Attr,
>> + CXXMemberCallExpr *Exp) {
>> + typedef typename AttrType::args_iterator iterator_type;
>> + SourceLocation ExpLocation = Exp->getExprLoc();
>> + Expr *Parent = Exp->getImplicitObjectArgument();
>> + AttrType *SpecificAttr = cast<AttrType>(Attr);
>> +
>> + if (SpecificAttr->args_size() == 0) {
>> + // The mutex held is the "this" object.
>> + addLock(ExpLocation, Parent, LK);
>> + return;
>> + }
>> +
>> + for (iterator_type I = SpecificAttr->args_begin(),
>> + E = SpecificAttr->args_end(); I != E; ++I)
>> + addLock(ExpLocation, *I, LK);
>> +}
>> +
>> +/// \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.
>> +///
>> +/// FIXME: For classes annotated with one of the guarded annotations, we
>> need
>> +/// to treat const method calls as reads and non-const method calls as
>> writes,
>> +/// and check that the appropriate locks are held. Non-const method calls
>> with
>> +/// the same signature as const method calls can be also treated as
>> reads.
>> +///
>> +/// FIXME: We need to also visit CallExprs to catch/check global
>> functions.
>> +void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
>> + NamedDecl *D = dyn_cast_or_null<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 with kind exclusive.
>> + case attr::ExclusiveLockFunction:
>> + addLocksToSet<ExclusiveLockFunctionAttr>(LK_Exclusive, Attr,
>> Exp);
>> + break;
>> +
>> + // When we encounter a shared lock function, we need to add the
>> lock
>> + // to our lockset with kind shared.
>> + case attr::SharedLockFunction:
>> + addLocksToSet<SharedLockFunctionAttr>(LK_Shared, Attr, Exp);
>> + 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: {
>> + 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;
>> + }
>> +
>> + case attr::ExclusiveLocksRequired: {
>> + // FIXME: Also use this attribute to add required locks to the
>> initial
>> + // lockset when processing a CFG for a function annotated with
>> this
>> + // attribute.
>> + ExclusiveLocksRequiredAttr *ELRAttr =
>> + cast<ExclusiveLocksRequiredAttr>(Attr);
>> +
>> + for (ExclusiveLocksRequiredAttr::args_iterator
>> + I = ELRAttr->args_begin(), E = ELRAttr->args_end(); I != E;
>> ++I)
>> + warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall);
>> + break;
>> + }
>> +
>> + case attr::SharedLocksRequired: {
>> + // FIXME: Also use this attribute to add required locks to the
>> initial
>> + // lockset when processing a CFG for a function annotated with
>> this
>> + // attribute.
>> + SharedLocksRequiredAttr *SLRAttr =
>> cast<SharedLocksRequiredAttr>(Attr);
>> +
>> + for (SharedLocksRequiredAttr::args_iterator I =
>> SLRAttr->args_begin(),
>> + E = SLRAttr->args_end(); I != E; ++I)
>> + warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall);
>> + break;
>> + }
>> +
>> + case attr::LocksExcluded: {
>> + LocksExcludedAttr *LEAttr = cast<LocksExcludedAttr>(Attr);
>> + for (LocksExcludedAttr::args_iterator I = LEAttr->args_begin(),
>> + E = LEAttr->args_end(); I != E; ++I) {
>> + MutexID Mutex(*I);
>> + if (locksetContains(Mutex))
>> + Handler.handleFunExcludesLock(D->getName(), Mutex.getName(),
>> + ExpLocation);
>> + }
>> + break;
>> + }
>> +
>> + case attr::LockReturned:
>> + // FIXME: Deal with this attribute.
>> + break;
>> +
>> + // Ignore other (non thread-safety) attributes
>> + default:
>> + break;
>> + }
>> + }
>> +}
>> +
>> +} // end anonymous namespace
>> +
>> +/// \brief Flags a warning for each lock that is in LSet2 but not LSet1,
>> or
>> +/// else mutexes that are held shared in one lockset and exclusive in the
>> other.
>> +static Lockset warnIfNotInFirstSetOrNotSameKind(ThreadSafetyHandler
>> &Handler,
>> + const Lockset LSet1,
>> + const Lockset LSet2,
>> + Lockset Intersection,
>> + Lockset::Factory &Fact) {
>> + for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I)
>> {
>> + const MutexID &LSet2Mutex = I.getKey();
>> + const LockData &LSet2LockData = I.getData();
>> + if (const LockData *LD = LSet1.lookup(LSet2Mutex)) {
>> + if (LD->LKind != LSet2LockData.LKind) {
>> + Handler.handleExclusiveAndShared(LSet2Mutex.getName(),
>> + LSet2LockData.AcquireLoc,
>> + LD->AcquireLoc);
>> + if (LD->LKind != LK_Exclusive)
>> + Intersection = Fact.add(Intersection, LSet2Mutex,
>> LSet2LockData);
>> + }
>> + } else {
>> + Handler.handleMutexHeldEndOfScope(LSet2Mutex.getName(),
>> + LSet2LockData.AcquireLoc);
>> + }
>> + }
>> + return Intersection;
>> +}
>> +
>> +
>> +/// \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(ThreadSafetyHandler &Handler,
>> + const Lockset LSet1, const Lockset LSet2,
>> + Lockset::Factory &Fact) {
>> + Lockset Intersection = LSet1;
>> + Intersection = warnIfNotInFirstSetOrNotSameKind(Handler, LSet1, LSet2,
>> + Intersection, Fact);
>> +
>> + for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I)
>> {
>> + if (!LSet2.contains(I.getKey())) {
>> + const MutexID &Mutex = I.getKey();
>> + const LockData &MissingLock = I.getData();
>> + Handler.handleMutexHeldEndOfScope(Mutex.getName(),
>> + MissingLock.AcquireLoc);
>> + Intersection = Fact.remove(Intersection, Mutex);
>> + }
>> + }
>> + return Intersection;
>> +}
>> +
>> +/// \brief Returns the location of the first Stmt in a Block.
>> +static SourceLocation getFirstStmtLocation(CFGBlock *Block) {
>> + SourceLocation Loc;
>> + for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
>> + BI != BE; ++BI) {
>> + if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&(*BI))) {
>> + Loc = CfgStmt->getStmt()->getLocStart();
>> + if (Loc.isValid()) return Loc;
>> + }
>> + }
>> + if (Stmt *S = Block->getTerminator().getStmt()) {
>> + Loc = S->getLocStart();
>> + if (Loc.isValid()) return Loc;
>> + }
>> + return Loc;
>> +}
>> +
>> +/// \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 before starting the loop
>> +/// \param LoopReentrySet Locks in the last CFG block of the loop
>> +static void warnBackEdgeUnequalLocksets(ThreadSafetyHandler &Handler,
>> + const Lockset LoopReentrySet,
>> + const Lockset LoopEntrySet,
>> + SourceLocation FirstLocInLoop,
>> + Lockset::Factory &Fact) {
>> + assert(FirstLocInLoop.isValid());
>> + // 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())) {
>> + // We report this error at the location of the first statement in a
>> loop
>> + Handler.handleNoLockLoopEntry(I.getKey().getName(),
>> FirstLocInLoop);
>> + }
>> + }
>> +
>> + // Warn for locks held at the end of the loop, but not at the start.
>> + warnIfNotInFirstSetOrNotSameKind(Handler, LoopEntrySet, LoopReentrySet,
>> + LoopReentrySet, Fact);
>> +}
>> +
>> +
>> +namespace clang { namespace thread_safety {
>> +/// \brief Check a function's CFG for thread-safety violations.
>> +///
>> +/// We traverse the blocks in the CFG, compute the set of mutexes 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.
>> +void runThreadSafetyAnalysis(AnalysisContext &AC,
>> + ThreadSafetyHandler &Handler) {
>> + CFG *CFGraph = AC.getCFG();
>> + if (!CFGraph) return;
>> + const Decl *D = AC.getDecl();
>> + if (D && D->getAttr<NoThreadSafetyAnalysisAttr>()) return;
>> +
>> + 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 Mutex 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 (*PI == 0 || !VisitedBlocks.alreadySet(*PI))
>> + continue;
>> +
>> + int PrevBlockID = (*PI)->getBlockID();
>> + if (!LocksetInitialized) {
>> + Entryset = ExitLocksets[PrevBlockID];
>> + LocksetInitialized = true;
>> + } else {
>> + Entryset = intersectAndWarn(Handler, Entryset,
>> + ExitLocksets[PrevBlockID],
>> LocksetFactory);
>> + }
>> + }
>> +
>> + BuildLockset LocksetBuilder(Handler, 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(const_cast<Stmt*>(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 (*SI == 0 || !VisitedBlocks.alreadySet(*SI))
>> + continue;
>> +
>> + CFGBlock *FirstLoopBlock = *SI;
>> + SourceLocation FirstLoopLocation =
>> getFirstStmtLocation(FirstLoopBlock);
>> +
>> + assert(FirstLoopLocation.isValid());
>> +
>> + // Fail gracefully in release code.
>> + if (!FirstLoopLocation.isValid())
>> + continue;
>> +
>> + Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
>> + Lockset LoopEnd = ExitLocksets[CurrBlockID];
>> + warnBackEdgeUnequalLocksets(Handler, LoopEnd, PreLoop,
>> FirstLoopLocation,
>> + LocksetFactory);
>> + }
>> + }
>> +
>> + Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
>> + if (!FinalLockset.isEmpty()) {
>> + for (Lockset::iterator I=FinalLockset.begin(), E=FinalLockset.end();
>> + I != E; ++I) {
>> + const MutexID &Mutex = I.getKey();
>> + const LockData &MissingLock = I.getData();
>> +
>> + std::string FunName = "<unknown>";
>> + if (const NamedDecl *ContextDecl =
>> dyn_cast<NamedDecl>(AC.getDecl())) {
>> + FunName = ContextDecl->getDeclName().getAsString();
>> + }
>> +
>> + Handler.handleNoUnlock(Mutex.getName(), FunName,
>> MissingLock.AcquireLoc);
>> + }
>> + }
>> +}
>> +
>> +/// \brief Helper function that returns a LockKind required for the given
>> level
>> +/// of access.
>> +LockKind getLockKindFromAccessKind(AccessKind AK) {
>> + switch (AK) {
>> + case AK_Read :
>> + return LK_Shared;
>> + case AK_Written :
>> + return LK_Exclusive;
>> + }
>> +}
>> +}} // end namespace clang::thread_safety
>>
>> Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=139369&r1=139368&r2=139369&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
>> +++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Fri Sep 9 11:11:56 2011
>> @@ -31,6 +31,7 @@
>> #include "clang/Analysis/CFG.h"
>> #include "clang/Analysis/Analyses/ReachableCode.h"
>> #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
>> +#include "clang/Analysis/Analyses/ThreadSafety.h"
>> #include "clang/Analysis/CFGStmtMap.h"
>> #include "clang/Analysis/Analyses/UninitializedValues.h"
>> #include "llvm/ADT/BitVector.h"
>> @@ -610,23 +611,6 @@
>> typedef std::pair<SourceLocation, PartialDiagnostic> DelayedDiag;
>> typedef llvm::SmallVector<DelayedDiag, 4> DiagList;
>>
>> -enum ProtectedOperationKind {
>> - POK_VarDereference,
>> - POK_VarAccess,
>> - POK_FunctionCall
>> -};
>> -
>> -enum LockKind {
>> - LK_Shared,
>> - LK_Exclusive
>> -};
>> -
>> -enum AccessKind {
>> - AK_Read,
>> - AK_Written
>> -};
>> -
>> -
>> struct SortDiagBySourceLocation {
>> Sema &S;
>> SortDiagBySourceLocation(Sema &S) : S(S) {}
>> @@ -639,39 +623,6 @@
>> }
>> };
>>
>> -/// \brief Helper function that returns a LockKind required for the given
>> level
>> -/// of access.
>> -LockKind getLockKindFromAccessKind(AccessKind AK) {
>> - switch (AK) {
>> - case AK_Read :
>> - return LK_Shared;
>> - case AK_Written :
>> - return LK_Exclusive;
>> - }
>> -}
>> -
>> -class ThreadSafetyHandler {
>> -public:
>> - typedef llvm::StringRef Name;
>> - ThreadSafetyHandler() {}
>> - virtual ~ThreadSafetyHandler() {}
>> - virtual void handleUnmatchedUnlock(Name LockName, SourceLocation Loc)
>> {}
>> - virtual void handleDoubleLock(Name LockName, SourceLocation Loc) {}
>> - virtual void handleMutexHeldEndOfScope(Name LockName, SourceLocation
>> Loc){}
>> - virtual void handleNoLockLoopEntry(Name LockName, SourceLocation Loc)
>> {}
>> - virtual void handleNoUnlock(Name LockName, Name FunName,
>> - SourceLocation Loc) {}
>> - virtual void handleExclusiveAndShared(Name LockName, SourceLocation
>> Loc1,
>> - SourceLocation Loc2) {}
>> - virtual void handleNoMutexHeld(const NamedDecl *D,
>> ProtectedOperationKind POK,
>> - AccessKind AK, SourceLocation Loc) {}
>> - virtual void handleMutexNotHeld(const NamedDecl *D,
>> - ProtectedOperationKind POK, Name
>> LockName,
>> - LockKind LK, SourceLocation Loc) {}
>> - virtual void handleFunExcludesLock(Name FunName, Name LockName,
>> - SourceLocation Loc) {}
>> -};
>> -
>> class ThreadSafetyReporter : public
>> clang::thread_safety::ThreadSafetyHandler {
>> Sema &S;
>> DiagList Warnings;
>> @@ -765,755 +716,6 @@
>> }
>> }
>>
>> -using namespace thread_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) {
>> - // Note that insert() is called by po_iterator, which doesn't check
>> to make
>> - // sure that Block is non-null. Moreover, the CFGBlock iterator will
>> - // occasionally hand out null pointers for pruned edges, so we catch
>> those
>> - // here.
>> - if (Block == 0)
>> - return false; // if an edge is trivially false.
>> - 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 method is for tracking visited blocks in the main threadsafety
>> loop.
>> - /// Block must not be null.
>> - 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 MutexID object uniquely identifies a particular mutex, 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 mutex 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 mutex 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 mutex 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
>> -class MutexID {
>> - 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 buildMutexID(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);
>> - buildMutexID(ME->getBase());
>> - } else if (isa<CXXThisExpr>(Exp)) {
>> - return;
>> - } else {
>> - // FIXME: add diagnostic
>> - llvm::report_fatal_error("Expected lock expression!");
>> - }
>> - }
>> -
>> -public:
>> - MutexID(Expr *LExpr) {
>> - buildMutexID(LExpr);
>> - assert(!DeclSeq.empty());
>> - }
>> -
>> - bool operator==(const MutexID &other) const {
>> - return DeclSeq == other.DeclSeq;
>> - }
>> -
>> - bool operator!=(const MutexID &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 MutexIDs 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 MutexID &other) const {
>> - return DeclSeq < other.DeclSeq;
>> - }
>> -
>> - /// \brief Returns the name of the first Decl in the list for a given
>> MutexID;
>> - /// 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, mutex
>> 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 MutexIDs to LockDatas.
>> -struct LockData {
>> - SourceLocation AcquireLoc;
>> -
>> - /// \brief LKind stores whether a lock is held shared or exclusively.
>> - /// Note that this analysis does not currently support either
>> re-entrant
>> - /// locking or lock "upgrading" and "downgrading" between exclusive and
>> - /// shared.
>> - ///
>> - /// FIXME: add support for re-entrant locking and lock up/downgrading
>> - LockKind LKind;
>> -
>> - LockData(SourceLocation AcquireLoc, LockKind LKind)
>> - : AcquireLoc(AcquireLoc), LKind(LKind) {}
>> -
>> - bool operator==(const LockData &other) const {
>> - return AcquireLoc == other.AcquireLoc && LKind == other.LKind;
>> - }
>> -
>> - bool operator!=(const LockData &other) const {
>> - return !(*this == other);
>> - }
>> -
>> - void Profile(llvm::FoldingSetNodeID &ID) const {
>> - ID.AddInteger(AcquireLoc.getRawEncoding());
>> - ID.AddInteger(LKind);
>> - }
>> -};
>> -
>> -/// A Lockset maps each MutexID (defined above) to information about how
>> it has
>> -/// been locked.
>> -typedef llvm::ImmutableMap<MutexID, 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> {
>> - ThreadSafetyHandler &Handler;
>> - Lockset LSet;
>> - Lockset::Factory &LocksetFactory;
>> -
>> - // Helper functions
>> - void removeLock(SourceLocation UnlockLoc, Expr *LockExp);
>> - void addLock(SourceLocation LockLoc, Expr *LockExp, LockKind LK);
>> - const ValueDecl *getValueDecl(Expr *Exp);
>> - void warnIfMutexNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK,
>> - Expr *MutexExp, ProtectedOperationKind POK);
>> - void checkAccess(Expr *Exp, AccessKind AK);
>> - void checkDereference(Expr *Exp, AccessKind AK);
>> -
>> - template <class AttrType>
>> - void addLocksToSet(LockKind LK, Attr *Attr, CXXMemberCallExpr *Exp);
>> -
>> - /// \brief Returns true if the lockset contains a lock, regardless of
>> whether
>> - /// the lock is held exclusively or shared.
>> - bool locksetContains(MutexID Lock) const {
>> - return LSet.lookup(Lock);
>> - }
>> -
>> - /// \brief Returns true if the lockset contains a lock with the passed
>> in
>> - /// locktype.
>> - bool locksetContains(MutexID Lock, LockKind KindRequested) const {
>> - const LockData *LockHeld = LSet.lookup(Lock);
>> - return (LockHeld && KindRequested == LockHeld->LKind);
>> - }
>> -
>> - /// \brief Returns true if the lockset contains a lock with at least
>> the
>> - /// passed in locktype. So for example, if we pass in LK_Shared, this
>> function
>> - /// returns true if the lock is held LK_Shared or LK_Exclusive. If we
>> pass in
>> - /// LK_Exclusive, this function returns true if the lock is held
>> LK_Exclusive.
>> - bool locksetContainsAtLeast(MutexID Lock, LockKind KindRequested) const
>> {
>> - switch (KindRequested) {
>> - case LK_Shared:
>> - return locksetContains(Lock);
>> - case LK_Exclusive:
>> - return locksetContains(Lock, KindRequested);
>> - }
>> - }
>> -
>> -public:
>> - BuildLockset(ThreadSafetyHandler &Handler, Lockset LS, Lockset::Factory
>> &F)
>> - : StmtVisitor<BuildLockset>(), Handler(Handler), LSet(LS),
>> - LocksetFactory(F) {}
>> -
>> - Lockset getLockset() {
>> - return LSet;
>> - }
>> -
>> - void VisitUnaryOperator(UnaryOperator *UO);
>> - void VisitBinaryOperator(BinaryOperator *BO);
>> - void VisitCastExpr(CastExpr *CE);
>> - void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp);
>> -};
>> -
>> -/// \brief Add a new lock to the lockset, warning if the lock is already
>> there.
>> -/// \param LockLoc The source location of the acquire
>> -/// \param LockExp The lock expression corresponding to the lock to be
>> added
>> -void BuildLockset::addLock(SourceLocation LockLoc, Expr *LockExp,
>> - LockKind LK) {
>> - // FIXME: deal with acquired before/after annotations
>> - MutexID Mutex(LockExp);
>> - LockData NewLock(LockLoc, LK);
>> -
>> - // FIXME: Don't always warn when we have support for reentrant locks.
>> - if (locksetContains(Mutex))
>> - Handler.handleDoubleLock(Mutex.getName(), LockLoc);
>> - LSet = LocksetFactory.add(LSet, Mutex, NewLock);
>> -}
>> -
>> -/// \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) {
>> - MutexID Mutex(LockExp);
>> -
>> - Lockset NewLSet = LocksetFactory.remove(LSet, Mutex);
>> - if(NewLSet == LSet)
>> - Handler.handleUnmatchedUnlock(Mutex.getName(), UnlockLoc);
>> -
>> - LSet = NewLSet;
>> -}
>> -
>> -/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
>> -const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
>> - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
>> - return DR->getDecl();
>> -
>> - if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
>> - return ME->getMemberDecl();
>> -
>> - return 0;
>> -}
>> -
>> -/// \brief Warn if the LSet does not contain a lock sufficient to protect
>> access
>> -/// of at least the passed in AccessType.
>> -void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp,
>> - AccessKind AK, Expr *MutexExp,
>> - ProtectedOperationKind POK) {
>> - LockKind LK = getLockKindFromAccessKind(AK);
>> - MutexID Mutex(MutexExp);
>> - if (!locksetContainsAtLeast(Mutex, LK))
>> - Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK,
>> Exp->getExprLoc());
>> -}
>> -
>> -
>> -/// \brief This method identifies variable dereferences and checks
>> pt_guarded_by
>> -/// and pt_guarded_var annotations. Note that we only check these
>> annotations
>> -/// at the time a pointer is dereferenced.
>> -/// FIXME: We need to check for other types of pointer dereferences
>> -/// (e.g. [], ->) and deal with them here.
>> -/// \param Exp An expression that has been read or written.
>> -void BuildLockset::checkDereference(Expr *Exp, AccessKind AK) {
>> - UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp);
>> - if (!UO || UO->getOpcode() != clang::UO_Deref)
>> - return;
>> - Exp = UO->getSubExpr()->IgnoreParenCasts();
>> -
>> - const ValueDecl *D = getValueDecl(Exp);
>> - if(!D || !D->hasAttrs())
>> - return;
>> -
>> - if (D->getAttr<PtGuardedVarAttr>() && LSet.isEmpty())
>> - Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
>> Exp->getExprLoc());
>> -
>> - const AttrVec &ArgAttrs = D->getAttrs();
>> - for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
>> - if (PtGuardedByAttr *PGBAttr =
>> dyn_cast<PtGuardedByAttr>(ArgAttrs[i]))
>> - warnIfMutexNotHeld(D, Exp, AK, PGBAttr->getArg(),
>> POK_VarDereference);
>> -}
>> -
>> -/// \brief Checks guarded_by and guarded_var attributes.
>> -/// Whenever we identify an access (read or write) of a DeclRefExpr or
>> -/// MemberExpr, we need to check whether there are any guarded_by or
>> -/// guarded_var attributes, and make sure we hold the appropriate
>> mutexes.
>> -void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) {
>> - const ValueDecl *D = getValueDecl(Exp);
>> - if(!D || !D->hasAttrs())
>> - return;
>> -
>> - if (D->getAttr<GuardedVarAttr>() && LSet.isEmpty())
>> - Handler.handleNoMutexHeld(D, POK_VarAccess, AK, Exp->getExprLoc());
>> -
>> - const AttrVec &ArgAttrs = D->getAttrs();
>> - for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
>> - if (GuardedByAttr *GBAttr = dyn_cast<GuardedByAttr>(ArgAttrs[i]))
>> - warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarAccess);
>> -}
>> -
>> -/// \brief For unary operations which read and write a variable, we need
>> to
>> -/// check whether we hold any required mutexes. Reads are checked in
>> -/// VisitCastExpr.
>> -void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
>> - switch (UO->getOpcode()) {
>> - case clang::UO_PostDec:
>> - case clang::UO_PostInc:
>> - case clang::UO_PreDec:
>> - case clang::UO_PreInc: {
>> - Expr *SubExp = UO->getSubExpr()->IgnoreParenCasts();
>> - checkAccess(SubExp, AK_Written);
>> - checkDereference(SubExp, AK_Written);
>> - break;
>> - }
>> - default:
>> - break;
>> - }
>> -}
>> -
>> -/// For binary operations which assign to a variable (writes), we need to
>> check
>> -/// whether we hold any required mutexes.
>> -/// FIXME: Deal with non-primitive types.
>> -void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
>> - if (!BO->isAssignmentOp())
>> - return;
>> - Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
>> - checkAccess(LHSExp, AK_Written);
>> - checkDereference(LHSExp, 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.
>> -void BuildLockset::VisitCastExpr(CastExpr *CE) {
>> - if (CE->getCastKind() != CK_LValueToRValue)
>> - return;
>> - Expr *SubExp = CE->getSubExpr()->IgnoreParenCasts();
>> - checkAccess(SubExp, AK_Read);
>> - checkDereference(SubExp, AK_Read);
>> -}
>> -
>> -/// \brief This function, parameterized by an attribute type, is used to
>> add a
>> -/// set of locks specified as attribute arguments to the lockset.
>> -template <typename AttrType>
>> -void BuildLockset::addLocksToSet(LockKind LK, Attr *Attr,
>> - CXXMemberCallExpr *Exp) {
>> - typedef typename AttrType::args_iterator iterator_type;
>> - SourceLocation ExpLocation = Exp->getExprLoc();
>> - Expr *Parent = Exp->getImplicitObjectArgument();
>> - AttrType *SpecificAttr = cast<AttrType>(Attr);
>> -
>> - if (SpecificAttr->args_size() == 0) {
>> - // The mutex held is the "this" object.
>> - addLock(ExpLocation, Parent, LK);
>> - return;
>> - }
>> -
>> - for (iterator_type I = SpecificAttr->args_begin(),
>> - E = SpecificAttr->args_end(); I != E; ++I)
>> - addLock(ExpLocation, *I, LK);
>> -}
>> -
>> -/// \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.
>> -///
>> -/// FIXME: For classes annotated with one of the guarded annotations, we
>> need
>> -/// to treat const method calls as reads and non-const method calls as
>> writes,
>> -/// and check that the appropriate locks are held. Non-const method calls
>> with
>> -/// the same signature as const method calls can be also treated as
>> reads.
>> -///
>> -/// FIXME: We need to also visit CallExprs to catch/check global
>> functions.
>> -void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
>> - NamedDecl *D = dyn_cast_or_null<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 with kind exclusive.
>> - case attr::ExclusiveLockFunction:
>> - addLocksToSet<ExclusiveLockFunctionAttr>(LK_Exclusive, Attr,
>> Exp);
>> - break;
>> -
>> - // When we encounter a shared lock function, we need to add the
>> lock
>> - // to our lockset with kind shared.
>> - case attr::SharedLockFunction:
>> - addLocksToSet<SharedLockFunctionAttr>(LK_Shared, Attr, Exp);
>> - 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: {
>> - 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;
>> - }
>> -
>> - case attr::ExclusiveLocksRequired: {
>> - // FIXME: Also use this attribute to add required locks to the
>> initial
>> - // lockset when processing a CFG for a function annotated with
>> this
>> - // attribute.
>> - ExclusiveLocksRequiredAttr *ELRAttr =
>> - cast<ExclusiveLocksRequiredAttr>(Attr);
>> -
>> - for (ExclusiveLocksRequiredAttr::args_iterator
>> - I = ELRAttr->args_begin(), E = ELRAttr->args_end(); I != E;
>> ++I)
>> - warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall);
>> - break;
>> - }
>> -
>> - case attr::SharedLocksRequired: {
>> - // FIXME: Also use this attribute to add required locks to the
>> initial
>> - // lockset when processing a CFG for a function annotated with
>> this
>> - // attribute.
>> - SharedLocksRequiredAttr *SLRAttr =
>> cast<SharedLocksRequiredAttr>(Attr);
>> -
>> - for (SharedLocksRequiredAttr::args_iterator I =
>> SLRAttr->args_begin(),
>> - E = SLRAttr->args_end(); I != E; ++I)
>> - warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall);
>> - break;
>> - }
>> -
>> - case attr::LocksExcluded: {
>> - LocksExcludedAttr *LEAttr = cast<LocksExcludedAttr>(Attr);
>> - for (LocksExcludedAttr::args_iterator I = LEAttr->args_begin(),
>> - E = LEAttr->args_end(); I != E; ++I) {
>> - MutexID Mutex(*I);
>> - if (locksetContains(Mutex))
>> - Handler.handleFunExcludesLock(D->getName(), Mutex.getName(),
>> - ExpLocation);
>> - }
>> - break;
>> - }
>> -
>> - case attr::LockReturned:
>> - // FIXME: Deal with this attribute.
>> - break;
>> -
>> - // Ignore other (non thread-safety) attributes
>> - default:
>> - break;
>> - }
>> - }
>> -}
>> -
>> -} // end anonymous namespace
>> -
>> -/// \brief Flags a warning for each lock that is in LSet2 but not LSet1,
>> or
>> -/// else mutexes that are held shared in one lockset and exclusive in the
>> other.
>> -static Lockset warnIfNotInFirstSetOrNotSameKind(ThreadSafetyHandler
>> &Handler,
>> - const Lockset LSet1,
>> - const Lockset LSet2,
>> - Lockset Intersection,
>> - Lockset::Factory &Fact) {
>> - for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I)
>> {
>> - const MutexID &LSet2Mutex = I.getKey();
>> - const LockData &LSet2LockData = I.getData();
>> - if (const LockData *LD = LSet1.lookup(LSet2Mutex)) {
>> - if (LD->LKind != LSet2LockData.LKind) {
>> - Handler.handleExclusiveAndShared(LSet2Mutex.getName(),
>> - LSet2LockData.AcquireLoc,
>> - LD->AcquireLoc);
>> - if (LD->LKind != LK_Exclusive)
>> - Intersection = Fact.add(Intersection, LSet2Mutex,
>> LSet2LockData);
>> - }
>> - } else {
>> - Handler.handleMutexHeldEndOfScope(LSet2Mutex.getName(),
>> - LSet2LockData.AcquireLoc);
>> - }
>> - }
>> - return Intersection;
>> -}
>> -
>> -
>> -/// \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(ThreadSafetyHandler &Handler,
>> - const Lockset LSet1, const Lockset LSet2,
>> - Lockset::Factory &Fact) {
>> - Lockset Intersection = LSet1;
>> - Intersection = warnIfNotInFirstSetOrNotSameKind(Handler, LSet1, LSet2,
>> - Intersection, Fact);
>> -
>> - for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I)
>> {
>> - if (!LSet2.contains(I.getKey())) {
>> - const MutexID &Mutex = I.getKey();
>> - const LockData &MissingLock = I.getData();
>> - Handler.handleMutexHeldEndOfScope(Mutex.getName(),
>> - MissingLock.AcquireLoc);
>> - Intersection = Fact.remove(Intersection, Mutex);
>> - }
>> - }
>> - return Intersection;
>> -}
>> -
>> -/// \brief Returns the location of the first Stmt in a Block.
>> -static SourceLocation getFirstStmtLocation(CFGBlock *Block) {
>> - SourceLocation Loc;
>> - for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
>> - BI != BE; ++BI) {
>> - if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&(*BI))) {
>> - Loc = CfgStmt->getStmt()->getLocStart();
>> - if (Loc.isValid()) return Loc;
>> - }
>> - }
>> - if (Stmt *S = Block->getTerminator().getStmt()) {
>> - Loc = S->getLocStart();
>> - if (Loc.isValid()) return Loc;
>> - }
>> - return Loc;
>> -}
>> -
>> -/// \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 before starting the loop
>> -/// \param LoopReentrySet Locks in the last CFG block of the loop
>> -static void warnBackEdgeUnequalLocksets(ThreadSafetyHandler &Handler,
>> - const Lockset LoopReentrySet,
>> - const Lockset LoopEntrySet,
>> - SourceLocation FirstLocInLoop,
>> - Lockset::Factory &Fact) {
>> - assert(FirstLocInLoop.isValid());
>> - // 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())) {
>> - // We report this error at the location of the first statement in a
>> loop
>> - Handler.handleNoLockLoopEntry(I.getKey().getName(),
>> FirstLocInLoop);
>> - }
>> - }
>> -
>> - // Warn for locks held at the end of the loop, but not at the start.
>> - warnIfNotInFirstSetOrNotSameKind(Handler, LoopEntrySet, LoopReentrySet,
>> - LoopReentrySet, Fact);
>> -}
>> -
>> -
>> -namespace clang { namespace thread_safety {
>> -/// \brief Check a function's CFG for thread-safety violations.
>> -///
>> -/// We traverse the blocks in the CFG, compute the set of mutexes 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.
>> -void runThreadSafetyAnalysis(AnalysisContext &AC,
>> - ThreadSafetyHandler &Handler) {
>> - CFG *CFGraph = AC.getCFG();
>> - if (!CFGraph) return;
>> - const Decl *D = AC.getDecl();
>> - if (D && D->getAttr<NoThreadSafetyAnalysisAttr>()) return;
>> -
>> - 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 Mutex 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 (*PI == 0 || !VisitedBlocks.alreadySet(*PI))
>> - continue;
>> -
>> - int PrevBlockID = (*PI)->getBlockID();
>> - if (!LocksetInitialized) {
>> - Entryset = ExitLocksets[PrevBlockID];
>> - LocksetInitialized = true;
>> - } else {
>> - Entryset = intersectAndWarn(Handler, Entryset,
>> - ExitLocksets[PrevBlockID],
>> LocksetFactory);
>> - }
>> - }
>> -
>> - BuildLockset LocksetBuilder(Handler, 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(const_cast<Stmt*>(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 (*SI == 0 || !VisitedBlocks.alreadySet(*SI))
>> - continue;
>> -
>> - CFGBlock *FirstLoopBlock = *SI;
>> - SourceLocation FirstLoopLocation =
>> getFirstStmtLocation(FirstLoopBlock);
>> -
>> - assert(FirstLoopLocation.isValid());
>> -
>> - // Fail gracefully in release code.
>> - if (!FirstLoopLocation.isValid())
>> - continue;
>> -
>> - Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
>> - Lockset LoopEnd = ExitLocksets[CurrBlockID];
>> - warnBackEdgeUnequalLocksets(Handler, LoopEnd, PreLoop,
>> FirstLoopLocation,
>> - LocksetFactory);
>> - }
>> - }
>> -
>> - Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
>> - if (!FinalLockset.isEmpty()) {
>> - for (Lockset::iterator I=FinalLockset.begin(), E=FinalLockset.end();
>> - I != E; ++I) {
>> - const MutexID &Mutex = I.getKey();
>> - const LockData &MissingLock = I.getData();
>> -
>> - std::string FunName = "<unknown>";
>> - if (const NamedDecl *ContextDecl =
>> dyn_cast<NamedDecl>(AC.getDecl())) {
>> - FunName = ContextDecl->getDeclName().getAsString();
>> - }
>> -
>> - Handler.handleNoUnlock(Mutex.getName(), FunName,
>> MissingLock.AcquireLoc);
>> - }
>> - }
>> -}
>> -
>> -}} // end namespace clang::thread_safety
>> -
>> -
>>
>> //===----------------------------------------------------------------------===//
>> // AnalysisBasedWarnings - Worker object used by Sema to execute
>> analysis-based
>> // warnings on a function, method, or block.
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 139369update.patch
Type: text/x-patch
Size: 8213 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20110912/b308025d/attachment.bin>
More information about the cfe-commits
mailing list