• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
checkDoubleNonblocking(const CallEvent & PreCallEvent,CheckerContext & Ctx) const25 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 
checkUnmatchedWaits(const CallEvent & PreCallEvent,CheckerContext & Ctx) const56 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 
checkMissingWaits(SymbolReaper & SymReaper,CheckerContext & Ctx) const99 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 
topRegionUsedByWait(const CallEvent & CE) const135 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 
allRegionsUsedByWait(llvm::SmallVector<const MemRegion *,2> & ReqRegions,const MemRegion * const MR,const CallEvent & CE,CheckerContext & Ctx) const146 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.
registerMPIChecker(CheckerManager & MGR)188 void clang::ento::registerMPIChecker(CheckerManager &MGR) {
189   MGR.registerChecker<clang::ento::mpi::MPIChecker>();
190 }
191