AnalysisConsumer.cpp revision 371b477836f289f2e9caaab58530f187b51bc86d
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/Analysis/Analyses/LiveVariables.h"
26#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
27#include "clang/StaticAnalyzer/Core/CheckerManager.h"
28#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
29#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
30#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
31#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
32#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
33#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
34
35#include "clang/Basic/FileManager.h"
36#include "clang/Basic/SourceManager.h"
37#include "clang/Frontend/AnalyzerOptions.h"
38#include "clang/Lex/Preprocessor.h"
39#include "llvm/Support/raw_ostream.h"
40#include "llvm/Support/Path.h"
41#include "llvm/Support/Program.h"
42#include "llvm/Support/Timer.h"
43#include "llvm/ADT/DepthFirstIterator.h"
44#include "llvm/ADT/OwningPtr.h"
45#include "llvm/ADT/SmallPtrSet.h"
46#include "llvm/ADT/Statistic.h"
47
48#include <queue>
49
50using namespace clang;
51using namespace ento;
52using llvm::SmallPtrSet;
53
54static ExplodedNode::Auditor* CreateUbiViz();
55
56STATISTIC(NumFunctionTopLevel, "The # of functions at top level.");
57STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level).");
58STATISTIC(NumBlocksInAnalyzedFunctions,
59                     "The # of basic blocks in the analyzed functions.");
60STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");
61
62//===----------------------------------------------------------------------===//
63// Special PathDiagnosticConsumers.
64//===----------------------------------------------------------------------===//
65
66static PathDiagnosticConsumer*
67createPlistHTMLDiagnosticConsumer(const std::string& prefix,
68                                const Preprocessor &PP) {
69  PathDiagnosticConsumer *PD =
70    createHTMLDiagnosticConsumer(llvm::sys::path::parent_path(prefix), PP);
71  return createPlistDiagnosticConsumer(prefix, PP, PD);
72}
73
74//===----------------------------------------------------------------------===//
75// AnalysisConsumer declaration.
76//===----------------------------------------------------------------------===//
77
78namespace {
79
80class AnalysisConsumer : public ASTConsumer,
81                         public RecursiveASTVisitor<AnalysisConsumer> {
82  enum AnalysisMode {
83    ANALYSIS_SYNTAX,
84    ANALYSIS_PATH,
85    ANALYSIS_ALL
86  };
87
88  /// Mode of the analyzes while recursively visiting Decls.
89  AnalysisMode RecVisitorMode;
90  /// Bug Reporter to use while recursively visiting Decls.
91  BugReporter *RecVisitorBR;
92
93public:
94  ASTContext *Ctx;
95  const Preprocessor &PP;
96  const std::string OutDir;
97  AnalyzerOptions Opts;
98  ArrayRef<std::string> Plugins;
99
100  /// \brief Stores the declarations from the local translation unit.
101  /// Note, we pre-compute the local declarations at parse time as an
102  /// optimization to make sure we do not deserialize everything from disk.
103  /// The local declaration to all declarations ratio might be very small when
104  /// working with a PCH file.
105  SetOfDecls LocalTUDecls;
106
107  // PD is owned by AnalysisManager.
108  PathDiagnosticConsumer *PD;
109
110  StoreManagerCreator CreateStoreMgr;
111  ConstraintManagerCreator CreateConstraintMgr;
112
113  OwningPtr<CheckerManager> checkerMgr;
114  OwningPtr<AnalysisManager> Mgr;
115
116  /// Time the analyzes time of each translation unit.
117  static llvm::Timer* TUTotalTimer;
118
119  /// The information about analyzed functions shared throughout the
120  /// translation unit.
121  FunctionSummariesTy FunctionSummaries;
122
123  AnalysisConsumer(const Preprocessor& pp,
124                   const std::string& outdir,
125                   const AnalyzerOptions& opts,
126                   ArrayRef<std::string> plugins)
127    : RecVisitorMode(ANALYSIS_ALL), RecVisitorBR(0),
128      Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) {
129    DigestAnalyzerOptions();
130    if (Opts.PrintStats) {
131      llvm::EnableStatistics();
132      TUTotalTimer = new llvm::Timer("Analyzer Total Time");
133    }
134  }
135
136  ~AnalysisConsumer() {
137    if (Opts.PrintStats)
138      delete TUTotalTimer;
139  }
140
141  void DigestAnalyzerOptions() {
142    // Create the PathDiagnosticConsumer.
143    if (!OutDir.empty()) {
144      switch (Opts.AnalysisDiagOpt) {
145      default:
146#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \
147        case PD_##NAME: PD = CREATEFN(OutDir, PP); break;
148#include "clang/Frontend/Analyses.def"
149      }
150    } else if (Opts.AnalysisDiagOpt == PD_TEXT) {
151      // Create the text client even without a specified output file since
152      // it just uses diagnostic notes.
153      PD = createTextPathDiagnosticConsumer("", PP);
154    }
155
156    // Create the analyzer component creators.
157    switch (Opts.AnalysisStoreOpt) {
158    default:
159      llvm_unreachable("Unknown store manager.");
160#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN)           \
161      case NAME##Model: CreateStoreMgr = CREATEFN; break;
162#include "clang/Frontend/Analyses.def"
163    }
164
165    switch (Opts.AnalysisConstraintsOpt) {
166    default:
167      llvm_unreachable("Unknown store manager.");
168#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN)     \
169      case NAME##Model: CreateConstraintMgr = CREATEFN; break;
170#include "clang/Frontend/Analyses.def"
171    }
172  }
173
174  void DisplayFunction(const Decl *D, AnalysisMode Mode) {
175    if (!Opts.AnalyzerDisplayProgress)
176      return;
177
178    SourceManager &SM = Mgr->getASTContext().getSourceManager();
179    PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
180    if (Loc.isValid()) {
181      llvm::errs() << "ANALYZE";
182      switch (Mode) {
183        case ANALYSIS_SYNTAX: llvm::errs() << "(Syntax)"; break;
184        case ANALYSIS_PATH: llvm::errs() << "(Path Sensitive)"; break;
185        case ANALYSIS_ALL: break;
186      };
187      llvm::errs() << ": " << Loc.getFilename();
188      if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
189        const NamedDecl *ND = cast<NamedDecl>(D);
190        llvm::errs() << ' ' << *ND << '\n';
191      }
192      else if (isa<BlockDecl>(D)) {
193        llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:"
194                     << Loc.getColumn() << '\n';
195      }
196      else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
197        Selector S = MD->getSelector();
198        llvm::errs() << ' ' << S.getAsString();
199      }
200    }
201  }
202
203  virtual void Initialize(ASTContext &Context) {
204    Ctx = &Context;
205    checkerMgr.reset(createCheckerManager(Opts, PP.getLangOpts(), Plugins,
206                                          PP.getDiagnostics()));
207    Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(),
208                                  PP.getLangOpts(), PD,
209                                  CreateStoreMgr, CreateConstraintMgr,
210                                  checkerMgr.get(),
211                                  Opts.MaxNodes, Opts.MaxLoop,
212                                  Opts.VisualizeEGDot, Opts.VisualizeEGUbi,
213                                  Opts.AnalysisPurgeOpt, Opts.EagerlyAssume,
214                                  Opts.TrimGraph,
215                                  Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
216                                  Opts.CFGAddInitializers,
217                                  Opts.EagerlyTrimEGraph,
218                                  Opts.IPAMode,
219                                  Opts.InlineMaxStackDepth,
220                                  Opts.InlineMaxFunctionSize,
221                                  Opts.InliningMode,
222                                  Opts.NoRetryExhausted));
223  }
224
225  /// \brief Store the top level decls in the set to be processed later on.
226  /// (Doing this pre-processing avoids deserialization of data from PCH.)
227  virtual bool HandleTopLevelDecl(DeclGroupRef D);
228  virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D);
229
230  virtual void HandleTranslationUnit(ASTContext &C);
231
232  /// \brief Build the call graph for all the top level decls of this TU and
233  /// use it to define the order in which the functions should be visited.
234  void HandleDeclsGallGraph(const unsigned LocalTUDeclsSize);
235
236  /// \brief Run analyzes(syntax or path sensitive) on the given function.
237  /// \param Mode - determines if we are requesting syntax only or path
238  /// sensitive only analysis.
239  /// \param VisitedCallees - The output parameter, which is populated with the
240  /// set of functions which should be considered analyzed after analyzing the
241  /// given root function.
242  void HandleCode(Decl *D, AnalysisMode Mode,
243                  SetOfConstDecls *VisitedCallees = 0);
244
245  void RunPathSensitiveChecks(Decl *D, SetOfConstDecls *VisitedCallees);
246  void ActionExprEngine(Decl *D, bool ObjCGCEnabled,
247                        SetOfConstDecls *VisitedCallees);
248
249  /// Visitors for the RecursiveASTVisitor.
250  bool shouldWalkTypesOfTypeLocs() const { return false; }
251
252  /// Handle callbacks for arbitrary Decls.
253  bool VisitDecl(Decl *D) {
254    checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR);
255    return true;
256  }
257
258  bool VisitFunctionDecl(FunctionDecl *FD) {
259    IdentifierInfo *II = FD->getIdentifier();
260    if (II && II->getName().startswith("__inline"))
261      return true;
262
263    // We skip function template definitions, as their semantics is
264    // only determined when they are instantiated.
265    if (FD->isThisDeclarationADefinition() &&
266        !FD->isDependentContext()) {
267      HandleCode(FD, RecVisitorMode);
268    }
269    return true;
270  }
271
272  bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
273    checkerMgr->runCheckersOnASTDecl(MD, *Mgr, *RecVisitorBR);
274    if (MD->isThisDeclarationADefinition())
275      HandleCode(MD, RecVisitorMode);
276    return true;
277  }
278
279private:
280  void storeTopLevelDecls(DeclGroupRef DG);
281
282  /// \brief Check if we should skip (not analyze) the given function.
283  bool skipFunction(Decl *D);
284
285};
286} // end anonymous namespace
287
288
289//===----------------------------------------------------------------------===//
290// AnalysisConsumer implementation.
291//===----------------------------------------------------------------------===//
292llvm::Timer* AnalysisConsumer::TUTotalTimer = 0;
293
294bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) {
295  storeTopLevelDecls(DG);
296  return true;
297}
298
299void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
300  storeTopLevelDecls(DG);
301}
302
303void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
304  for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) {
305
306    // Skip ObjCMethodDecl, wait for the objc container to avoid
307    // analyzing twice.
308    if (isa<ObjCMethodDecl>(*I))
309      continue;
310
311    LocalTUDecls.push_back(*I);
312  }
313}
314
315void AnalysisConsumer::HandleDeclsGallGraph(const unsigned LocalTUDeclsSize) {
316  // Otherwise, use the Callgraph to derive the order.
317  // Build the Call Graph.
318  CallGraph CG;
319
320  // Add all the top level declarations to the graph.
321  // Note: CallGraph can trigger deserialization of more items from a pch
322  // (though HandleInterestingDecl); triggering additions to LocalTUDecls.
323  // We rely on random access to add the initially processed Decls to CG.
324  for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
325    CG.addToCallGraph(LocalTUDecls[i]);
326  }
327
328  // Find the top level nodes - children of root + the unreachable (parentless)
329  // nodes.
330  llvm::SmallVector<CallGraphNode*, 24> TopLevelFunctions;
331  for (CallGraph::nodes_iterator TI = CG.parentless_begin(),
332                                 TE = CG.parentless_end(); TI != TE; ++TI) {
333    TopLevelFunctions.push_back(*TI);
334    NumFunctionTopLevel++;
335  }
336  CallGraphNode *Entry = CG.getRoot();
337  for (CallGraphNode::iterator I = Entry->begin(),
338                               E = Entry->end(); I != E; ++I) {
339    TopLevelFunctions.push_back(*I);
340    NumFunctionTopLevel++;
341  }
342
343  // Make sure the nodes are sorted in order reverse of their definition in the
344  // translation unit. This step is very important for performance. It ensures
345  // that we analyze the root functions before the externally available
346  // subroutines.
347  std::deque<CallGraphNode*> BFSQueue;
348  for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator
349         TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend();
350         TI != TE; ++TI)
351    BFSQueue.push_back(*TI);
352
353  // BFS over all of the functions, while skipping the ones inlined into
354  // the previously processed functions. Use external Visited set, which is
355  // also modified when we inline a function.
356  SmallPtrSet<CallGraphNode*,24> Visited;
357  while(!BFSQueue.empty()) {
358    CallGraphNode *N = BFSQueue.front();
359    BFSQueue.pop_front();
360
361    // Push the children into the queue.
362    for (CallGraphNode::const_iterator CI = N->begin(),
363         CE = N->end(); CI != CE; ++CI) {
364      if (!Visited.count(*CI))
365        BFSQueue.push_back(*CI);
366    }
367
368    // Skip the functions which have been processed already or previously
369    // inlined.
370    if (Visited.count(N))
371      continue;
372
373    // Analyze the function.
374    SetOfConstDecls VisitedCallees;
375    Decl *D = N->getDecl();
376    assert(D);
377    HandleCode(D, ANALYSIS_PATH,
378               (Mgr->InliningMode == All ? 0 : &VisitedCallees));
379
380    // Add the visited callees to the global visited set.
381    for (SetOfConstDecls::iterator I = VisitedCallees.begin(),
382                                   E = VisitedCallees.end(); I != E; ++I) {
383      CallGraphNode *VN = CG.getNode(*I);
384      if (VN)
385        Visited.insert(VN);
386    }
387    Visited.insert(N);
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 LocalTUDeclsSize = LocalTUDecls.size();
418    for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
419      TraverseDecl(LocalTUDecls[i]);
420    }
421
422    if (Mgr->shouldInlineCall())
423      HandleDeclsGallGraph(LocalTUDeclsSize);
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