Index: clang/include/clang/Driver/CC1Options.td =================================================================== --- clang/include/clang/Driver/CC1Options.td +++ clang/include/clang/Driver/CC1Options.td @@ -79,6 +79,8 @@ def analyze_function_EQ : Joined<["-"], "analyze-function=">, Alias; def trim_egraph : Flag<["-"], "trim-egraph">, HelpText<"Only show error-related paths in the analysis graph">; +def diff_egraph : Flag<["-"], "diff-egraph">, + HelpText<"Only show the differences in program states of the analysis graph">; def analyzer_viz_egraph_graphviz : Flag<["-"], "analyzer-viz-egraph-graphviz">, HelpText<"Display exploded graph using GraphViz">; def analyzer_dump_egraph : Separate<["-"], "analyzer-dump-egraph">, Index: clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -208,6 +208,7 @@ unsigned eagerlyAssumeBinOpBifurcation : 1; unsigned TrimGraph : 1; + unsigned DiffGraph : 1; unsigned visualizeExplodedGraphWithGraphViz : 1; unsigned UnoptimizedCFG : 1; unsigned PrintStats : 1; @@ -265,8 +266,8 @@ ShowConfigOptionsList(false), AnalyzeAll(false), AnalyzerDisplayProgress(false), AnalyzeNestedBlocks(false), eagerlyAssumeBinOpBifurcation(false), TrimGraph(false), - visualizeExplodedGraphWithGraphViz(false), UnoptimizedCFG(false), - PrintStats(false), NoRetryExhausted(false) { + DiffGraph(false), visualizeExplodedGraphWithGraphViz(false), + UnoptimizedCFG(false), PrintStats(false), NoRetryExhausted(false) { llvm::sort(AnalyzerConfigCmdFlags); } Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -424,10 +424,19 @@ } // Pretty-printing. - void print(raw_ostream &Out, const char *nl = "\n", const char *sep = "", - const LocationContext *CurrentLC = nullptr) const; - void printDOT(raw_ostream &Out, - const LocationContext *CurrentLC = nullptr) const; + void print(raw_ostream &Out, const LocationContext *LCtx = nullptr, + const char *NL = "\n", const char *Sep = "") const; + + void printDOT(raw_ostream &Out, const LocationContext *LCtx = nullptr) const; + + void printDiff(raw_ostream &Out, ProgramStateRef PreviousState, + const LocationContext *CurrentLCtx, + const LocationContext *PreviousLCtx, const char *NL = "\n", + const char *Sep = "") const; + + void printDiffDOT(raw_ostream &Out, ProgramStateRef PreviousState, + const LocationContext *CurrentLCtx, + const LocationContext *PreviousLCtx) const; void dump() const; Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -307,6 +307,7 @@ Opts.AnalyzeSpecificFunction = Args.getLastArgValue(OPT_analyze_function); Opts.UnoptimizedCFG = Args.hasArg(OPT_analysis_UnoptimizedCFG); Opts.TrimGraph = Args.hasArg(OPT_trim_egraph); + Opts.DiffGraph = Args.hasArg(OPT_diff_egraph); Opts.maxBlockVisitOnPath = getLastArgIntValue(Args, OPT_analyzer_max_loop, 4, Diags); Opts.PrintStats = Args.hasArg(OPT_analyzer_stats); Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -3017,7 +3017,8 @@ std::string sbuf; llvm::raw_string_ostream Out(sbuf); - ProgramStateRef State = N->getState(); + ProgramStateRef CurrentState = N->getState(); + const LocationContext *CurrentLCtx = N->getLocationContext(); // Dump program point for all the previously skipped nodes. traverseHiddenNodes( @@ -3036,15 +3037,32 @@ Out << "\\l\\|"; - Out << "StateID: ST" << State->getID() << ", NodeID: N" << N->getID(G) - << " <" << (const void *)N << ">\\|"; + Out << "StateID: ST" << CurrentState->getID() << ", NodeID: N" + << N->getID(G) << " <" << (const void *)N << ">\\l"; bool SameAsAllPredecessors = std::all_of(N->pred_begin(), N->pred_end(), [&](const ExplodedNode *P) { - return P->getState() == State; + return P->getState() == CurrentState; }); - if (!SameAsAllPredecessors) - State->printDOT(Out, N->getLocationContext()); + + if (SameAsAllPredecessors) + return Out.str(); + + if (!CurrentState->getAnalysisManager().getAnalyzerOptions().DiffGraph) { + CurrentState->printDOT(Out, CurrentLCtx); + } else { + ProgramStateRef PreviousState = nullptr; + const LocationContext *PreviousLCtx = nullptr; + + if (!N->pred_empty()) { + const ExplodedNode *PreviousNode = *N->pred_begin(); + PreviousState = PreviousNode->getState(); + PreviousLCtx = PreviousNode->getLocationContext(); + } + + CurrentState->printDiffDOT(Out, PreviousState, CurrentLCtx, PreviousLCtx); + } + return Out.str(); } }; Index: clang/lib/StaticAnalyzer/Core/ProgramState.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ProgramState.cpp +++ clang/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -440,16 +440,16 @@ // State pretty-printing. //===----------------------------------------------------------------------===// -void ProgramState::print(raw_ostream &Out, - const char *NL, const char *Sep, - const LocationContext *LC) const { - // Print the store. +void ProgramState::print(raw_ostream &Out, const LocationContext *LCtx, + const char *NL, const char *Sep) const { ProgramStateManager &Mgr = getStateManager(); - const ASTContext &Context = getStateManager().getContext(); + const ASTContext &Ctx = Mgr.getContext(); + + // Print out the store. Mgr.getStoreManager().print(getStore(), Out, NL); // Print out the environment. - Env.print(Out, NL, Sep, Context, LC); + Env.print(Out, NL, Sep, Ctx, LCtx); // Print out the constraints. Mgr.getConstraintManager().print(this, Out, NL, Sep); @@ -458,18 +458,53 @@ printDynamicTypeInfo(this, Out, NL, Sep); // Print checker-specific data. - Mgr.getOwningEngine().printState(Out, this, NL, Sep, LC); + Mgr.getOwningEngine().printState(Out, this, NL, Sep, LCtx); } void ProgramState::printDOT(raw_ostream &Out, - const LocationContext *LC) const { - print(Out, "\\l", "\\|", LC); + const LocationContext *LCtx) const { + print(Out, LCtx, "\\l", "\\|"); } LLVM_DUMP_METHOD void ProgramState::dump() const { print(llvm::errs()); } +void ProgramState::printDiff(raw_ostream &Out, ProgramStateRef PreviousState, + const LocationContext *CurrentLCtx, + const LocationContext *PreviousLCtx, + const char *NL, const char *Sep) const { + // If there is no previous state just print the current state. + if (!PreviousState) { + print(Out, CurrentLCtx, NL, Sep); + return; + } + + ProgramStateManager &Mgr = getStateManager(); + const ASTContext &Ctx = Mgr.getContext(); + + // Print out the store. + Mgr.getStoreManager().print(getStore(), Out, NL); + + // Print out the environment. + Env.print(Out, NL, Sep, Ctx, CurrentLCtx); + + // Print out the constraints. + Mgr.getConstraintManager().print(this, Out, NL, Sep); + + // Print out the tracked dynamic types. + printDynamicTypeInfo(this, Out, NL, Sep); + + // Print checker-specific data. + Mgr.getOwningEngine().printState(Out, this, NL, Sep, CurrentLCtx); +} + +void ProgramState::printDiffDOT(raw_ostream &Out, ProgramStateRef PreviousState, + const LocationContext *CurrentLCtx, + const LocationContext *PreviousLCtx) const { + printDiff(Out, PreviousState, CurrentLCtx, PreviousLCtx, "\\l", "\\|"); +} + AnalysisManager& ProgramState::getAnalysisManager() const { return stateMgr->getOwningEngine().getAnalysisManager(); }