AnalysisConsumer.cpp revision 219077
1218887Sdim//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===//
2218887Sdim//
3218887Sdim//                     The LLVM Compiler Infrastructure
4218887Sdim//
5218887Sdim// This file is distributed under the University of Illinois Open Source
6218887Sdim// License. See LICENSE.TXT for details.
7218887Sdim//
8218887Sdim//===----------------------------------------------------------------------===//
9218887Sdim//
10218887Sdim// "Meta" ASTConsumer for running different source analyses.
11218887Sdim//
12218887Sdim//===----------------------------------------------------------------------===//
13218887Sdim
14218887Sdim#include "AnalysisConsumer.h"
15218887Sdim#include "clang/AST/ASTConsumer.h"
16218887Sdim#include "clang/AST/Decl.h"
17218887Sdim#include "clang/AST/DeclCXX.h"
18218887Sdim#include "clang/AST/DeclObjC.h"
19218887Sdim#include "clang/AST/ParentMap.h"
20218887Sdim#include "clang/Analysis/Analyses/LiveVariables.h"
21218887Sdim#include "clang/Analysis/Analyses/UninitializedValues.h"
22218887Sdim#include "clang/Analysis/CFG.h"
23218887Sdim#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
24218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
25218887Sdim#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
26218887Sdim#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
27218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
28218887Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
29218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
30218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h"
31218887Sdim#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h"
32218887Sdim
33218887Sdim// FIXME: Restructure checker registration.
34218887Sdim#include "../Checkers/ClangSACheckers.h"
35218887Sdim#include "../Checkers/ExperimentalChecks.h"
36218887Sdim#include "../Checkers/InternalChecks.h"
37218887Sdim#include "../Checkers/BasicObjCFoundationChecks.h"
38218887Sdim
39218887Sdim#include "clang/Basic/FileManager.h"
40218887Sdim#include "clang/Basic/SourceManager.h"
41218887Sdim#include "clang/Frontend/AnalyzerOptions.h"
42218887Sdim#include "clang/Lex/Preprocessor.h"
43218887Sdim#include "llvm/Support/raw_ostream.h"
44218887Sdim#include "llvm/Support/Path.h"
45218887Sdim#include "llvm/Support/Program.h"
46218887Sdim#include "llvm/ADT/OwningPtr.h"
47218887Sdim
48218887Sdimusing namespace clang;
49218887Sdimusing namespace ento;
50218887Sdim
51218887Sdimstatic ExplodedNode::Auditor* CreateUbiViz();
52218887Sdim
53218887Sdim//===----------------------------------------------------------------------===//
54218887Sdim// Special PathDiagnosticClients.
55218887Sdim//===----------------------------------------------------------------------===//
56218887Sdim
57218887Sdimstatic PathDiagnosticClient*
58218887SdimcreatePlistHTMLDiagnosticClient(const std::string& prefix,
59218887Sdim                                const Preprocessor &PP) {
60218887Sdim  PathDiagnosticClient *PD =
61218887Sdim    createHTMLDiagnosticClient(llvm::sys::path::parent_path(prefix), PP);
62218887Sdim  return createPlistDiagnosticClient(prefix, PP, PD);
63218887Sdim}
64218887Sdim
65218887Sdim//===----------------------------------------------------------------------===//
66218887Sdim// AnalysisConsumer declaration.
67218887Sdim//===----------------------------------------------------------------------===//
68218887Sdim
69218887Sdimnamespace {
70218887Sdim
71218887Sdimclass AnalysisConsumer : public ASTConsumer {
72218887Sdimpublic:
73218887Sdim  typedef void (*CodeAction)(AnalysisConsumer &C, AnalysisManager &M, Decl *D);
74218887Sdim  typedef void (*TUAction)(AnalysisConsumer &C, AnalysisManager &M,
75218887Sdim                           TranslationUnitDecl &TU);
76218887Sdim
77218887Sdimprivate:
78218887Sdim  typedef std::vector<CodeAction> Actions;
79218887Sdim  typedef std::vector<TUAction> TUActions;
80218887Sdim
81218887Sdim  Actions FunctionActions;
82218887Sdim  Actions ObjCMethodActions;
83218887Sdim  Actions ObjCImplementationActions;
84218887Sdim  Actions CXXMethodActions;
85218887Sdim
86218887Sdimpublic:
87218887Sdim  ASTContext* Ctx;
88218887Sdim  const Preprocessor &PP;
89218887Sdim  const std::string OutDir;
90218887Sdim  AnalyzerOptions Opts;
91218887Sdim
92218887Sdim  // PD is owned by AnalysisManager.
93218887Sdim  PathDiagnosticClient *PD;
94218887Sdim
95218887Sdim  StoreManagerCreator CreateStoreMgr;
96218887Sdim  ConstraintManagerCreator CreateConstraintMgr;
97218887Sdim
98218887Sdim  llvm::OwningPtr<CheckerManager> checkerMgr;
99218887Sdim  llvm::OwningPtr<AnalysisManager> Mgr;
100218887Sdim
101218887Sdim  AnalysisConsumer(const Preprocessor& pp,
102218887Sdim                   const std::string& outdir,
103218887Sdim                   const AnalyzerOptions& opts)
104218887Sdim    : Ctx(0), PP(pp), OutDir(outdir),
105218887Sdim      Opts(opts), PD(0) {
106218887Sdim    DigestAnalyzerOptions();
107218887Sdim  }
108218887Sdim
109218887Sdim  void DigestAnalyzerOptions() {
110218887Sdim    // Create the PathDiagnosticClient.
111218887Sdim    if (!OutDir.empty()) {
112218887Sdim      switch (Opts.AnalysisDiagOpt) {
113218887Sdim      default:
114218887Sdim#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \
115218887Sdim        case PD_##NAME: PD = CREATEFN(OutDir, PP); break;
116218887Sdim#include "clang/Frontend/Analyses.def"
117218887Sdim      }
118218887Sdim    } else if (Opts.AnalysisDiagOpt == PD_TEXT) {
119218887Sdim      // Create the text client even without a specified output file since
120218887Sdim      // it just uses diagnostic notes.
121218887Sdim      PD = createTextPathDiagnosticClient("", PP);
122218887Sdim    }
123218887Sdim
124218887Sdim    // Create the analyzer component creators.
125218887Sdim    switch (Opts.AnalysisStoreOpt) {
126218887Sdim    default:
127218887Sdim      assert(0 && "Unknown store manager.");
128218887Sdim#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN)           \
129218887Sdim      case NAME##Model: CreateStoreMgr = CREATEFN; break;
130218887Sdim#include "clang/Frontend/Analyses.def"
131218887Sdim    }
132218887Sdim
133218887Sdim    switch (Opts.AnalysisConstraintsOpt) {
134218887Sdim    default:
135218887Sdim      assert(0 && "Unknown store manager.");
136218887Sdim#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN)     \
137218887Sdim      case NAME##Model: CreateConstraintMgr = CREATEFN; break;
138218887Sdim#include "clang/Frontend/Analyses.def"
139218887Sdim    }
140218887Sdim  }
141218887Sdim
142218887Sdim  void DisplayFunction(const Decl *D) {
143218887Sdim    if (!Opts.AnalyzerDisplayProgress)
144218887Sdim      return;
145218887Sdim
146218887Sdim    SourceManager &SM = Mgr->getASTContext().getSourceManager();
147218887Sdim    PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
148218887Sdim    if (Loc.isValid()) {
149218887Sdim      llvm::errs() << "ANALYZE: " << Loc.getFilename();
150218887Sdim
151218887Sdim      if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
152218887Sdim        const NamedDecl *ND = cast<NamedDecl>(D);
153218887Sdim        llvm::errs() << ' ' << ND << '\n';
154218887Sdim      }
155218887Sdim      else if (isa<BlockDecl>(D)) {
156218887Sdim        llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:"
157218887Sdim                     << Loc.getColumn() << '\n';
158218887Sdim      }
159218887Sdim      else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
160218887Sdim        Selector S = MD->getSelector();
161218887Sdim        llvm::errs() << ' ' << S.getAsString();
162218887Sdim      }
163218887Sdim    }
164218887Sdim  }
165218887Sdim
166218887Sdim  void addCodeAction(CodeAction action) {
167218887Sdim    FunctionActions.push_back(action);
168218887Sdim    ObjCMethodActions.push_back(action);
169218887Sdim    CXXMethodActions.push_back(action);
170218887Sdim  }
171218887Sdim
172218887Sdim  void addObjCImplementationAction(CodeAction action) {
173218887Sdim    ObjCImplementationActions.push_back(action);
174218887Sdim  }
175218887Sdim
176218887Sdim  virtual void Initialize(ASTContext &Context) {
177218887Sdim    Ctx = &Context;
178219077Sdim    checkerMgr.reset(registerCheckers(Opts, PP.getLangOptions(),
179219077Sdim                                      PP.getDiagnostics()));
180218887Sdim    Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(),
181218887Sdim                                  PP.getLangOptions(), PD,
182218887Sdim                                  CreateStoreMgr, CreateConstraintMgr,
183218887Sdim                                  checkerMgr.get(),
184218887Sdim                                  /* Indexer */ 0,
185218887Sdim                                  Opts.MaxNodes, Opts.MaxLoop,
186218887Sdim                                  Opts.VisualizeEGDot, Opts.VisualizeEGUbi,
187218887Sdim                                  Opts.PurgeDead, Opts.EagerlyAssume,
188218887Sdim                                  Opts.TrimGraph, Opts.InlineCall,
189218887Sdim                                  Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
190218887Sdim                                  Opts.CFGAddInitializers,
191218887Sdim                                  Opts.EagerlyTrimEGraph));
192218887Sdim  }
193218887Sdim
194218887Sdim  virtual void HandleTranslationUnit(ASTContext &C);
195218887Sdim  void HandleDeclContext(ASTContext &C, DeclContext *dc);
196218887Sdim
197218887Sdim  void HandleCode(Decl *D, Actions& actions);
198218887Sdim};
199218887Sdim} // end anonymous namespace
200218887Sdim
201218887Sdim//===----------------------------------------------------------------------===//
202218887Sdim// AnalysisConsumer implementation.
203218887Sdim//===----------------------------------------------------------------------===//
204218887Sdim
205218887Sdimvoid AnalysisConsumer::HandleDeclContext(ASTContext &C, DeclContext *dc) {
206218887Sdim  BugReporter BR(*Mgr);
207218887Sdim  for (DeclContext::decl_iterator I = dc->decls_begin(), E = dc->decls_end();
208218887Sdim       I != E; ++I) {
209218887Sdim    Decl *D = *I;
210218887Sdim    checkerMgr->runCheckersOnASTDecl(D, *Mgr, BR);
211218887Sdim
212218887Sdim    switch (D->getKind()) {
213218887Sdim      case Decl::Namespace: {
214218887Sdim        HandleDeclContext(C, cast<NamespaceDecl>(D));
215218887Sdim        break;
216218887Sdim      }
217218887Sdim      case Decl::CXXConstructor:
218218887Sdim      case Decl::CXXDestructor:
219218887Sdim      case Decl::CXXConversion:
220218887Sdim      case Decl::CXXMethod:
221218887Sdim      case Decl::Function: {
222218887Sdim        FunctionDecl* FD = cast<FunctionDecl>(D);
223218887Sdim        // We skip function template definitions, as their semantics is
224218887Sdim        // only determined when they are instantiated.
225218887Sdim        if (FD->isThisDeclarationADefinition() &&
226218887Sdim            !FD->isDependentContext()) {
227218887Sdim          if (!Opts.AnalyzeSpecificFunction.empty() &&
228218887Sdim              FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction)
229218887Sdim            break;
230218887Sdim          DisplayFunction(FD);
231218887Sdim          HandleCode(FD, FunctionActions);
232218887Sdim        }
233218887Sdim        break;
234218887Sdim      }
235218887Sdim
236218887Sdim      case Decl::ObjCImplementation: {
237218887Sdim        ObjCImplementationDecl* ID = cast<ObjCImplementationDecl>(*I);
238218887Sdim        HandleCode(ID, ObjCImplementationActions);
239218887Sdim
240218887Sdim        for (ObjCImplementationDecl::method_iterator MI = ID->meth_begin(),
241218887Sdim             ME = ID->meth_end(); MI != ME; ++MI) {
242218887Sdim          if ((*MI)->isThisDeclarationADefinition()) {
243218887Sdim            if (!Opts.AnalyzeSpecificFunction.empty() &&
244218887Sdim                Opts.AnalyzeSpecificFunction != (*MI)->getSelector().getAsString())
245218887Sdim              break;
246218887Sdim            DisplayFunction(*MI);
247218887Sdim            HandleCode(*MI, ObjCMethodActions);
248218887Sdim          }
249218887Sdim        }
250218887Sdim        break;
251218887Sdim      }
252218887Sdim
253218887Sdim      default:
254218887Sdim        break;
255218887Sdim    }
256218887Sdim  }
257218887Sdim}
258218887Sdim
259218887Sdimvoid AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
260218887Sdim  BugReporter BR(*Mgr);
261218887Sdim  TranslationUnitDecl *TU = C.getTranslationUnitDecl();
262218887Sdim  checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
263218887Sdim  HandleDeclContext(C, TU);
264218887Sdim
265218887Sdim  // Explicitly destroy the PathDiagnosticClient.  This will flush its output.
266218887Sdim  // FIXME: This should be replaced with something that doesn't rely on
267218887Sdim  // side-effects in PathDiagnosticClient's destructor. This is required when
268218887Sdim  // used with option -disable-free.
269218887Sdim  Mgr.reset(NULL);
270218887Sdim}
271218887Sdim
272218887Sdimstatic void FindBlocks(DeclContext *D, llvm::SmallVectorImpl<Decl*> &WL) {
273218887Sdim  if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
274218887Sdim    WL.push_back(BD);
275218887Sdim
276218887Sdim  for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end();
277218887Sdim       I!=E; ++I)
278218887Sdim    if (DeclContext *DC = dyn_cast<DeclContext>(*I))
279218887Sdim      FindBlocks(DC, WL);
280218887Sdim}
281218887Sdim
282218887Sdimvoid AnalysisConsumer::HandleCode(Decl *D, Actions& actions) {
283218887Sdim
284218887Sdim  // Don't run the actions if an error has occured with parsing the file.
285218887Sdim  Diagnostic &Diags = PP.getDiagnostics();
286218887Sdim  if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
287218887Sdim    return;
288218887Sdim
289218887Sdim  // Don't run the actions on declarations in header files unless
290218887Sdim  // otherwise specified.
291218887Sdim  SourceManager &SM = Ctx->getSourceManager();
292218887Sdim  SourceLocation SL = SM.getInstantiationLoc(D->getLocation());
293218887Sdim  if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL))
294218887Sdim    return;
295218887Sdim
296218887Sdim  // Clear the AnalysisManager of old AnalysisContexts.
297218887Sdim  Mgr->ClearContexts();
298218887Sdim
299218887Sdim  // Dispatch on the actions.
300218887Sdim  llvm::SmallVector<Decl*, 10> WL;
301218887Sdim  WL.push_back(D);
302218887Sdim
303218887Sdim  if (D->hasBody() && Opts.AnalyzeNestedBlocks)
304218887Sdim    FindBlocks(cast<DeclContext>(D), WL);
305218887Sdim
306218887Sdim  BugReporter BR(*Mgr);
307218887Sdim  for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
308218887Sdim       WI != WE; ++WI)
309218887Sdim    if ((*WI)->hasBody())
310218887Sdim      checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR);
311218887Sdim
312218887Sdim  for (Actions::iterator I = actions.begin(), E = actions.end(); I != E; ++I)
313218887Sdim    for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
314218887Sdim         WI != WE; ++WI)
315218887Sdim      (*I)(*this, *Mgr, *WI);
316218887Sdim}
317218887Sdim
318218887Sdim//===----------------------------------------------------------------------===//
319218887Sdim// Analyses
320218887Sdim//===----------------------------------------------------------------------===//
321218887Sdim
322218887Sdimstatic void ActionWarnUninitVals(AnalysisConsumer &C, AnalysisManager& mgr,
323218887Sdim                                 Decl *D) {
324218887Sdim  if (CFG* c = mgr.getCFG(D)) {
325218887Sdim    CheckUninitializedValues(*c, mgr.getASTContext(), mgr.getDiagnostic());
326218887Sdim  }
327218887Sdim}
328218887Sdim
329218887Sdim
330218887Sdimstatic void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr,
331218887Sdim                               Decl *D,
332218887Sdim                               TransferFuncs* tf) {
333218887Sdim
334218887Sdim  llvm::OwningPtr<TransferFuncs> TF(tf);
335218887Sdim
336218887Sdim  // Construct the analysis engine.  We first query for the LiveVariables
337218887Sdim  // information to see if the CFG is valid.
338218887Sdim  // FIXME: Inter-procedural analysis will need to handle invalid CFGs.
339218887Sdim  if (!mgr.getLiveVariables(D))
340218887Sdim    return;
341218887Sdim  ExprEngine Eng(mgr, TF.take());
342218887Sdim
343218887Sdim  RegisterNSErrorChecks(Eng.getBugReporter(), Eng, *D);
344218887Sdim
345218887Sdim  if (C.Opts.EnableExperimentalChecks)
346218887Sdim    RegisterExperimentalChecks(Eng);
347218887Sdim
348218887Sdim  if (C.Opts.BufferOverflows)
349218887Sdim    RegisterArrayBoundCheckerV2(Eng);
350218887Sdim
351218887Sdim  // Enable AnalyzerStatsChecker if it was given as an argument
352218887Sdim  if (C.Opts.AnalyzerStats)
353218887Sdim    RegisterAnalyzerStatsChecker(Eng);
354218887Sdim
355218887Sdim  // Set the graph auditor.
356218887Sdim  llvm::OwningPtr<ExplodedNode::Auditor> Auditor;
357218887Sdim  if (mgr.shouldVisualizeUbigraph()) {
358218887Sdim    Auditor.reset(CreateUbiViz());
359218887Sdim    ExplodedNode::SetAuditor(Auditor.get());
360218887Sdim  }
361218887Sdim
362218887Sdim  // Execute the worklist algorithm.
363218887Sdim  Eng.ExecuteWorkList(mgr.getStackFrame(D, 0), mgr.getMaxNodes());
364218887Sdim
365218887Sdim  // Release the auditor (if any) so that it doesn't monitor the graph
366218887Sdim  // created BugReporter.
367218887Sdim  ExplodedNode::SetAuditor(0);
368218887Sdim
369218887Sdim  // Visualize the exploded graph.
370218887Sdim  if (mgr.shouldVisualizeGraphviz())
371218887Sdim    Eng.ViewGraph(mgr.shouldTrimGraph());
372218887Sdim
373218887Sdim  // Display warnings.
374218887Sdim  Eng.getBugReporter().FlushReports();
375218887Sdim}
376218887Sdim
377218887Sdimstatic void ActionObjCMemCheckerAux(AnalysisConsumer &C, AnalysisManager& mgr,
378218887Sdim                                  Decl *D, bool GCEnabled) {
379218887Sdim
380218887Sdim  TransferFuncs* TF = MakeCFRefCountTF(mgr.getASTContext(),
381218887Sdim                                         GCEnabled,
382218887Sdim                                         mgr.getLangOptions());
383218887Sdim
384218887Sdim  ActionExprEngine(C, mgr, D, TF);
385218887Sdim}
386218887Sdim
387218887Sdimstatic void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr,
388218887Sdim                               Decl *D) {
389218887Sdim
390218887Sdim switch (mgr.getLangOptions().getGCMode()) {
391218887Sdim default:
392218887Sdim   assert (false && "Invalid GC mode.");
393218887Sdim case LangOptions::NonGC:
394218887Sdim   ActionObjCMemCheckerAux(C, mgr, D, false);
395218887Sdim   break;
396218887Sdim
397218887Sdim case LangOptions::GCOnly:
398218887Sdim   ActionObjCMemCheckerAux(C, mgr, D, true);
399218887Sdim   break;
400218887Sdim
401218887Sdim case LangOptions::HybridGC:
402218887Sdim   ActionObjCMemCheckerAux(C, mgr, D, false);
403218887Sdim   ActionObjCMemCheckerAux(C, mgr, D, true);
404218887Sdim   break;
405218887Sdim }
406218887Sdim}
407218887Sdim
408218887Sdim//===----------------------------------------------------------------------===//
409218887Sdim// AnalysisConsumer creation.
410218887Sdim//===----------------------------------------------------------------------===//
411218887Sdim
412218887SdimASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp,
413218887Sdim                                           const std::string& OutDir,
414218887Sdim                                           const AnalyzerOptions& Opts) {
415218887Sdim  llvm::OwningPtr<AnalysisConsumer> C(new AnalysisConsumer(pp, OutDir, Opts));
416218887Sdim
417218887Sdim  for (unsigned i = 0; i < Opts.AnalysisList.size(); ++i)
418218887Sdim    switch (Opts.AnalysisList[i]) {
419218887Sdim#define ANALYSIS(NAME, CMD, DESC, SCOPE)\
420218887Sdim    case NAME:\
421218887Sdim      C->add ## SCOPE ## Action(&Action ## NAME);\
422218887Sdim      break;
423218887Sdim#include "clang/Frontend/Analyses.def"
424218887Sdim    default: break;
425218887Sdim    }
426218887Sdim
427218887Sdim  // Last, disable the effects of '-Werror' when using the AnalysisConsumer.
428218887Sdim  pp.getDiagnostics().setWarningsAsErrors(false);
429218887Sdim
430218887Sdim  return C.take();
431218887Sdim}
432218887Sdim
433218887Sdim//===----------------------------------------------------------------------===//
434218887Sdim// Ubigraph Visualization.  FIXME: Move to separate file.
435218887Sdim//===----------------------------------------------------------------------===//
436218887Sdim
437218887Sdimnamespace {
438218887Sdim
439218887Sdimclass UbigraphViz : public ExplodedNode::Auditor {
440218887Sdim  llvm::OwningPtr<llvm::raw_ostream> Out;
441218887Sdim  llvm::sys::Path Dir, Filename;
442218887Sdim  unsigned Cntr;
443218887Sdim
444218887Sdim  typedef llvm::DenseMap<void*,unsigned> VMap;
445218887Sdim  VMap M;
446218887Sdim
447218887Sdimpublic:
448218887Sdim  UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
449218887Sdim              llvm::sys::Path& filename);
450218887Sdim
451218887Sdim  ~UbigraphViz();
452218887Sdim
453218887Sdim  virtual void AddEdge(ExplodedNode* Src, ExplodedNode* Dst);
454218887Sdim};
455218887Sdim
456218887Sdim} // end anonymous namespace
457218887Sdim
458218887Sdimstatic ExplodedNode::Auditor* CreateUbiViz() {
459218887Sdim  std::string ErrMsg;
460218887Sdim
461218887Sdim  llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg);
462218887Sdim  if (!ErrMsg.empty())
463218887Sdim    return 0;
464218887Sdim
465218887Sdim  llvm::sys::Path Filename = Dir;
466218887Sdim  Filename.appendComponent("llvm_ubi");
467218887Sdim  Filename.makeUnique(true,&ErrMsg);
468218887Sdim
469218887Sdim  if (!ErrMsg.empty())
470218887Sdim    return 0;
471218887Sdim
472218887Sdim  llvm::errs() << "Writing '" << Filename.str() << "'.\n";
473218887Sdim
474218887Sdim  llvm::OwningPtr<llvm::raw_fd_ostream> Stream;
475218887Sdim  Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg));
476218887Sdim
477218887Sdim  if (!ErrMsg.empty())
478218887Sdim    return 0;
479218887Sdim
480218887Sdim  return new UbigraphViz(Stream.take(), Dir, Filename);
481218887Sdim}
482218887Sdim
483218887Sdimvoid UbigraphViz::AddEdge(ExplodedNode* Src, ExplodedNode* Dst) {
484218887Sdim
485218887Sdim  assert (Src != Dst && "Self-edges are not allowed.");
486218887Sdim
487218887Sdim  // Lookup the Src.  If it is a new node, it's a root.
488218887Sdim  VMap::iterator SrcI= M.find(Src);
489218887Sdim  unsigned SrcID;
490218887Sdim
491218887Sdim  if (SrcI == M.end()) {
492218887Sdim    M[Src] = SrcID = Cntr++;
493218887Sdim    *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n";
494218887Sdim  }
495218887Sdim  else
496218887Sdim    SrcID = SrcI->second;
497218887Sdim
498218887Sdim  // Lookup the Dst.
499218887Sdim  VMap::iterator DstI= M.find(Dst);
500218887Sdim  unsigned DstID;
501218887Sdim
502218887Sdim  if (DstI == M.end()) {
503218887Sdim    M[Dst] = DstID = Cntr++;
504218887Sdim    *Out << "('vertex', " << DstID << ")\n";
505218887Sdim  }
506218887Sdim  else {
507218887Sdim    // We have hit DstID before.  Change its style to reflect a cache hit.
508218887Sdim    DstID = DstI->second;
509218887Sdim    *Out << "('change_vertex_style', " << DstID << ", 1)\n";
510218887Sdim  }
511218887Sdim
512218887Sdim  // Add the edge.
513218887Sdim  *Out << "('edge', " << SrcID << ", " << DstID
514218887Sdim       << ", ('arrow','true'), ('oriented', 'true'))\n";
515218887Sdim}
516218887Sdim
517218887SdimUbigraphViz::UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
518218887Sdim                         llvm::sys::Path& filename)
519218887Sdim  : Out(out), Dir(dir), Filename(filename), Cntr(0) {
520218887Sdim
521218887Sdim  *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n";
522218887Sdim  *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66'),"
523218887Sdim          " ('size', '1.5'))\n";
524218887Sdim}
525218887Sdim
526218887SdimUbigraphViz::~UbigraphViz() {
527218887Sdim  Out.reset(0);
528218887Sdim  llvm::errs() << "Running 'ubiviz' program... ";
529218887Sdim  std::string ErrMsg;
530218887Sdim  llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz");
531218887Sdim  std::vector<const char*> args;
532218887Sdim  args.push_back(Ubiviz.c_str());
533218887Sdim  args.push_back(Filename.c_str());
534218887Sdim  args.push_back(0);
535218887Sdim
536218887Sdim  if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) {
537218887Sdim    llvm::errs() << "Error viewing graph: " << ErrMsg << "\n";
538218887Sdim  }
539218887Sdim
540218887Sdim  // Delete the directory.
541218887Sdim  Dir.eraseFromDisk(true);
542218887Sdim}
543