AnalysisConsumer.cpp revision 218893
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
14234353Sdim#include "AnalysisConsumer.h"
15234353Sdim#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"
22234353Sdim#include "clang/Analysis/CFG.h"
23218887Sdim#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
24234353Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
25239462Sdim#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
33226633Sdim// 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"
42234353Sdim#include "clang/Lex/Preprocessor.h"
43234353Sdim#include "llvm/Support/raw_ostream.h"
44218887Sdim#include "llvm/Support/Path.h"
45234353Sdim#include "llvm/Support/Program.h"
46234353Sdim#include "llvm/ADT/OwningPtr.h"
47218887Sdim
48234353Sdimusing namespace clang;
49234353Sdimusing namespace ento;
50218887Sdim
51218887Sdimstatic ExplodedNode::Auditor* CreateUbiViz();
52234353Sdim
53218887Sdim//===----------------------------------------------------------------------===//
54218887Sdim// Special PathDiagnosticClients.
55218887Sdim//===----------------------------------------------------------------------===//
56234353Sdim
57234353Sdimstatic PathDiagnosticClient*
58234353SdimcreatePlistHTMLDiagnosticClient(const std::string& prefix,
59234353Sdim                                const Preprocessor &PP) {
60234353Sdim  PathDiagnosticClient *PD =
61239462Sdim    createHTMLDiagnosticClient(llvm::sys::path::parent_path(prefix), PP);
62234353Sdim  return createPlistDiagnosticClient(prefix, PP, PD);
63218887Sdim}
64226633Sdim
65218887Sdim//===----------------------------------------------------------------------===//
66218887Sdim// AnalysisConsumer declaration.
67239462Sdim//===----------------------------------------------------------------------===//
68239462Sdim
69239462Sdimnamespace {
70239462Sdim
71239462Sdimclass AnalysisConsumer : public ASTConsumer {
72218887Sdimpublic:
73218887Sdim  typedef void (*CodeAction)(AnalysisConsumer &C, AnalysisManager &M, Decl *D);
74239462Sdim  typedef void (*TUAction)(AnalysisConsumer &C, AnalysisManager &M,
75239462Sdim                           TranslationUnitDecl &TU);
76239462Sdim
77239462Sdimprivate:
78239462Sdim  typedef std::vector<CodeAction> Actions;
79239462Sdim  typedef std::vector<TUAction> TUActions;
80239462Sdim
81239462Sdim  Actions FunctionActions;
82239462Sdim  Actions ObjCMethodActions;
83239462Sdim  Actions ObjCImplementationActions;
84239462Sdim  Actions CXXMethodActions;
85239462Sdim
86239462Sdimpublic:
87239462Sdim  ASTContext* Ctx;
88239462Sdim  const Preprocessor &PP;
89239462Sdim  const std::string OutDir;
90239462Sdim  AnalyzerOptions Opts;
91239462Sdim
92239462Sdim  // PD is owned by AnalysisManager.
93239462Sdim  PathDiagnosticClient *PD;
94239462Sdim
95239462Sdim  StoreManagerCreator CreateStoreMgr;
96239462Sdim  ConstraintManagerCreator CreateConstraintMgr;
97239462Sdim
98239462Sdim  llvm::OwningPtr<CheckerManager> checkerMgr;
99239462Sdim  llvm::OwningPtr<AnalysisManager> Mgr;
100239462Sdim
101239462Sdim  AnalysisConsumer(const Preprocessor& pp,
102239462Sdim                   const std::string& outdir,
103239462Sdim                   const AnalyzerOptions& opts)
104239462Sdim    : Ctx(0), PP(pp), OutDir(outdir),
105239462Sdim      Opts(opts), PD(0) {
106239462Sdim    DigestAnalyzerOptions();
107239462Sdim  }
108239462Sdim
109239462Sdim  void DigestAnalyzerOptions() {
110239462Sdim    // Create the PathDiagnosticClient.
111239462Sdim    if (!OutDir.empty()) {
112239462Sdim      switch (Opts.AnalysisDiagOpt) {
113239462Sdim      default:
114239462Sdim#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \
115239462Sdim        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);
122234353Sdim    }
123234353Sdim
124234353Sdim    // Create the analyzer component creators.
125234353Sdim    switch (Opts.AnalysisStoreOpt) {
126234353Sdim    default:
127234353Sdim      assert(0 && "Unknown store manager.");
128234353Sdim#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN)           \
129234353Sdim      case NAME##Model: CreateStoreMgr = CREATEFN; break;
130234353Sdim#include "clang/Frontend/Analyses.def"
131234353Sdim    }
132234353Sdim
133234353Sdim    switch (Opts.AnalysisConstraintsOpt) {
134234353Sdim    default:
135218887Sdim      assert(0 && "Unknown store manager.");
136226633Sdim#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN)     \
137218887Sdim      case NAME##Model: CreateConstraintMgr = CREATEFN; break;
138218887Sdim#include "clang/Frontend/Analyses.def"
139218887Sdim    }
140226633Sdim  }
141218887Sdim
142234353Sdim  void DisplayFunction(const Decl *D) {
143234353Sdim    if (!Opts.AnalyzerDisplayProgress)
144234353Sdim      return;
145234353Sdim
146234353Sdim    SourceManager &SM = Mgr->getASTContext().getSourceManager();
147234353Sdim    PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
148239462Sdim    if (Loc.isValid()) {
149239462Sdim      llvm::errs() << "ANALYZE: " << Loc.getFilename();
150239462Sdim
151234353Sdim      if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
152218887Sdim        const NamedDecl *ND = cast<NamedDecl>(D);
153218887Sdim        llvm::errs() << ' ' << ND << '\n';
154218887Sdim      }
155234353Sdim      else if (isa<BlockDecl>(D)) {
156234353Sdim        llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:"
157218887Sdim                     << Loc.getColumn() << '\n';
158234353Sdim      }
159234353Sdim      else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
160234353Sdim        Selector S = MD->getSelector();
161234353Sdim        llvm::errs() << ' ' << S.getAsString();
162234353Sdim      }
163234353Sdim    }
164234353Sdim  }
165218887Sdim
166218887Sdim  void addCodeAction(CodeAction action) {
167226633Sdim    FunctionActions.push_back(action);
168226633Sdim    ObjCMethodActions.push_back(action);
169234353Sdim    CXXMethodActions.push_back(action);
170239462Sdim  }
171218887Sdim
172234353Sdim  void addObjCImplementationAction(CodeAction action) {
173234353Sdim    ObjCImplementationActions.push_back(action);
174234353Sdim  }
175234353Sdim
176218887Sdim  virtual void Initialize(ASTContext &Context) {
177218887Sdim    Ctx = &Context;
178234353Sdim    checkerMgr.reset(registerCheckers(Opts, PP.getDiagnostics()));
179234353Sdim    Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(),
180234353Sdim                                  PP.getLangOptions(), PD,
181234353Sdim                                  CreateStoreMgr, CreateConstraintMgr,
182234353Sdim                                  checkerMgr.get(),
183218887Sdim                                  /* Indexer */ 0,
184226633Sdim                                  Opts.MaxNodes, Opts.MaxLoop,
185239462Sdim                                  Opts.VisualizeEGDot, Opts.VisualizeEGUbi,
186239462Sdim                                  Opts.PurgeDead, Opts.EagerlyAssume,
187218887Sdim                                  Opts.TrimGraph, Opts.InlineCall,
188218887Sdim                                  Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
189218887Sdim                                  Opts.CFGAddInitializers,
190218887Sdim                                  Opts.EagerlyTrimEGraph));
191239462Sdim  }
192218887Sdim
193218887Sdim  virtual void HandleTranslationUnit(ASTContext &C);
194218887Sdim  void HandleDeclContext(ASTContext &C, DeclContext *dc);
195218887Sdim
196218887Sdim  void HandleCode(Decl *D, Actions& actions);
197239462Sdim};
198218887Sdim} // end anonymous namespace
199218887Sdim
200218887Sdim//===----------------------------------------------------------------------===//
201218887Sdim// AnalysisConsumer implementation.
202218887Sdim//===----------------------------------------------------------------------===//
203226633Sdim
204218887Sdimvoid AnalysisConsumer::HandleDeclContext(ASTContext &C, DeclContext *dc) {
205218887Sdim  BugReporter BR(*Mgr);
206218887Sdim  for (DeclContext::decl_iterator I = dc->decls_begin(), E = dc->decls_end();
207218887Sdim       I != E; ++I) {
208218887Sdim    Decl *D = *I;
209218887Sdim    checkerMgr->runCheckersOnASTDecl(D, *Mgr, BR);
210218887Sdim
211226633Sdim    switch (D->getKind()) {
212218887Sdim      case Decl::Namespace: {
213218887Sdim        HandleDeclContext(C, cast<NamespaceDecl>(D));
214218887Sdim        break;
215218887Sdim      }
216218887Sdim      case Decl::CXXConstructor:
217218887Sdim      case Decl::CXXDestructor:
218234353Sdim      case Decl::CXXConversion:
219218887Sdim      case Decl::CXXMethod:
220218887Sdim      case Decl::Function: {
221218887Sdim        FunctionDecl* FD = cast<FunctionDecl>(D);
222218887Sdim        // We skip function template definitions, as their semantics is
223218887Sdim        // only determined when they are instantiated.
224218887Sdim        if (FD->isThisDeclarationADefinition() &&
225234353Sdim            !FD->isDependentContext()) {
226234353Sdim          if (!Opts.AnalyzeSpecificFunction.empty() &&
227234353Sdim              FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction)
228234353Sdim            break;
229234353Sdim          DisplayFunction(FD);
230234353Sdim          HandleCode(FD, FunctionActions);
231234353Sdim        }
232218887Sdim        break;
233218887Sdim      }
234226633Sdim
235218887Sdim      case Decl::ObjCImplementation: {
236218887Sdim        ObjCImplementationDecl* ID = cast<ObjCImplementationDecl>(*I);
237218887Sdim        HandleCode(ID, ObjCImplementationActions);
238218887Sdim
239218887Sdim        for (ObjCImplementationDecl::method_iterator MI = ID->meth_begin(),
240218887Sdim             ME = ID->meth_end(); MI != ME; ++MI) {
241218887Sdim          if ((*MI)->isThisDeclarationADefinition()) {
242218887Sdim            if (!Opts.AnalyzeSpecificFunction.empty() &&
243218887Sdim                Opts.AnalyzeSpecificFunction != (*MI)->getSelector().getAsString())
244218887Sdim              break;
245218887Sdim            DisplayFunction(*MI);
246218887Sdim            HandleCode(*MI, ObjCMethodActions);
247218887Sdim          }
248218887Sdim        }
249234353Sdim        break;
250226633Sdim      }
251239462Sdim
252239462Sdim      default:
253239462Sdim        break;
254239462Sdim    }
255239462Sdim  }
256239462Sdim}
257218887Sdim
258218887Sdimvoid AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
259218887Sdim  BugReporter BR(*Mgr);
260226633Sdim  TranslationUnitDecl *TU = C.getTranslationUnitDecl();
261234353Sdim  checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
262218887Sdim  HandleDeclContext(C, TU);
263234353Sdim
264234353Sdim  // Explicitly destroy the PathDiagnosticClient.  This will flush its output.
265234353Sdim  // FIXME: This should be replaced with something that doesn't rely on
266234353Sdim  // side-effects in PathDiagnosticClient's destructor. This is required when
267234353Sdim  // used with option -disable-free.
268234353Sdim  Mgr.reset(NULL);
269218887Sdim}
270218887Sdim
271234353Sdimstatic void FindBlocks(DeclContext *D, llvm::SmallVectorImpl<Decl*> &WL) {
272234353Sdim  if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
273234353Sdim    WL.push_back(BD);
274234353Sdim
275234353Sdim  for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end();
276218887Sdim       I!=E; ++I)
277218887Sdim    if (DeclContext *DC = dyn_cast<DeclContext>(*I))
278234353Sdim      FindBlocks(DC, WL);
279234353Sdim}
280239462Sdim
281234353Sdimvoid AnalysisConsumer::HandleCode(Decl *D, Actions& actions) {
282234353Sdim
283234353Sdim  // Don't run the actions if an error has occured with parsing the file.
284234353Sdim  Diagnostic &Diags = PP.getDiagnostics();
285234353Sdim  if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
286234353Sdim    return;
287234353Sdim
288234353Sdim  // Don't run the actions on declarations in header files unless
289234353Sdim  // otherwise specified.
290234353Sdim  SourceManager &SM = Ctx->getSourceManager();
291234353Sdim  SourceLocation SL = SM.getInstantiationLoc(D->getLocation());
292234353Sdim  if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL))
293234353Sdim    return;
294234353Sdim
295234353Sdim  // Clear the AnalysisManager of old AnalysisContexts.
296239462Sdim  Mgr->ClearContexts();
297234353Sdim
298234353Sdim  // Dispatch on the actions.
299234353Sdim  llvm::SmallVector<Decl*, 10> WL;
300234353Sdim  WL.push_back(D);
301234353Sdim
302234353Sdim  if (D->hasBody() && Opts.AnalyzeNestedBlocks)
303234353Sdim    FindBlocks(cast<DeclContext>(D), WL);
304234353Sdim
305234353Sdim  BugReporter BR(*Mgr);
306234353Sdim  for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
307234353Sdim       WI != WE; ++WI)
308234353Sdim    if ((*WI)->hasBody())
309234353Sdim      checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR);
310234353Sdim
311234353Sdim  for (Actions::iterator I = actions.begin(), E = actions.end(); I != E; ++I)
312234353Sdim    for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
313234353Sdim         WI != WE; ++WI)
314234353Sdim      (*I)(*this, *Mgr, *WI);
315234353Sdim}
316234353Sdim
317234353Sdim//===----------------------------------------------------------------------===//
318234353Sdim// Analyses
319234353Sdim//===----------------------------------------------------------------------===//
320234353Sdim
321234353Sdimstatic void ActionWarnUninitVals(AnalysisConsumer &C, AnalysisManager& mgr,
322234353Sdim                                 Decl *D) {
323234353Sdim  if (CFG* c = mgr.getCFG(D)) {
324234353Sdim    CheckUninitializedValues(*c, mgr.getASTContext(), mgr.getDiagnostic());
325234353Sdim  }
326234353Sdim}
327234353Sdim
328234353Sdim
329234353Sdimstatic void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr,
330234353Sdim                               Decl *D,
331218887Sdim                               TransferFuncs* tf) {
332218887Sdim
333218887Sdim  llvm::OwningPtr<TransferFuncs> TF(tf);
334234353Sdim
335218887Sdim  // Construct the analysis engine.  We first query for the LiveVariables
336218887Sdim  // information to see if the CFG is valid.
337218887Sdim  // FIXME: Inter-procedural analysis will need to handle invalid CFGs.
338234353Sdim  if (!mgr.getLiveVariables(D))
339218887Sdim    return;
340234353Sdim  ExprEngine Eng(mgr, TF.take());
341234353Sdim
342234353Sdim  if (C.Opts.EnableExperimentalInternalChecks)
343234353Sdim    RegisterExperimentalInternalChecks(Eng);
344234353Sdim
345234353Sdim  RegisterNSErrorChecks(Eng.getBugReporter(), Eng, *D);
346234353Sdim
347234353Sdim  if (C.Opts.EnableExperimentalChecks)
348234353Sdim    RegisterExperimentalChecks(Eng);
349234353Sdim
350234353Sdim  if (C.Opts.BufferOverflows)
351234353Sdim    RegisterArrayBoundCheckerV2(Eng);
352234353Sdim
353234353Sdim  // Enable AnalyzerStatsChecker if it was given as an argument
354234353Sdim  if (C.Opts.AnalyzerStats)
355234353Sdim    RegisterAnalyzerStatsChecker(Eng);
356234353Sdim
357239462Sdim  // Set the graph auditor.
358226633Sdim  llvm::OwningPtr<ExplodedNode::Auditor> Auditor;
359226633Sdim  if (mgr.shouldVisualizeUbigraph()) {
360226633Sdim    Auditor.reset(CreateUbiViz());
361239462Sdim    ExplodedNode::SetAuditor(Auditor.get());
362234353Sdim  }
363234353Sdim
364234353Sdim  // Execute the worklist algorithm.
365239462Sdim  Eng.ExecuteWorkList(mgr.getStackFrame(D, 0), mgr.getMaxNodes());
366234353Sdim
367239462Sdim  // Release the auditor (if any) so that it doesn't monitor the graph
368239462Sdim  // created BugReporter.
369239462Sdim  ExplodedNode::SetAuditor(0);
370239462Sdim
371239462Sdim  // Visualize the exploded graph.
372239462Sdim  if (mgr.shouldVisualizeGraphviz())
373234353Sdim    Eng.ViewGraph(mgr.shouldTrimGraph());
374234353Sdim
375234353Sdim  // Display warnings.
376234353Sdim  Eng.getBugReporter().FlushReports();
377234353Sdim}
378234353Sdim
379234353Sdimstatic void ActionObjCMemCheckerAux(AnalysisConsumer &C, AnalysisManager& mgr,
380234353Sdim                                  Decl *D, bool GCEnabled) {
381226633Sdim
382234353Sdim  TransferFuncs* TF = MakeCFRefCountTF(mgr.getASTContext(),
383234353Sdim                                         GCEnabled,
384234353Sdim                                         mgr.getLangOptions());
385234353Sdim
386234353Sdim  ActionExprEngine(C, mgr, D, TF);
387234353Sdim}
388218887Sdim
389234353Sdimstatic void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr,
390234353Sdim                               Decl *D) {
391234353Sdim
392234353Sdim switch (mgr.getLangOptions().getGCMode()) {
393239462Sdim default:
394234353Sdim   assert (false && "Invalid GC mode.");
395234353Sdim case LangOptions::NonGC:
396234353Sdim   ActionObjCMemCheckerAux(C, mgr, D, false);
397239462Sdim   break;
398234353Sdim
399234353Sdim case LangOptions::GCOnly:
400234353Sdim   ActionObjCMemCheckerAux(C, mgr, D, true);
401234353Sdim   break;
402234353Sdim
403234353Sdim case LangOptions::HybridGC:
404234353Sdim   ActionObjCMemCheckerAux(C, mgr, D, false);
405239462Sdim   ActionObjCMemCheckerAux(C, mgr, D, true);
406234353Sdim   break;
407239462Sdim }
408239462Sdim}
409239462Sdim
410239462Sdim//===----------------------------------------------------------------------===//
411239462Sdim// AnalysisConsumer creation.
412239462Sdim//===----------------------------------------------------------------------===//
413239462Sdim
414234353SdimASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp,
415234353Sdim                                           const std::string& OutDir,
416234353Sdim                                           const AnalyzerOptions& Opts) {
417234353Sdim  llvm::OwningPtr<AnalysisConsumer> C(new AnalysisConsumer(pp, OutDir, Opts));
418234353Sdim
419234353Sdim  for (unsigned i = 0; i < Opts.AnalysisList.size(); ++i)
420234353Sdim    switch (Opts.AnalysisList[i]) {
421234353Sdim#define ANALYSIS(NAME, CMD, DESC, SCOPE)\
422234353Sdim    case NAME:\
423234353Sdim      C->add ## SCOPE ## Action(&Action ## NAME);\
424234353Sdim      break;
425234353Sdim#include "clang/Frontend/Analyses.def"
426234353Sdim    default: break;
427239462Sdim    }
428239462Sdim
429234353Sdim  // Last, disable the effects of '-Werror' when using the AnalysisConsumer.
430234353Sdim  pp.getDiagnostics().setWarningsAsErrors(false);
431234353Sdim
432226633Sdim  return C.take();
433234353Sdim}
434226633Sdim
435218887Sdim//===----------------------------------------------------------------------===//
436218887Sdim// Ubigraph Visualization.  FIXME: Move to separate file.
437218887Sdim//===----------------------------------------------------------------------===//
438234353Sdim
439234353Sdimnamespace {
440234353Sdim
441234353Sdimclass UbigraphViz : public ExplodedNode::Auditor {
442218887Sdim  llvm::OwningPtr<llvm::raw_ostream> Out;
443234353Sdim  llvm::sys::Path Dir, Filename;
444234353Sdim  unsigned Cntr;
445223017Sdim
446234353Sdim  typedef llvm::DenseMap<void*,unsigned> VMap;
447234353Sdim  VMap M;
448234353Sdim
449234353Sdimpublic:
450234353Sdim  UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
451234353Sdim              llvm::sys::Path& filename);
452234353Sdim
453234353Sdim  ~UbigraphViz();
454234353Sdim
455234353Sdim  virtual void AddEdge(ExplodedNode* Src, ExplodedNode* Dst);
456234353Sdim};
457234353Sdim
458239462Sdim} // end anonymous namespace
459239462Sdim
460239462Sdimstatic ExplodedNode::Auditor* CreateUbiViz() {
461239462Sdim  std::string ErrMsg;
462239462Sdim
463239462Sdim  llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg);
464239462Sdim  if (!ErrMsg.empty())
465239462Sdim    return 0;
466239462Sdim
467234353Sdim  llvm::sys::Path Filename = Dir;
468234353Sdim  Filename.appendComponent("llvm_ubi");
469239462Sdim  Filename.makeUnique(true,&ErrMsg);
470234353Sdim
471234353Sdim  if (!ErrMsg.empty())
472234353Sdim    return 0;
473234353Sdim
474234353Sdim  llvm::errs() << "Writing '" << Filename.str() << "'.\n";
475234353Sdim
476234353Sdim  llvm::OwningPtr<llvm::raw_fd_ostream> Stream;
477226633Sdim  Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg));
478218887Sdim
479226633Sdim  if (!ErrMsg.empty())
480218887Sdim    return 0;
481218887Sdim
482234353Sdim  return new UbigraphViz(Stream.take(), Dir, Filename);
483234353Sdim}
484234353Sdim
485234353Sdimvoid UbigraphViz::AddEdge(ExplodedNode* Src, ExplodedNode* Dst) {
486234353Sdim
487234353Sdim  assert (Src != Dst && "Self-edges are not allowed.");
488234353Sdim
489234353Sdim  // Lookup the Src.  If it is a new node, it's a root.
490234353Sdim  VMap::iterator SrcI= M.find(Src);
491234353Sdim  unsigned SrcID;
492218887Sdim
493218887Sdim  if (SrcI == M.end()) {
494226633Sdim    M[Src] = SrcID = Cntr++;
495218887Sdim    *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n";
496218887Sdim  }
497218887Sdim  else
498218887Sdim    SrcID = SrcI->second;
499218887Sdim
500218887Sdim  // Lookup the Dst.
501218887Sdim  VMap::iterator DstI= M.find(Dst);
502218887Sdim  unsigned DstID;
503218887Sdim
504234353Sdim  if (DstI == M.end()) {
505234353Sdim    M[Dst] = DstID = Cntr++;
506234353Sdim    *Out << "('vertex', " << DstID << ")\n";
507234353Sdim  }
508234353Sdim  else {
509234353Sdim    // We have hit DstID before.  Change its style to reflect a cache hit.
510234353Sdim    DstID = DstI->second;
511234353Sdim    *Out << "('change_vertex_style', " << DstID << ", 1)\n";
512234353Sdim  }
513234353Sdim
514234353Sdim  // Add the edge.
515218887Sdim  *Out << "('edge', " << SrcID << ", " << DstID
516234353Sdim       << ", ('arrow','true'), ('oriented', 'true'))\n";
517234353Sdim}
518234353Sdim
519234353SdimUbigraphViz::UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
520221345Sdim                         llvm::sys::Path& filename)
521218887Sdim  : Out(out), Dir(dir), Filename(filename), Cntr(0) {
522218887Sdim
523218887Sdim  *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n";
524226633Sdim  *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66'),"
525218887Sdim          " ('size', '1.5'))\n";
526234353Sdim}
527234353Sdim
528234353SdimUbigraphViz::~UbigraphViz() {
529234353Sdim  Out.reset(0);
530234353Sdim  llvm::errs() << "Running 'ubiviz' program... ";
531234353Sdim  std::string ErrMsg;
532234353Sdim  llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz");
533234353Sdim  std::vector<const char*> args;
534218887Sdim  args.push_back(Ubiviz.c_str());
535218887Sdim  args.push_back(Filename.c_str());
536234353Sdim  args.push_back(0);
537239462Sdim
538239462Sdim  if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) {
539239462Sdim    llvm::errs() << "Error viewing graph: " << ErrMsg << "\n";
540239462Sdim  }
541239462Sdim
542234353Sdim  // Delete the directory.
543239462Sdim  Dir.eraseFromDisk(true);
544234353Sdim}
545218887Sdim