diff options
Diffstat (limited to 'lib/Analysis/ThreadSafety.cpp')
| -rw-r--r-- | lib/Analysis/ThreadSafety.cpp | 301 | 
1 files changed, 251 insertions, 50 deletions
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp index f45d6e7ef139e..e2c6ab5d94850 100644 --- a/lib/Analysis/ThreadSafety.cpp +++ b/lib/Analysis/ThreadSafety.cpp @@ -44,14 +44,13 @@  #include <sstream>  #include <utility>  #include <vector> - - -namespace clang { -namespace threadSafety { +using namespace clang; +using namespace threadSafety;  // Key method definition  ThreadSafetyHandler::~ThreadSafetyHandler() {} +namespace {  class TILPrinter :    public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {}; @@ -69,7 +68,6 @@ static void warnInvalidLock(ThreadSafetyHandler &Handler,      Handler.handleInvalidLockExp(Kind, Loc);  } -  /// \brief A set of CapabilityInfo objects, which are compiled from the  /// requires attributes on a function.  class CapExprSet : public SmallVector<CapabilityExpr, 4> { @@ -101,17 +99,22 @@ private:    LockKind          LKind;            ///<  exclusive or shared    SourceLocation    AcquireLoc;       ///<  where it was acquired.    bool              Asserted;         ///<  true if the lock was asserted +  bool              Declared;         ///<  true if the lock was declared  public:    FactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc, -            bool Asrt) -      : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Asserted(Asrt) {} +            bool Asrt, bool Declrd = false) +      : CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Asserted(Asrt), +        Declared(Declrd) {}    virtual ~FactEntry() {} -  LockKind          kind()       const { return LKind;    } +  LockKind          kind()       const { return LKind;      }    SourceLocation    loc()        const { return AcquireLoc; }    bool              asserted()   const { return Asserted; } +  bool              declared()   const { return Declared; } + +  void setDeclared(bool D) { Declared = D; }    virtual void    handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan, @@ -231,14 +234,61 @@ public:    FactEntry *findPartialMatch(FactManager &FM,                                const CapabilityExpr &CapE) const { -    auto I = std::find_if(begin(), end(), [&](FactID ID) { +    auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {        return FM[ID].partiallyMatches(CapE);      });      return I != end() ? &FM[*I] : nullptr;    } + +  bool containsMutexDecl(FactManager &FM, const ValueDecl* Vd) const { +    auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool { +      return FM[ID].valueDecl() == Vd; +    }); +    return I != end(); +  }  }; +class ThreadSafetyAnalyzer; +} // namespace + +namespace clang { +namespace threadSafety { +class BeforeSet { +private: +  typedef SmallVector<const ValueDecl*, 4>  BeforeVect; + +  struct BeforeInfo { +    BeforeInfo() : Vect(nullptr), Visited(false) { } +    BeforeInfo(BeforeInfo &&O) +        : Vect(std::move(O.Vect)), Visited(O.Visited) +    {} + +    std::unique_ptr<BeforeVect> Vect; +    int                         Visited; +  }; + +  typedef llvm::DenseMap<const ValueDecl*, BeforeInfo>  BeforeMap; +  typedef llvm::DenseMap<const ValueDecl*, bool>        CycleMap; + +public: +  BeforeSet() { } + +  BeforeInfo* insertAttrExprs(const ValueDecl* Vd, +                              ThreadSafetyAnalyzer& Analyzer); + +  void checkBeforeAfter(const ValueDecl* Vd, +                        const FactSet& FSet, +                        ThreadSafetyAnalyzer& Analyzer, +                        SourceLocation Loc, StringRef CapKind); + +private: +  BeforeMap BMap; +  CycleMap  CycMap; +}; +} // end namespace threadSafety +} // end namespace clang +namespace {  typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;  class LocalVariableMap; @@ -853,6 +903,7 @@ public:  /// \brief Class which implements the core thread safety analysis routines.  class ThreadSafetyAnalyzer {    friend class BuildLockset; +  friend class threadSafety::BeforeSet;    llvm::BumpPtrAllocator Bpa;    threadSafety::til::MemRegionRef Arena; @@ -864,9 +915,11 @@ class ThreadSafetyAnalyzer {    FactManager               FactMan;    std::vector<CFGBlockInfo> BlockInfo; +  BeforeSet* GlobalBeforeSet; +  public: -  ThreadSafetyAnalyzer(ThreadSafetyHandler &H) -     : Arena(&Bpa), SxBuilder(Arena), Handler(H) {} +  ThreadSafetyAnalyzer(ThreadSafetyHandler &H, BeforeSet* Bset) +     : Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}    bool inCurrentScope(const CapabilityExpr &CapE); @@ -906,6 +959,134 @@ public:    void runAnalysis(AnalysisDeclContext &AC);  }; +} // namespace + +/// Process acquired_before and acquired_after attributes on Vd. +BeforeSet::BeforeInfo* BeforeSet::insertAttrExprs(const ValueDecl* Vd, +    ThreadSafetyAnalyzer& Analyzer) { +  // Create a new entry for Vd. +  auto& Entry = BMap.FindAndConstruct(Vd); +  BeforeInfo* Info = &Entry.second; +  BeforeVect* Bv = nullptr; + +  for (Attr* At : Vd->attrs()) { +    switch (At->getKind()) { +      case attr::AcquiredBefore: { +        auto *A = cast<AcquiredBeforeAttr>(At); + +        // Create a new BeforeVect for Vd if necessary. +        if (!Bv) { +          Bv = new BeforeVect; +          Info->Vect.reset(Bv); +        } +        // Read exprs from the attribute, and add them to BeforeVect. +        for (const auto *Arg : A->args()) { +          CapabilityExpr Cp = +            Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr); +          if (const ValueDecl *Cpvd = Cp.valueDecl()) { +            Bv->push_back(Cpvd); +            auto It = BMap.find(Cpvd); +            if (It == BMap.end()) +              insertAttrExprs(Cpvd, Analyzer); +          } +        } +        break; +      } +      case attr::AcquiredAfter: { +        auto *A = cast<AcquiredAfterAttr>(At); + +        // Read exprs from the attribute, and add them to BeforeVect. +        for (const auto *Arg : A->args()) { +          CapabilityExpr Cp = +            Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr); +          if (const ValueDecl *ArgVd = Cp.valueDecl()) { +            // Get entry for mutex listed in attribute +            BeforeInfo* ArgInfo; +            auto It = BMap.find(ArgVd); +            if (It == BMap.end()) +              ArgInfo = insertAttrExprs(ArgVd, Analyzer); +            else +              ArgInfo = &It->second; + +            // Create a new BeforeVect if necessary. +            BeforeVect* ArgBv = ArgInfo->Vect.get(); +            if (!ArgBv) { +              ArgBv = new BeforeVect; +              ArgInfo->Vect.reset(ArgBv); +            } +            ArgBv->push_back(Vd); +          } +        } +        break; +      } +      default: +        break; +    } +  } + +  return Info; +} + + +/// Return true if any mutexes in FSet are in the acquired_before set of Vd. +void BeforeSet::checkBeforeAfter(const ValueDecl* StartVd, +                                 const FactSet& FSet, +                                 ThreadSafetyAnalyzer& Analyzer, +                                 SourceLocation Loc, StringRef CapKind) { +  SmallVector<BeforeInfo*, 8> InfoVect; + +  // Do a depth-first traversal of Vd. +  // Return true if there are cycles. +  std::function<bool (const ValueDecl*)> traverse = [&](const ValueDecl* Vd) { +    if (!Vd) +      return false; + +    BeforeSet::BeforeInfo* Info; +    auto It = BMap.find(Vd); +    if (It == BMap.end()) +      Info = insertAttrExprs(Vd, Analyzer); +    else +      Info = &It->second; + +    if (Info->Visited == 1) +      return true; + +    if (Info->Visited == 2) +      return false; + +    BeforeVect* Bv = Info->Vect.get(); +    if (!Bv) +      return false; + +    InfoVect.push_back(Info); +    Info->Visited = 1; +    for (auto *Vdb : *Bv) { +      // Exclude mutexes in our immediate before set. +      if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) { +        StringRef L1 = StartVd->getName(); +        StringRef L2 = Vdb->getName(); +        Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2, Loc); +      } +      // Transitively search other before sets, and warn on cycles. +      if (traverse(Vdb)) { +        if (CycMap.find(Vd) == CycMap.end()) { +          CycMap.insert(std::make_pair(Vd, true)); +          StringRef L1 = Vd->getName(); +          Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->getLocation()); +        } +      } +    } +    Info->Visited = 2; +    return false; +  }; + +  traverse(StartVd); + +  for (auto* Info : InfoVect) +    Info->Visited = 0; +} + +  /// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs.  static const ValueDecl *getValueDecl(const Expr *Exp) { @@ -921,6 +1102,7 @@ static const ValueDecl *getValueDecl(const Expr *Exp) {    return nullptr;  } +namespace {  template <typename Ty>  class has_arg_iterator_range {    typedef char yes[1]; @@ -935,6 +1117,7 @@ class has_arg_iterator_range {  public:    static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes);  }; +} // namespace  static StringRef ClassifyDiagnostic(const CapabilityAttr *A) {    return A->getName(); @@ -1020,7 +1203,13 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet,      }    } -  // FIXME: deal with acquired before/after annotations. +  // Check before/after constraints +  if (Handler.issueBetaWarnings() && +      !Entry->asserted() && !Entry->declared()) { +    GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *this, +                                      Entry->loc(), DiagKind); +  } +    // FIXME: Don't always warn when we have support for reentrant locks.    if (FSet.findLock(FactMan, *Entry)) {      if (!Entry->asserted()) @@ -1119,8 +1308,7 @@ void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,    }  } - -bool getStaticBooleanValue(Expr* E, bool& TCond) { +static bool getStaticBooleanValue(Expr *E, bool &TCond) {    if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) {      TCond = false;      return true; @@ -1230,7 +1418,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,    CapExprSet SharedLocksToAdd;    // If the condition is a call to a Trylock function, then grab the attributes -  for (auto *Attr : FunDecl->getAttrs()) { +  for (auto *Attr : FunDecl->attrs()) {      switch (Attr->getKind()) {        case attr::ExclusiveTrylockFunction: {          ExclusiveTrylockFunctionAttr *A = @@ -1265,6 +1453,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,              CapDiagKind);  } +namespace {  /// \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 @@ -1308,7 +1497,7 @@ public:    void VisitCXXConstructExpr(CXXConstructExpr *Exp);    void VisitDeclStmt(DeclStmt *S);  }; - +} // namespace  /// \brief Warn if the LSet does not contain a lock sufficient to protect access  /// of at least the passed in AccessKind. @@ -1500,13 +1689,23 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK,  ///  void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {    SourceLocation Loc = Exp->getExprLoc(); -  const AttrVec &ArgAttrs = D->getAttrs();    CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;    CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove; +  CapExprSet ScopedExclusiveReqs, ScopedSharedReqs;    StringRef CapDiagKind = "mutex"; -  for(unsigned i = 0; i < ArgAttrs.size(); ++i) { -    Attr *At = const_cast<Attr*>(ArgAttrs[i]); +  // Figure out if we're calling the constructor of scoped lockable class +  bool isScopedVar = false; +  if (VD) { +    if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) { +      const CXXRecordDecl* PD = CD->getParent(); +      if (PD && PD->hasAttr<ScopedLockableAttr>()) +        isScopedVar = true; +    } +  } + +  for(Attr *Atconst : D->attrs()) { +    Attr* At = const_cast<Attr*>(Atconst);      switch (At->getKind()) {        // When we encounter a lock function, we need to add the lock to our        // lockset. @@ -1564,10 +1763,17 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {        case attr::RequiresCapability: {          RequiresCapabilityAttr *A = cast<RequiresCapabilityAttr>(At); -        for (auto *Arg : A->args()) +        for (auto *Arg : A->args()) {            warnIfMutexNotHeld(D, Exp, A->isShared() ? AK_Read : AK_Written, Arg,                               POK_FunctionCall, ClassifyDiagnostic(A),                               Exp->getExprLoc()); +          // use for adopting a lock +          if (isScopedVar) { +            Analyzer->getMutexIDs(A->isShared() ? ScopedSharedReqs +                                                : ScopedExclusiveReqs, +                                  A, Exp, D, VD); +          } +        }          break;        } @@ -1584,16 +1790,6 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {      }    } -  // Figure out if we're calling the constructor of scoped lockable class -  bool isScopedVar = false; -  if (VD) { -    if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) { -      const CXXRecordDecl* PD = CD->getParent(); -      if (PD && PD->hasAttr<ScopedLockableAttr>()) -        isScopedVar = true; -    } -  } -    // Add locks.    for (const auto &M : ExclusiveLocksToAdd)      Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>( @@ -1611,9 +1807,10 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {      // FIXME: does this store a pointer to DRE?      CapabilityExpr Scp = Analyzer->SxBuilder.translateAttrExpr(&DRE, nullptr); -    CapExprSet UnderlyingMutexes(ExclusiveLocksToAdd); -    std::copy(SharedLocksToAdd.begin(), SharedLocksToAdd.end(), -              std::back_inserter(UnderlyingMutexes)); +    std::copy(ScopedExclusiveReqs.begin(), ScopedExclusiveReqs.end(), +              std::back_inserter(ExclusiveLocksToAdd)); +    std::copy(ScopedSharedReqs.begin(), ScopedSharedReqs.end(), +              std::back_inserter(SharedLocksToAdd));      Analyzer->addLock(FSet,                        llvm::make_unique<ScopedLockableFactEntry>(                            Scp, MLoc, ExclusiveLocksToAdd, SharedLocksToAdd), @@ -1863,7 +2060,7 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,  // Return true if block B never continues to its successors. -inline bool neverReturns(const CFGBlock* B) { +static bool neverReturns(const CFGBlock *B) {    if (B->hasNoReturnElement())      return true;    if (B->empty()) @@ -1940,14 +2137,13 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {    if (!SortedGraph->empty() && D->hasAttrs()) {      const CFGBlock *FirstBlock = *SortedGraph->begin();      FactSet &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet; -    const AttrVec &ArgAttrs = D->getAttrs();      CapExprSet ExclusiveLocksToAdd;      CapExprSet SharedLocksToAdd;      StringRef CapDiagKind = "mutex";      SourceLocation Loc = D->getLocation(); -    for (const auto *Attr : ArgAttrs) { +    for (const auto *Attr : D->attrs()) {        Loc = Attr->getLocation();        if (const auto *A = dyn_cast<RequiresCapabilityAttr>(Attr)) {          getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A, @@ -1979,14 +2175,16 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {      }      // FIXME -- Loc can be wrong here. -    for (const auto &Mu : ExclusiveLocksToAdd) -      addLock(InitialLockset, -              llvm::make_unique<LockableFactEntry>(Mu, LK_Exclusive, Loc), -              CapDiagKind, true); -    for (const auto &Mu : SharedLocksToAdd) -      addLock(InitialLockset, -              llvm::make_unique<LockableFactEntry>(Mu, LK_Shared, Loc), -              CapDiagKind, true); +    for (const auto &Mu : ExclusiveLocksToAdd) { +      auto Entry = llvm::make_unique<LockableFactEntry>(Mu, LK_Exclusive, Loc); +      Entry->setDeclared(true); +      addLock(InitialLockset, std::move(Entry), CapDiagKind, true); +    } +    for (const auto &Mu : SharedLocksToAdd) { +      auto Entry = llvm::make_unique<LockableFactEntry>(Mu, LK_Shared, Loc); +      Entry->setDeclared(true); +      addLock(InitialLockset, std::move(Entry), CapDiagKind, true); +    }    }    for (const auto *CurrBlock : *SortedGraph) { @@ -2179,15 +2377,20 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {  /// 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(AnalysisDeclContext &AC, -                             ThreadSafetyHandler &Handler) { -  ThreadSafetyAnalyzer Analyzer(Handler); +void threadSafety::runThreadSafetyAnalysis(AnalysisDeclContext &AC, +                                           ThreadSafetyHandler &Handler, +                                           BeforeSet **BSet) { +  if (!*BSet) +    *BSet = new BeforeSet; +  ThreadSafetyAnalyzer Analyzer(Handler, *BSet);    Analyzer.runAnalysis(AC);  } +void threadSafety::threadSafetyCleanup(BeforeSet *Cache) { delete Cache; } +  /// \brief Helper function that returns a LockKind required for the given level  /// of access. -LockKind getLockKindFromAccessKind(AccessKind AK) { +LockKind threadSafety::getLockKindFromAccessKind(AccessKind AK) {    switch (AK) {      case AK_Read :        return LK_Shared; @@ -2196,5 +2399,3 @@ LockKind getLockKindFromAccessKind(AccessKind AK) {    }    llvm_unreachable("Unknown AccessKind");  } - -}} // end namespace clang::threadSafety  | 
