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 49 using namespace clang; 50 using namespace ento; 51 using llvm::SmallPtrSet; 52 53 static ExplodedNode::Auditor* CreateUbiViz(); 54 55 STATISTIC(NumFunctionTopLevel, "The # of functions at top level."); 56 STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level)."); 57 STATISTIC(NumBlocksInAnalyzedFunctions, 58 "The # of basic blocks in the analyzed functions."); 59 STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); 60 61 //===----------------------------------------------------------------------===// 62 // Special PathDiagnosticConsumers. 63 //===----------------------------------------------------------------------===// 64 65 static PathDiagnosticConsumer* 66 createPlistHTMLDiagnosticConsumer(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 77 namespace { 78 79 class 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 92 public: 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(const unsigned LocalTUDeclsSize); 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 278 private: 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 //===----------------------------------------------------------------------===// 291 llvm::Timer* AnalysisConsumer::TUTotalTimer = 0; 292 293 bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) { 294 storeTopLevelDecls(DG); 295 return true; 296 } 297 298 void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) { 299 storeTopLevelDecls(DG); 300 } 301 302 void 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 314 void AnalysisConsumer::HandleDeclsGallGraph(const unsigned LocalTUDeclsSize) { 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: CallGraph can trigger deserialization of more items from a pch 321 // (though HandleInterestingDecl); triggering additions to LocalTUDecls. 322 // We rely on random access to add the initially processed Decls to CG. 323 for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { 324 CG.addToCallGraph(LocalTUDecls[i]); 325 } 326 327 // Find the top level nodes - children of root + the unreachable (parentless) 328 // nodes. 329 llvm::SmallVector<CallGraphNode*, 24> TopLevelFunctions; 330 for (CallGraph::nodes_iterator TI = CG.parentless_begin(), 331 TE = CG.parentless_end(); TI != TE; ++TI) { 332 TopLevelFunctions.push_back(*TI); 333 NumFunctionTopLevel++; 334 } 335 CallGraphNode *Entry = CG.getRoot(); 336 for (CallGraphNode::iterator I = Entry->begin(), 337 E = Entry->end(); I != E; ++I) { 338 TopLevelFunctions.push_back(*I); 339 NumFunctionTopLevel++; 340 } 341 342 // Make sure the nodes are sorted in order reverse of their definition in the 343 // translation unit. This step is very important for performance. It ensures 344 // that we analyze the root functions before the externally available 345 // subroutines. 346 std::deque<CallGraphNode*> BFSQueue; 347 for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator 348 TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend(); 349 TI != TE; ++TI) 350 BFSQueue.push_front(*TI); 351 352 // BFS over all of the functions, while skipping the ones inlined into 353 // the previously processed functions. Use external Visited set, which is 354 // also modified when we inline a function. 355 SmallPtrSet<CallGraphNode*,24> Visited; 356 while(!BFSQueue.empty()) { 357 CallGraphNode *N = BFSQueue.front(); 358 BFSQueue.pop_front(); 359 360 // Skip the functions which have been processed already or previously 361 // inlined. 362 if (Visited.count(N)) 363 continue; 364 365 // Analyze the function. 366 SetOfConstDecls VisitedCallees; 367 Decl *D = N->getDecl(); 368 assert(D); 369 HandleCode(D, ANALYSIS_PATH, 370 (Mgr->InliningMode == All ? 0 : &VisitedCallees)); 371 372 // Add the visited callees to the global visited set. 373 for (SetOfConstDecls::iterator I = VisitedCallees.begin(), 374 E = VisitedCallees.end(); I != E; ++I) { 375 CallGraphNode *VN = CG.getNode(*I); 376 if (VN) 377 Visited.insert(VN); 378 } 379 Visited.insert(N); 380 381 // Push the children into the queue. 382 for (CallGraphNode::const_iterator CI = N->begin(), 383 CE = N->end(); CI != CE; ++CI) { 384 BFSQueue.push_front(*CI); 385 } 386 } 387 } 388 389 void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { 390 // Don't run the actions if an error has occurred with parsing the file. 391 DiagnosticsEngine &Diags = PP.getDiagnostics(); 392 if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) 393 return; 394 395 { 396 if (TUTotalTimer) TUTotalTimer->startTimer(); 397 398 // Introduce a scope to destroy BR before Mgr. 399 BugReporter BR(*Mgr); 400 TranslationUnitDecl *TU = C.getTranslationUnitDecl(); 401 checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); 402 403 // Run the AST-only checks using the order in which functions are defined. 404 // If inlining is not turned on, use the simplest function order for path 405 // sensitive analyzes as well. 406 RecVisitorMode = (Mgr->shouldInlineCall() ? ANALYSIS_SYNTAX : ANALYSIS_ALL); 407 RecVisitorBR = &BR; 408 409 // Process all the top level declarations. 410 // 411 // Note: TraverseDecl may modify LocalTUDecls, but only by appending more 412 // entries. Thus we don't use an iterator, but rely on LocalTUDecls 413 // random access. By doing so, we automatically compensate for iterators 414 // possibly being invalidated, although this is a bit slower. 415 const unsigned LocalTUDeclsSize = LocalTUDecls.size(); 416 for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { 417 TraverseDecl(LocalTUDecls[i]); 418 } 419 420 if (Mgr->shouldInlineCall()) 421 HandleDeclsGallGraph(LocalTUDeclsSize); 422 423 // After all decls handled, run checkers on the entire TranslationUnit. 424 checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); 425 426 RecVisitorBR = 0; 427 } 428 429 // Explicitly destroy the PathDiagnosticConsumer. This will flush its output. 430 // FIXME: This should be replaced with something that doesn't rely on 431 // side-effects in PathDiagnosticConsumer's destructor. This is required when 432 // used with option -disable-free. 433 Mgr.reset(NULL); 434 435 if (TUTotalTimer) TUTotalTimer->stopTimer(); 436 437 // Count how many basic blocks we have not covered. 438 NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks(); 439 if (NumBlocksInAnalyzedFunctions > 0) 440 PercentReachableBlocks = 441 (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) / 442 NumBlocksInAnalyzedFunctions; 443 444 } 445 446 static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) { 447 if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) 448 WL.push_back(BD); 449 450 for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); 451 I!=E; ++I) 452 if (DeclContext *DC = dyn_cast<DeclContext>(*I)) 453 FindBlocks(DC, WL); 454 } 455 456 static std::string getFunctionName(const Decl *D) { 457 if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) { 458 return ID->getSelector().getAsString(); 459 } 460 if (const FunctionDecl *ND = dyn_cast<FunctionDecl>(D)) { 461 IdentifierInfo *II = ND->getIdentifier(); 462 if (II) 463 return II->getName(); 464 } 465 return ""; 466 } 467 468 bool AnalysisConsumer::skipFunction(Decl *D) { 469 if (!Opts.AnalyzeSpecificFunction.empty() && 470 getFunctionName(D) != Opts.AnalyzeSpecificFunction) 471 return true; 472 473 // Don't run the actions on declarations in header files unless 474 // otherwise specified. 475 SourceManager &SM = Ctx->getSourceManager(); 476 SourceLocation SL = SM.getExpansionLoc(D->getLocation()); 477 if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL)) 478 return true; 479 480 return false; 481 } 482 483 void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, 484 SetOfConstDecls *VisitedCallees) { 485 if (skipFunction(D)) 486 return; 487 488 DisplayFunction(D, Mode); 489 490 // Clear the AnalysisManager of old AnalysisDeclContexts. 491 Mgr->ClearContexts(); 492 493 // Dispatch on the actions. 494 SmallVector<Decl*, 10> WL; 495 WL.push_back(D); 496 497 if (D->hasBody() && Opts.AnalyzeNestedBlocks) 498 FindBlocks(cast<DeclContext>(D), WL); 499 500 BugReporter BR(*Mgr); 501 for (SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end(); 502 WI != WE; ++WI) 503 if ((*WI)->hasBody()) { 504 if (Mode != ANALYSIS_PATH) 505 checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR); 506 if (Mode != ANALYSIS_SYNTAX && checkerMgr->hasPathSensitiveCheckers()) { 507 RunPathSensitiveChecks(*WI, VisitedCallees); 508 NumFunctionsAnalyzed++; 509 } 510 } 511 } 512 513 //===----------------------------------------------------------------------===// 514 // Path-sensitive checking. 515 //===----------------------------------------------------------------------===// 516 517 void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, 518 SetOfConstDecls *VisitedCallees) { 519 // Construct the analysis engine. First check if the CFG is valid. 520 // FIXME: Inter-procedural analysis will need to handle invalid CFGs. 521 if (!Mgr->getCFG(D)) 522 return; 523 524 ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries); 525 526 // Set the graph auditor. 527 OwningPtr<ExplodedNode::Auditor> Auditor; 528 if (Mgr->shouldVisualizeUbigraph()) { 529 Auditor.reset(CreateUbiViz()); 530 ExplodedNode::SetAuditor(Auditor.get()); 531 } 532 533 // Execute the worklist algorithm. 534 Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D), 535 Mgr->getMaxNodes()); 536 537 // Release the auditor (if any) so that it doesn't monitor the graph 538 // created BugReporter. 539 ExplodedNode::SetAuditor(0); 540 541 // Visualize the exploded graph. 542 if (Mgr->shouldVisualizeGraphviz()) 543 Eng.ViewGraph(Mgr->shouldTrimGraph()); 544 545 // Display warnings. 546 Eng.getBugReporter().FlushReports(); 547 } 548 549 void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, 550 SetOfConstDecls *Visited) { 551 552 switch (Mgr->getLangOpts().getGC()) { 553 case LangOptions::NonGC: 554 ActionExprEngine(D, false, Visited); 555 break; 556 557 case LangOptions::GCOnly: 558 ActionExprEngine(D, true, Visited); 559 break; 560 561 case LangOptions::HybridGC: 562 ActionExprEngine(D, false, Visited); 563 ActionExprEngine(D, true, Visited); 564 break; 565 } 566 } 567 568 //===----------------------------------------------------------------------===// 569 // AnalysisConsumer creation. 570 //===----------------------------------------------------------------------===// 571 572 ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp, 573 const std::string& outDir, 574 const AnalyzerOptions& opts, 575 ArrayRef<std::string> plugins) { 576 // Disable the effects of '-Werror' when using the AnalysisConsumer. 577 pp.getDiagnostics().setWarningsAsErrors(false); 578 579 return new AnalysisConsumer(pp, outDir, opts, plugins); 580 } 581 582 //===----------------------------------------------------------------------===// 583 // Ubigraph Visualization. FIXME: Move to separate file. 584 //===----------------------------------------------------------------------===// 585 586 namespace { 587 588 class UbigraphViz : public ExplodedNode::Auditor { 589 OwningPtr<raw_ostream> Out; 590 llvm::sys::Path Dir, Filename; 591 unsigned Cntr; 592 593 typedef llvm::DenseMap<void*,unsigned> VMap; 594 VMap M; 595 596 public: 597 UbigraphViz(raw_ostream *out, llvm::sys::Path& dir, 598 llvm::sys::Path& filename); 599 600 ~UbigraphViz(); 601 602 virtual void AddEdge(ExplodedNode *Src, ExplodedNode *Dst); 603 }; 604 605 } // end anonymous namespace 606 607 static ExplodedNode::Auditor* CreateUbiViz() { 608 std::string ErrMsg; 609 610 llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg); 611 if (!ErrMsg.empty()) 612 return 0; 613 614 llvm::sys::Path Filename = Dir; 615 Filename.appendComponent("llvm_ubi"); 616 Filename.makeUnique(true,&ErrMsg); 617 618 if (!ErrMsg.empty()) 619 return 0; 620 621 llvm::errs() << "Writing '" << Filename.str() << "'.\n"; 622 623 OwningPtr<llvm::raw_fd_ostream> Stream; 624 Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg)); 625 626 if (!ErrMsg.empty()) 627 return 0; 628 629 return new UbigraphViz(Stream.take(), Dir, Filename); 630 } 631 632 void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) { 633 634 assert (Src != Dst && "Self-edges are not allowed."); 635 636 // Lookup the Src. If it is a new node, it's a root. 637 VMap::iterator SrcI= M.find(Src); 638 unsigned SrcID; 639 640 if (SrcI == M.end()) { 641 M[Src] = SrcID = Cntr++; 642 *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n"; 643 } 644 else 645 SrcID = SrcI->second; 646 647 // Lookup the Dst. 648 VMap::iterator DstI= M.find(Dst); 649 unsigned DstID; 650 651 if (DstI == M.end()) { 652 M[Dst] = DstID = Cntr++; 653 *Out << "('vertex', " << DstID << ")\n"; 654 } 655 else { 656 // We have hit DstID before. Change its style to reflect a cache hit. 657 DstID = DstI->second; 658 *Out << "('change_vertex_style', " << DstID << ", 1)\n"; 659 } 660 661 // Add the edge. 662 *Out << "('edge', " << SrcID << ", " << DstID 663 << ", ('arrow','true'), ('oriented', 'true'))\n"; 664 } 665 666 UbigraphViz::UbigraphViz(raw_ostream *out, llvm::sys::Path& dir, 667 llvm::sys::Path& filename) 668 : Out(out), Dir(dir), Filename(filename), Cntr(0) { 669 670 *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n"; 671 *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66')," 672 " ('size', '1.5'))\n"; 673 } 674 675 UbigraphViz::~UbigraphViz() { 676 Out.reset(0); 677 llvm::errs() << "Running 'ubiviz' program... "; 678 std::string ErrMsg; 679 llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz"); 680 std::vector<const char*> args; 681 args.push_back(Ubiviz.c_str()); 682 args.push_back(Filename.c_str()); 683 args.push_back(0); 684 685 if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) { 686 llvm::errs() << "Error viewing graph: " << ErrMsg << "\n"; 687 } 688 689 // Delete the directory. 690 Dir.eraseFromDisk(true); 691 } 692