clang 24.0.0git
MPIChecker.cpp
Go to the documentation of this file.
1//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===//
2//
3// 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
6//
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"
20
21namespace clang {
22namespace ento {
23namespace mpi {
24
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 if (!ErrorNode)
47 return;
48 BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode,
49 Ctx.getBugReporter());
50 }
51 // no error
52 else {
53 State = State->set<RequestMap>(MR, Request::State::Nonblocking);
54 Ctx.addTransition(State);
55 }
56}
57
59 CheckerContext &Ctx) const {
60 if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier()))
61 return;
62 const MemRegion *const MR = topRegionUsedByWait(PreCallEvent);
63 if (!MR)
64 return;
65 const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
66
67 // The region must be typed, in order to reason about it.
68 if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
69 return;
70
72 allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx);
73 if (ReqRegions.empty())
74 return;
75
76 ProgramStateRef State = Ctx.getState();
77 ExplodedNode *ErrorNode{nullptr};
78
79 // Check all request regions used by the wait function.
80 for (const auto &ReqRegion : ReqRegions) {
81 const Request *const Req = State->get<RequestMap>(ReqRegion);
82 State = State->set<RequestMap>(ReqRegion, Request::State::Wait);
83 if (!Req) {
84 if (!ErrorNode)
85 ErrorNode = Ctx.generateNonFatalErrorNode(State);
86 if (!ErrorNode)
87 continue;
88 // A wait has no matching nonblocking call.
89 BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode,
90 Ctx.getBugReporter());
91 }
92 }
93
94 if (!ErrorNode) {
95 Ctx.addTransition(State);
96 } else {
97 Ctx.addTransition(State, ErrorNode);
98 }
99}
100
102 CheckerContext &Ctx) const {
103 ProgramStateRef State = Ctx.getState();
104 const auto &Requests = State->get<RequestMap>();
105 if (Requests.isEmpty())
106 return;
107
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 if (!ErrorNode)
115 ErrorNode = Ctx.generateNonFatalErrorNode(State);
116 if (!ErrorNode)
117 continue;
118 BReporter.reportMissingWait(Req.second, Req.first, ErrorNode,
119 Ctx.getBugReporter());
120 }
121 State = State->remove<RequestMap>(Req.first);
122 }
123 }
124
125 // Transition to update the state regarding removed requests.
126 if (!ErrorNode) {
127 Ctx.addTransition(State);
128 } else {
129 Ctx.addTransition(State, ErrorNode);
130 }
131}
132
133const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
134
135 if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
136 return CE.getArgSVal(0).getAsRegion();
137 } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
138 return CE.getArgSVal(1).getAsRegion();
139 } else {
140 return (const MemRegion *)nullptr;
141 }
142}
143
144void MPIChecker::allRegionsUsedByWait(
145 llvm::SmallVector<const MemRegion *, 2> &ReqRegions,
146 const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {
147
148 MemRegionManager &RegionManager = MR->getMemRegionManager();
149
150 if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
151 const SubRegion *SuperRegion{nullptr};
152 if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
153 SuperRegion = cast<SubRegion>(ER->getSuperRegion());
154 }
155
156 // A single request is passed to MPI_Waitall.
157 if (!SuperRegion) {
158 ReqRegions.push_back(MR);
159 return;
160 }
161
162 DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
163 Ctx.getState(), SuperRegion, Ctx.getSValBuilder(),
164 CE.getArgExpr(1)->getType()->getPointeeType());
165 const llvm::APSInt &ArrSize =
166 ElementCount.castAs<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}
190
191bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) {
192 return true;
193}
This file defines the main class of MPI-Checker which serves as an entry point.
Represents an abstract call to a function or method along a particular path.
Definition CallEvent.h:152
const IdentifierInfo * getCalleeIdentifier() const
Returns the name of the callee, if its name is a simple identifier.
Definition CallEvent.h:361
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
virtual unsigned getNumArgs() const =0
Returns the number of arguments (explicit and implicit).
const ProgramStateRef & getState() const
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Creates a new ExplodedNode and adds it to the ExplodedGraph as a child of 'getPredecessor()',...
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Creates a new ExplodedNode and adds it to the ExplodedGraph as a child of 'getPredecessor()',...
ElementRegion is used to represent both array elements and casts.
Definition MemRegion.h:1237
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:97
virtual MemRegionManager & getMemRegionManager() const =0
const MemRegion * getAsRegion() const
Definition SVals.cpp:119
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getSuperRegion() const
Definition MemRegion.h:493
A class responsible for cleaning up unused symbols.
bool isLiveRegion(const MemRegion *region)
void checkMissingWaits(clang::ento::SymbolReaper &SymReaper, clang::ento::CheckerContext &Ctx) const
Check if a nonblocking call is not matched by a wait.
void checkDoubleNonblocking(const clang::ento::CallEvent &PreCallEvent, clang::ento::CheckerContext &Ctx) const
Checks if a request is used by nonblocking calls multiple times in sequence without intermediate wait...
void checkUnmatchedWaits(const clang::ento::CallEvent &PreCallEvent, clang::ento::CheckerContext &Ctx) const
Checks if the request used by the wait function was not used at all before.
const State CurrentState
Definition MPITypes.h:41
DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB, QualType Ty)
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
U cast(CodeGen::Address addr)
Definition Address.h:327