196 lines
6.5 KiB
C++
196 lines
6.5 KiB
C++
|
//===-- 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"
|
||
|
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.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 &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;
|
||
|
}
|
||
|
|
||
|
DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
|
||
|
Ctx.getState(), SuperRegion, Ctx.getSValBuilder(),
|
||
|
CE.getArgExpr(1)->getType()->getPointeeType());
|
||
|
const llvm::APSInt &ArrSize =
|
||
|
ElementCount.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 CheckerManager &mgr) {
|
||
|
return true;
|
||
|
}
|