AnalysisConsumer.cpp revision 3bbd8cd831788c506f2980293eb3c7e1b3ca2501
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/Statistic.h" 45 46#include <queue> 47 48using namespace clang; 49using namespace ento; 50using llvm::SmallPtrSet; 51 52static ExplodedNode::Auditor* CreateUbiViz(); 53 54STATISTIC(NumFunctionTopLevel, "The # of functions at top level."); 55STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level)."); 56 57//===----------------------------------------------------------------------===// 58// Special PathDiagnosticConsumers. 59//===----------------------------------------------------------------------===// 60 61static PathDiagnosticConsumer* 62createPlistHTMLDiagnosticConsumer(const std::string& prefix, 63 const Preprocessor &PP) { 64 PathDiagnosticConsumer *PD = 65 createHTMLDiagnosticConsumer(llvm::sys::path::parent_path(prefix), PP); 66 return createPlistDiagnosticConsumer(prefix, PP, PD); 67} 68 69//===----------------------------------------------------------------------===// 70// AnalysisConsumer declaration. 71//===----------------------------------------------------------------------===// 72 73namespace { 74 75class AnalysisConsumer : public ASTConsumer, 76 public RecursiveASTVisitor<AnalysisConsumer> { 77 enum AnalysisMode { 78 ANALYSIS_SYNTAX, 79 ANALYSIS_PATH, 80 ANALYSIS_ALL 81 }; 82 83 /// Mode of the analyzes while recursively visiting Decls. 84 AnalysisMode RecVisitorMode; 85 /// Bug Reporter to use while recursively visiting Decls. 86 BugReporter *RecVisitorBR; 87 88public: 89 ASTContext *Ctx; 90 const Preprocessor &PP; 91 const std::string OutDir; 92 AnalyzerOptions Opts; 93 ArrayRef<std::string> Plugins; 94 95 // PD is owned by AnalysisManager. 96 PathDiagnosticConsumer *PD; 97 98 StoreManagerCreator CreateStoreMgr; 99 ConstraintManagerCreator CreateConstraintMgr; 100 101 OwningPtr<CheckerManager> checkerMgr; 102 OwningPtr<AnalysisManager> Mgr; 103 104 /// Time the analyzes time of each translation unit. 105 static llvm::Timer* TUTotalTimer; 106 107 /// The information about analyzed functions shared throughout the 108 /// translation unit. 109 FunctionSummariesTy FunctionSummaries; 110 111 AnalysisConsumer(const Preprocessor& pp, 112 const std::string& outdir, 113 const AnalyzerOptions& opts, 114 ArrayRef<std::string> plugins) 115 : RecVisitorMode(ANALYSIS_ALL), RecVisitorBR(0), 116 Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) { 117 DigestAnalyzerOptions(); 118 if (Opts.PrintStats) { 119 llvm::EnableStatistics(); 120 TUTotalTimer = new llvm::Timer("Analyzer Total Time"); 121 } 122 } 123 124 ~AnalysisConsumer() { 125 if (Opts.PrintStats) 126 delete TUTotalTimer; 127 } 128 129 void DigestAnalyzerOptions() { 130 // Create the PathDiagnosticConsumer. 131 if (!OutDir.empty()) { 132 switch (Opts.AnalysisDiagOpt) { 133 default: 134#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \ 135 case PD_##NAME: PD = CREATEFN(OutDir, PP); break; 136#include "clang/Frontend/Analyses.def" 137 } 138 } else if (Opts.AnalysisDiagOpt == PD_TEXT) { 139 // Create the text client even without a specified output file since 140 // it just uses diagnostic notes. 141 PD = createTextPathDiagnosticConsumer("", PP); 142 } 143 144 // Create the analyzer component creators. 145 switch (Opts.AnalysisStoreOpt) { 146 default: 147 llvm_unreachable("Unknown store manager."); 148#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \ 149 case NAME##Model: CreateStoreMgr = CREATEFN; break; 150#include "clang/Frontend/Analyses.def" 151 } 152 153 switch (Opts.AnalysisConstraintsOpt) { 154 default: 155 llvm_unreachable("Unknown store manager."); 156#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ 157 case NAME##Model: CreateConstraintMgr = CREATEFN; break; 158#include "clang/Frontend/Analyses.def" 159 } 160 } 161 162 void DisplayFunction(const Decl *D, AnalysisMode Mode) { 163 if (!Opts.AnalyzerDisplayProgress) 164 return; 165 166 SourceManager &SM = Mgr->getASTContext().getSourceManager(); 167 PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); 168 if (Loc.isValid()) { 169 llvm::errs() << "ANALYZE"; 170 switch (Mode) { 171 case ANALYSIS_SYNTAX: llvm::errs() << "(Syntax)"; break; 172 case ANALYSIS_PATH: llvm::errs() << "(Path Sensitive)"; break; 173 case ANALYSIS_ALL: break; 174 }; 175 llvm::errs() << ": " << Loc.getFilename(); 176 if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { 177 const NamedDecl *ND = cast<NamedDecl>(D); 178 llvm::errs() << ' ' << *ND << '\n'; 179 } 180 else if (isa<BlockDecl>(D)) { 181 llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:" 182 << Loc.getColumn() << '\n'; 183 } 184 else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { 185 Selector S = MD->getSelector(); 186 llvm::errs() << ' ' << S.getAsString(); 187 } 188 } 189 } 190 191 virtual void Initialize(ASTContext &Context) { 192 Ctx = &Context; 193 checkerMgr.reset(createCheckerManager(Opts, PP.getLangOpts(), Plugins, 194 PP.getDiagnostics())); 195 Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(), 196 PP.getLangOpts(), PD, 197 CreateStoreMgr, CreateConstraintMgr, 198 checkerMgr.get(), 199 /* Indexer */ 0, 200 Opts.MaxNodes, Opts.MaxLoop, 201 Opts.VisualizeEGDot, Opts.VisualizeEGUbi, 202 Opts.AnalysisPurgeOpt, Opts.EagerlyAssume, 203 Opts.TrimGraph, 204 Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors, 205 Opts.CFGAddInitializers, 206 Opts.EagerlyTrimEGraph, 207 Opts.IPAMode, 208 Opts.InlineMaxStackDepth, 209 Opts.InlineMaxFunctionSize, 210 Opts.InliningMode, 211 Opts.NoRetryExhausted)); 212 } 213 214 virtual void HandleTranslationUnit(ASTContext &C); 215 216 /// \brief Build the call graph for the TU and use it to define the order 217 /// in which the functions should be visited. 218 void HandleDeclsGallGraph(TranslationUnitDecl *TU); 219 220 /// \brief Run analyzes(syntax or path sensitive) on the given function. 221 /// \param Mode - determines if we are requesting syntax only or path 222 /// sensitive only analysis. 223 /// \param VisitedCallees - The output parameter, which is populated with the 224 /// set of functions which should be considered analyzed after analyzing the 225 /// given root function. 226 void HandleCode(Decl *D, AnalysisMode Mode, SetOfDecls *VisitedCallees = 0); 227 228 /// \brief Check if we should skip (not analyze) the given function. 229 bool skipFunction(Decl *D); 230 231 void RunPathSensitiveChecks(Decl *D, SetOfDecls *VisitedCallees); 232 void ActionExprEngine(Decl *D, bool ObjCGCEnabled, SetOfDecls *VisitedCallees); 233 234 /// Visitors for the RecursiveASTVisitor. 235 236 /// Handle callbacks for arbitrary Decls. 237 bool VisitDecl(Decl *D) { 238 checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR); 239 return true; 240 } 241 242 bool VisitFunctionDecl(FunctionDecl *FD) { 243 IdentifierInfo *II = FD->getIdentifier(); 244 if (II && II->getName().startswith("__inline")) 245 return true; 246 247 // We skip function template definitions, as their semantics is 248 // only determined when they are instantiated. 249 if (FD->isThisDeclarationADefinition() && 250 !FD->isDependentContext()) { 251 HandleCode(FD, RecVisitorMode); 252 } 253 return true; 254 } 255 256 bool VisitObjCMethodDecl(ObjCMethodDecl *MD) { 257 checkerMgr->runCheckersOnASTDecl(MD, *Mgr, *RecVisitorBR); 258 if (MD->isThisDeclarationADefinition()) 259 HandleCode(MD, RecVisitorMode); 260 return true; 261 } 262}; 263} // end anonymous namespace 264 265 266//===----------------------------------------------------------------------===// 267// AnalysisConsumer implementation. 268//===----------------------------------------------------------------------===// 269llvm::Timer* AnalysisConsumer::TUTotalTimer = 0; 270 271void AnalysisConsumer::HandleDeclsGallGraph(TranslationUnitDecl *TU) { 272 // Otherwise, use the Callgraph to derive the order. 273 // Build the Call Graph. 274 CallGraph CG; 275 CG.addToCallGraph(TU); 276 277 // Find the top level nodes - children of root + the unreachable (parentless) 278 // nodes. 279 llvm::SmallVector<CallGraphNode*, 24> TopLevelFunctions; 280 for (CallGraph::nodes_iterator TI = CG.parentless_begin(), 281 TE = CG.parentless_end(); TI != TE; ++TI) { 282 TopLevelFunctions.push_back(*TI); 283 NumFunctionTopLevel++; 284 } 285 CallGraphNode *Entry = CG.getRoot(); 286 for (CallGraphNode::iterator I = Entry->begin(), 287 E = Entry->end(); I != E; ++I) { 288 TopLevelFunctions.push_back(*I); 289 NumFunctionTopLevel++; 290 } 291 292 // Make sure the nodes are sorted in order reverse of their definition in the 293 // translation unit. This step is very important for performance. It ensures 294 // that we analyze the root functions before the externally available 295 // subroutines. 296 std::queue<CallGraphNode*> BFSQueue; 297 for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator 298 TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend(); 299 TI != TE; ++TI) 300 BFSQueue.push(*TI); 301 302 // BFS over all of the functions, while skipping the ones inlined into 303 // the previously processed functions. Use external Visited set, which is 304 // also modified when we inline a function. 305 SmallPtrSet<CallGraphNode*,24> Visited; 306 while(!BFSQueue.empty()) { 307 CallGraphNode *N = BFSQueue.front(); 308 BFSQueue.pop(); 309 310 // Skip the functions which have been processed already or previously 311 // inlined. 312 if (Visited.count(N)) 313 continue; 314 315 // Analyze the function. 316 SetOfDecls VisitedCallees; 317 Decl *D = N->getDecl(); 318 assert(D); 319 HandleCode(D, ANALYSIS_PATH, 320 (Mgr->InliningMode == All ? 0 : &VisitedCallees)); 321 322 // Add the visited callees to the global visited set. 323 for (SetOfDecls::const_iterator I = VisitedCallees.begin(), 324 E = VisitedCallees.end(); I != E; ++I) { 325 CallGraphNode *VN = CG.getNode(*I); 326 if (VN) 327 Visited.insert(VN); 328 } 329 Visited.insert(N); 330 331 // Push the children into the queue. 332 for (CallGraphNode::const_iterator CI = N->begin(), 333 CE = N->end(); CI != CE; ++CI) { 334 BFSQueue.push(*CI); 335 } 336 } 337} 338 339void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { 340 // Don't run the actions if an error has occurred with parsing the file. 341 DiagnosticsEngine &Diags = PP.getDiagnostics(); 342 if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) 343 return; 344 345 { 346 if (TUTotalTimer) TUTotalTimer->startTimer(); 347 348 // Introduce a scope to destroy BR before Mgr. 349 BugReporter BR(*Mgr); 350 TranslationUnitDecl *TU = C.getTranslationUnitDecl(); 351 checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); 352 353 // Run the AST-only checks using the order in which functions are defined. 354 // If inlining is not turned on, use the simplest function order for path 355 // sensitive analyzes as well. 356 RecVisitorMode = (Mgr->shouldInlineCall() ? ANALYSIS_SYNTAX : ANALYSIS_ALL); 357 RecVisitorBR = &BR; 358 TraverseDecl(TU); 359 360 if (Mgr->shouldInlineCall()) 361 HandleDeclsGallGraph(TU); 362 363 // After all decls handled, run checkers on the entire TranslationUnit. 364 checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); 365 366 RecVisitorBR = 0; 367 } 368 369 // Explicitly destroy the PathDiagnosticConsumer. This will flush its output. 370 // FIXME: This should be replaced with something that doesn't rely on 371 // side-effects in PathDiagnosticConsumer's destructor. This is required when 372 // used with option -disable-free. 373 Mgr.reset(NULL); 374 375 if (TUTotalTimer) TUTotalTimer->stopTimer(); 376} 377 378static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) { 379 if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) 380 WL.push_back(BD); 381 382 for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); 383 I!=E; ++I) 384 if (DeclContext *DC = dyn_cast<DeclContext>(*I)) 385 FindBlocks(DC, WL); 386} 387 388static std::string getFunctionName(const Decl *D) { 389 if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) { 390 return ID->getSelector().getAsString(); 391 } 392 if (const FunctionDecl *ND = dyn_cast<FunctionDecl>(D)) { 393 IdentifierInfo *II = ND->getIdentifier(); 394 if (II) 395 return II->getName(); 396 } 397 return ""; 398} 399 400bool AnalysisConsumer::skipFunction(Decl *D) { 401 if (!Opts.AnalyzeSpecificFunction.empty() && 402 getFunctionName(D) != Opts.AnalyzeSpecificFunction) 403 return true; 404 405 // Don't run the actions on declarations in header files unless 406 // otherwise specified. 407 SourceManager &SM = Ctx->getSourceManager(); 408 SourceLocation SL = SM.getExpansionLoc(D->getLocation()); 409 if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL)) 410 return true; 411 412 return false; 413} 414 415void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, 416 SetOfDecls *VisitedCallees) { 417 if (skipFunction(D)) 418 return; 419 420 DisplayFunction(D, Mode); 421 422 // Clear the AnalysisManager of old AnalysisDeclContexts. 423 Mgr->ClearContexts(); 424 425 // Dispatch on the actions. 426 SmallVector<Decl*, 10> WL; 427 WL.push_back(D); 428 429 if (D->hasBody() && Opts.AnalyzeNestedBlocks) 430 FindBlocks(cast<DeclContext>(D), WL); 431 432 BugReporter BR(*Mgr); 433 for (SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end(); 434 WI != WE; ++WI) 435 if ((*WI)->hasBody()) { 436 if (Mode != ANALYSIS_PATH) 437 checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR); 438 if (Mode != ANALYSIS_SYNTAX && checkerMgr->hasPathSensitiveCheckers()) { 439 RunPathSensitiveChecks(*WI, VisitedCallees); 440 NumFunctionsAnalyzed++; 441 } 442 } 443} 444 445//===----------------------------------------------------------------------===// 446// Path-sensitive checking. 447//===----------------------------------------------------------------------===// 448 449void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, 450 SetOfDecls *VisitedCallees) { 451 // Construct the analysis engine. First check if the CFG is valid. 452 // FIXME: Inter-procedural analysis will need to handle invalid CFGs. 453 if (!Mgr->getCFG(D)) 454 return; 455 456 ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries); 457 458 // Set the graph auditor. 459 OwningPtr<ExplodedNode::Auditor> Auditor; 460 if (Mgr->shouldVisualizeUbigraph()) { 461 Auditor.reset(CreateUbiViz()); 462 ExplodedNode::SetAuditor(Auditor.get()); 463 } 464 465 // Execute the worklist algorithm. 466 Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D, 0), 467 Mgr->getMaxNodes()); 468 469 // Release the auditor (if any) so that it doesn't monitor the graph 470 // created BugReporter. 471 ExplodedNode::SetAuditor(0); 472 473 // Visualize the exploded graph. 474 if (Mgr->shouldVisualizeGraphviz()) 475 Eng.ViewGraph(Mgr->shouldTrimGraph()); 476 477 // Display warnings. 478 Eng.getBugReporter().FlushReports(); 479} 480 481void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, SetOfDecls *Visited) { 482 483 switch (Mgr->getLangOpts().getGC()) { 484 case LangOptions::NonGC: 485 ActionExprEngine(D, false, Visited); 486 break; 487 488 case LangOptions::GCOnly: 489 ActionExprEngine(D, true, Visited); 490 break; 491 492 case LangOptions::HybridGC: 493 ActionExprEngine(D, false, Visited); 494 ActionExprEngine(D, true, Visited); 495 break; 496 } 497} 498 499//===----------------------------------------------------------------------===// 500// AnalysisConsumer creation. 501//===----------------------------------------------------------------------===// 502 503ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp, 504 const std::string& outDir, 505 const AnalyzerOptions& opts, 506 ArrayRef<std::string> plugins) { 507 // Disable the effects of '-Werror' when using the AnalysisConsumer. 508 pp.getDiagnostics().setWarningsAsErrors(false); 509 510 return new AnalysisConsumer(pp, outDir, opts, plugins); 511} 512 513//===----------------------------------------------------------------------===// 514// Ubigraph Visualization. FIXME: Move to separate file. 515//===----------------------------------------------------------------------===// 516 517namespace { 518 519class UbigraphViz : public ExplodedNode::Auditor { 520 OwningPtr<raw_ostream> Out; 521 llvm::sys::Path Dir, Filename; 522 unsigned Cntr; 523 524 typedef llvm::DenseMap<void*,unsigned> VMap; 525 VMap M; 526 527public: 528 UbigraphViz(raw_ostream *out, llvm::sys::Path& dir, 529 llvm::sys::Path& filename); 530 531 ~UbigraphViz(); 532 533 virtual void AddEdge(ExplodedNode *Src, ExplodedNode *Dst); 534}; 535 536} // end anonymous namespace 537 538static ExplodedNode::Auditor* CreateUbiViz() { 539 std::string ErrMsg; 540 541 llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg); 542 if (!ErrMsg.empty()) 543 return 0; 544 545 llvm::sys::Path Filename = Dir; 546 Filename.appendComponent("llvm_ubi"); 547 Filename.makeUnique(true,&ErrMsg); 548 549 if (!ErrMsg.empty()) 550 return 0; 551 552 llvm::errs() << "Writing '" << Filename.str() << "'.\n"; 553 554 OwningPtr<llvm::raw_fd_ostream> Stream; 555 Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg)); 556 557 if (!ErrMsg.empty()) 558 return 0; 559 560 return new UbigraphViz(Stream.take(), Dir, Filename); 561} 562 563void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) { 564 565 assert (Src != Dst && "Self-edges are not allowed."); 566 567 // Lookup the Src. If it is a new node, it's a root. 568 VMap::iterator SrcI= M.find(Src); 569 unsigned SrcID; 570 571 if (SrcI == M.end()) { 572 M[Src] = SrcID = Cntr++; 573 *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n"; 574 } 575 else 576 SrcID = SrcI->second; 577 578 // Lookup the Dst. 579 VMap::iterator DstI= M.find(Dst); 580 unsigned DstID; 581 582 if (DstI == M.end()) { 583 M[Dst] = DstID = Cntr++; 584 *Out << "('vertex', " << DstID << ")\n"; 585 } 586 else { 587 // We have hit DstID before. Change its style to reflect a cache hit. 588 DstID = DstI->second; 589 *Out << "('change_vertex_style', " << DstID << ", 1)\n"; 590 } 591 592 // Add the edge. 593 *Out << "('edge', " << SrcID << ", " << DstID 594 << ", ('arrow','true'), ('oriented', 'true'))\n"; 595} 596 597UbigraphViz::UbigraphViz(raw_ostream *out, llvm::sys::Path& dir, 598 llvm::sys::Path& filename) 599 : Out(out), Dir(dir), Filename(filename), Cntr(0) { 600 601 *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n"; 602 *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66')," 603 " ('size', '1.5'))\n"; 604} 605 606UbigraphViz::~UbigraphViz() { 607 Out.reset(0); 608 llvm::errs() << "Running 'ubiviz' program... "; 609 std::string ErrMsg; 610 llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz"); 611 std::vector<const char*> args; 612 args.push_back(Ubiviz.c_str()); 613 args.push_back(Filename.c_str()); 614 args.push_back(0); 615 616 if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) { 617 llvm::errs() << "Error viewing graph: " << ErrMsg << "\n"; 618 } 619 620 // Delete the directory. 621 Dir.eraseFromDisk(true); 622} 623