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