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