| //===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file defines the main class of MPI-Checker which serves as an entry |
| /// point. It is created once for each translation unit analysed. |
| /// The checker defines path-sensitive checks, to verify correct usage of the |
| /// MPI API. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "MPIChecker.h" |
| #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
| |
| namespace clang { |
| namespace ento { |
| namespace mpi { |
| |
| void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent, |
| CheckerContext &Ctx) const { |
| if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) { |
| return; |
| } |
| const MemRegion *const MR = |
| PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion(); |
| if (!MR) |
| return; |
| const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); |
| |
| // The region must be typed, in order to reason about it. |
| if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) |
| return; |
| |
| ProgramStateRef State = Ctx.getState(); |
| const Request *const Req = State->get<RequestMap>(MR); |
| |
| // double nonblocking detected |
| if (Req && Req->CurrentState == Request::State::Nonblocking) { |
| ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode(); |
| BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, |
| Ctx.getBugReporter()); |
| Ctx.addTransition(ErrorNode->getState(), ErrorNode); |
| } |
| // no error |
| else { |
| State = State->set<RequestMap>(MR, Request::State::Nonblocking); |
| Ctx.addTransition(State); |
| } |
| } |
| |
| void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent, |
| CheckerContext &Ctx) const { |
| if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier())) |
| return; |
| const MemRegion *const MR = topRegionUsedByWait(PreCallEvent); |
| if (!MR) |
| return; |
| const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); |
| |
| // The region must be typed, in order to reason about it. |
| if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) |
| return; |
| |
| llvm::SmallVector<const MemRegion *, 2> ReqRegions; |
| allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx); |
| if (ReqRegions.empty()) |
| return; |
| |
| ProgramStateRef State = Ctx.getState(); |
| static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait"); |
| ExplodedNode *ErrorNode{nullptr}; |
| |
| // Check all request regions used by the wait function. |
| for (const auto &ReqRegion : ReqRegions) { |
| const Request *const Req = State->get<RequestMap>(ReqRegion); |
| State = State->set<RequestMap>(ReqRegion, Request::State::Wait); |
| if (!Req) { |
| if (!ErrorNode) { |
| ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); |
| State = ErrorNode->getState(); |
| } |
| // A wait has no matching nonblocking call. |
| BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, |
| Ctx.getBugReporter()); |
| } |
| } |
| |
| if (!ErrorNode) { |
| Ctx.addTransition(State); |
| } else { |
| Ctx.addTransition(State, ErrorNode); |
| } |
| } |
| |
| void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, |
| CheckerContext &Ctx) const { |
| ProgramStateRef State = Ctx.getState(); |
| const auto &Requests = State->get<RequestMap>(); |
| if (Requests.isEmpty()) |
| return; |
| |
| static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); |
| ExplodedNode *ErrorNode{nullptr}; |
| |
| auto ReqMap = State->get<RequestMap>(); |
| for (const auto &Req : ReqMap) { |
| if (!SymReaper.isLiveRegion(Req.first)) { |
| if (Req.second.CurrentState == Request::State::Nonblocking) { |
| |
| if (!ErrorNode) { |
| ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); |
| State = ErrorNode->getState(); |
| } |
| BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, |
| Ctx.getBugReporter()); |
| } |
| State = State->remove<RequestMap>(Req.first); |
| } |
| } |
| |
| // Transition to update the state regarding removed requests. |
| if (!ErrorNode) { |
| Ctx.addTransition(State); |
| } else { |
| Ctx.addTransition(State, ErrorNode); |
| } |
| } |
| |
| const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const { |
| |
| if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { |
| return CE.getArgSVal(0).getAsRegion(); |
| } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { |
| return CE.getArgSVal(1).getAsRegion(); |
| } else { |
| return (const MemRegion *)nullptr; |
| } |
| } |
| |
| void MPIChecker::allRegionsUsedByWait( |
| llvm::SmallVector<const MemRegion *, 2> &ReqRegions, |
| const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { |
| |
| MemRegionManager *const RegionManager = MR->getMemRegionManager(); |
| |
| if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { |
| const SubRegion *SuperRegion{nullptr}; |
| if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { |
| SuperRegion = cast<SubRegion>(ER->getSuperRegion()); |
| } |
| |
| // A single request is passed to MPI_Waitall. |
| if (!SuperRegion) { |
| ReqRegions.push_back(MR); |
| return; |
| } |
| |
| const auto &Size = Ctx.getStoreManager().getSizeInElements( |
| Ctx.getState(), SuperRegion, |
| CE.getArgExpr(1)->getType()->getPointeeType()); |
| const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue(); |
| |
| for (size_t i = 0; i < ArrSize; ++i) { |
| const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); |
| |
| const ElementRegion *const ER = RegionManager->getElementRegion( |
| CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, |
| Ctx.getASTContext()); |
| |
| ReqRegions.push_back(ER->getAs<MemRegion>()); |
| } |
| } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { |
| ReqRegions.push_back(MR); |
| } |
| } |
| |
| } // end of namespace: mpi |
| } // end of namespace: ento |
| } // end of namespace: clang |
| |
| // Registers the checker for static analysis. |
| void clang::ento::registerMPIChecker(CheckerManager &MGR) { |
| MGR.registerChecker<clang::ento::mpi::MPIChecker>(); |
| } |
| |
| bool clang::ento::shouldRegisterMPIChecker(const LangOptions &LO) { |
| return true; |
| } |