diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp new file mode 100644 index 00000000000..19bc716e878 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -0,0 +1,190 @@ +//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \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 "../ClangSACheckers.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.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); + } + } + + if (!ErrorNode) { + Ctx.addTransition(State); + } else { + Ctx.addTransition(State, ErrorNode); + } +} + +void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, + CheckerContext &Ctx) const { + if (!SymReaper.hasDeadSymbols()) + return; + + 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); + } + 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 MemRegion *SuperRegion{nullptr}; + if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { + SuperRegion = 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>(); +} |