Skip to content

Commit

Permalink
[analyzer] Add checker for correct usage of MPI API in C and C++.
Browse files Browse the repository at this point in the history
This commit adds a static analysis checker to check for the correct usage of the
MPI API in C and C++.

3 path-sensitive checks are included:

- Double nonblocking: Double request usage by nonblocking calls
  without intermediate wait.
- Missing wait: Nonblocking call without matching wait.
- Unmatched wait: Waiting for a request that was never used by a
  nonblocking call.

Examples of how to use the checker can be found
at https://github.com/0ax1/MPI-Checker

Reviewers: zaks.anna

A patch by Alexander Droste!

Differential Revision: http://reviews.llvm.org/D12761

llvm-svn: 271907
  • Loading branch information
devincoughlin committed Jun 6, 2016
1 parent cdf3492 commit 83ccd1a
Show file tree
Hide file tree
Showing 15 changed files with 1,546 additions and 0 deletions.
8 changes: 8 additions & 0 deletions clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
Expand Up @@ -72,6 +72,8 @@ def Containers : Package<"containers">, InPackage<CoreFoundation>;
def LocalizabilityAlpha : Package<"localizability">, InPackage<CocoaAlpha>;
def LocalizabilityOptIn : Package<"localizability">, InPackage<CocoaOptIn>;

def MPI : Package<"mpi">, InPackage<OptIn>;

def LLVM : Package<"llvm">;
def Debug : Package<"debug">;

Expand Down Expand Up @@ -577,6 +579,12 @@ def PluralMisuseChecker : Checker<"PluralMisuseChecker">,
DescFile<"LocalizationChecker.cpp">;
}

let ParentPackage = MPI in {
def MPIChecker : Checker<"MPI-Checker">,
HelpText<"Checks MPI code">,
DescFile<"MPIChecker.cpp">;
}

//===----------------------------------------------------------------------===//
// Checkers for LLVM development.
//===----------------------------------------------------------------------===//
Expand Down
22 changes: 22 additions & 0 deletions clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
Expand Up @@ -150,6 +150,28 @@ class MemRegion : public llvm::FoldingSetNode {
template<typename RegionTy> const RegionTy* getAs() const;

virtual bool isBoundable() const { return false; }


/// Get descriptive name for memory region. The name is obtained from
/// the variable/field declaration retrieved from the memory region.
/// Regions that point to an element of an array are returned as: "arr[0]".
/// Regions that point to a struct are returned as: "st.var".
//
/// \param UseQuotes Set if the name should be quoted.
///
/// \returns variable name for memory region
std::string getDescriptiveName(bool UseQuotes = true) const;


/// Retrieve source range from memory region. The range retrieval
/// is based on the decl obtained from the memory region.
/// For a VarRegion the range of the base region is returned.
/// For a FieldRegion the range of the field is returned.
/// If no declaration is found, an empty source range is returned.
/// The client is responsible for checking if the returned range is valid.
///
/// \returns source range for declaration retrieved from memory region
clang::SourceRange sourceRange() const;
};

/// MemSpaceRegion - A memory region that represents a "memory space";
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
Expand Up @@ -41,6 +41,9 @@ add_clang_library(clangStaticAnalyzerCheckers
MallocChecker.cpp
MallocOverflowSecurityChecker.cpp
MallocSizeofChecker.cpp
MPI-Checker/MPIBugReporter.cpp
MPI-Checker/MPIChecker.cpp
MPI-Checker/MPIFunctionClassifier.cpp
NSAutoreleasePoolChecker.cpp
NSErrorChecker.cpp
NoReturnFunctionChecker.cpp
Expand Down
112 changes: 112 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
@@ -0,0 +1,112 @@
//===-- MPIBugReporter.cpp - bug reporter -----------------------*- 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 prefabricated reports which are emitted in
/// case of MPI related bugs, detected by path-sensitive analysis.
///
//===----------------------------------------------------------------------===//

#include "MPIBugReporter.h"
#include "MPIChecker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"

namespace clang {
namespace ento {
namespace mpi {

void MPIBugReporter::reportDoubleNonblocking(
const CallEvent &MPICallEvent, const ento::mpi::Request &Req,
const MemRegion *const RequestRegion,
const ExplodedNode *const ExplNode) const {

std::string ErrorText;
ErrorText = "Double nonblocking on request " +
RequestRegion->getDescriptiveName() + ". ";

auto Report = llvm::make_unique<BugReport>(*DoubleNonblockingBugType,
ErrorText, ExplNode);

Report->addRange(MPICallEvent.getSourceRange());
SourceRange Range = RequestRegion->sourceRange();

if (Range.isValid())
Report->addRange(Range);

Report->addVisitor(llvm::make_unique<RequestNodeVisitor>(
RequestRegion, "Request is previously used by nonblocking call here. "));
Report->markInteresting(RequestRegion);

BReporter.emitReport(std::move(Report));
}

void MPIBugReporter::reportMissingWait(
const ento::mpi::Request &Req, const MemRegion *const RequestRegion,
const ExplodedNode *const ExplNode) const {
std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
" has no matching wait. "};

auto Report =
llvm::make_unique<BugReport>(*MissingWaitBugType, ErrorText, ExplNode);

SourceRange Range = RequestRegion->sourceRange();
if (Range.isValid())
Report->addRange(Range);
Report->addVisitor(llvm::make_unique<RequestNodeVisitor>(
RequestRegion, "Request is previously used by nonblocking call here. "));
Report->markInteresting(RequestRegion);

BReporter.emitReport(std::move(Report));
}

void MPIBugReporter::reportUnmatchedWait(
const CallEvent &CE, const clang::ento::MemRegion *const RequestRegion,
const ExplodedNode *const ExplNode) const {
std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
" has no matching nonblocking call. "};

auto Report =
llvm::make_unique<BugReport>(*UnmatchedWaitBugType, ErrorText, ExplNode);

Report->addRange(CE.getSourceRange());
SourceRange Range = RequestRegion->sourceRange();
if (Range.isValid())
Report->addRange(Range);

BReporter.emitReport(std::move(Report));
}

PathDiagnosticPiece *MPIBugReporter::RequestNodeVisitor::VisitNode(
const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC,
BugReport &BR) {

if (IsNodeFound)
return nullptr;

const Request *const Req = N->getState()->get<RequestMap>(RequestRegion);
const Request *const PrevReq =
PrevN->getState()->get<RequestMap>(RequestRegion);

// Check if request was previously unused or in a different state.
if ((Req && !PrevReq) || (Req->CurrentState != PrevReq->CurrentState)) {
IsNodeFound = true;

ProgramPoint P = PrevN->getLocation();
PathDiagnosticLocation L =
PathDiagnosticLocation::create(P, BRC.getSourceManager());

return new PathDiagnosticEventPiece(L, ErrorText);
}

return nullptr;
}

} // end of namespace: mpi
} // end of namespace: ento
} // end of namespace: clang
110 changes: 110 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
@@ -0,0 +1,110 @@
//===-- MPIBugReporter.h - bug reporter -----------------------*- 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 prefabricated reports which are emitted in
/// case of MPI related bugs, detected by path-sensitive analysis.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H
#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H

#include "MPIFunctionClassifier.h"
#include "MPITypes.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"

namespace clang {
namespace ento {
namespace mpi {

class MPIBugReporter {
public:
MPIBugReporter(BugReporter &BR, const CheckerBase &CB,
const MPIFunctionClassifier &FC)
: BReporter{BR} {
UnmatchedWaitBugType.reset(new BugType(&CB, "Unmatched wait", MPIError));
DoubleNonblockingBugType.reset(
new BugType(&CB, "Double nonblocking", MPIError));
MissingWaitBugType.reset(new BugType(&CB, "Missing wait", MPIError));
}

/// Report duplicate request use by nonblocking calls without intermediate
/// wait.
///
/// \param MPICallEvent MPI call that caused the double nonblocking
/// \param Req request that was used by two nonblocking calls in sequence
/// \param RequestRegion memory region of the request
/// \param ExplNode node in the graph the bug appeared at
void reportDoubleNonblocking(const CallEvent &MPICallEvent,
const Request &Req,
const MemRegion *const RequestRegion,
const ExplodedNode *const ExplNode) const;

/// Report a missing wait for a nonblocking call. A missing wait report
/// is emitted if a nonblocking call is not matched in the scope of a
/// function.
///
/// \param Req request that is not matched by a wait
/// \param RequestRegion memory region of the request
/// \param ExplNode node in the graph the bug appeared at
void reportMissingWait(const Request &Req,
const MemRegion *const RequestRegion,
const ExplodedNode *const ExplNode) const;

/// Report a wait on a request that has not been used at all before.
///
/// \param CE wait call that uses the request
/// \param ReqRegion memory region of the request
/// \param ExplNode node in the graph the bug appeared at
void reportUnmatchedWait(const CallEvent &CE,
const MemRegion *const RequestRegion,
const ExplodedNode *const ExplNode) const;

private:
const std::string MPIError{"MPI Error"};

// path-sensitive bug types
std::unique_ptr<BugType> UnmatchedWaitBugType;
std::unique_ptr<BugType> MissingWaitBugType;
std::unique_ptr<BugType> DoubleNonblockingBugType;

BugReporter &BReporter;

/// Bug visitor class to find the node where the request region was previously
/// used in order to include it into the BugReport path.
class RequestNodeVisitor : public BugReporterVisitorImpl<RequestNodeVisitor> {
public:
RequestNodeVisitor(const MemRegion *const MemoryRegion,
const std::string &ErrText)
: RequestRegion(MemoryRegion), ErrorText{ErrText} {}

void Profile(llvm::FoldingSetNodeID &ID) const override {
static int X = 0;
ID.AddPointer(&X);
ID.AddPointer(RequestRegion);
}

PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
BugReport &BR) override;

private:
const MemRegion *const RequestRegion;
bool IsNodeFound{false};
std::string ErrorText;
};
};

} // end of namespace: mpi
} // end of namespace: ento
} // end of namespace: clang

#endif

0 comments on commit 83ccd1a

Please sign in to comment.