diff options
author | Tom Care <tom.care@uqconnect.edu.au> | 2010-09-10 00:44:44 +0000 |
---|---|---|
committer | Tom Care <tom.care@uqconnect.edu.au> | 2010-09-10 00:44:44 +0000 |
commit | 52d861ce41ce84d8389495ea78d97bcc962ac5ba (patch) | |
tree | b935271dea250ba726bac204b0e88d360a8671a7 | |
parent | 36897b05ca2886e287f01802614bc10cbadcec22 (diff) |
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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@113568 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Checker/PathSensitive/GRExprEngine.h | 1 | ||||
-rw-r--r-- | include/clang/Driver/CC1Options.td | 2 | ||||
-rw-r--r-- | include/clang/Frontend/AnalyzerOptions.h | 3 | ||||
-rw-r--r-- | lib/Checker/AnalysisConsumer.cpp | 4 | ||||
-rw-r--r-- | lib/Checker/AnalyzerStatsChecker.cpp | 104 | ||||
-rw-r--r-- | lib/Checker/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Checker/GRExprEngineExperimentalChecks.h | 1 | ||||
-rw-r--r-- | lib/Frontend/CompilerInvocation.cpp | 3 | ||||
-rw-r--r-- | test/Analysis/analyzer-stats.c | 14 |
9 files changed, 132 insertions, 1 deletions
diff --git a/include/clang/Checker/PathSensitive/GRExprEngine.h b/include/clang/Checker/PathSensitive/GRExprEngine.h index 5ba0b36b31..b8ccc06b7d 100644 --- a/include/clang/Checker/PathSensitive/GRExprEngine.h +++ b/include/clang/Checker/PathSensitive/GRExprEngine.h @@ -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(); } diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 005f839bf5..99f748e6a7 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -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">; diff --git a/include/clang/Frontend/AnalyzerOptions.h b/include/clang/Frontend/AnalyzerOptions.h index 9ed15ba2f9..cd45945899 100644 --- a/include/clang/Frontend/AnalyzerOptions.h +++ b/include/clang/Frontend/AnalyzerOptions.h @@ -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; diff --git a/lib/Checker/AnalysisConsumer.cpp b/lib/Checker/AnalysisConsumer.cpp index ad5ccb503b..e717ce0a9c 100644 --- a/lib/Checker/AnalysisConsumer.cpp +++ b/lib/Checker/AnalysisConsumer.cpp @@ -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()) { diff --git a/lib/Checker/AnalyzerStatsChecker.cpp b/lib/Checker/AnalyzerStatsChecker.cpp new file mode 100644 index 0000000000..9c6fcd2be1 --- /dev/null +++ b/lib/Checker/AnalyzerStatsChecker.cpp @@ -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()); +} diff --git a/lib/Checker/CMakeLists.txt b/lib/Checker/CMakeLists.txt index 76da8d815a..f93fcc91fd 100644 --- a/lib/Checker/CMakeLists.txt +++ b/lib/Checker/CMakeLists.txt @@ -7,6 +7,7 @@ add_clang_library(clangChecker AggExprVisitor.cpp AnalysisConsumer.cpp AnalysisManager.cpp + AnalyzerStatsChecker.cpp ArrayBoundChecker.cpp AttrNonNullChecker.cpp BasicConstraintManager.cpp diff --git a/lib/Checker/GRExprEngineExperimentalChecks.h b/lib/Checker/GRExprEngineExperimentalChecks.h index 7b5b0ed7ba..461318ea74 100644 --- a/lib/Checker/GRExprEngineExperimentalChecks.h +++ b/lib/Checker/GRExprEngineExperimentalChecks.h @@ -19,6 +19,7 @@ namespace clang { class GRExprEngine; +void RegisterAnalyzerStatsChecker(GRExprEngine &Eng); void RegisterCStringChecker(GRExprEngine &Eng); void RegisterIdempotentOperationChecker(GRExprEngine &Eng); void RegisterMallocChecker(GRExprEngine &Eng); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 36d8dad58f..64ac3bad8c 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -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); diff --git a/test/Analysis/analyzer-stats.c b/test/Analysis/analyzer-stats.c new file mode 100644 index 0000000000..0c502cd021 --- /dev/null +++ b/test/Analysis/analyzer-stats.c @@ -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; +} |