MPIChecker.cpp 6.5 KB

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