From: Ted Kremenek Date: Wed, 27 Aug 2008 22:31:43 +0000 (+0000) Subject: Added Ubigraph visualization for the static analyzer (this is pretty alpha quality). X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f8ce6991f41d55b6e8526b7a7919771428e2b181;p=clang Added Ubigraph visualization for the static analyzer (this is pretty alpha quality). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@55442 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Driver/AnalysisConsumer.cpp b/Driver/AnalysisConsumer.cpp index 9b84aa37a3..7142b79181 100644 --- a/Driver/AnalysisConsumer.cpp +++ b/Driver/AnalysisConsumer.cpp @@ -31,11 +31,13 @@ #include "clang/Analysis/PathSensitive/GRTransferFuncs.h" #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "llvm/Support/Streams.h" - +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" #include using namespace clang; +static ExplodedNodeImpl::Auditor* CreateUbiViz(); //===----------------------------------------------------------------------===// // Basic type definitions. @@ -59,7 +61,8 @@ namespace { Actions ObjCImplementationActions; public: - const bool Visualize; + const bool VisGraphviz; + const bool VisUbigraph; const bool TrimGraph; const LangOptions& LOpts; Diagnostic &Diags; @@ -76,8 +79,9 @@ namespace { const LangOptions& lopts, const std::string& fname, const std::string& htmldir, - bool visualize, bool trim, bool analyzeAll) - : Visualize(visualize), TrimGraph(trim), LOpts(lopts), Diags(diags), + bool visgraphviz, bool visubi, bool trim, bool analyzeAll) + : VisGraphviz(visgraphviz), VisUbigraph(visubi), TrimGraph(trim), + LOpts(lopts), Diags(diags), Ctx(0), PP(pp), PPF(ppf), HTMLDir(htmldir), FName(fname), @@ -168,8 +172,16 @@ namespace { return liveness.get(); } + bool shouldVisualizeGraphviz() const { + return C.VisGraphviz; + } + + bool shouldVisualizeUbigraph() const { + return C.VisUbigraph; + } + bool shouldVisualize() const { - return C.Visualize; + return C.VisGraphviz || C.VisUbigraph; } bool shouldTrimGraph() const { @@ -319,15 +331,26 @@ static void ActionGRExprEngine(AnalysisManager& mgr, GRTransferFuncs* tf, Eng.RegisterInternalChecks(); RegisterAppleChecks(Eng); } + + // Set the graph auditor. + llvm::OwningPtr Auditor; + if (mgr.shouldVisualizeUbigraph()) { + Auditor.reset(CreateUbiViz()); + ExplodedNodeImpl::SetAuditor(Auditor.get()); + } // Execute the worklist algorithm. Eng.ExecuteWorkList(); + // Release the auditor (if any) so that it doesn't monitor the graph + // created BugReporter. + ExplodedNodeImpl::SetAuditor(0); + // Display warnings. Eng.EmitWarnings(mgr); // Visualize the exploded graph. - if (mgr.shouldVisualize()) + if (mgr.shouldVisualizeGraphviz()) Eng.ViewGraph(mgr.shouldTrimGraph()); } @@ -418,12 +441,13 @@ ASTConsumer* clang::CreateAnalysisConsumer(Analyses* Beg, Analyses* End, const LangOptions& lopts, const std::string& fname, const std::string& htmldir, - bool visualize, bool trim, + bool VisGraphviz, bool VisUbi, + bool trim, bool analyzeAll) { llvm::OwningPtr C(new AnalysisConsumer(diags, pp, ppf, lopts, fname, htmldir, - visualize, trim, analyzeAll)); + VisGraphviz, VisUbi, trim, analyzeAll)); for ( ; Beg != End ; ++Beg) switch (*Beg) { @@ -438,3 +462,75 @@ ASTConsumer* clang::CreateAnalysisConsumer(Analyses* Beg, Analyses* End, return C.take(); } +//===----------------------------------------------------------------------===// +// Ubigraph Visualization. FIXME: Move to separate file. +//===----------------------------------------------------------------------===// + +namespace { + +class UbigraphViz : public ExplodedNodeImpl::Auditor { + llvm::OwningPtr Out; + unsigned Cntr; + + typedef llvm::DenseMap VMap; + VMap M; + +public: + UbigraphViz(llvm::raw_ostream* out) : Out(out), Cntr(0) {} + virtual void AddEdge(ExplodedNodeImpl* Src, ExplodedNodeImpl* Dst); +}; + +} // end anonymous namespace + +static ExplodedNodeImpl::Auditor* CreateUbiViz() { + std::string ErrMsg; + + llvm::sys::Path Filename = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg); + if (!ErrMsg.empty()) + return 0; + + Filename.appendComponent("llvm_ubi"); + Filename.makeUnique(true,&ErrMsg); + + if (!ErrMsg.empty()) + return 0; + + llvm::cerr << "Writing '" << Filename << "'.\n"; + + llvm::OwningPtr Stream; + std::string filename = Filename.toString(); + Stream.reset(new llvm::raw_fd_ostream(filename.c_str(), ErrMsg)); + + if (!ErrMsg.empty()) + return 0; + + return new UbigraphViz(Stream.take()); +} + +void UbigraphViz::AddEdge(ExplodedNodeImpl* Src, ExplodedNodeImpl* Dst) { + // Lookup the Src. If it is a new node, it's a root. + VMap::iterator SrcI= M.find(Src); + unsigned SrcID; + + if (SrcI == M.end()) { + M[Src] = SrcID = Cntr++; + *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n"; + } + else + SrcID = SrcI->second; + + // Lookup the Dst. + VMap::iterator DstI= M.find(Dst); + unsigned DstID; + + if (DstI == M.end()) { + M[Dst] = DstID = Cntr++; + *Out << "('vertex', " << DstID << ")\n"; + } + else + DstID = DstI->second; + + // Add the edge. + *Out << "('edge', " << SrcID << ", " << DstID << ")\n"; +} + diff --git a/Driver/AnalysisConsumer.h b/Driver/AnalysisConsumer.h index 5a2550da43..daec6f3441 100644 --- a/Driver/AnalysisConsumer.h +++ b/Driver/AnalysisConsumer.h @@ -28,8 +28,10 @@ ASTConsumer* CreateAnalysisConsumer(Analyses* Beg, Analyses* End, const LangOptions& lopts, const std::string& fname, const std::string& htmldir, - bool visualize, bool trim, - bool analyzeAll); + bool VisualizeGraphViz, + bool VisualizeUbi, + bool VizTrimGraph, + bool AnalyzeAll); } // end clang namespace #endif diff --git a/Driver/clang.cpp b/Driver/clang.cpp index fad1624ef2..8b24474681 100644 --- a/Driver/clang.cpp +++ b/Driver/clang.cpp @@ -159,11 +159,15 @@ NoCaretDiagnostics("fno-caret-diagnostics", //===----------------------------------------------------------------------===// static llvm::cl::opt -VisualizeEG("visualize-egraph", - llvm::cl::desc("Display static analysis Exploded Graph")); +VisualizeEGDot("analyzer-viz-egraph-graphviz", + llvm::cl::desc("Display exploded graph using GraphViz")); static llvm::cl::opt -AnalyzeAll("checker-opt-analyze-headers", +VisualizeEGUbi("analyzer-viz-egraph-ubigraph", + llvm::cl::desc("Display exploded graph using Ubigraph")); + +static llvm::cl::opt +AnalyzeAll("analyzer-opt-analyze-headers", llvm::cl::desc("Force the static analyzer to analyze " "functions defined in header files")); @@ -964,8 +968,8 @@ static ASTConsumer* CreateASTConsumer(const std::string& InFile, &AnalysisList[0]+AnalysisList.size(), Diag, PP, PPF, LangOpts, AnalyzeSpecificFunction, - OutputFile, VisualizeEG, TrimGraph, - AnalyzeAll); + OutputFile, VisualizeEGDot, VisualizeEGUbi, + TrimGraph, AnalyzeAll); } } diff --git a/utils/ubiviz b/utils/ubiviz new file mode 100755 index 0000000000..1a8f742c3c --- /dev/null +++ b/utils/ubiviz @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This script reads visualization data emitted by the static analyzer for +# display in Ubigraph. +# +##===----------------------------------------------------------------------===## + +import xmlrpclib +import sys + +def Error(message): + print >> sys.stderr, 'ubiviz: ' + message + sys.exit(1) + +def StreamData(filename): + file = open(filename) + for ln in file: + yield eval(ln) + file.close() + +def Display(G, data): + action = data[0] + if action == 'vertex': + vertex = data[1] + G.new_vertex_w_id(vertex) + for attribute in data[2:]: + G.set_vertex_attribute(vertex, attribute[0], attribute[1]) + elif action == 'edge': + src = data[1] + dst = data[2] + edge = G.new_edge(src,dst) + for attribute in data[3:]: + G.set_edge_attribute(edge, attribute[0], attribute[1]) + +def main(args): + if len(args) == 0: + Error('no input files') + + server = xmlrpclib.Server('http://127.0.0.1:20738/RPC2') + G = server.ubigraph + + for arg in args: + G.clear() + for x in StreamData(arg): + Display(G,x) + +if __name__ == '__main__': + main(sys.argv[1:]) + + \ No newline at end of file