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