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