AnalysisConsumer.cpp revision 87e154c09bbb060a0620bc988d7723bee64fb79c
1//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// "Meta" ASTConsumer for running different source analyses.
11//
12//===----------------------------------------------------------------------===//
13
14#define DEBUG_TYPE "AnalysisConsumer"
15
16#include "AnalysisConsumer.h"
17#include "clang/AST/ASTConsumer.h"
18#include "clang/AST/Decl.h"
19#include "clang/AST/DeclCXX.h"
20#include "clang/AST/DeclObjC.h"
21#include "clang/AST/ParentMap.h"
22#include "clang/AST/RecursiveASTVisitor.h"
23#include "clang/Analysis/CFG.h"
24#include "clang/Analysis/CallGraph.h"
25#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
26#include "clang/StaticAnalyzer/Core/CheckerManager.h"
27#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
28#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
29#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
30#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
31#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
32#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
33
34#include "clang/Basic/FileManager.h"
35#include "clang/Basic/SourceManager.h"
36#include "clang/Frontend/AnalyzerOptions.h"
37#include "clang/Lex/Preprocessor.h"
38#include "llvm/Support/raw_ostream.h"
39#include "llvm/Support/Path.h"
40#include "llvm/Support/Program.h"
41#include "llvm/Support/Timer.h"
42#include "llvm/ADT/DepthFirstIterator.h"
43#include "llvm/ADT/OwningPtr.h"
44#include "llvm/ADT/SmallPtrSet.h"
45#include "llvm/ADT/Statistic.h"
46
47#include <queue>
48
49using namespace clang;
50using namespace ento;
51using llvm::SmallPtrSet;
52
53static ExplodedNode::Auditor* CreateUbiViz();
54
55STATISTIC(NumFunctionTopLevel, "The # of functions at top level.");
56STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level).");
57STATISTIC(NumBlocksInAnalyzedFunctions,
58                     "The # of basic blocks in the analyzed functions.");
59STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");
60
61//===----------------------------------------------------------------------===//
62// Special PathDiagnosticConsumers.
63//===----------------------------------------------------------------------===//
64
65static PathDiagnosticConsumer*
66createPlistHTMLDiagnosticConsumer(const std::string& prefix,
67                                const Preprocessor &PP) {
68  PathDiagnosticConsumer *PD =
69    createHTMLDiagnosticConsumer(llvm::sys::path::parent_path(prefix), PP);
70  return createPlistDiagnosticConsumer(prefix, PP, PD);
71}
72
73//===----------------------------------------------------------------------===//
74// AnalysisConsumer declaration.
75//===----------------------------------------------------------------------===//
76
77namespace {
78
79class AnalysisConsumer : public ASTConsumer,
80                         public RecursiveASTVisitor<AnalysisConsumer> {
81  enum AnalysisMode {
82    ANALYSIS_SYNTAX,
83    ANALYSIS_PATH,
84    ANALYSIS_ALL
85  };
86
87  /// Mode of the analyzes while recursively visiting Decls.
88  AnalysisMode RecVisitorMode;
89  /// Bug Reporter to use while recursively visiting Decls.
90  BugReporter *RecVisitorBR;
91
92public:
93  ASTContext *Ctx;
94  const Preprocessor &PP;
95  const std::string OutDir;
96  AnalyzerOptions Opts;
97  ArrayRef<std::string> Plugins;
98
99  /// \brief Stores the declarations from the local translation unit.
100  /// Note, we pre-compute the local declarations at parse time as an
101  /// optimization to make sure we do not deserialize everything from disk.
102  /// The local declaration to all declarations ratio might be very small when
103  /// working with a PCH file.
104  SetOfDecls LocalTUDecls;
105
106  // PD is owned by AnalysisManager.
107  PathDiagnosticConsumer *PD;
108
109  StoreManagerCreator CreateStoreMgr;
110  ConstraintManagerCreator CreateConstraintMgr;
111
112  OwningPtr<CheckerManager> checkerMgr;
113  OwningPtr<AnalysisManager> Mgr;
114
115  /// Time the analyzes time of each translation unit.
116  static llvm::Timer* TUTotalTimer;
117
118  /// The information about analyzed functions shared throughout the
119  /// translation unit.
120  FunctionSummariesTy FunctionSummaries;
121
122  AnalysisConsumer(const Preprocessor& pp,
123                   const std::string& outdir,
124                   const AnalyzerOptions& opts,
125                   ArrayRef<std::string> plugins)
126    : RecVisitorMode(ANALYSIS_ALL), RecVisitorBR(0),
127      Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) {
128    DigestAnalyzerOptions();
129    if (Opts.PrintStats) {
130      llvm::EnableStatistics();
131      TUTotalTimer = new llvm::Timer("Analyzer Total Time");
132    }
133  }
134
135  ~AnalysisConsumer() {
136    if (Opts.PrintStats)
137      delete TUTotalTimer;
138  }
139
140  void DigestAnalyzerOptions() {
141    // Create the PathDiagnosticConsumer.
142    if (!OutDir.empty()) {
143      switch (Opts.AnalysisDiagOpt) {
144      default:
145#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \
146        case PD_##NAME: PD = CREATEFN(OutDir, PP); break;
147#include "clang/Frontend/Analyses.def"
148      }
149    } else if (Opts.AnalysisDiagOpt == PD_TEXT) {
150      // Create the text client even without a specified output file since
151      // it just uses diagnostic notes.
152      PD = createTextPathDiagnosticConsumer("", PP);
153    }
154
155    // Create the analyzer component creators.
156    switch (Opts.AnalysisStoreOpt) {
157    default:
158      llvm_unreachable("Unknown store manager.");
159#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN)           \
160      case NAME##Model: CreateStoreMgr = CREATEFN; break;
161#include "clang/Frontend/Analyses.def"
162    }
163
164    switch (Opts.AnalysisConstraintsOpt) {
165    default:
166      llvm_unreachable("Unknown store manager.");
167#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN)     \
168      case NAME##Model: CreateConstraintMgr = CREATEFN; break;
169#include "clang/Frontend/Analyses.def"
170    }
171  }
172
173  void DisplayFunction(const Decl *D, AnalysisMode Mode) {
174    if (!Opts.AnalyzerDisplayProgress)
175      return;
176
177    SourceManager &SM = Mgr->getASTContext().getSourceManager();
178    PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
179    if (Loc.isValid()) {
180      llvm::errs() << "ANALYZE";
181      switch (Mode) {
182        case ANALYSIS_SYNTAX: llvm::errs() << "(Syntax)"; break;
183        case ANALYSIS_PATH: llvm::errs() << "(Path Sensitive)"; break;
184        case ANALYSIS_ALL: break;
185      };
186      llvm::errs() << ": " << Loc.getFilename();
187      if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
188        const NamedDecl *ND = cast<NamedDecl>(D);
189        llvm::errs() << ' ' << *ND << '\n';
190      }
191      else if (isa<BlockDecl>(D)) {
192        llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:"
193                     << Loc.getColumn() << '\n';
194      }
195      else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
196        Selector S = MD->getSelector();
197        llvm::errs() << ' ' << S.getAsString();
198      }
199    }
200  }
201
202  virtual void Initialize(ASTContext &Context) {
203    Ctx = &Context;
204    checkerMgr.reset(createCheckerManager(Opts, PP.getLangOpts(), Plugins,
205                                          PP.getDiagnostics()));
206    Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(),
207                                  PP.getLangOpts(), PD,
208                                  CreateStoreMgr, CreateConstraintMgr,
209                                  checkerMgr.get(),
210                                  Opts.MaxNodes, Opts.MaxLoop,
211                                  Opts.VisualizeEGDot, Opts.VisualizeEGUbi,
212                                  Opts.AnalysisPurgeOpt, Opts.EagerlyAssume,
213                                  Opts.TrimGraph,
214                                  Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
215                                  Opts.CFGAddInitializers,
216                                  Opts.EagerlyTrimEGraph,
217                                  Opts.IPAMode,
218                                  Opts.InlineMaxStackDepth,
219                                  Opts.InlineMaxFunctionSize,
220                                  Opts.InliningMode,
221                                  Opts.NoRetryExhausted));
222  }
223
224  /// \brief Store the top level decls in the set to be processed later on.
225  /// (Doing this pre-processing avoids deserialization of data from PCH.)
226  virtual bool HandleTopLevelDecl(DeclGroupRef D);
227  virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D);
228
229  virtual void HandleTranslationUnit(ASTContext &C);
230
231  /// \brief Build the call graph for all the top level decls of this TU and
232  /// use it to define the order in which the functions should be visited.
233  void HandleDeclsGallGraph();
234
235  /// \brief Run analyzes(syntax or path sensitive) on the given function.
236  /// \param Mode - determines if we are requesting syntax only or path
237  /// sensitive only analysis.
238  /// \param VisitedCallees - The output parameter, which is populated with the
239  /// set of functions which should be considered analyzed after analyzing the
240  /// given root function.
241  void HandleCode(Decl *D, AnalysisMode Mode,
242                  SetOfConstDecls *VisitedCallees = 0);
243
244  void RunPathSensitiveChecks(Decl *D, SetOfConstDecls *VisitedCallees);
245  void ActionExprEngine(Decl *D, bool ObjCGCEnabled,
246                        SetOfConstDecls *VisitedCallees);
247
248  /// Visitors for the RecursiveASTVisitor.
249
250  /// Handle callbacks for arbitrary Decls.
251  bool VisitDecl(Decl *D) {
252    checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR);
253    return true;
254  }
255
256  bool VisitFunctionDecl(FunctionDecl *FD) {
257    IdentifierInfo *II = FD->getIdentifier();
258    if (II && II->getName().startswith("__inline"))
259      return true;
260
261    // We skip function template definitions, as their semantics is
262    // only determined when they are instantiated.
263    if (FD->isThisDeclarationADefinition() &&
264        !FD->isDependentContext()) {
265      HandleCode(FD, RecVisitorMode);
266    }
267    return true;
268  }
269
270  bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
271    checkerMgr->runCheckersOnASTDecl(MD, *Mgr, *RecVisitorBR);
272    if (MD->isThisDeclarationADefinition())
273      HandleCode(MD, RecVisitorMode);
274    return true;
275  }
276
277private:
278  void storeTopLevelDecls(DeclGroupRef DG);
279
280  /// \brief Check if we should skip (not analyze) the given function.
281  bool skipFunction(Decl *D);
282
283};
284} // end anonymous namespace
285
286
287//===----------------------------------------------------------------------===//
288// AnalysisConsumer implementation.
289//===----------------------------------------------------------------------===//
290llvm::Timer* AnalysisConsumer::TUTotalTimer = 0;
291
292bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) {
293  storeTopLevelDecls(DG);
294  return true;
295}
296
297void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
298  storeTopLevelDecls(DG);
299}
300
301void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
302  for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) {
303
304    // Skip ObjCMethodDecl, wait for the objc container to avoid
305    // analyzing twice.
306    if (isa<ObjCMethodDecl>(*I))
307      continue;
308
309    LocalTUDecls.insert(*I);
310  }
311}
312
313void AnalysisConsumer::HandleDeclsGallGraph() {
314  // Otherwise, use the Callgraph to derive the order.
315  // Build the Call Graph.
316  CallGraph CG;
317  // Add all the top level declarations to the graph.
318  for (SetOfDecls::iterator I = LocalTUDecls.begin(),
319                            E = LocalTUDecls.end(); I != E; ++I)
320    CG.addToCallGraph(*I);
321
322  // Find the top level nodes - children of root + the unreachable (parentless)
323  // nodes.
324  llvm::SmallVector<CallGraphNode*, 24> TopLevelFunctions;
325  for (CallGraph::nodes_iterator TI = CG.parentless_begin(),
326                                 TE = CG.parentless_end(); TI != TE; ++TI) {
327    TopLevelFunctions.push_back(*TI);
328    NumFunctionTopLevel++;
329  }
330  CallGraphNode *Entry = CG.getRoot();
331  for (CallGraphNode::iterator I = Entry->begin(),
332                               E = Entry->end(); I != E; ++I) {
333    TopLevelFunctions.push_back(*I);
334    NumFunctionTopLevel++;
335  }
336
337  // Make sure the nodes are sorted in order reverse of their definition in the
338  // translation unit. This step is very important for performance. It ensures
339  // that we analyze the root functions before the externally available
340  // subroutines.
341  std::queue<CallGraphNode*> BFSQueue;
342  for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator
343         TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend();
344         TI != TE; ++TI)
345    BFSQueue.push(*TI);
346
347  // BFS over all of the functions, while skipping the ones inlined into
348  // the previously processed functions. Use external Visited set, which is
349  // also modified when we inline a function.
350  SmallPtrSet<CallGraphNode*,24> Visited;
351  while(!BFSQueue.empty()) {
352    CallGraphNode *N = BFSQueue.front();
353    BFSQueue.pop();
354
355    // Skip the functions which have been processed already or previously
356    // inlined.
357    if (Visited.count(N))
358      continue;
359
360    // Analyze the function.
361    SetOfConstDecls VisitedCallees;
362    Decl *D = N->getDecl();
363    assert(D);
364    HandleCode(D, ANALYSIS_PATH,
365               (Mgr->InliningMode == All ? 0 : &VisitedCallees));
366
367    // Add the visited callees to the global visited set.
368    for (SetOfConstDecls::const_iterator I = VisitedCallees.begin(),
369                                         E = VisitedCallees.end(); I != E; ++I){
370      CallGraphNode *VN = CG.getNode(*I);
371      if (VN)
372        Visited.insert(VN);
373    }
374    Visited.insert(N);
375
376    // Push the children into the queue.
377    for (CallGraphNode::const_iterator CI = N->begin(),
378                                       CE = N->end(); CI != CE; ++CI) {
379      BFSQueue.push(*CI);
380    }
381  }
382}
383
384void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
385  // Don't run the actions if an error has occurred with parsing the file.
386  DiagnosticsEngine &Diags = PP.getDiagnostics();
387  if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
388    return;
389
390  {
391    if (TUTotalTimer) TUTotalTimer->startTimer();
392
393    // Introduce a scope to destroy BR before Mgr.
394    BugReporter BR(*Mgr);
395    TranslationUnitDecl *TU = C.getTranslationUnitDecl();
396    checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
397
398    // Run the AST-only checks using the order in which functions are defined.
399    // If inlining is not turned on, use the simplest function order for path
400    // sensitive analyzes as well.
401    RecVisitorMode = (Mgr->shouldInlineCall() ? ANALYSIS_SYNTAX : ANALYSIS_ALL);
402    RecVisitorBR = &BR;
403
404    // Process all the top level declarations.
405    for (SetOfDecls::iterator I = LocalTUDecls.begin(),
406                              E = LocalTUDecls.end(); I != E; ++I)
407      TraverseDecl(*I);
408
409    if (Mgr->shouldInlineCall())
410      HandleDeclsGallGraph();
411
412    // After all decls handled, run checkers on the entire TranslationUnit.
413    checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
414
415    RecVisitorBR = 0;
416  }
417
418  // Explicitly destroy the PathDiagnosticConsumer.  This will flush its output.
419  // FIXME: This should be replaced with something that doesn't rely on
420  // side-effects in PathDiagnosticConsumer's destructor. This is required when
421  // used with option -disable-free.
422  Mgr.reset(NULL);
423
424  if (TUTotalTimer) TUTotalTimer->stopTimer();
425
426  // Count how many basic blocks we have not covered.
427  NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks();
428  if (NumBlocksInAnalyzedFunctions > 0)
429    PercentReachableBlocks =
430      (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
431        NumBlocksInAnalyzedFunctions;
432
433}
434
435static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) {
436  if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
437    WL.push_back(BD);
438
439  for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end();
440       I!=E; ++I)
441    if (DeclContext *DC = dyn_cast<DeclContext>(*I))
442      FindBlocks(DC, WL);
443}
444
445static std::string getFunctionName(const Decl *D) {
446  if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) {
447    return ID->getSelector().getAsString();
448  }
449  if (const FunctionDecl *ND = dyn_cast<FunctionDecl>(D)) {
450    IdentifierInfo *II = ND->getIdentifier();
451    if (II)
452      return II->getName();
453  }
454  return "";
455}
456
457bool AnalysisConsumer::skipFunction(Decl *D) {
458  if (!Opts.AnalyzeSpecificFunction.empty() &&
459      getFunctionName(D) != Opts.AnalyzeSpecificFunction)
460    return true;
461
462  // Don't run the actions on declarations in header files unless
463  // otherwise specified.
464  SourceManager &SM = Ctx->getSourceManager();
465  SourceLocation SL = SM.getExpansionLoc(D->getLocation());
466  if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL))
467    return true;
468
469  return false;
470}
471
472void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
473                                  SetOfConstDecls *VisitedCallees) {
474  if (skipFunction(D))
475    return;
476
477  DisplayFunction(D, Mode);
478
479  // Clear the AnalysisManager of old AnalysisDeclContexts.
480  Mgr->ClearContexts();
481
482  // Dispatch on the actions.
483  SmallVector<Decl*, 10> WL;
484  WL.push_back(D);
485
486  if (D->hasBody() && Opts.AnalyzeNestedBlocks)
487    FindBlocks(cast<DeclContext>(D), WL);
488
489  BugReporter BR(*Mgr);
490  for (SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
491       WI != WE; ++WI)
492    if ((*WI)->hasBody()) {
493      if (Mode != ANALYSIS_PATH)
494        checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR);
495      if (Mode != ANALYSIS_SYNTAX && checkerMgr->hasPathSensitiveCheckers()) {
496        RunPathSensitiveChecks(*WI, VisitedCallees);
497        NumFunctionsAnalyzed++;
498      }
499    }
500}
501
502//===----------------------------------------------------------------------===//
503// Path-sensitive checking.
504//===----------------------------------------------------------------------===//
505
506void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
507                                        SetOfConstDecls *VisitedCallees) {
508  // Construct the analysis engine.  First check if the CFG is valid.
509  // FIXME: Inter-procedural analysis will need to handle invalid CFGs.
510  if (!Mgr->getCFG(D))
511    return;
512
513  ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries);
514
515  // Set the graph auditor.
516  OwningPtr<ExplodedNode::Auditor> Auditor;
517  if (Mgr->shouldVisualizeUbigraph()) {
518    Auditor.reset(CreateUbiViz());
519    ExplodedNode::SetAuditor(Auditor.get());
520  }
521
522  // Execute the worklist algorithm.
523  Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D, 0),
524                      Mgr->getMaxNodes());
525
526  // Release the auditor (if any) so that it doesn't monitor the graph
527  // created BugReporter.
528  ExplodedNode::SetAuditor(0);
529
530  // Visualize the exploded graph.
531  if (Mgr->shouldVisualizeGraphviz())
532    Eng.ViewGraph(Mgr->shouldTrimGraph());
533
534  // Display warnings.
535  Eng.getBugReporter().FlushReports();
536}
537
538void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
539                                              SetOfConstDecls *Visited) {
540
541  switch (Mgr->getLangOpts().getGC()) {
542  case LangOptions::NonGC:
543    ActionExprEngine(D, false, Visited);
544    break;
545
546  case LangOptions::GCOnly:
547    ActionExprEngine(D, true, Visited);
548    break;
549
550  case LangOptions::HybridGC:
551    ActionExprEngine(D, false, Visited);
552    ActionExprEngine(D, true, Visited);
553    break;
554  }
555}
556
557//===----------------------------------------------------------------------===//
558// AnalysisConsumer creation.
559//===----------------------------------------------------------------------===//
560
561ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp,
562                                          const std::string& outDir,
563                                          const AnalyzerOptions& opts,
564                                          ArrayRef<std::string> plugins) {
565  // Disable the effects of '-Werror' when using the AnalysisConsumer.
566  pp.getDiagnostics().setWarningsAsErrors(false);
567
568  return new AnalysisConsumer(pp, outDir, opts, plugins);
569}
570
571//===----------------------------------------------------------------------===//
572// Ubigraph Visualization.  FIXME: Move to separate file.
573//===----------------------------------------------------------------------===//
574
575namespace {
576
577class UbigraphViz : public ExplodedNode::Auditor {
578  OwningPtr<raw_ostream> Out;
579  llvm::sys::Path Dir, Filename;
580  unsigned Cntr;
581
582  typedef llvm::DenseMap<void*,unsigned> VMap;
583  VMap M;
584
585public:
586  UbigraphViz(raw_ostream *out, llvm::sys::Path& dir,
587              llvm::sys::Path& filename);
588
589  ~UbigraphViz();
590
591  virtual void AddEdge(ExplodedNode *Src, ExplodedNode *Dst);
592};
593
594} // end anonymous namespace
595
596static ExplodedNode::Auditor* CreateUbiViz() {
597  std::string ErrMsg;
598
599  llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg);
600  if (!ErrMsg.empty())
601    return 0;
602
603  llvm::sys::Path Filename = Dir;
604  Filename.appendComponent("llvm_ubi");
605  Filename.makeUnique(true,&ErrMsg);
606
607  if (!ErrMsg.empty())
608    return 0;
609
610  llvm::errs() << "Writing '" << Filename.str() << "'.\n";
611
612  OwningPtr<llvm::raw_fd_ostream> Stream;
613  Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg));
614
615  if (!ErrMsg.empty())
616    return 0;
617
618  return new UbigraphViz(Stream.take(), Dir, Filename);
619}
620
621void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) {
622
623  assert (Src != Dst && "Self-edges are not allowed.");
624
625  // Lookup the Src.  If it is a new node, it's a root.
626  VMap::iterator SrcI= M.find(Src);
627  unsigned SrcID;
628
629  if (SrcI == M.end()) {
630    M[Src] = SrcID = Cntr++;
631    *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n";
632  }
633  else
634    SrcID = SrcI->second;
635
636  // Lookup the Dst.
637  VMap::iterator DstI= M.find(Dst);
638  unsigned DstID;
639
640  if (DstI == M.end()) {
641    M[Dst] = DstID = Cntr++;
642    *Out << "('vertex', " << DstID << ")\n";
643  }
644  else {
645    // We have hit DstID before.  Change its style to reflect a cache hit.
646    DstID = DstI->second;
647    *Out << "('change_vertex_style', " << DstID << ", 1)\n";
648  }
649
650  // Add the edge.
651  *Out << "('edge', " << SrcID << ", " << DstID
652       << ", ('arrow','true'), ('oriented', 'true'))\n";
653}
654
655UbigraphViz::UbigraphViz(raw_ostream *out, llvm::sys::Path& dir,
656                         llvm::sys::Path& filename)
657  : Out(out), Dir(dir), Filename(filename), Cntr(0) {
658
659  *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n";
660  *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66'),"
661          " ('size', '1.5'))\n";
662}
663
664UbigraphViz::~UbigraphViz() {
665  Out.reset(0);
666  llvm::errs() << "Running 'ubiviz' program... ";
667  std::string ErrMsg;
668  llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz");
669  std::vector<const char*> args;
670  args.push_back(Ubiviz.c_str());
671  args.push_back(Filename.c_str());
672  args.push_back(0);
673
674  if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) {
675    llvm::errs() << "Error viewing graph: " << ErrMsg << "\n";
676  }
677
678  // Delete the directory.
679  Dir.eraseFromDisk(true);
680}
681