Added AnalyzerStatsChecker, a path sensitive check that reports visitation statistics about analysis. Running clang with the -analyzer-stats flag will emit warnings containing the information. We can then run a postanalysis script to take this data and give useful information about how much the analyzer missed in a project.

llvm-svn: 113568
This commit is contained in:
Tom Care 2010-09-10 00:44:44 +00:00
parent 712bd0a604
commit 4545b2df42
9 changed files with 132 additions and 1 deletions

View File

@ -264,6 +264,7 @@ public:
// Functions for external checking of whether we have unfinished work
bool wasBlockAborted() const { return CoreEngine.wasBlockAborted(); }
bool hasEmptyWorkList() const { return !CoreEngine.getWorkList()->hasWork(); }
bool hasWorkRemaining() const {
return wasBlockAborted() || CoreEngine.getWorkList()->hasWork();
}

View File

@ -64,6 +64,8 @@ def analysis_WarnSizeofPointer : Flag<"-warn-sizeof-pointer">,
HelpText<"Warn about unintended use of sizeof() on pointer expressions">;
def analysis_WarnIdempotentOps : Flag<"-analyzer-check-idempotent-operations">,
HelpText<"Warn about idempotent operations">;
def analysis_AnalyzerStats : Flag<"-analyzer-stats">,
HelpText<"Emit warnings with analyzer statistics">;
def analyzer_store : Separate<"-analyzer-store">,
HelpText<"Source Code Analysis - Abstract Memory Store Models">;

View File

@ -65,6 +65,7 @@ public:
unsigned AnalyzeAll : 1;
unsigned AnalyzerDisplayProgress : 1;
unsigned AnalyzeNestedBlocks : 1;
unsigned AnalyzerStats : 1;
unsigned EagerlyAssume : 1;
unsigned IdempotentOps : 1;
unsigned PurgeDead : 1;
@ -73,7 +74,6 @@ public:
unsigned VisualizeEGUbi : 1;
unsigned EnableExperimentalChecks : 1;
unsigned EnableExperimentalInternalChecks : 1;
unsigned EnableIdempotentOperationChecker : 1;
unsigned InlineCall : 1;
unsigned UnoptimizedCFG : 1;
@ -85,6 +85,7 @@ public:
AnalyzeAll = 0;
AnalyzerDisplayProgress = 0;
AnalyzeNestedBlocks = 0;
AnalyzerStats = 0;
EagerlyAssume = 0;
PurgeDead = 1;
TrimGraph = 0;

View File

@ -350,6 +350,10 @@ static void ActionGRExprEngine(AnalysisConsumer &C, AnalysisManager& mgr,
|| C.Opts.EnableExperimentalInternalChecks)
RegisterIdempotentOperationChecker(Eng);
// Enable AnalyzerStatsChecker if it was given as an argument
if (C.Opts.AnalyzerStats)
RegisterAnalyzerStatsChecker(Eng);
// Set the graph auditor.
llvm::OwningPtr<ExplodedNode::Auditor> Auditor;
if (mgr.shouldVisualizeUbigraph()) {

View File

@ -0,0 +1,104 @@
//==--AnalyzerStatsChecker.cpp - Analyzer visitation statistics --*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This file reports various statistics about analyzer visitation.
//===----------------------------------------------------------------------===//
#include "clang/Checker/PathSensitive/CheckerVisitor.h"
#include "clang/Checker/PathSensitive/ExplodedGraph.h"
#include "clang/Checker/BugReporter/BugReporter.h"
#include "GRExprEngineExperimentalChecks.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace clang;
namespace {
class AnalyzerStatsChecker : public CheckerVisitor<AnalyzerStatsChecker> {
public:
static void *getTag();
void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, GRExprEngine &Eng);
private:
llvm::SmallPtrSet<const CFGBlock*, 256> reachable;
};
}
void *AnalyzerStatsChecker::getTag() {
static int x = 0;
return &x;
}
void clang::RegisterAnalyzerStatsChecker(GRExprEngine &Eng) {
Eng.registerCheck(new AnalyzerStatsChecker());
}
void AnalyzerStatsChecker::VisitEndAnalysis(ExplodedGraph &G,
BugReporter &B,
GRExprEngine &Eng) {
const CFG *C = 0;
const Decl *D = 0;
const LocationContext *LC = 0;
const SourceManager &SM = B.getSourceManager();
// Iterate over explodedgraph
for (ExplodedGraph::node_iterator I = G.nodes_begin();
I != G.nodes_end(); ++I) {
const ProgramPoint &P = I->getLocation();
// Save the LocationContext if we don't have it already
if (!LC)
LC = P.getLocationContext();
if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
const CFGBlock *CB = BE->getDst();
reachable.insert(CB);
}
}
// Get the CFG and the Decl of this block
C = LC->getCFG();
D = LC->getAnalysisContext()->getDecl();
unsigned total = 0, unreachable = 0;
// Find CFGBlocks that were not covered by any node
for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) {
const CFGBlock *CB = *I;
++total;
// Check if the block is unreachable
if (!reachable.count(CB)) {
++unreachable;
}
}
// We never 'reach' the entry block, so correct the unreachable count
unreachable--;
// Generate the warning string
llvm::SmallString<128> buf;
llvm::raw_svector_ostream output(buf);
PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
output << Loc.getFilename() << " : ";
if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
const NamedDecl *ND = cast<NamedDecl>(D);
output << ND;
}
else if (isa<BlockDecl>(D)) {
output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
}
output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: "
<< unreachable << " | Aborted Block: "
<< (Eng.wasBlockAborted() ? "no" : "yes")
<< " | Empty WorkList: "
<< (Eng.hasEmptyWorkList() ? "yes" : "no") << "\n";
B.EmitBasicReport("Analyzer Statistics", "Internal Statistics", output.str(),
D->getLocation());
}

View File

@ -7,6 +7,7 @@ add_clang_library(clangChecker
AggExprVisitor.cpp
AnalysisConsumer.cpp
AnalysisManager.cpp
AnalyzerStatsChecker.cpp
ArrayBoundChecker.cpp
AttrNonNullChecker.cpp
BasicConstraintManager.cpp

View File

@ -19,6 +19,7 @@ namespace clang {
class GRExprEngine;
void RegisterAnalyzerStatsChecker(GRExprEngine &Eng);
void RegisterCStringChecker(GRExprEngine &Eng);
void RegisterIdempotentOperationChecker(GRExprEngine &Eng);
void RegisterMallocChecker(GRExprEngine &Eng);

View File

@ -99,6 +99,8 @@ static void AnalyzerOptsToArgs(const AnalyzerOptions &Opts,
Res.push_back("-analyzer-display-progress");
if (Opts.AnalyzeNestedBlocks)
Res.push_back("-analyzer-opt-analyze-nested-blocks");
if (Opts.AnalyzerStats)
Res.push_back("-analyzer-stats");
if (Opts.EagerlyAssume)
Res.push_back("-analyzer-eagerly-assume");
if (!Opts.PurgeDead)
@ -815,6 +817,7 @@ static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args,
Opts.AnalyzerDisplayProgress = Args.hasArg(OPT_analyzer_display_progress);
Opts.AnalyzeNestedBlocks =
Args.hasArg(OPT_analyzer_opt_analyze_nested_blocks);
Opts.AnalyzerStats = Args.hasArg(OPT_analysis_AnalyzerStats);
Opts.PurgeDead = !Args.hasArg(OPT_analyzer_no_purge_dead);
Opts.EagerlyAssume = Args.hasArg(OPT_analyzer_eagerly_assume);
Opts.AnalyzeSpecificFunction = Args.getLastArgValue(OPT_analyze_function);

View File

@ -0,0 +1,14 @@
// RUN: %clang_cc1 -analyze -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-check-dead-stores -verify -Wno-unreachable-code -analyzer-opt-analyze-nested-blocks -analyzer-stats %s
int foo();
int test() { // expected-warning{{Total CFGBlocks}}
int a = 1;
a = 34 / 12;
if (foo())
return a;
a /= 4;
return a;
}