Coverage Report

Created: 2023-09-30 09:22

/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
}