r288257 - [analyzer] Minor fixes and improvements to debug.ExprInspection
Artem Dergachev via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 30 09:57:18 PST 2016
Author: dergachev
Date: Wed Nov 30 11:57:18 2016
New Revision: 288257
URL: http://llvm.org/viewvc/llvm-project?rev=288257&view=rev
Log:
[analyzer] Minor fixes and improvements to debug.ExprInspection
- Fix the bug with transition handling in ExprInspectionChecker's
checkDeadSymbols implementation.
- Test this bug by adding a new function clang_analyzer_numTimesReached() to
catch number of passes through the code, which should be handy for testing
against unintended state splits.
- Add two more functions should help debugging issues quickly without running
the debugger or dumping exploded graphs - clang_analyzer_dump() which dump()s
an SVal argument to a warning message, and clang_analyzer_printState(), which
dump()s the current program state to stderr.
Differential Revision: https://reviews.llvm.org/D26835
Added:
cfe/trunk/test/Analysis/expr-inspection.c
Modified:
cfe/trunk/docs/analyzer/DebugChecks.rst
cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
cfe/trunk/test/Analysis/symbol-reaper.c
Modified: cfe/trunk/docs/analyzer/DebugChecks.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/analyzer/DebugChecks.rst?rev=288257&r1=288256&r2=288257&view=diff
==============================================================================
--- cfe/trunk/docs/analyzer/DebugChecks.rst (original)
+++ cfe/trunk/docs/analyzer/DebugChecks.rst Wed Nov 30 11:57:18 2016
@@ -138,6 +138,17 @@ ExprInspection checks
clang_analyzer_warnIfReached(); // no-warning
}
+- void clang_analyzer_numTimesReached();
+
+ Same as above, but include the number of times this call expression
+ gets reached by the analyzer during the current analysis.
+
+ Example usage::
+
+ for (int x = 0; x < 3; ++x) {
+ clang_analyzer_numTimesReached(); // expected-warning{{3}}
+ }
+
- void clang_analyzer_warnOnDeadSymbol(int);
Subscribe for a delayed warning when the symbol that represents the value of
@@ -180,6 +191,18 @@ ExprInspection checks
clang_analyzer_explain(ptr); // expected-warning{{memory address '0'}}
}
+- void clang_analyzer_dump(a single argument of any type);
+
+ Similar to clang_analyzer_explain, but produces a raw dump of the value,
+ same as SVal::dump().
+
+ Example usage::
+
+ void clang_analyzer_dump(int);
+ void foo(int x) {
+ clang_analyzer_dump(x); // expected-warning{{reg_$0<x>}}
+ }
+
- size_t clang_analyzer_getExtent(void *);
This function returns the value that represents the extent of a memory region
@@ -197,6 +220,22 @@ ExprInspection checks
clang_analyzer_explain(ys); // expected-warning{{'8'}}
}
+- void clang_analyzer_printState();
+
+ Dumps the current ProgramState to the stderr. Quickly lookup the program state
+ at any execution point without ViewExplodedGraph or re-compiling the program.
+ This is not very useful for writing tests (apart from testing how ProgramState
+ gets printed), but useful for debugging tests. Also, this method doesn't
+ produce a warning, so it gets printed on the console before all other
+ ExprInspection warnings.
+
+ Example usage::
+
+ void foo() {
+ int x = 1;
+ clang_analyzer_printState(); // Read the stderr!
+ }
+
Statistics
==========
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp?rev=288257&r1=288256&r2=288257&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp Wed Nov 30 11:57:18 2016
@@ -18,25 +18,41 @@ using namespace clang;
using namespace ento;
namespace {
-class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols> {
+class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols,
+ check::EndAnalysis> {
mutable std::unique_ptr<BugType> BT;
+ // These stats are per-analysis, not per-branch, hence they shouldn't
+ // stay inside the program state.
+ struct ReachedStat {
+ ExplodedNode *ExampleNode;
+ unsigned NumTimesReached;
+ };
+ mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats;
+
void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const;
void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerDump(const CallExpr *CE, CheckerContext &C) const;
void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const;
void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;
typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
CheckerContext &C) const;
- void reportBug(llvm::StringRef Msg, CheckerContext &C) const;
+ ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C) const;
+ ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR,
+ ExplodedNode *N) const;
public:
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
+ ExprEngine &Eng) const;
};
}
@@ -56,7 +72,12 @@ bool ExprInspectionChecker::evalCall(con
.Case("clang_analyzer_warnOnDeadSymbol",
&ExprInspectionChecker::analyzerWarnOnDeadSymbol)
.Case("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
+ .Case("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump)
.Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
+ .Case("clang_analyzer_printState",
+ &ExprInspectionChecker::analyzerPrintState)
+ .Case("clang_analyzer_numTimesReached",
+ &ExprInspectionChecker::analyzerNumTimesReached)
.Default(nullptr);
if (!Handler)
@@ -98,16 +119,24 @@ static const char *getArgumentValueStrin
}
}
-void ExprInspectionChecker::reportBug(llvm::StringRef Msg,
- CheckerContext &C) const {
- if (!BT)
- BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
-
+ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
+ CheckerContext &C) const {
ExplodedNode *N = C.generateNonFatalErrorNode();
+ reportBug(Msg, C.getBugReporter(), N);
+ return N;
+}
+
+ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
+ BugReporter &BR,
+ ExplodedNode *N) const {
if (!N)
- return;
+ return nullptr;
+
+ if (!BT)
+ BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
- C.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
+ BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
+ return N;
}
void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
@@ -127,6 +156,15 @@ void ExprInspectionChecker::analyzerWarn
reportBug("REACHABLE", C);
}
+void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE,
+ CheckerContext &C) const {
+ ++ReachedStats[CE].NumTimesReached;
+ if (!ReachedStats[CE].ExampleNode) {
+ // Later, in checkEndAnalysis, we'd throw a report against it.
+ ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode();
+ }
+}
+
void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
CheckerContext &C) const {
const LocationContext *LC = C.getPredecessor()->getLocationContext();
@@ -144,22 +182,43 @@ void ExprInspectionChecker::analyzerChec
void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
CheckerContext &C) const {
- if (CE->getNumArgs() == 0)
+ if (CE->getNumArgs() == 0) {
reportBug("Missing argument for explaining", C);
+ return;
+ }
SVal V = C.getSVal(CE->getArg(0));
SValExplainer Ex(C.getASTContext());
reportBug(Ex.Visit(V), C);
}
+void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getNumArgs() == 0) {
+ reportBug("Missing argument for dumping", C);
+ return;
+ }
+
+ SVal V = C.getSVal(CE->getArg(0));
+
+ llvm::SmallString<32> Str;
+ llvm::raw_svector_ostream OS(Str);
+ V.dumpToStream(OS);
+ reportBug(OS.str(), C);
+}
+
void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
CheckerContext &C) const {
- if (CE->getNumArgs() == 0)
+ if (CE->getNumArgs() == 0) {
reportBug("Missing region for obtaining extent", C);
+ return;
+ }
auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion());
- if (!MR)
+ if (!MR) {
reportBug("Obtaining extent of a non-region", C);
+ return;
+ }
ProgramStateRef State = C.getState();
State = State->BindExpr(CE, C.getLocationContext(),
@@ -167,6 +226,11 @@ void ExprInspectionChecker::analyzerGetE
C.addTransition(State);
}
+void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,
+ CheckerContext &C) const {
+ C.getState()->dump();
+}
+
void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
CheckerContext &C) const {
if (CE->getNumArgs() == 0)
@@ -185,15 +249,28 @@ void ExprInspectionChecker::checkDeadSym
CheckerContext &C) const {
ProgramStateRef State = C.getState();
const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
+ ExplodedNode *N = C.getPredecessor();
for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
SymbolRef Sym = *I;
if (!SymReaper.isDead(Sym))
continue;
- reportBug("SYMBOL DEAD", C);
+ // The non-fatal error node should be the same for all reports.
+ if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C))
+ N = BugNode;
State = State->remove<MarkedSymbols>(Sym);
}
- C.addTransition(State);
+ C.addTransition(State, N);
+}
+
+void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
+ ExprEngine &Eng) const {
+ for (auto Item: ReachedStats) {
+ unsigned NumTimesReached = Item.second.NumTimesReached;
+ ExplodedNode *N = Item.second.ExampleNode;
+
+ reportBug(std::to_string(NumTimesReached), BR, N);
+ }
}
void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
Added: cfe/trunk/test/Analysis/expr-inspection.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/expr-inspection.c?rev=288257&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/expr-inspection.c (added)
+++ cfe/trunk/test/Analysis/expr-inspection.c Wed Nov 30 11:57:18 2016
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=debug.ExprInspection -verify %s 2>&1 | FileCheck %s
+
+// Self-tests for the debug.ExprInspection checker.
+
+void clang_analyzer_dump(int x);
+void clang_analyzer_printState();
+void clang_analyzer_numTimesReached();
+
+void foo(int x) {
+ clang_analyzer_dump(x); // expected-warning{{reg_$0<x>}}
+ int y = 1;
+ clang_analyzer_printState();
+ for (; y < 3; ++y)
+ clang_analyzer_numTimesReached(); // expected-warning{{2}}
+}
+
+// CHECK: Store (direct and default bindings)
+// CHECK-NEXT: (y,0,direct) : 1 S32b
+
+// CHECK: Expressions:
+// CHECK-NEXT: clang_analyzer_printState : &code{clang_analyzer_printState}
+// CHECK-NEXT: Ranges are empty.
Modified: cfe/trunk/test/Analysis/symbol-reaper.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/symbol-reaper.c?rev=288257&r1=288256&r2=288257&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/symbol-reaper.c (original)
+++ cfe/trunk/test/Analysis/symbol-reaper.c Wed Nov 30 11:57:18 2016
@@ -2,6 +2,7 @@
void clang_analyzer_eval(int);
void clang_analyzer_warnOnDeadSymbol(int);
+void clang_analyzer_numTimesReached();
int conjure_index();
@@ -10,6 +11,9 @@ void test_that_expr_inspection_works() {
int x = conjure_index();
clang_analyzer_warnOnDeadSymbol(x);
} while(0); // expected-warning{{SYMBOL DEAD}}
+
+ // Make sure we don't accidentally split state in ExprInspection.
+ clang_analyzer_numTimesReached(); // expected-warning{{1}}
}
// These tests verify the reaping of symbols that are only referenced as
More information about the cfe-commits
mailing list