AnalysisConsumer.cpp revision 371b477836f289f2e9caaab58530f187b51bc86d
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/Analysis/Analyses/LiveVariables.h" 26#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" 27#include "clang/StaticAnalyzer/Core/CheckerManager.h" 28#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" 29#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 30#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 31#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 32#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 33#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" 34 35#include "clang/Basic/FileManager.h" 36#include "clang/Basic/SourceManager.h" 37#include "clang/Frontend/AnalyzerOptions.h" 38#include "clang/Lex/Preprocessor.h" 39#include "llvm/Support/raw_ostream.h" 40#include "llvm/Support/Path.h" 41#include "llvm/Support/Program.h" 42#include "llvm/Support/Timer.h" 43#include "llvm/ADT/DepthFirstIterator.h" 44#include "llvm/ADT/OwningPtr.h" 45#include "llvm/ADT/SmallPtrSet.h" 46#include "llvm/ADT/Statistic.h" 47 48#include <queue> 49 50using namespace clang; 51using namespace ento; 52using llvm::SmallPtrSet; 53 54static ExplodedNode::Auditor* CreateUbiViz(); 55 56STATISTIC(NumFunctionTopLevel, "The # of functions at top level."); 57STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level)."); 58STATISTIC(NumBlocksInAnalyzedFunctions, 59 "The # of basic blocks in the analyzed functions."); 60STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); 61 62//===----------------------------------------------------------------------===// 63// Special PathDiagnosticConsumers. 64//===----------------------------------------------------------------------===// 65 66static PathDiagnosticConsumer* 67createPlistHTMLDiagnosticConsumer(const std::string& prefix, 68 const Preprocessor &PP) { 69 PathDiagnosticConsumer *PD = 70 createHTMLDiagnosticConsumer(llvm::sys::path::parent_path(prefix), PP); 71 return createPlistDiagnosticConsumer(prefix, PP, PD); 72} 73 74//===----------------------------------------------------------------------===// 75// AnalysisConsumer declaration. 76//===----------------------------------------------------------------------===// 77 78namespace { 79 80class AnalysisConsumer : public ASTConsumer, 81 public RecursiveASTVisitor<AnalysisConsumer> { 82 enum AnalysisMode { 83 ANALYSIS_SYNTAX, 84 ANALYSIS_PATH, 85 ANALYSIS_ALL 86 }; 87 88 /// Mode of the analyzes while recursively visiting Decls. 89 AnalysisMode RecVisitorMode; 90 /// Bug Reporter to use while recursively visiting Decls. 91 BugReporter *RecVisitorBR; 92 93public: 94 ASTContext *Ctx; 95 const Preprocessor &PP; 96 const std::string OutDir; 97 AnalyzerOptions Opts; 98 ArrayRef<std::string> Plugins; 99 100 /// \brief Stores the declarations from the local translation unit. 101 /// Note, we pre-compute the local declarations at parse time as an 102 /// optimization to make sure we do not deserialize everything from disk. 103 /// The local declaration to all declarations ratio might be very small when 104 /// working with a PCH file. 105 SetOfDecls LocalTUDecls; 106 107 // PD is owned by AnalysisManager. 108 PathDiagnosticConsumer *PD; 109 110 StoreManagerCreator CreateStoreMgr; 111 ConstraintManagerCreator CreateConstraintMgr; 112 113 OwningPtr<CheckerManager> checkerMgr; 114 OwningPtr<AnalysisManager> Mgr; 115 116 /// Time the analyzes time of each translation unit. 117 static llvm::Timer* TUTotalTimer; 118 119 /// The information about analyzed functions shared throughout the 120 /// translation unit. 121 FunctionSummariesTy FunctionSummaries; 122 123 AnalysisConsumer(const Preprocessor& pp, 124 const std::string& outdir, 125 const AnalyzerOptions& opts, 126 ArrayRef<std::string> plugins) 127 : RecVisitorMode(ANALYSIS_ALL), RecVisitorBR(0), 128 Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) { 129 DigestAnalyzerOptions(); 130 if (Opts.PrintStats) { 131 llvm::EnableStatistics(); 132 TUTotalTimer = new llvm::Timer("Analyzer Total Time"); 133 } 134 } 135 136 ~AnalysisConsumer() { 137 if (Opts.PrintStats) 138 delete TUTotalTimer; 139 } 140 141 void DigestAnalyzerOptions() { 142 // Create the PathDiagnosticConsumer. 143 if (!OutDir.empty()) { 144 switch (Opts.AnalysisDiagOpt) { 145 default: 146#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \ 147 case PD_##NAME: PD = CREATEFN(OutDir, PP); break; 148#include "clang/Frontend/Analyses.def" 149 } 150 } else if (Opts.AnalysisDiagOpt == PD_TEXT) { 151 // Create the text client even without a specified output file since 152 // it just uses diagnostic notes. 153 PD = createTextPathDiagnosticConsumer("", PP); 154 } 155 156 // Create the analyzer component creators. 157 switch (Opts.AnalysisStoreOpt) { 158 default: 159 llvm_unreachable("Unknown store manager."); 160#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \ 161 case NAME##Model: CreateStoreMgr = CREATEFN; break; 162#include "clang/Frontend/Analyses.def" 163 } 164 165 switch (Opts.AnalysisConstraintsOpt) { 166 default: 167 llvm_unreachable("Unknown store manager."); 168#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ 169 case NAME##Model: CreateConstraintMgr = CREATEFN; break; 170#include "clang/Frontend/Analyses.def" 171 } 172 } 173 174 void DisplayFunction(const Decl *D, AnalysisMode Mode) { 175 if (!Opts.AnalyzerDisplayProgress) 176 return; 177 178 SourceManager &SM = Mgr->getASTContext().getSourceManager(); 179 PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); 180 if (Loc.isValid()) { 181 llvm::errs() << "ANALYZE"; 182 switch (Mode) { 183 case ANALYSIS_SYNTAX: llvm::errs() << "(Syntax)"; break; 184 case ANALYSIS_PATH: llvm::errs() << "(Path Sensitive)"; break; 185 case ANALYSIS_ALL: break; 186 }; 187 llvm::errs() << ": " << Loc.getFilename(); 188 if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { 189 const NamedDecl *ND = cast<NamedDecl>(D); 190 llvm::errs() << ' ' << *ND << '\n'; 191 } 192 else if (isa<BlockDecl>(D)) { 193 llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:" 194 << Loc.getColumn() << '\n'; 195 } 196 else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { 197 Selector S = MD->getSelector(); 198 llvm::errs() << ' ' << S.getAsString(); 199 } 200 } 201 } 202 203 virtual void Initialize(ASTContext &Context) { 204 Ctx = &Context; 205 checkerMgr.reset(createCheckerManager(Opts, PP.getLangOpts(), Plugins, 206 PP.getDiagnostics())); 207 Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(), 208 PP.getLangOpts(), PD, 209 CreateStoreMgr, CreateConstraintMgr, 210 checkerMgr.get(), 211 Opts.MaxNodes, Opts.MaxLoop, 212 Opts.VisualizeEGDot, Opts.VisualizeEGUbi, 213 Opts.AnalysisPurgeOpt, Opts.EagerlyAssume, 214 Opts.TrimGraph, 215 Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors, 216 Opts.CFGAddInitializers, 217 Opts.EagerlyTrimEGraph, 218 Opts.IPAMode, 219 Opts.InlineMaxStackDepth, 220 Opts.InlineMaxFunctionSize, 221 Opts.InliningMode, 222 Opts.NoRetryExhausted)); 223 } 224 225 /// \brief Store the top level decls in the set to be processed later on. 226 /// (Doing this pre-processing avoids deserialization of data from PCH.) 227 virtual bool HandleTopLevelDecl(DeclGroupRef D); 228 virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D); 229 230 virtual void HandleTranslationUnit(ASTContext &C); 231 232 /// \brief Build the call graph for all the top level decls of this TU and 233 /// use it to define the order in which the functions should be visited. 234 void HandleDeclsGallGraph(const unsigned LocalTUDeclsSize); 235 236 /// \brief Run analyzes(syntax or path sensitive) on the given function. 237 /// \param Mode - determines if we are requesting syntax only or path 238 /// sensitive only analysis. 239 /// \param VisitedCallees - The output parameter, which is populated with the 240 /// set of functions which should be considered analyzed after analyzing the 241 /// given root function. 242 void HandleCode(Decl *D, AnalysisMode Mode, 243 SetOfConstDecls *VisitedCallees = 0); 244 245 void RunPathSensitiveChecks(Decl *D, SetOfConstDecls *VisitedCallees); 246 void ActionExprEngine(Decl *D, bool ObjCGCEnabled, 247 SetOfConstDecls *VisitedCallees); 248 249 /// Visitors for the RecursiveASTVisitor. 250 bool shouldWalkTypesOfTypeLocs() const { return false; } 251 252 /// Handle callbacks for arbitrary Decls. 253 bool VisitDecl(Decl *D) { 254 checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR); 255 return true; 256 } 257 258 bool VisitFunctionDecl(FunctionDecl *FD) { 259 IdentifierInfo *II = FD->getIdentifier(); 260 if (II && II->getName().startswith("__inline")) 261 return true; 262 263 // We skip function template definitions, as their semantics is 264 // only determined when they are instantiated. 265 if (FD->isThisDeclarationADefinition() && 266 !FD->isDependentContext()) { 267 HandleCode(FD, RecVisitorMode); 268 } 269 return true; 270 } 271 272 bool VisitObjCMethodDecl(ObjCMethodDecl *MD) { 273 checkerMgr->runCheckersOnASTDecl(MD, *Mgr, *RecVisitorBR); 274 if (MD->isThisDeclarationADefinition()) 275 HandleCode(MD, RecVisitorMode); 276 return true; 277 } 278 279private: 280 void storeTopLevelDecls(DeclGroupRef DG); 281 282 /// \brief Check if we should skip (not analyze) the given function. 283 bool skipFunction(Decl *D); 284 285}; 286} // end anonymous namespace 287 288 289//===----------------------------------------------------------------------===// 290// AnalysisConsumer implementation. 291//===----------------------------------------------------------------------===// 292llvm::Timer* AnalysisConsumer::TUTotalTimer = 0; 293 294bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) { 295 storeTopLevelDecls(DG); 296 return true; 297} 298 299void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) { 300 storeTopLevelDecls(DG); 301} 302 303void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) { 304 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { 305 306 // Skip ObjCMethodDecl, wait for the objc container to avoid 307 // analyzing twice. 308 if (isa<ObjCMethodDecl>(*I)) 309 continue; 310 311 LocalTUDecls.push_back(*I); 312 } 313} 314 315void AnalysisConsumer::HandleDeclsGallGraph(const unsigned LocalTUDeclsSize) { 316 // Otherwise, use the Callgraph to derive the order. 317 // Build the Call Graph. 318 CallGraph CG; 319 320 // Add all the top level declarations to the graph. 321 // Note: CallGraph can trigger deserialization of more items from a pch 322 // (though HandleInterestingDecl); triggering additions to LocalTUDecls. 323 // We rely on random access to add the initially processed Decls to CG. 324 for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { 325 CG.addToCallGraph(LocalTUDecls[i]); 326 } 327 328 // Find the top level nodes - children of root + the unreachable (parentless) 329 // nodes. 330 llvm::SmallVector<CallGraphNode*, 24> TopLevelFunctions; 331 for (CallGraph::nodes_iterator TI = CG.parentless_begin(), 332 TE = CG.parentless_end(); TI != TE; ++TI) { 333 TopLevelFunctions.push_back(*TI); 334 NumFunctionTopLevel++; 335 } 336 CallGraphNode *Entry = CG.getRoot(); 337 for (CallGraphNode::iterator I = Entry->begin(), 338 E = Entry->end(); I != E; ++I) { 339 TopLevelFunctions.push_back(*I); 340 NumFunctionTopLevel++; 341 } 342 343 // Make sure the nodes are sorted in order reverse of their definition in the 344 // translation unit. This step is very important for performance. It ensures 345 // that we analyze the root functions before the externally available 346 // subroutines. 347 std::deque<CallGraphNode*> BFSQueue; 348 for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator 349 TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend(); 350 TI != TE; ++TI) 351 BFSQueue.push_back(*TI); 352 353 // BFS over all of the functions, while skipping the ones inlined into 354 // the previously processed functions. Use external Visited set, which is 355 // also modified when we inline a function. 356 SmallPtrSet<CallGraphNode*,24> Visited; 357 while(!BFSQueue.empty()) { 358 CallGraphNode *N = BFSQueue.front(); 359 BFSQueue.pop_front(); 360 361 // Push the children into the queue. 362 for (CallGraphNode::const_iterator CI = N->begin(), 363 CE = N->end(); CI != CE; ++CI) { 364 if (!Visited.count(*CI)) 365 BFSQueue.push_back(*CI); 366 } 367 368 // Skip the functions which have been processed already or previously 369 // inlined. 370 if (Visited.count(N)) 371 continue; 372 373 // Analyze the function. 374 SetOfConstDecls VisitedCallees; 375 Decl *D = N->getDecl(); 376 assert(D); 377 HandleCode(D, ANALYSIS_PATH, 378 (Mgr->InliningMode == All ? 0 : &VisitedCallees)); 379 380 // Add the visited callees to the global visited set. 381 for (SetOfConstDecls::iterator I = VisitedCallees.begin(), 382 E = VisitedCallees.end(); I != E; ++I) { 383 CallGraphNode *VN = CG.getNode(*I); 384 if (VN) 385 Visited.insert(VN); 386 } 387 Visited.insert(N); 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 LocalTUDeclsSize = LocalTUDecls.size(); 418 for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { 419 TraverseDecl(LocalTUDecls[i]); 420 } 421 422 if (Mgr->shouldInlineCall()) 423 HandleDeclsGallGraph(LocalTUDeclsSize); 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