forked from OSchip/llvm-project
[analyzer] Provide an option to dump generated exploded graphs to a given file.
Dumping graphs instead of opening them is often very useful, e.g. for transfer or converting to SVG. Basic sanity check for generated exploded graphs. Differential Revision: https://reviews.llvm.org/D52637 llvm-svn: 343352
This commit is contained in:
parent
86714886d9
commit
c704f4fbd0
|
@ -80,6 +80,9 @@ def trim_egraph : Flag<["-"], "trim-egraph">,
|
||||||
HelpText<"Only show error-related paths in the analysis graph">;
|
HelpText<"Only show error-related paths in the analysis graph">;
|
||||||
def analyzer_viz_egraph_graphviz : Flag<["-"], "analyzer-viz-egraph-graphviz">,
|
def analyzer_viz_egraph_graphviz : Flag<["-"], "analyzer-viz-egraph-graphviz">,
|
||||||
HelpText<"Display exploded graph using GraphViz">;
|
HelpText<"Display exploded graph using GraphViz">;
|
||||||
|
def analyzer_dump_egraph : Separate<["-"], "analyzer-dump-egraph">,
|
||||||
|
HelpText<"Dump exploded graph to the specified file">;
|
||||||
|
def analyzer_dump_egraph_EQ : Joined<["-"], "analyzer-dump-egraph=">, Alias<analyzer_dump_egraph>;
|
||||||
|
|
||||||
def analyzer_inline_max_stack_depth : Separate<["-"], "analyzer-inline-max-stack-depth">,
|
def analyzer_inline_max_stack_depth : Separate<["-"], "analyzer-inline-max-stack-depth">,
|
||||||
HelpText<"Bound on stack depth while inlining (4 by default)">;
|
HelpText<"Bound on stack depth while inlining (4 by default)">;
|
||||||
|
|
|
@ -140,6 +140,9 @@ public:
|
||||||
|
|
||||||
std::string AnalyzeSpecificFunction;
|
std::string AnalyzeSpecificFunction;
|
||||||
|
|
||||||
|
/// File path to which the exploded graph should be dumped.
|
||||||
|
std::string DumpExplodedGraphTo;
|
||||||
|
|
||||||
/// Store full compiler invocation for reproducible instructions in the
|
/// Store full compiler invocation for reproducible instructions in the
|
||||||
/// generated report.
|
/// generated report.
|
||||||
std::string FullCompilerInvocation;
|
std::string FullCompilerInvocation;
|
||||||
|
|
|
@ -204,6 +204,18 @@ public:
|
||||||
void enqueueEndOfPath(ExplodedNodeSet &S);
|
void enqueueEndOfPath(ExplodedNodeSet &S);
|
||||||
void GenerateCallExitNode(ExplodedNode *N);
|
void GenerateCallExitNode(ExplodedNode *N);
|
||||||
|
|
||||||
|
|
||||||
|
/// Dump graph to the specified filename.
|
||||||
|
/// If filename is empty, generate a temporary one.
|
||||||
|
/// \return The filename the graph is written into.
|
||||||
|
std::string DumpGraph(bool trim = false, StringRef Filename="");
|
||||||
|
|
||||||
|
/// Dump the graph consisting of the given nodes to a specified filename.
|
||||||
|
/// Generate a temporary filename if it's not provided.
|
||||||
|
/// \return The filename the graph is written into.
|
||||||
|
std::string DumpGraph(ArrayRef<const ExplodedNode *> Nodes,
|
||||||
|
StringRef Filename = "");
|
||||||
|
|
||||||
/// Visualize the ExplodedGraph created by executing the simulation.
|
/// Visualize the ExplodedGraph created by executing the simulation.
|
||||||
void ViewGraph(bool trim = false);
|
void ViewGraph(bool trim = false);
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,7 @@ static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args,
|
||||||
|
|
||||||
Opts.visualizeExplodedGraphWithGraphViz =
|
Opts.visualizeExplodedGraphWithGraphViz =
|
||||||
Args.hasArg(OPT_analyzer_viz_egraph_graphviz);
|
Args.hasArg(OPT_analyzer_viz_egraph_graphviz);
|
||||||
|
Opts.DumpExplodedGraphTo = Args.getLastArgValue(OPT_analyzer_dump_egraph);
|
||||||
Opts.NoRetryExhausted = Args.hasArg(OPT_analyzer_disable_retry_exhausted);
|
Opts.NoRetryExhausted = Args.hasArg(OPT_analyzer_disable_retry_exhausted);
|
||||||
Opts.AnalyzeAll = Args.hasArg(OPT_analyzer_opt_analyze_headers);
|
Opts.AnalyzeAll = Args.hasArg(OPT_analyzer_opt_analyze_headers);
|
||||||
Opts.AnalyzerDisplayProgress = Args.hasArg(OPT_analyzer_display_progress);
|
Opts.AnalyzerDisplayProgress = Args.hasArg(OPT_analyzer_display_progress);
|
||||||
|
|
|
@ -3057,6 +3057,23 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ExprEngine::ViewGraph(bool trim) {
|
void ExprEngine::ViewGraph(bool trim) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
std::string Filename = DumpGraph(trim);
|
||||||
|
llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
|
||||||
|
#endif
|
||||||
|
llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
std::string Filename = DumpGraph(Nodes);
|
||||||
|
llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
|
||||||
|
#endif
|
||||||
|
llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (trim) {
|
if (trim) {
|
||||||
std::vector<const ExplodedNode *> Src;
|
std::vector<const ExplodedNode *> Src;
|
||||||
|
@ -3067,22 +3084,30 @@ void ExprEngine::ViewGraph(bool trim) {
|
||||||
const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode());
|
const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode());
|
||||||
if (N) Src.push_back(N);
|
if (N) Src.push_back(N);
|
||||||
}
|
}
|
||||||
|
return DumpGraph(Src, Filename);
|
||||||
ViewGraph(Src);
|
|
||||||
} else {
|
} else {
|
||||||
llvm::ViewGraph(&G, "ExprEngine");
|
return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false,
|
||||||
|
/*Title=*/"Exploded Graph", /*Filename=*/Filename);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) {
|
std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes,
|
||||||
|
StringRef Filename) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::unique_ptr<ExplodedGraph> TrimmedG(G.trim(Nodes));
|
std::unique_ptr<ExplodedGraph> TrimmedG(G.trim(Nodes));
|
||||||
|
|
||||||
if (!TrimmedG.get()) {
|
if (!TrimmedG.get()) {
|
||||||
llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n";
|
llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n";
|
||||||
} else {
|
} else {
|
||||||
llvm::ViewGraph(TrimmedG.get(), "TrimmedExprEngine");
|
return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine",
|
||||||
|
/*ShortNames=*/false,
|
||||||
|
/*Title=*/"Trimmed Exploded Graph",
|
||||||
|
/*Filename=*/Filename);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -745,6 +745,9 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
|
||||||
Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D),
|
Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D),
|
||||||
Mgr->options.getMaxNodesPerTopLevelFunction());
|
Mgr->options.getMaxNodesPerTopLevelFunction());
|
||||||
|
|
||||||
|
if (!Mgr->options.DumpExplodedGraphTo.empty())
|
||||||
|
Eng.DumpGraph(Mgr->options.TrimGraph, Mgr->options.DumpExplodedGraphTo);
|
||||||
|
|
||||||
// Visualize the exploded graph.
|
// Visualize the exploded graph.
|
||||||
if (Mgr->options.visualizeExplodedGraphWithGraphViz)
|
if (Mgr->options.visualizeExplodedGraphWithGraphViz)
|
||||||
Eng.ViewGraph(Mgr->options.TrimGraph);
|
Eng.ViewGraph(Mgr->options.TrimGraph);
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-dump-egraph=%t.dot %s
|
||||||
|
// RUN: cat %t.dot | FileCheck %s
|
||||||
|
// REQUIRES: asserts
|
||||||
|
|
||||||
|
int getJ();
|
||||||
|
|
||||||
|
int foo() {
|
||||||
|
int *x = 0;
|
||||||
|
return *x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK: digraph "Exploded Graph" {
|
||||||
|
// CHECK: Edge: (B2, B1)
|
||||||
|
// CHECK: Block Entrance: B1
|
||||||
|
// CHECK: Bug report attached
|
Loading…
Reference in New Issue