forked from OSchip/llvm-project
parent
e3013dd62d
commit
3d312b175a
|
@ -41,6 +41,7 @@ using namespace thread_safety;
|
||||||
ThreadSafetyHandler::~ThreadSafetyHandler() {}
|
ThreadSafetyHandler::~ThreadSafetyHandler() {}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// \brief Implements a set of CFGBlocks using a BitVector.
|
/// \brief Implements a set of CFGBlocks using a BitVector.
|
||||||
///
|
///
|
||||||
/// This class contains a minimal interface, primarily dictated by the SetType
|
/// This class contains a minimal interface, primarily dictated by the SetType
|
||||||
|
@ -83,6 +84,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// \brief We create a helper class which we use to iterate through CFGBlocks in
|
/// \brief We create a helper class which we use to iterate through CFGBlocks in
|
||||||
/// the topological order.
|
/// the topological order.
|
||||||
class TopologicallySortedCFG {
|
class TopologicallySortedCFG {
|
||||||
|
@ -116,6 +118,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// \brief A MutexID object uniquely identifies a particular mutex, and
|
/// \brief A MutexID object uniquely identifies a particular mutex, and
|
||||||
/// is built from an Expr* (i.e. calling a lock function).
|
/// is built from an Expr* (i.e. calling a lock function).
|
||||||
///
|
///
|
||||||
|
@ -275,6 +278,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// \brief This is a helper class that stores info about the most recent
|
/// \brief This is a helper class that stores info about the most recent
|
||||||
/// accquire of a Lock.
|
/// accquire of a Lock.
|
||||||
///
|
///
|
||||||
|
@ -302,11 +306,12 @@ struct LockData {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Profile(llvm::FoldingSetNodeID &ID) const {
|
void Profile(llvm::FoldingSetNodeID &ID) const {
|
||||||
ID.AddInteger(AcquireLoc.getRawEncoding());
|
ID.AddInteger(AcquireLoc.getRawEncoding());
|
||||||
ID.AddInteger(LKind);
|
ID.AddInteger(LKind);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// A Lockset maps each MutexID (defined above) to information about how it has
|
/// A Lockset maps each MutexID (defined above) to information about how it has
|
||||||
/// been locked.
|
/// been locked.
|
||||||
typedef llvm::ImmutableMap<MutexID, LockData> Lockset;
|
typedef llvm::ImmutableMap<MutexID, LockData> Lockset;
|
||||||
|
@ -324,25 +329,27 @@ class BuildLockset : public StmtVisitor<BuildLockset> {
|
||||||
// Helper functions
|
// Helper functions
|
||||||
void removeLock(SourceLocation UnlockLoc, MutexID &Mutex);
|
void removeLock(SourceLocation UnlockLoc, MutexID &Mutex);
|
||||||
void addLock(SourceLocation LockLoc, MutexID &Mutex, LockKind LK);
|
void addLock(SourceLocation LockLoc, MutexID &Mutex, LockKind LK);
|
||||||
|
|
||||||
|
template <class AttrType>
|
||||||
|
void addLocksToSet(LockKind LK, AttrType *Attr, CXXMemberCallExpr *Exp,
|
||||||
|
NamedDecl *D);
|
||||||
|
|
||||||
const ValueDecl *getValueDecl(Expr *Exp);
|
const ValueDecl *getValueDecl(Expr *Exp);
|
||||||
void warnIfMutexNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK,
|
void warnIfMutexNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK,
|
||||||
Expr *MutexExp, ProtectedOperationKind POK);
|
Expr *MutexExp, ProtectedOperationKind POK);
|
||||||
void checkAccess(Expr *Exp, AccessKind AK);
|
void checkAccess(Expr *Exp, AccessKind AK);
|
||||||
void checkDereference(Expr *Exp, AccessKind AK);
|
void checkDereference(Expr *Exp, AccessKind AK);
|
||||||
|
|
||||||
template <class AttrType>
|
|
||||||
void addLocksToSet(LockKind LK, AttrType *Attr, CXXMemberCallExpr *Exp,
|
|
||||||
NamedDecl* D);
|
|
||||||
|
|
||||||
/// \brief Returns true if the lockset contains a lock, regardless of whether
|
/// \brief Returns true if the lockset contains a lock, regardless of whether
|
||||||
/// the lock is held exclusively or shared.
|
/// the lock is held exclusively or shared.
|
||||||
bool locksetContains(MutexID Lock) const {
|
bool locksetContains(const MutexID &Lock) const {
|
||||||
return LSet.lookup(Lock);
|
return LSet.lookup(Lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Returns true if the lockset contains a lock with the passed in
|
/// \brief Returns true if the lockset contains a lock with the passed in
|
||||||
/// locktype.
|
/// locktype.
|
||||||
bool locksetContains(MutexID Lock, LockKind KindRequested) const {
|
bool locksetContains(const MutexID &Lock, LockKind KindRequested) const {
|
||||||
const LockData *LockHeld = LSet.lookup(Lock);
|
const LockData *LockHeld = LSet.lookup(Lock);
|
||||||
return (LockHeld && KindRequested == LockHeld->LKind);
|
return (LockHeld && KindRequested == LockHeld->LKind);
|
||||||
}
|
}
|
||||||
|
@ -351,7 +358,8 @@ class BuildLockset : public StmtVisitor<BuildLockset> {
|
||||||
/// passed in locktype. So for example, if we pass in LK_Shared, this function
|
/// 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
|
/// 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.
|
/// LK_Exclusive, this function returns true if the lock is held LK_Exclusive.
|
||||||
bool locksetContainsAtLeast(MutexID Lock, LockKind KindRequested) const {
|
bool locksetContainsAtLeast(const MutexID &Lock,
|
||||||
|
LockKind KindRequested) const {
|
||||||
switch (KindRequested) {
|
switch (KindRequested) {
|
||||||
case LK_Shared:
|
case LK_Shared:
|
||||||
return locksetContains(Lock);
|
return locksetContains(Lock);
|
||||||
|
@ -402,6 +410,36 @@ void BuildLockset::removeLock(SourceLocation UnlockLoc, MutexID &Mutex) {
|
||||||
LSet = NewLSet;
|
LSet = NewLSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \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, AttrType *Attr,
|
||||||
|
CXXMemberCallExpr *Exp,
|
||||||
|
NamedDecl* FunDecl) {
|
||||||
|
typedef typename AttrType::args_iterator iterator_type;
|
||||||
|
|
||||||
|
SourceLocation ExpLocation = Exp->getExprLoc();
|
||||||
|
|
||||||
|
if (Attr->args_size() == 0) {
|
||||||
|
// The mutex held is the "this" object.
|
||||||
|
|
||||||
|
MutexID Mutex(0, Exp, FunDecl);
|
||||||
|
if (!Mutex.isValid())
|
||||||
|
Handler.handleInvalidLockExp(Exp->getExprLoc());
|
||||||
|
else
|
||||||
|
addLock(ExpLocation, Mutex, LK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) {
|
||||||
|
MutexID Mutex(*I, Exp, FunDecl);
|
||||||
|
if (!Mutex.isValid())
|
||||||
|
Handler.handleInvalidLockExp(Exp->getExprLoc());
|
||||||
|
else
|
||||||
|
addLock(ExpLocation, Mutex, LK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
|
/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
|
||||||
const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
|
const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
|
||||||
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
|
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
|
||||||
|
@ -427,7 +465,6 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp,
|
||||||
Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK, Exp->getExprLoc());
|
Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK, Exp->getExprLoc());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// \brief This method identifies variable dereferences and checks pt_guarded_by
|
/// \brief This method identifies variable dereferences and checks pt_guarded_by
|
||||||
/// and pt_guarded_var annotations. Note that we only check these annotations
|
/// and pt_guarded_var annotations. Note that we only check these annotations
|
||||||
/// at the time a pointer is dereferenced.
|
/// at the time a pointer is dereferenced.
|
||||||
|
@ -512,36 +549,6 @@ void BuildLockset::VisitCastExpr(CastExpr *CE) {
|
||||||
checkDereference(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, AttrType *Attr,
|
|
||||||
CXXMemberCallExpr *Exp,
|
|
||||||
NamedDecl* FunDecl) {
|
|
||||||
typedef typename AttrType::args_iterator iterator_type;
|
|
||||||
|
|
||||||
SourceLocation ExpLocation = Exp->getExprLoc();
|
|
||||||
|
|
||||||
if (Attr->args_size() == 0) {
|
|
||||||
// The mutex held is the "this" object.
|
|
||||||
|
|
||||||
MutexID Mutex(0, Exp, FunDecl);
|
|
||||||
if (!Mutex.isValid())
|
|
||||||
Handler.handleInvalidLockExp(Exp->getExprLoc());
|
|
||||||
else
|
|
||||||
addLock(ExpLocation, Mutex, LK);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) {
|
|
||||||
MutexID Mutex(*I, Exp, FunDecl);
|
|
||||||
if (!Mutex.isValid())
|
|
||||||
Handler.handleInvalidLockExp(Exp->getExprLoc());
|
|
||||||
else
|
|
||||||
addLock(ExpLocation, Mutex, LK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief When visiting CXXMemberCallExprs we need to examine the attributes on
|
/// \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
|
/// the method that is being called and add, remove or check locks in the
|
||||||
/// lockset accordingly.
|
/// lockset accordingly.
|
||||||
|
@ -647,7 +654,23 @@ void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end anonymous namespace
|
|
||||||
|
/// \brief Class which implements the core thread safety analysis routines.
|
||||||
|
class ThreadSafetyAnalyzer {
|
||||||
|
ThreadSafetyHandler &Handler;
|
||||||
|
Lockset::Factory LocksetFactory;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {}
|
||||||
|
|
||||||
|
Lockset intersectAndWarn(const Lockset LSet1, const Lockset LSet2,
|
||||||
|
LockErrorKind LEK);
|
||||||
|
|
||||||
|
Lockset addLock(Lockset &LSet, Expr *MutexExp, const NamedDecl *D,
|
||||||
|
LockKind LK, SourceLocation Loc);
|
||||||
|
|
||||||
|
void runAnalysis(AnalysisContext &AC);
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Compute the intersection of two locksets and issue warnings for any
|
/// \brief Compute the intersection of two locksets and issue warnings for any
|
||||||
/// locks in the symmetric difference.
|
/// locks in the symmetric difference.
|
||||||
|
@ -657,9 +680,9 @@ void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
|
||||||
/// A; if () then B; else C; D; we need to check that the lockset after B and C
|
/// 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
|
/// are the same. In the event of a difference, we use the intersection of these
|
||||||
/// two locksets at the start of D.
|
/// two locksets at the start of D.
|
||||||
static Lockset intersectAndWarn(ThreadSafetyHandler &Handler,
|
Lockset ThreadSafetyAnalyzer::intersectAndWarn(const Lockset LSet1,
|
||||||
const Lockset LSet1, const Lockset LSet2,
|
const Lockset LSet2,
|
||||||
Lockset::Factory &Fact, LockErrorKind LEK) {
|
LockErrorKind LEK) {
|
||||||
Lockset Intersection = LSet1;
|
Lockset Intersection = LSet1;
|
||||||
for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) {
|
for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) {
|
||||||
const MutexID &LSet2Mutex = I.getKey();
|
const MutexID &LSet2Mutex = I.getKey();
|
||||||
|
@ -670,7 +693,8 @@ static Lockset intersectAndWarn(ThreadSafetyHandler &Handler,
|
||||||
LSet2LockData.AcquireLoc,
|
LSet2LockData.AcquireLoc,
|
||||||
LD->AcquireLoc);
|
LD->AcquireLoc);
|
||||||
if (LD->LKind != LK_Exclusive)
|
if (LD->LKind != LK_Exclusive)
|
||||||
Intersection = Fact.add(Intersection, LSet2Mutex, LSet2LockData);
|
Intersection = LocksetFactory.add(Intersection, LSet2Mutex,
|
||||||
|
LSet2LockData);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Handler.handleMutexHeldEndOfScope(LSet2Mutex.getName(),
|
Handler.handleMutexHeldEndOfScope(LSet2Mutex.getName(),
|
||||||
|
@ -684,16 +708,15 @@ static Lockset intersectAndWarn(ThreadSafetyHandler &Handler,
|
||||||
const LockData &MissingLock = I.getData();
|
const LockData &MissingLock = I.getData();
|
||||||
Handler.handleMutexHeldEndOfScope(Mutex.getName(),
|
Handler.handleMutexHeldEndOfScope(Mutex.getName(),
|
||||||
MissingLock.AcquireLoc, LEK);
|
MissingLock.AcquireLoc, LEK);
|
||||||
Intersection = Fact.remove(Intersection, Mutex);
|
Intersection = LocksetFactory.remove(Intersection, Mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Intersection;
|
return Intersection;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Lockset addLock(ThreadSafetyHandler &Handler,
|
Lockset ThreadSafetyAnalyzer::addLock(Lockset &LSet, Expr *MutexExp,
|
||||||
Lockset::Factory &LocksetFactory,
|
const NamedDecl *D,
|
||||||
Lockset &LSet, Expr *MutexExp, const NamedDecl *D,
|
LockKind LK, SourceLocation Loc) {
|
||||||
LockKind LK, SourceLocation Loc) {
|
|
||||||
MutexID Mutex(MutexExp, 0, D);
|
MutexID Mutex(MutexExp, 0, D);
|
||||||
if (!Mutex.isValid()) {
|
if (!Mutex.isValid()) {
|
||||||
Handler.handleInvalidLockExp(MutexExp->getExprLoc());
|
Handler.handleInvalidLockExp(MutexExp->getExprLoc());
|
||||||
|
@ -703,15 +726,12 @@ static Lockset addLock(ThreadSafetyHandler &Handler,
|
||||||
return LocksetFactory.add(LSet, Mutex, NewLock);
|
return LocksetFactory.add(LSet, Mutex, NewLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace clang {
|
|
||||||
namespace thread_safety {
|
|
||||||
/// \brief Check a function's CFG for thread-safety violations.
|
/// \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
|
/// 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.
|
/// at the end of each block, and issue warnings for thread safety violations.
|
||||||
/// Each block in the CFG is traversed exactly once.
|
/// Each block in the CFG is traversed exactly once.
|
||||||
void runThreadSafetyAnalysis(AnalysisContext &AC,
|
void ThreadSafetyAnalyzer::runAnalysis(AnalysisContext &AC) {
|
||||||
ThreadSafetyHandler &Handler) {
|
|
||||||
CFG *CFGraph = AC.getCFG();
|
CFG *CFGraph = AC.getCFG();
|
||||||
if (!CFGraph) return;
|
if (!CFGraph) return;
|
||||||
const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl());
|
const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl());
|
||||||
|
@ -721,9 +741,7 @@ void runThreadSafetyAnalysis(AnalysisContext &AC,
|
||||||
if (D->getAttr<NoThreadSafetyAnalysisAttr>())
|
if (D->getAttr<NoThreadSafetyAnalysisAttr>())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Lockset::Factory LocksetFactory;
|
// FIXME: Switch to SmallVector? Otherwise improve performance impact?
|
||||||
|
|
||||||
// FIXME: Swith to SmallVector? Otherwise improve performance impact?
|
|
||||||
std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(),
|
std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(),
|
||||||
LocksetFactory.getEmptyMap());
|
LocksetFactory.getEmptyMap());
|
||||||
std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(),
|
std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(),
|
||||||
|
@ -735,6 +753,8 @@ void runThreadSafetyAnalysis(AnalysisContext &AC,
|
||||||
TopologicallySortedCFG SortedGraph(CFGraph);
|
TopologicallySortedCFG SortedGraph(CFGraph);
|
||||||
CFGBlockSet VisitedBlocks(CFGraph);
|
CFGBlockSet VisitedBlocks(CFGraph);
|
||||||
|
|
||||||
|
// Add locks from exclusive_locks_required and shared_locks_required
|
||||||
|
// to initial lockset.
|
||||||
if (!SortedGraph.empty() && D->hasAttrs()) {
|
if (!SortedGraph.empty() && D->hasAttrs()) {
|
||||||
const CFGBlock *FirstBlock = *SortedGraph.begin();
|
const CFGBlock *FirstBlock = *SortedGraph.begin();
|
||||||
Lockset &InitialLockset = EntryLocksets[FirstBlock->getBlockID()];
|
Lockset &InitialLockset = EntryLocksets[FirstBlock->getBlockID()];
|
||||||
|
@ -747,7 +767,7 @@ void runThreadSafetyAnalysis(AnalysisContext &AC,
|
||||||
for (SharedLocksRequiredAttr::args_iterator
|
for (SharedLocksRequiredAttr::args_iterator
|
||||||
SLRIter = SLRAttr->args_begin(),
|
SLRIter = SLRAttr->args_begin(),
|
||||||
SLREnd = SLRAttr->args_end(); SLRIter != SLREnd; ++SLRIter)
|
SLREnd = SLRAttr->args_end(); SLRIter != SLREnd; ++SLRIter)
|
||||||
InitialLockset = addLock(Handler, LocksetFactory, InitialLockset,
|
InitialLockset = addLock(InitialLockset,
|
||||||
*SLRIter, D, LK_Shared,
|
*SLRIter, D, LK_Shared,
|
||||||
AttrLoc);
|
AttrLoc);
|
||||||
} else if (ExclusiveLocksRequiredAttr *ELRAttr
|
} else if (ExclusiveLocksRequiredAttr *ELRAttr
|
||||||
|
@ -755,7 +775,7 @@ void runThreadSafetyAnalysis(AnalysisContext &AC,
|
||||||
for (ExclusiveLocksRequiredAttr::args_iterator
|
for (ExclusiveLocksRequiredAttr::args_iterator
|
||||||
ELRIter = ELRAttr->args_begin(),
|
ELRIter = ELRAttr->args_begin(),
|
||||||
ELREnd = ELRAttr->args_end(); ELRIter != ELREnd; ++ELRIter)
|
ELREnd = ELRAttr->args_end(); ELRIter != ELREnd; ++ELRIter)
|
||||||
InitialLockset = addLock(Handler, LocksetFactory, InitialLockset,
|
InitialLockset = addLock(InitialLockset,
|
||||||
*ELRIter, D, LK_Exclusive,
|
*ELRIter, D, LK_Exclusive,
|
||||||
AttrLoc);
|
AttrLoc);
|
||||||
}
|
}
|
||||||
|
@ -799,8 +819,7 @@ void runThreadSafetyAnalysis(AnalysisContext &AC,
|
||||||
Entryset = ExitLocksets[PrevBlockID];
|
Entryset = ExitLocksets[PrevBlockID];
|
||||||
LocksetInitialized = true;
|
LocksetInitialized = true;
|
||||||
} else {
|
} else {
|
||||||
Entryset = intersectAndWarn(Handler, Entryset,
|
Entryset = intersectAndWarn(Entryset, ExitLocksets[PrevBlockID],
|
||||||
ExitLocksets[PrevBlockID], LocksetFactory,
|
|
||||||
LEK_LockedSomePredecessors);
|
LEK_LockedSomePredecessors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -827,8 +846,7 @@ void runThreadSafetyAnalysis(AnalysisContext &AC,
|
||||||
CFGBlock *FirstLoopBlock = *SI;
|
CFGBlock *FirstLoopBlock = *SI;
|
||||||
Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
|
Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
|
||||||
Lockset LoopEnd = ExitLocksets[CurrBlockID];
|
Lockset LoopEnd = ExitLocksets[CurrBlockID];
|
||||||
intersectAndWarn(Handler, LoopEnd, PreLoop, LocksetFactory,
|
intersectAndWarn(LoopEnd, PreLoop, LEK_LockedSomeLoopIterations);
|
||||||
LEK_LockedSomeLoopIterations);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,8 +854,24 @@ void runThreadSafetyAnalysis(AnalysisContext &AC,
|
||||||
Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
|
Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
|
||||||
|
|
||||||
// FIXME: Should we call this function for all blocks which exit the function?
|
// FIXME: Should we call this function for all blocks which exit the function?
|
||||||
intersectAndWarn(Handler, InitialLockset, FinalLockset, LocksetFactory,
|
intersectAndWarn(InitialLockset, FinalLockset, LEK_LockedAtEndOfFunction);
|
||||||
LEK_LockedAtEndOfFunction);
|
}
|
||||||
|
|
||||||
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
||||||
|
ThreadSafetyAnalyzer Analyzer(Handler);
|
||||||
|
Analyzer.runAnalysis(AC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Helper function that returns a LockKind required for the given level
|
/// \brief Helper function that returns a LockKind required for the given level
|
||||||
|
@ -851,4 +885,5 @@ LockKind getLockKindFromAccessKind(AccessKind AK) {
|
||||||
}
|
}
|
||||||
llvm_unreachable("Unknown AccessKind");
|
llvm_unreachable("Unknown AccessKind");
|
||||||
}
|
}
|
||||||
|
|
||||||
}} // end namespace clang::thread_safety
|
}} // end namespace clang::thread_safety
|
||||||
|
|
Loading…
Reference in New Issue