blob: 7f9ba0de1dc248dcf6d81706dc6f70fa969d913f [file] [log] [blame]
Devin Coughlin160f19c2016-06-13 03:22:41 +00001//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Devin Coughlin160f19c2016-06-13 03:22:41 +00006//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file defines the main class of MPI-Checker which serves as an entry
11/// point. It is created once for each translation unit analysed.
12/// The checker defines path-sensitive checks, to verify correct usage of the
13/// MPI API.
14///
15//===----------------------------------------------------------------------===//
16
17#include "MPIChecker.h"
Kristof Umann76a21502018-12-15 16:23:51 +000018#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
Devin Coughlin160f19c2016-06-13 03:22:41 +000019
20namespace clang {
21namespace ento {
22namespace mpi {
23
24void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,
25 CheckerContext &Ctx) const {
26 if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) {
27 return;
28 }
29 const MemRegion *const MR =
30 PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion();
31 if (!MR)
32 return;
33 const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
34
35 // The region must be typed, in order to reason about it.
36 if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
37 return;
38
39 ProgramStateRef State = Ctx.getState();
40 const Request *const Req = State->get<RequestMap>(MR);
41
42 // double nonblocking detected
43 if (Req && Req->CurrentState == Request::State::Nonblocking) {
44 ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();
Alexander Kornienkoc5e50932016-07-25 15:27:16 +000045 BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode,
46 Ctx.getBugReporter());
Devin Coughlin160f19c2016-06-13 03:22:41 +000047 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
56void 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.
Alexander Kornienkoc5e50932016-07-25 15:27:16 +000088 BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode,
89 Ctx.getBugReporter());
Devin Coughlin160f19c2016-06-13 03:22:41 +000090 }
91 }
92
93 if (!ErrorNode) {
94 Ctx.addTransition(State);
95 } else {
96 Ctx.addTransition(State, ErrorNode);
97 }
98}
99
100void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper,
101 CheckerContext &Ctx) const {
Devin Coughlin160f19c2016-06-13 03:22:41 +0000102 ProgramStateRef State = Ctx.getState();
103 const auto &Requests = State->get<RequestMap>();
104 if (Requests.isEmpty())
105 return;
106
107 static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait");
108 ExplodedNode *ErrorNode{nullptr};
109
110 auto ReqMap = State->get<RequestMap>();
111 for (const auto &Req : ReqMap) {
112 if (!SymReaper.isLiveRegion(Req.first)) {
113 if (Req.second.CurrentState == Request::State::Nonblocking) {
114
115 if (!ErrorNode) {
116 ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
117 State = ErrorNode->getState();
118 }
Alexander Kornienkoc5e50932016-07-25 15:27:16 +0000119 BReporter.reportMissingWait(Req.second, Req.first, ErrorNode,
120 Ctx.getBugReporter());
Devin Coughlin160f19c2016-06-13 03:22:41 +0000121 }
122 State = State->remove<RequestMap>(Req.first);
123 }
124 }
125
126 // Transition to update the state regarding removed requests.
127 if (!ErrorNode) {
128 Ctx.addTransition(State);
129 } else {
130 Ctx.addTransition(State, ErrorNode);
131 }
132}
133
134const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
135
136 if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
137 return CE.getArgSVal(0).getAsRegion();
138 } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
139 return CE.getArgSVal(1).getAsRegion();
140 } else {
141 return (const MemRegion *)nullptr;
142 }
143}
144
145void MPIChecker::allRegionsUsedByWait(
146 llvm::SmallVector<const MemRegion *, 2> &ReqRegions,
147 const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {
148
149 MemRegionManager *const RegionManager = MR->getMemRegionManager();
150
151 if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
Artem Dergachev6dd11042017-04-13 09:56:07 +0000152 const SubRegion *SuperRegion{nullptr};
Devin Coughlin160f19c2016-06-13 03:22:41 +0000153 if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
Artem Dergachev6dd11042017-04-13 09:56:07 +0000154 SuperRegion = cast<SubRegion>(ER->getSuperRegion());
Devin Coughlin160f19c2016-06-13 03:22:41 +0000155 }
156
157 // A single request is passed to MPI_Waitall.
158 if (!SuperRegion) {
159 ReqRegions.push_back(MR);
160 return;
161 }
162
163 const auto &Size = Ctx.getStoreManager().getSizeInElements(
164 Ctx.getState(), SuperRegion,
165 CE.getArgExpr(1)->getType()->getPointeeType());
166 const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue();
167
168 for (size_t i = 0; i < ArrSize; ++i) {
169 const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
170
171 const ElementRegion *const ER = RegionManager->getElementRegion(
172 CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,
173 Ctx.getASTContext());
174
175 ReqRegions.push_back(ER->getAs<MemRegion>());
176 }
177 } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
178 ReqRegions.push_back(MR);
179 }
180}
181
182} // end of namespace: mpi
183} // end of namespace: ento
184} // end of namespace: clang
185
186// Registers the checker for static analysis.
187void clang::ento::registerMPIChecker(CheckerManager &MGR) {
188 MGR.registerChecker<clang::ento::mpi::MPIChecker>();
189}
Kristof Umann058a7a42019-01-26 14:23:08 +0000190
191bool clang::ento::shouldRegisterMPIChecker(const LangOptions &LO) {
192 return true;
193}