Home | History | Annotate | Download | only in MPI-Checker
      1 //===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 ///
     10 /// \file
     11 /// This file defines the main class of MPI-Checker which serves as an entry
     12 /// point. It is created once for each translation unit analysed.
     13 /// The checker defines path-sensitive checks, to verify correct usage of the
     14 /// MPI API.
     15 ///
     16 //===----------------------------------------------------------------------===//
     17 
     18 #include "MPIChecker.h"
     19 #include "../ClangSACheckers.h"
     20 
     21 namespace clang {
     22 namespace ento {
     23 namespace mpi {
     24 
     25 void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,
     26                                         CheckerContext &Ctx) const {
     27   if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) {
     28     return;
     29   }
     30   const MemRegion *const MR =
     31       PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion();
     32   if (!MR)
     33     return;
     34   const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
     35 
     36   // The region must be typed, in order to reason about it.
     37   if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
     38     return;
     39 
     40   ProgramStateRef State = Ctx.getState();
     41   const Request *const Req = State->get<RequestMap>(MR);
     42 
     43   // double nonblocking detected
     44   if (Req && Req->CurrentState == Request::State::Nonblocking) {
     45     ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();
     46     BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, Ctx.getBugReporter());
     47     Ctx.addTransition(ErrorNode->getState(), ErrorNode);
     48   }
     49   // no error
     50   else {
     51     State = State->set<RequestMap>(MR, Request::State::Nonblocking);
     52     Ctx.addTransition(State);
     53   }
     54 }
     55 
     56 void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent,
     57                                      CheckerContext &Ctx) const {
     58   if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier()))
     59     return;
     60   const MemRegion *const MR = topRegionUsedByWait(PreCallEvent);
     61   if (!MR)
     62     return;
     63   const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
     64 
     65   // The region must be typed, in order to reason about it.
     66   if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
     67     return;
     68 
     69   llvm::SmallVector<const MemRegion *, 2> ReqRegions;
     70   allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx);
     71   if (ReqRegions.empty())
     72     return;
     73 
     74   ProgramStateRef State = Ctx.getState();
     75   static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait");
     76   ExplodedNode *ErrorNode{nullptr};
     77 
     78   // Check all request regions used by the wait function.
     79   for (const auto &ReqRegion : ReqRegions) {
     80     const Request *const Req = State->get<RequestMap>(ReqRegion);
     81     State = State->set<RequestMap>(ReqRegion, Request::State::Wait);
     82     if (!Req) {
     83       if (!ErrorNode) {
     84         ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
     85         State = ErrorNode->getState();
     86       }
     87       // A wait has no matching nonblocking call.
     88       BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, Ctx.getBugReporter());
     89     }
     90   }
     91 
     92   if (!ErrorNode) {
     93     Ctx.addTransition(State);
     94   } else {
     95     Ctx.addTransition(State, ErrorNode);
     96   }
     97 }
     98 
     99 void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper,
    100                                    CheckerContext &Ctx) const {
    101   if (!SymReaper.hasDeadSymbols())
    102     return;
    103 
    104   ProgramStateRef State = Ctx.getState();
    105   const auto &Requests = State->get<RequestMap>();
    106   if (Requests.isEmpty())
    107     return;
    108 
    109   static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait");
    110   ExplodedNode *ErrorNode{nullptr};
    111 
    112   auto ReqMap = State->get<RequestMap>();
    113   for (const auto &Req : ReqMap) {
    114     if (!SymReaper.isLiveRegion(Req.first)) {
    115       if (Req.second.CurrentState == Request::State::Nonblocking) {
    116 
    117         if (!ErrorNode) {
    118           ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
    119           State = ErrorNode->getState();
    120         }
    121         BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, Ctx.getBugReporter());
    122       }
    123       State = State->remove<RequestMap>(Req.first);
    124     }
    125   }
    126 
    127   // Transition to update the state regarding removed requests.
    128   if (!ErrorNode) {
    129     Ctx.addTransition(State);
    130   } else {
    131     Ctx.addTransition(State, ErrorNode);
    132   }
    133 }
    134 
    135 const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
    136 
    137   if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
    138     return CE.getArgSVal(0).getAsRegion();
    139   } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
    140     return CE.getArgSVal(1).getAsRegion();
    141   } else {
    142     return (const MemRegion *)nullptr;
    143   }
    144 }
    145 
    146 void MPIChecker::allRegionsUsedByWait(
    147     llvm::SmallVector<const MemRegion *, 2> &ReqRegions,
    148     const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {
    149 
    150   MemRegionManager *const RegionManager = MR->getMemRegionManager();
    151 
    152   if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
    153     const MemRegion *SuperRegion{nullptr};
    154     if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
    155       SuperRegion = ER->getSuperRegion();
    156     }
    157 
    158     // A single request is passed to MPI_Waitall.
    159     if (!SuperRegion) {
    160       ReqRegions.push_back(MR);
    161       return;
    162     }
    163 
    164     const auto &Size = Ctx.getStoreManager().getSizeInElements(
    165         Ctx.getState(), SuperRegion,
    166         CE.getArgExpr(1)->getType()->getPointeeType());
    167     const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue();
    168 
    169     for (size_t i = 0; i < ArrSize; ++i) {
    170       const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
    171 
    172       const ElementRegion *const ER = RegionManager->getElementRegion(
    173           CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,
    174           Ctx.getASTContext());
    175 
    176       ReqRegions.push_back(ER->getAs<MemRegion>());
    177     }
    178   } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
    179     ReqRegions.push_back(MR);
    180   }
    181 }
    182 
    183 } // end of namespace: mpi
    184 } // end of namespace: ento
    185 } // end of namespace: clang
    186 
    187 // Registers the checker for static analysis.
    188 void clang::ento::registerMPIChecker(CheckerManager &MGR) {
    189   MGR.registerChecker<clang::ento::mpi::MPIChecker>();
    190 }
    191