/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
Line | Count | Source (jump to first uncovered line) |
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" |
18 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
19 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" |
20 | | |
21 | | namespace clang { |
22 | | namespace ento { |
23 | | namespace mpi { |
24 | | |
25 | | void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent, |
26 | 131 | CheckerContext &Ctx) const { |
27 | 131 | if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) { |
28 | 83 | return; |
29 | 83 | } |
30 | 48 | const MemRegion *const MR = |
31 | 48 | PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion(); |
32 | 48 | if (!MR) |
33 | 0 | return; |
34 | 48 | const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); |
35 | | |
36 | | // The region must be typed, in order to reason about it. |
37 | 48 | if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())11 )) |
38 | 0 | return; |
39 | | |
40 | 48 | ProgramStateRef State = Ctx.getState(); |
41 | 48 | const Request *const Req = State->get<RequestMap>(MR); |
42 | | |
43 | | // double nonblocking detected |
44 | 48 | if (Req && Req->CurrentState == Request::State::Nonblocking11 ) { |
45 | 9 | ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode(); |
46 | 9 | BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, |
47 | 9 | Ctx.getBugReporter()); |
48 | 9 | Ctx.addTransition(ErrorNode->getState(), ErrorNode); |
49 | 9 | } |
50 | | // no error |
51 | 39 | else { |
52 | 39 | State = State->set<RequestMap>(MR, Request::State::Nonblocking); |
53 | 39 | Ctx.addTransition(State); |
54 | 39 | } |
55 | 48 | } |
56 | | |
57 | | void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent, |
58 | 131 | CheckerContext &Ctx) const { |
59 | 131 | if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier())) |
60 | 86 | return; |
61 | 45 | const MemRegion *const MR = topRegionUsedByWait(PreCallEvent); |
62 | 45 | if (!MR) |
63 | 0 | return; |
64 | 45 | const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); |
65 | | |
66 | | // The region must be typed, in order to reason about it. |
67 | 45 | if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())15 )) |
68 | 0 | return; |
69 | | |
70 | 45 | llvm::SmallVector<const MemRegion *, 2> ReqRegions; |
71 | 45 | allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx); |
72 | 45 | if (ReqRegions.empty()) |
73 | 0 | return; |
74 | | |
75 | 45 | ProgramStateRef State = Ctx.getState(); |
76 | 45 | static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait"); |
77 | 45 | ExplodedNode *ErrorNode{nullptr}; |
78 | | |
79 | | // Check all request regions used by the wait function. |
80 | 58 | for (const auto &ReqRegion : ReqRegions) { |
81 | 58 | const Request *const Req = State->get<RequestMap>(ReqRegion); |
82 | 58 | State = State->set<RequestMap>(ReqRegion, Request::State::Wait); |
83 | 58 | if (!Req) { |
84 | 21 | if (!ErrorNode) { |
85 | 16 | ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); |
86 | 16 | State = ErrorNode->getState(); |
87 | 16 | } |
88 | | // A wait has no matching nonblocking call. |
89 | 21 | BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, |
90 | 21 | Ctx.getBugReporter()); |
91 | 21 | } |
92 | 58 | } |
93 | | |
94 | 45 | if (!ErrorNode) { |
95 | 29 | Ctx.addTransition(State); |
96 | 29 | } else { |
97 | 16 | Ctx.addTransition(State, ErrorNode); |
98 | 16 | } |
99 | 45 | } |
100 | | |
101 | | void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, |
102 | 326 | CheckerContext &Ctx) const { |
103 | 326 | ProgramStateRef State = Ctx.getState(); |
104 | 326 | const auto &Requests = State->get<RequestMap>(); |
105 | 326 | if (Requests.isEmpty()) |
106 | 204 | return; |
107 | | |
108 | 122 | static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); |
109 | 122 | ExplodedNode *ErrorNode{nullptr}; |
110 | | |
111 | 122 | auto ReqMap = State->get<RequestMap>(); |
112 | 189 | for (const auto &Req : ReqMap) { |
113 | 189 | if (!SymReaper.isLiveRegion(Req.first)) { |
114 | 56 | if (Req.second.CurrentState == Request::State::Nonblocking) { |
115 | | |
116 | 4 | if (!ErrorNode) { |
117 | 4 | ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); |
118 | 4 | State = ErrorNode->getState(); |
119 | 4 | } |
120 | 4 | BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, |
121 | 4 | Ctx.getBugReporter()); |
122 | 4 | } |
123 | 56 | State = State->remove<RequestMap>(Req.first); |
124 | 56 | } |
125 | 189 | } |
126 | | |
127 | | // Transition to update the state regarding removed requests. |
128 | 122 | if (!ErrorNode) { |
129 | 118 | Ctx.addTransition(State); |
130 | 118 | } else { |
131 | 4 | Ctx.addTransition(State, ErrorNode); |
132 | 4 | } |
133 | 122 | } |
134 | | |
135 | 45 | const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const { |
136 | | |
137 | 45 | if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { |
138 | 39 | return CE.getArgSVal(0).getAsRegion(); |
139 | 39 | } else if (6 FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())6 ) { |
140 | 6 | return CE.getArgSVal(1).getAsRegion(); |
141 | 6 | } else { |
142 | 0 | return (const MemRegion *)nullptr; |
143 | 0 | } |
144 | 45 | } |
145 | | |
146 | | void MPIChecker::allRegionsUsedByWait( |
147 | | llvm::SmallVector<const MemRegion *, 2> &ReqRegions, |
148 | 45 | const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { |
149 | | |
150 | 45 | MemRegionManager &RegionManager = MR->getMemRegionManager(); |
151 | | |
152 | 45 | if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { |
153 | 6 | const SubRegion *SuperRegion{nullptr}; |
154 | 6 | if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { |
155 | 5 | SuperRegion = cast<SubRegion>(ER->getSuperRegion()); |
156 | 5 | } |
157 | | |
158 | | // A single request is passed to MPI_Waitall. |
159 | 6 | if (!SuperRegion) { |
160 | 1 | ReqRegions.push_back(MR); |
161 | 1 | return; |
162 | 1 | } |
163 | | |
164 | 5 | DefinedOrUnknownSVal ElementCount = getDynamicElementCount( |
165 | 5 | Ctx.getState(), SuperRegion, Ctx.getSValBuilder(), |
166 | 5 | CE.getArgExpr(1)->getType()->getPointeeType()); |
167 | 5 | const llvm::APSInt &ArrSize = |
168 | 5 | ElementCount.castAs<nonloc::ConcreteInt>().getValue(); |
169 | | |
170 | 23 | for (size_t i = 0; i < ArrSize; ++i18 ) { |
171 | 18 | const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); |
172 | | |
173 | 18 | const ElementRegion *const ER = RegionManager.getElementRegion( |
174 | 18 | CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, |
175 | 18 | Ctx.getASTContext()); |
176 | | |
177 | 18 | ReqRegions.push_back(ER->getAs<MemRegion>()); |
178 | 18 | } |
179 | 39 | } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { |
180 | 39 | ReqRegions.push_back(MR); |
181 | 39 | } |
182 | 45 | } |
183 | | |
184 | | } // end of namespace: mpi |
185 | | } // end of namespace: ento |
186 | | } // end of namespace: clang |
187 | | |
188 | | // Registers the checker for static analysis. |
189 | 3 | void clang::ento::registerMPIChecker(CheckerManager &MGR) { |
190 | 3 | MGR.registerChecker<clang::ento::mpi::MPIChecker>(); |
191 | 3 | } |
192 | | |
193 | 6 | bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) { |
194 | 6 | return true; |
195 | 6 | } |