AnalysisConsumer.cpp revision c8848f34bbde083b8d89f445eb605eaabf30d6a8
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  bool shouldWalkTypesOfTypeLocs() const { return false; }
250
251  /// Handle callbacks for arbitrary Decls.
252  bool VisitDecl(Decl *D) {
253    checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR);
254    return true;
255  }
256
257  bool VisitFunctionDecl(FunctionDecl *FD) {
258    IdentifierInfo *II = FD->getIdentifier();
259    if (II && II->getName().startswith("__inline"))
260      return true;
261
262    // We skip function template definitions, as their semantics is
263    // only determined when they are instantiated.
264    if (FD->isThisDeclarationADefinition() &&
265        !FD->isDependentContext()) {
266      HandleCode(FD, RecVisitorMode);
267    }
268    return true;
269  }
270
271  bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
272    checkerMgr->runCheckersOnASTDecl(MD, *Mgr, *RecVisitorBR);
273    if (MD->isThisDeclarationADefinition())
274      HandleCode(MD, RecVisitorMode);
275    return true;
276  }
277
278private:
279  void storeTopLevelDecls(DeclGroupRef DG);
280
281  /// \brief Check if we should skip (not analyze) the given function.
282  bool skipFunction(Decl *D);
283
284};
285} // end anonymous namespace
286
287
288//===----------------------------------------------------------------------===//
289// AnalysisConsumer implementation.
290//===----------------------------------------------------------------------===//
291llvm::Timer* AnalysisConsumer::TUTotalTimer = 0;
292
293bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) {
294  storeTopLevelDecls(DG);
295  return true;
296}
297
298void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
299  storeTopLevelDecls(DG);
300}
301
302void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
303  for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) {
304
305    // Skip ObjCMethodDecl, wait for the objc container to avoid
306    // analyzing twice.
307    if (isa<ObjCMethodDecl>(*I))
308      continue;
309
310    LocalTUDecls.push_back(*I);
311  }
312}
313
314void AnalysisConsumer::HandleDeclsGallGraph() {
315  // Otherwise, use the Callgraph to derive the order.
316  // Build the Call Graph.
317  CallGraph CG;
318  // Add all the top level declarations to the graph.
319  for (SetOfDecls::iterator I = LocalTUDecls.begin(),
320                            E = LocalTUDecls.end(); I != E; ++I)
321    CG.addToCallGraph(*I);
322
323  // Find the top level nodes - children of root + the unreachable (parentless)
324  // nodes.
325  llvm::SmallVector<CallGraphNode*, 24> TopLevelFunctions;
326  for (CallGraph::nodes_iterator TI = CG.parentless_begin(),
327                                 TE = CG.parentless_end(); TI != TE; ++TI) {
328    TopLevelFunctions.push_back(*TI);
329    NumFunctionTopLevel++;
330  }
331  CallGraphNode *Entry = CG.getRoot();
332  for (CallGraphNode::iterator I = Entry->begin(),
333                               E = Entry->end(); I != E; ++I) {
334    TopLevelFunctions.push_back(*I);
335    NumFunctionTopLevel++;
336  }
337
338  // Make sure the nodes are sorted in order reverse of their definition in the
339  // translation unit. This step is very important for performance. It ensures
340  // that we analyze the root functions before the externally available
341  // subroutines.
342  std::deque<CallGraphNode*> BFSQueue;
343  for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator
344         TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend();
345         TI != TE; ++TI)
346    BFSQueue.push_front(*TI);
347
348  // BFS over all of the functions, while skipping the ones inlined into
349  // the previously processed functions. Use external Visited set, which is
350  // also modified when we inline a function.
351  SmallPtrSet<CallGraphNode*,24> Visited;
352  while(!BFSQueue.empty()) {
353    CallGraphNode *N = BFSQueue.front();
354    BFSQueue.pop_front();
355
356    // Skip the functions which have been processed already or previously
357    // inlined.
358    if (Visited.count(N))
359      continue;
360
361    // Analyze the function.
362    SetOfConstDecls VisitedCallees;
363    Decl *D = N->getDecl();
364    assert(D);
365    HandleCode(D, ANALYSIS_PATH,
366               (Mgr->InliningMode == All ? 0 : &VisitedCallees));
367
368    // Add the visited callees to the global visited set.
369    for (SetOfConstDecls::iterator I = VisitedCallees.begin(),
370                                   E = VisitedCallees.end(); I != E; ++I) {
371      CallGraphNode *VN = CG.getNode(*I);
372      if (VN)
373        Visited.insert(VN);
374    }
375    Visited.insert(N);
376
377    // Push the children into the queue.
378    for (CallGraphNode::const_iterator CI = N->begin(),
379                                       CE = N->end(); CI != CE; ++CI) {
380      BFSQueue.push_front(*CI);
381    }
382  }
383}
384
385void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
386  // Don't run the actions if an error has occurred with parsing the file.
387  DiagnosticsEngine &Diags = PP.getDiagnostics();
388  if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
389    return;
390
391  {
392    if (TUTotalTimer) TUTotalTimer->startTimer();
393
394    // Introduce a scope to destroy BR before Mgr.
395    BugReporter BR(*Mgr);
396    TranslationUnitDecl *TU = C.getTranslationUnitDecl();
397    checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
398
399    // Run the AST-only checks using the order in which functions are defined.
400    // If inlining is not turned on, use the simplest function order for path
401    // sensitive analyzes as well.
402    RecVisitorMode = (Mgr->shouldInlineCall() ? ANALYSIS_SYNTAX : ANALYSIS_ALL);
403    RecVisitorBR = &BR;
404
405    // Process all the top level declarations.
406    //
407    // Note: TraverseDecl may modify LocalTUDecls, but only by appending more
408    // entries.  Thus we don't use an iterator, but rely on LocalTUDecls
409    // random access.  By doing so, we automatically compensate for iterators
410    // possibly being invalidated, although this is a bit slower.
411    const unsigned n = LocalTUDecls.size();
412    for (unsigned i = 0 ; i < n ; ++i) {
413      TraverseDecl(LocalTUDecls[i]);
414    }
415
416    if (Mgr->shouldInlineCall())
417      HandleDeclsGallGraph();
418
419    // After all decls handled, run checkers on the entire TranslationUnit.
420    checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
421
422    RecVisitorBR = 0;
423  }
424
425  // Explicitly destroy the PathDiagnosticConsumer.  This will flush its output.
426  // FIXME: This should be replaced with something that doesn't rely on
427  // side-effects in PathDiagnosticConsumer's destructor. This is required when
428  // used with option -disable-free.
429  Mgr.reset(NULL);
430
431  if (TUTotalTimer) TUTotalTimer->stopTimer();
432
433  // Count how many basic blocks we have not covered.
434  NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks();
435  if (NumBlocksInAnalyzedFunctions > 0)
436    PercentReachableBlocks =
437      (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
438        NumBlocksInAnalyzedFunctions;
439
440}
441
442static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) {
443  if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
444    WL.push_back(BD);
445
446  for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end();
447       I!=E; ++I)
448    if (DeclContext *DC = dyn_cast<DeclContext>(*I))
449      FindBlocks(DC, WL);
450}
451
452static std::string getFunctionName(const Decl *D) {
453  if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) {
454    return ID->getSelector().getAsString();
455  }
456  if (const FunctionDecl *ND = dyn_cast<FunctionDecl>(D)) {
457    IdentifierInfo *II = ND->getIdentifier();
458    if (II)
459      return II->getName();
460  }
461  return "";
462}
463
464bool AnalysisConsumer::skipFunction(Decl *D) {
465  if (!Opts.AnalyzeSpecificFunction.empty() &&
466      getFunctionName(D) != Opts.AnalyzeSpecificFunction)
467    return true;
468
469  // Don't run the actions on declarations in header files unless
470  // otherwise specified.
471  SourceManager &SM = Ctx->getSourceManager();
472  SourceLocation SL = SM.getExpansionLoc(D->getLocation());
473  if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL))
474    return true;
475
476  return false;
477}
478
479void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
480                                  SetOfConstDecls *VisitedCallees) {
481  if (skipFunction(D))
482    return;
483
484  DisplayFunction(D, Mode);
485
486  // Clear the AnalysisManager of old AnalysisDeclContexts.
487  Mgr->ClearContexts();
488
489  // Dispatch on the actions.
490  SmallVector<Decl*, 10> WL;
491  WL.push_back(D);
492
493  if (D->hasBody() && Opts.AnalyzeNestedBlocks)
494    FindBlocks(cast<DeclContext>(D), WL);
495
496  BugReporter BR(*Mgr);
497  for (SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
498       WI != WE; ++WI)
499    if ((*WI)->hasBody()) {
500      if (Mode != ANALYSIS_PATH)
501        checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR);
502      if (Mode != ANALYSIS_SYNTAX && checkerMgr->hasPathSensitiveCheckers()) {
503        RunPathSensitiveChecks(*WI, VisitedCallees);
504        NumFunctionsAnalyzed++;
505      }
506    }
507}
508
509//===----------------------------------------------------------------------===//
510// Path-sensitive checking.
511//===----------------------------------------------------------------------===//
512
513void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
514                                        SetOfConstDecls *VisitedCallees) {
515  // Construct the analysis engine.  First check if the CFG is valid.
516  // FIXME: Inter-procedural analysis will need to handle invalid CFGs.
517  if (!Mgr->getCFG(D))
518    return;
519
520  ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries);
521
522  // Set the graph auditor.
523  OwningPtr<ExplodedNode::Auditor> Auditor;
524  if (Mgr->shouldVisualizeUbigraph()) {
525    Auditor.reset(CreateUbiViz());
526    ExplodedNode::SetAuditor(Auditor.get());
527  }
528
529  // Execute the worklist algorithm.
530  Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D),
531                      Mgr->getMaxNodes());
532
533  // Release the auditor (if any) so that it doesn't monitor the graph
534  // created BugReporter.
535  ExplodedNode::SetAuditor(0);
536
537  // Visualize the exploded graph.
538  if (Mgr->shouldVisualizeGraphviz())
539    Eng.ViewGraph(Mgr->shouldTrimGraph());
540
541  // Display warnings.
542  Eng.getBugReporter().FlushReports();
543}
544
545void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
546                                              SetOfConstDecls *Visited) {
547
548  switch (Mgr->getLangOpts().getGC()) {
549  case LangOptions::NonGC:
550    ActionExprEngine(D, false, Visited);
551    break;
552
553  case LangOptions::GCOnly:
554    ActionExprEngine(D, true, Visited);
555    break;
556
557  case LangOptions::HybridGC:
558    ActionExprEngine(D, false, Visited);
559    ActionExprEngine(D, true, Visited);
560    break;
561  }
562}
563
564//===----------------------------------------------------------------------===//
565// AnalysisConsumer creation.
566//===----------------------------------------------------------------------===//
567
568ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp,
569                                          const std::string& outDir,
570                                          const AnalyzerOptions& opts,
571                                          ArrayRef<std::string> plugins) {
572  // Disable the effects of '-Werror' when using the AnalysisConsumer.
573  pp.getDiagnostics().setWarningsAsErrors(false);
574
575  return new AnalysisConsumer(pp, outDir, opts, plugins);
576}
577
578//===----------------------------------------------------------------------===//
579// Ubigraph Visualization.  FIXME: Move to separate file.
580//===----------------------------------------------------------------------===//
581
582namespace {
583
584class UbigraphViz : public ExplodedNode::Auditor {
585  OwningPtr<raw_ostream> Out;
586  llvm::sys::Path Dir, Filename;
587  unsigned Cntr;
588
589  typedef llvm::DenseMap<void*,unsigned> VMap;
590  VMap M;
591
592public:
593  UbigraphViz(raw_ostream *out, llvm::sys::Path& dir,
594              llvm::sys::Path& filename);
595
596  ~UbigraphViz();
597
598  virtual void AddEdge(ExplodedNode *Src, ExplodedNode *Dst);
599};
600
601} // end anonymous namespace
602
603static ExplodedNode::Auditor* CreateUbiViz() {
604  std::string ErrMsg;
605
606  llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg);
607  if (!ErrMsg.empty())
608    return 0;
609
610  llvm::sys::Path Filename = Dir;
611  Filename.appendComponent("llvm_ubi");
612  Filename.makeUnique(true,&ErrMsg);
613
614  if (!ErrMsg.empty())
615    return 0;
616
617  llvm::errs() << "Writing '" << Filename.str() << "'.\n";
618
619  OwningPtr<llvm::raw_fd_ostream> Stream;
620  Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg));
621
622  if (!ErrMsg.empty())
623    return 0;
624
625  return new UbigraphViz(Stream.take(), Dir, Filename);
626}
627
628void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) {
629
630  assert (Src != Dst && "Self-edges are not allowed.");
631
632  // Lookup the Src.  If it is a new node, it's a root.
633  VMap::iterator SrcI= M.find(Src);
634  unsigned SrcID;
635
636  if (SrcI == M.end()) {
637    M[Src] = SrcID = Cntr++;
638    *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n";
639  }
640  else
641    SrcID = SrcI->second;
642
643  // Lookup the Dst.
644  VMap::iterator DstI= M.find(Dst);
645  unsigned DstID;
646
647  if (DstI == M.end()) {
648    M[Dst] = DstID = Cntr++;
649    *Out << "('vertex', " << DstID << ")\n";
650  }
651  else {
652    // We have hit DstID before.  Change its style to reflect a cache hit.
653    DstID = DstI->second;
654    *Out << "('change_vertex_style', " << DstID << ", 1)\n";
655  }
656
657  // Add the edge.
658  *Out << "('edge', " << SrcID << ", " << DstID
659       << ", ('arrow','true'), ('oriented', 'true'))\n";
660}
661
662UbigraphViz::UbigraphViz(raw_ostream *out, llvm::sys::Path& dir,
663                         llvm::sys::Path& filename)
664  : Out(out), Dir(dir), Filename(filename), Cntr(0) {
665
666  *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n";
667  *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66'),"
668          " ('size', '1.5'))\n";
669}
670
671UbigraphViz::~UbigraphViz() {
672  Out.reset(0);
673  llvm::errs() << "Running 'ubiviz' program... ";
674  std::string ErrMsg;
675  llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz");
676  std::vector<const char*> args;
677  args.push_back(Ubiviz.c_str());
678  args.push_back(Filename.c_str());
679  args.push_back(0);
680
681  if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) {
682    llvm::errs() << "Error viewing graph: " << ErrMsg << "\n";
683  }
684
685  // Delete the directory.
686  Dir.eraseFromDisk(true);
687}
688