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