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