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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits