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