AnalysisConsumer.cpp revision cb0a5039c243f5b0c178e70f424adac334e5789b
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  SetOfDecls::Factory LocalTUDeclsFactory;
107
108  // PD is owned by AnalysisManager.
109  PathDiagnosticConsumer *PD;
110
111  StoreManagerCreator CreateStoreMgr;
112  ConstraintManagerCreator CreateConstraintMgr;
113
114  OwningPtr<CheckerManager> checkerMgr;
115  OwningPtr<AnalysisManager> Mgr;
116
117  /// Time the analyzes time of each translation unit.
118  static llvm::Timer* TUTotalTimer;
119
120  /// The information about analyzed functions shared throughout the
121  /// translation unit.
122  FunctionSummariesTy FunctionSummaries;
123
124  AnalysisConsumer(const Preprocessor& pp,
125                   const std::string& outdir,
126                   const AnalyzerOptions& opts,
127                   ArrayRef<std::string> plugins)
128    : RecVisitorMode(ANALYSIS_ALL), RecVisitorBR(0),
129      Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) {
130    DigestAnalyzerOptions();
131    if (Opts.PrintStats) {
132      llvm::EnableStatistics();
133      TUTotalTimer = new llvm::Timer("Analyzer Total Time");
134    }
135  }
136
137  ~AnalysisConsumer() {
138    if (Opts.PrintStats)
139      delete TUTotalTimer;
140  }
141
142  void DigestAnalyzerOptions() {
143    // Create the PathDiagnosticConsumer.
144    if (!OutDir.empty()) {
145      switch (Opts.AnalysisDiagOpt) {
146      default:
147#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \
148        case PD_##NAME: PD = CREATEFN(OutDir, PP); break;
149#include "clang/Frontend/Analyses.def"
150      }
151    } else if (Opts.AnalysisDiagOpt == PD_TEXT) {
152      // Create the text client even without a specified output file since
153      // it just uses diagnostic notes.
154      PD = createTextPathDiagnosticConsumer("", PP);
155    }
156
157    // Create the analyzer component creators.
158    switch (Opts.AnalysisStoreOpt) {
159    default:
160      llvm_unreachable("Unknown store manager.");
161#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN)           \
162      case NAME##Model: CreateStoreMgr = CREATEFN; break;
163#include "clang/Frontend/Analyses.def"
164    }
165
166    switch (Opts.AnalysisConstraintsOpt) {
167    default:
168      llvm_unreachable("Unknown store manager.");
169#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN)     \
170      case NAME##Model: CreateConstraintMgr = CREATEFN; break;
171#include "clang/Frontend/Analyses.def"
172    }
173  }
174
175  void DisplayFunction(const Decl *D, AnalysisMode Mode) {
176    if (!Opts.AnalyzerDisplayProgress)
177      return;
178
179    SourceManager &SM = Mgr->getASTContext().getSourceManager();
180    PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
181    if (Loc.isValid()) {
182      llvm::errs() << "ANALYZE";
183      switch (Mode) {
184        case ANALYSIS_SYNTAX: llvm::errs() << "(Syntax)"; break;
185        case ANALYSIS_PATH: llvm::errs() << "(Path Sensitive)"; break;
186        case ANALYSIS_ALL: break;
187      };
188      llvm::errs() << ": " << Loc.getFilename();
189      if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
190        const NamedDecl *ND = cast<NamedDecl>(D);
191        llvm::errs() << ' ' << *ND << '\n';
192      }
193      else if (isa<BlockDecl>(D)) {
194        llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:"
195                     << Loc.getColumn() << '\n';
196      }
197      else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
198        Selector S = MD->getSelector();
199        llvm::errs() << ' ' << S.getAsString();
200      }
201    }
202  }
203
204  virtual void Initialize(ASTContext &Context) {
205    Ctx = &Context;
206    checkerMgr.reset(createCheckerManager(Opts, PP.getLangOpts(), Plugins,
207                                          PP.getDiagnostics()));
208    Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(),
209                                  PP.getLangOpts(), PD,
210                                  CreateStoreMgr, CreateConstraintMgr,
211                                  checkerMgr.get(),
212                                  Opts.MaxNodes, Opts.MaxLoop,
213                                  Opts.VisualizeEGDot, Opts.VisualizeEGUbi,
214                                  Opts.AnalysisPurgeOpt, Opts.EagerlyAssume,
215                                  Opts.TrimGraph,
216                                  Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
217                                  Opts.CFGAddInitializers,
218                                  Opts.EagerlyTrimEGraph,
219                                  Opts.IPAMode,
220                                  Opts.InlineMaxStackDepth,
221                                  Opts.InlineMaxFunctionSize,
222                                  Opts.InliningMode,
223                                  Opts.NoRetryExhausted));
224  }
225
226  /// \brief Store the top level decls in the set to be processed later on.
227  /// (Doing this pre-processing avoids deserialization of data from PCH.)
228  virtual bool HandleTopLevelDecl(DeclGroupRef D);
229  virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D);
230
231  virtual void HandleTranslationUnit(ASTContext &C);
232
233  /// \brief Build the call graph for all the top level decls of this TU and
234  /// use it to define the order in which the functions should be visited.
235  void HandleDeclsGallGraph();
236
237  /// \brief Run analyzes(syntax or path sensitive) on the given function.
238  /// \param Mode - determines if we are requesting syntax only or path
239  /// sensitive only analysis.
240  /// \param VisitedCallees - The output parameter, which is populated with the
241  /// set of functions which should be considered analyzed after analyzing the
242  /// given root function.
243  void HandleCode(Decl *D, AnalysisMode Mode,
244                  SetOfConstDecls *VisitedCallees = 0);
245
246  void RunPathSensitiveChecks(Decl *D, SetOfConstDecls *VisitedCallees);
247  void ActionExprEngine(Decl *D, bool ObjCGCEnabled,
248                        SetOfConstDecls *VisitedCallees);
249
250  /// Visitors for the RecursiveASTVisitor.
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    // We use an ImmutableList to avoid issues with invalidating iterators
312    // to the list while we are traversing it.
313    LocalTUDecls = LocalTUDeclsFactory.add(*I, LocalTUDecls);
314  }
315}
316
317void AnalysisConsumer::HandleDeclsGallGraph() {
318  // Otherwise, use the Callgraph to derive the order.
319  // Build the Call Graph.
320  CallGraph CG;
321  // Add all the top level declarations to the graph.
322  //
323  // NOTE: We use an ImmutableList to avoid issues with invalidating iterators
324  // to the list while we are traversing it.
325  for (SetOfDecls::iterator I = LocalTUDecls.begin(),
326                            E = LocalTUDecls.end(); I != E; ++I)
327    CG.addToCallGraph(*I);
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: We use an ImmutableList to avoid issues with invalidating iterators
414    // to the list while we are traversing it.
415    //
416    for (SetOfDecls::iterator I = LocalTUDecls.begin(),
417         E = LocalTUDecls.end(); I != E; ++I) {
418      TraverseDecl(*I);
419    }
420
421    if (Mgr->shouldInlineCall())
422      HandleDeclsGallGraph();
423
424    // After all decls handled, run checkers on the entire TranslationUnit.
425    checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
426
427    RecVisitorBR = 0;
428  }
429
430  // Explicitly destroy the PathDiagnosticConsumer.  This will flush its output.
431  // FIXME: This should be replaced with something that doesn't rely on
432  // side-effects in PathDiagnosticConsumer's destructor. This is required when
433  // used with option -disable-free.
434  Mgr.reset(NULL);
435
436  if (TUTotalTimer) TUTotalTimer->stopTimer();
437
438  // Count how many basic blocks we have not covered.
439  NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks();
440  if (NumBlocksInAnalyzedFunctions > 0)
441    PercentReachableBlocks =
442      (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
443        NumBlocksInAnalyzedFunctions;
444
445}
446
447static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) {
448  if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
449    WL.push_back(BD);
450
451  for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end();
452       I!=E; ++I)
453    if (DeclContext *DC = dyn_cast<DeclContext>(*I))
454      FindBlocks(DC, WL);
455}
456
457static std::string getFunctionName(const Decl *D) {
458  if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) {
459    return ID->getSelector().getAsString();
460  }
461  if (const FunctionDecl *ND = dyn_cast<FunctionDecl>(D)) {
462    IdentifierInfo *II = ND->getIdentifier();
463    if (II)
464      return II->getName();
465  }
466  return "";
467}
468
469bool AnalysisConsumer::skipFunction(Decl *D) {
470  if (!Opts.AnalyzeSpecificFunction.empty() &&
471      getFunctionName(D) != Opts.AnalyzeSpecificFunction)
472    return true;
473
474  // Don't run the actions on declarations in header files unless
475  // otherwise specified.
476  SourceManager &SM = Ctx->getSourceManager();
477  SourceLocation SL = SM.getExpansionLoc(D->getLocation());
478  if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL))
479    return true;
480
481  return false;
482}
483
484void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
485                                  SetOfConstDecls *VisitedCallees) {
486  if (skipFunction(D))
487    return;
488
489  DisplayFunction(D, Mode);
490
491  // Clear the AnalysisManager of old AnalysisDeclContexts.
492  Mgr->ClearContexts();
493
494  // Dispatch on the actions.
495  SmallVector<Decl*, 10> WL;
496  WL.push_back(D);
497
498  if (D->hasBody() && Opts.AnalyzeNestedBlocks)
499    FindBlocks(cast<DeclContext>(D), WL);
500
501  BugReporter BR(*Mgr);
502  for (SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
503       WI != WE; ++WI)
504    if ((*WI)->hasBody()) {
505      if (Mode != ANALYSIS_PATH)
506        checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR);
507      if (Mode != ANALYSIS_SYNTAX && checkerMgr->hasPathSensitiveCheckers()) {
508        RunPathSensitiveChecks(*WI, VisitedCallees);
509        NumFunctionsAnalyzed++;
510      }
511    }
512}
513
514//===----------------------------------------------------------------------===//
515// Path-sensitive checking.
516//===----------------------------------------------------------------------===//
517
518void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
519                                        SetOfConstDecls *VisitedCallees) {
520  // Construct the analysis engine.  First check if the CFG is valid.
521  // FIXME: Inter-procedural analysis will need to handle invalid CFGs.
522  if (!Mgr->getCFG(D))
523    return;
524
525  ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries);
526
527  // Set the graph auditor.
528  OwningPtr<ExplodedNode::Auditor> Auditor;
529  if (Mgr->shouldVisualizeUbigraph()) {
530    Auditor.reset(CreateUbiViz());
531    ExplodedNode::SetAuditor(Auditor.get());
532  }
533
534  // Execute the worklist algorithm.
535  Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D, 0),
536                      Mgr->getMaxNodes());
537
538  // Release the auditor (if any) so that it doesn't monitor the graph
539  // created BugReporter.
540  ExplodedNode::SetAuditor(0);
541
542  // Visualize the exploded graph.
543  if (Mgr->shouldVisualizeGraphviz())
544    Eng.ViewGraph(Mgr->shouldTrimGraph());
545
546  // Display warnings.
547  Eng.getBugReporter().FlushReports();
548}
549
550void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
551                                              SetOfConstDecls *Visited) {
552
553  switch (Mgr->getLangOpts().getGC()) {
554  case LangOptions::NonGC:
555    ActionExprEngine(D, false, Visited);
556    break;
557
558  case LangOptions::GCOnly:
559    ActionExprEngine(D, true, Visited);
560    break;
561
562  case LangOptions::HybridGC:
563    ActionExprEngine(D, false, Visited);
564    ActionExprEngine(D, true, Visited);
565    break;
566  }
567}
568
569//===----------------------------------------------------------------------===//
570// AnalysisConsumer creation.
571//===----------------------------------------------------------------------===//
572
573ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp,
574                                          const std::string& outDir,
575                                          const AnalyzerOptions& opts,
576                                          ArrayRef<std::string> plugins) {
577  // Disable the effects of '-Werror' when using the AnalysisConsumer.
578  pp.getDiagnostics().setWarningsAsErrors(false);
579
580  return new AnalysisConsumer(pp, outDir, opts, plugins);
581}
582
583//===----------------------------------------------------------------------===//
584// Ubigraph Visualization.  FIXME: Move to separate file.
585//===----------------------------------------------------------------------===//
586
587namespace {
588
589class UbigraphViz : public ExplodedNode::Auditor {
590  OwningPtr<raw_ostream> Out;
591  llvm::sys::Path Dir, Filename;
592  unsigned Cntr;
593
594  typedef llvm::DenseMap<void*,unsigned> VMap;
595  VMap M;
596
597public:
598  UbigraphViz(raw_ostream *out, llvm::sys::Path& dir,
599              llvm::sys::Path& filename);
600
601  ~UbigraphViz();
602
603  virtual void AddEdge(ExplodedNode *Src, ExplodedNode *Dst);
604};
605
606} // end anonymous namespace
607
608static ExplodedNode::Auditor* CreateUbiViz() {
609  std::string ErrMsg;
610
611  llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg);
612  if (!ErrMsg.empty())
613    return 0;
614
615  llvm::sys::Path Filename = Dir;
616  Filename.appendComponent("llvm_ubi");
617  Filename.makeUnique(true,&ErrMsg);
618
619  if (!ErrMsg.empty())
620    return 0;
621
622  llvm::errs() << "Writing '" << Filename.str() << "'.\n";
623
624  OwningPtr<llvm::raw_fd_ostream> Stream;
625  Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg));
626
627  if (!ErrMsg.empty())
628    return 0;
629
630  return new UbigraphViz(Stream.take(), Dir, Filename);
631}
632
633void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) {
634
635  assert (Src != Dst && "Self-edges are not allowed.");
636
637  // Lookup the Src.  If it is a new node, it's a root.
638  VMap::iterator SrcI= M.find(Src);
639  unsigned SrcID;
640
641  if (SrcI == M.end()) {
642    M[Src] = SrcID = Cntr++;
643    *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n";
644  }
645  else
646    SrcID = SrcI->second;
647
648  // Lookup the Dst.
649  VMap::iterator DstI= M.find(Dst);
650  unsigned DstID;
651
652  if (DstI == M.end()) {
653    M[Dst] = DstID = Cntr++;
654    *Out << "('vertex', " << DstID << ")\n";
655  }
656  else {
657    // We have hit DstID before.  Change its style to reflect a cache hit.
658    DstID = DstI->second;
659    *Out << "('change_vertex_style', " << DstID << ", 1)\n";
660  }
661
662  // Add the edge.
663  *Out << "('edge', " << SrcID << ", " << DstID
664       << ", ('arrow','true'), ('oriented', 'true'))\n";
665}
666
667UbigraphViz::UbigraphViz(raw_ostream *out, llvm::sys::Path& dir,
668                         llvm::sys::Path& filename)
669  : Out(out), Dir(dir), Filename(filename), Cntr(0) {
670
671  *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n";
672  *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66'),"
673          " ('size', '1.5'))\n";
674}
675
676UbigraphViz::~UbigraphViz() {
677  Out.reset(0);
678  llvm::errs() << "Running 'ubiviz' program... ";
679  std::string ErrMsg;
680  llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz");
681  std::vector<const char*> args;
682  args.push_back(Ubiviz.c_str());
683  args.push_back(Filename.c_str());
684  args.push_back(0);
685
686  if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) {
687    llvm::errs() << "Error viewing graph: " << ErrMsg << "\n";
688  }
689
690  // Delete the directory.
691  Dir.eraseFromDisk(true);
692}
693