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