AnalysisConsumer.cpp revision 58f2e7c3c3860e410fa3d8252862ef10be7cdc70
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#include "AnalysisConsumer.h" 15#include "clang/AST/ASTConsumer.h" 16#include "clang/AST/Decl.h" 17#include "clang/AST/DeclCXX.h" 18#include "clang/AST/DeclObjC.h" 19#include "clang/AST/ParentMap.h" 20#include "clang/Analysis/Analyses/LiveVariables.h" 21#include "clang/Analysis/Analyses/UninitializedValues.h" 22#include "clang/Analysis/CFG.h" 23#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" 24#include "clang/StaticAnalyzer/Core/CheckerManager.h" 25#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" 26#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 27#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 28#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 29#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 30#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h" 31#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h" 32 33// FIXME: Restructure checker registration. 34#include "../Checkers/InternalChecks.h" 35#include "../Checkers/BasicObjCFoundationChecks.h" 36 37#include "clang/Basic/FileManager.h" 38#include "clang/Basic/SourceManager.h" 39#include "clang/Frontend/AnalyzerOptions.h" 40#include "clang/Lex/Preprocessor.h" 41#include "llvm/Support/raw_ostream.h" 42#include "llvm/Support/Path.h" 43#include "llvm/Support/Program.h" 44#include "llvm/ADT/OwningPtr.h" 45 46using namespace clang; 47using namespace ento; 48 49static ExplodedNode::Auditor* CreateUbiViz(); 50 51//===----------------------------------------------------------------------===// 52// Special PathDiagnosticClients. 53//===----------------------------------------------------------------------===// 54 55static PathDiagnosticClient* 56createPlistHTMLDiagnosticClient(const std::string& prefix, 57 const Preprocessor &PP) { 58 PathDiagnosticClient *PD = 59 createHTMLDiagnosticClient(llvm::sys::path::parent_path(prefix), PP); 60 return createPlistDiagnosticClient(prefix, PP, PD); 61} 62 63//===----------------------------------------------------------------------===// 64// AnalysisConsumer declaration. 65//===----------------------------------------------------------------------===// 66 67namespace { 68 69class AnalysisConsumer : public ASTConsumer { 70public: 71 typedef void (*CodeAction)(AnalysisConsumer &C, AnalysisManager &M, Decl *D); 72 typedef void (*TUAction)(AnalysisConsumer &C, AnalysisManager &M, 73 TranslationUnitDecl &TU); 74 75private: 76 typedef std::vector<CodeAction> Actions; 77 typedef std::vector<TUAction> TUActions; 78 79 Actions FunctionActions; 80 Actions ObjCMethodActions; 81 Actions ObjCImplementationActions; 82 Actions CXXMethodActions; 83 84public: 85 ASTContext* Ctx; 86 const Preprocessor &PP; 87 const std::string OutDir; 88 AnalyzerOptions Opts; 89 90 // PD is owned by AnalysisManager. 91 PathDiagnosticClient *PD; 92 93 StoreManagerCreator CreateStoreMgr; 94 ConstraintManagerCreator CreateConstraintMgr; 95 96 llvm::OwningPtr<CheckerManager> checkerMgr; 97 llvm::OwningPtr<AnalysisManager> Mgr; 98 99 AnalysisConsumer(const Preprocessor& pp, 100 const std::string& outdir, 101 const AnalyzerOptions& opts) 102 : Ctx(0), PP(pp), OutDir(outdir), 103 Opts(opts), PD(0) { 104 DigestAnalyzerOptions(); 105 } 106 107 void DigestAnalyzerOptions() { 108 // Create the PathDiagnosticClient. 109 if (!OutDir.empty()) { 110 switch (Opts.AnalysisDiagOpt) { 111 default: 112#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \ 113 case PD_##NAME: PD = CREATEFN(OutDir, PP); break; 114#include "clang/Frontend/Analyses.def" 115 } 116 } else if (Opts.AnalysisDiagOpt == PD_TEXT) { 117 // Create the text client even without a specified output file since 118 // it just uses diagnostic notes. 119 PD = createTextPathDiagnosticClient("", PP); 120 } 121 122 // Create the analyzer component creators. 123 switch (Opts.AnalysisStoreOpt) { 124 default: 125 assert(0 && "Unknown store manager."); 126#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \ 127 case NAME##Model: CreateStoreMgr = CREATEFN; break; 128#include "clang/Frontend/Analyses.def" 129 } 130 131 switch (Opts.AnalysisConstraintsOpt) { 132 default: 133 assert(0 && "Unknown store manager."); 134#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ 135 case NAME##Model: CreateConstraintMgr = CREATEFN; break; 136#include "clang/Frontend/Analyses.def" 137 } 138 } 139 140 void DisplayFunction(const Decl *D) { 141 if (!Opts.AnalyzerDisplayProgress) 142 return; 143 144 SourceManager &SM = Mgr->getASTContext().getSourceManager(); 145 PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); 146 if (Loc.isValid()) { 147 llvm::errs() << "ANALYZE: " << Loc.getFilename(); 148 149 if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { 150 const NamedDecl *ND = cast<NamedDecl>(D); 151 llvm::errs() << ' ' << ND << '\n'; 152 } 153 else if (isa<BlockDecl>(D)) { 154 llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:" 155 << Loc.getColumn() << '\n'; 156 } 157 else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { 158 Selector S = MD->getSelector(); 159 llvm::errs() << ' ' << S.getAsString(); 160 } 161 } 162 } 163 164 void addCodeAction(CodeAction action) { 165 FunctionActions.push_back(action); 166 ObjCMethodActions.push_back(action); 167 CXXMethodActions.push_back(action); 168 } 169 170 void addObjCImplementationAction(CodeAction action) { 171 ObjCImplementationActions.push_back(action); 172 } 173 174 virtual void Initialize(ASTContext &Context) { 175 Ctx = &Context; 176 checkerMgr.reset(registerCheckers(Opts, PP.getLangOptions(), 177 PP.getDiagnostics())); 178 Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(), 179 PP.getLangOptions(), PD, 180 CreateStoreMgr, CreateConstraintMgr, 181 checkerMgr.get(), 182 /* Indexer */ 0, 183 Opts.MaxNodes, Opts.MaxLoop, 184 Opts.VisualizeEGDot, Opts.VisualizeEGUbi, 185 Opts.PurgeDead, Opts.EagerlyAssume, 186 Opts.TrimGraph, Opts.InlineCall, 187 Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors, 188 Opts.CFGAddInitializers, 189 Opts.EagerlyTrimEGraph)); 190 } 191 192 virtual void HandleTranslationUnit(ASTContext &C); 193 void HandleDeclContext(ASTContext &C, DeclContext *dc); 194 195 void HandleCode(Decl *D, Actions& actions); 196}; 197} // end anonymous namespace 198 199//===----------------------------------------------------------------------===// 200// AnalysisConsumer implementation. 201//===----------------------------------------------------------------------===// 202 203void AnalysisConsumer::HandleDeclContext(ASTContext &C, DeclContext *dc) { 204 BugReporter BR(*Mgr); 205 for (DeclContext::decl_iterator I = dc->decls_begin(), E = dc->decls_end(); 206 I != E; ++I) { 207 Decl *D = *I; 208 checkerMgr->runCheckersOnASTDecl(D, *Mgr, BR); 209 210 switch (D->getKind()) { 211 case Decl::Namespace: { 212 HandleDeclContext(C, cast<NamespaceDecl>(D)); 213 break; 214 } 215 case Decl::CXXConstructor: 216 case Decl::CXXDestructor: 217 case Decl::CXXConversion: 218 case Decl::CXXMethod: 219 case Decl::Function: { 220 FunctionDecl* FD = cast<FunctionDecl>(D); 221 // We skip function template definitions, as their semantics is 222 // only determined when they are instantiated. 223 if (FD->isThisDeclarationADefinition() && 224 !FD->isDependentContext()) { 225 if (!Opts.AnalyzeSpecificFunction.empty() && 226 FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction) 227 break; 228 DisplayFunction(FD); 229 HandleCode(FD, FunctionActions); 230 } 231 break; 232 } 233 234 case Decl::ObjCImplementation: { 235 ObjCImplementationDecl* ID = cast<ObjCImplementationDecl>(*I); 236 HandleCode(ID, ObjCImplementationActions); 237 238 for (ObjCImplementationDecl::method_iterator MI = ID->meth_begin(), 239 ME = ID->meth_end(); MI != ME; ++MI) { 240 if ((*MI)->isThisDeclarationADefinition()) { 241 if (!Opts.AnalyzeSpecificFunction.empty() && 242 Opts.AnalyzeSpecificFunction != (*MI)->getSelector().getAsString()) 243 break; 244 DisplayFunction(*MI); 245 HandleCode(*MI, ObjCMethodActions); 246 } 247 } 248 break; 249 } 250 251 default: 252 break; 253 } 254 } 255} 256 257void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { 258 BugReporter BR(*Mgr); 259 TranslationUnitDecl *TU = C.getTranslationUnitDecl(); 260 checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); 261 HandleDeclContext(C, TU); 262 263 // Explicitly destroy the PathDiagnosticClient. This will flush its output. 264 // FIXME: This should be replaced with something that doesn't rely on 265 // side-effects in PathDiagnosticClient's destructor. This is required when 266 // used with option -disable-free. 267 Mgr.reset(NULL); 268} 269 270static void FindBlocks(DeclContext *D, llvm::SmallVectorImpl<Decl*> &WL) { 271 if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) 272 WL.push_back(BD); 273 274 for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); 275 I!=E; ++I) 276 if (DeclContext *DC = dyn_cast<DeclContext>(*I)) 277 FindBlocks(DC, WL); 278} 279 280void AnalysisConsumer::HandleCode(Decl *D, Actions& actions) { 281 282 // Don't run the actions if an error has occured with parsing the file. 283 Diagnostic &Diags = PP.getDiagnostics(); 284 if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) 285 return; 286 287 // Don't run the actions on declarations in header files unless 288 // otherwise specified. 289 SourceManager &SM = Ctx->getSourceManager(); 290 SourceLocation SL = SM.getInstantiationLoc(D->getLocation()); 291 if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL)) 292 return; 293 294 // Clear the AnalysisManager of old AnalysisContexts. 295 Mgr->ClearContexts(); 296 297 // Dispatch on the actions. 298 llvm::SmallVector<Decl*, 10> WL; 299 WL.push_back(D); 300 301 if (D->hasBody() && Opts.AnalyzeNestedBlocks) 302 FindBlocks(cast<DeclContext>(D), WL); 303 304 BugReporter BR(*Mgr); 305 for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end(); 306 WI != WE; ++WI) 307 if ((*WI)->hasBody()) 308 checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR); 309 310 for (Actions::iterator I = actions.begin(), E = actions.end(); I != E; ++I) 311 for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end(); 312 WI != WE; ++WI) 313 (*I)(*this, *Mgr, *WI); 314} 315 316//===----------------------------------------------------------------------===// 317// Analyses 318//===----------------------------------------------------------------------===// 319 320static void ActionWarnUninitVals(AnalysisConsumer &C, AnalysisManager& mgr, 321 Decl *D) { 322 if (CFG* c = mgr.getCFG(D)) { 323 CheckUninitializedValues(*c, mgr.getASTContext(), mgr.getDiagnostic()); 324 } 325} 326 327 328static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr, 329 Decl *D, 330 TransferFuncs* tf) { 331 332 llvm::OwningPtr<TransferFuncs> TF(tf); 333 334 // Construct the analysis engine. We first query for the LiveVariables 335 // information to see if the CFG is valid. 336 // FIXME: Inter-procedural analysis will need to handle invalid CFGs. 337 if (!mgr.getLiveVariables(D)) 338 return; 339 ExprEngine Eng(mgr, TF.take()); 340 341 RegisterNSErrorChecks(Eng.getBugReporter(), Eng, *D); 342 343 if (C.Opts.BufferOverflows) 344 RegisterArrayBoundCheckerV2(Eng); 345 346 // Set the graph auditor. 347 llvm::OwningPtr<ExplodedNode::Auditor> Auditor; 348 if (mgr.shouldVisualizeUbigraph()) { 349 Auditor.reset(CreateUbiViz()); 350 ExplodedNode::SetAuditor(Auditor.get()); 351 } 352 353 // Execute the worklist algorithm. 354 Eng.ExecuteWorkList(mgr.getStackFrame(D, 0), mgr.getMaxNodes()); 355 356 // Release the auditor (if any) so that it doesn't monitor the graph 357 // created BugReporter. 358 ExplodedNode::SetAuditor(0); 359 360 // Visualize the exploded graph. 361 if (mgr.shouldVisualizeGraphviz()) 362 Eng.ViewGraph(mgr.shouldTrimGraph()); 363 364 // Display warnings. 365 Eng.getBugReporter().FlushReports(); 366} 367 368static void ActionObjCMemCheckerAux(AnalysisConsumer &C, AnalysisManager& mgr, 369 Decl *D, bool GCEnabled) { 370 371 TransferFuncs* TF = MakeCFRefCountTF(mgr.getASTContext(), 372 GCEnabled, 373 mgr.getLangOptions()); 374 375 ActionExprEngine(C, mgr, D, TF); 376} 377 378static void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr, 379 Decl *D) { 380 381 switch (mgr.getLangOptions().getGCMode()) { 382 default: 383 assert (false && "Invalid GC mode."); 384 case LangOptions::NonGC: 385 ActionObjCMemCheckerAux(C, mgr, D, false); 386 break; 387 388 case LangOptions::GCOnly: 389 ActionObjCMemCheckerAux(C, mgr, D, true); 390 break; 391 392 case LangOptions::HybridGC: 393 ActionObjCMemCheckerAux(C, mgr, D, false); 394 ActionObjCMemCheckerAux(C, mgr, D, true); 395 break; 396 } 397} 398 399//===----------------------------------------------------------------------===// 400// AnalysisConsumer creation. 401//===----------------------------------------------------------------------===// 402 403ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp, 404 const std::string& OutDir, 405 const AnalyzerOptions& Opts) { 406 llvm::OwningPtr<AnalysisConsumer> C(new AnalysisConsumer(pp, OutDir, Opts)); 407 408 for (unsigned i = 0; i < Opts.AnalysisList.size(); ++i) 409 switch (Opts.AnalysisList[i]) { 410#define ANALYSIS(NAME, CMD, DESC, SCOPE)\ 411 case NAME:\ 412 C->add ## SCOPE ## Action(&Action ## NAME);\ 413 break; 414#include "clang/Frontend/Analyses.def" 415 default: break; 416 } 417 418 // Last, disable the effects of '-Werror' when using the AnalysisConsumer. 419 pp.getDiagnostics().setWarningsAsErrors(false); 420 421 return C.take(); 422} 423 424//===----------------------------------------------------------------------===// 425// Ubigraph Visualization. FIXME: Move to separate file. 426//===----------------------------------------------------------------------===// 427 428namespace { 429 430class UbigraphViz : public ExplodedNode::Auditor { 431 llvm::OwningPtr<llvm::raw_ostream> Out; 432 llvm::sys::Path Dir, Filename; 433 unsigned Cntr; 434 435 typedef llvm::DenseMap<void*,unsigned> VMap; 436 VMap M; 437 438public: 439 UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir, 440 llvm::sys::Path& filename); 441 442 ~UbigraphViz(); 443 444 virtual void AddEdge(ExplodedNode* Src, ExplodedNode* Dst); 445}; 446 447} // end anonymous namespace 448 449static ExplodedNode::Auditor* CreateUbiViz() { 450 std::string ErrMsg; 451 452 llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg); 453 if (!ErrMsg.empty()) 454 return 0; 455 456 llvm::sys::Path Filename = Dir; 457 Filename.appendComponent("llvm_ubi"); 458 Filename.makeUnique(true,&ErrMsg); 459 460 if (!ErrMsg.empty()) 461 return 0; 462 463 llvm::errs() << "Writing '" << Filename.str() << "'.\n"; 464 465 llvm::OwningPtr<llvm::raw_fd_ostream> Stream; 466 Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg)); 467 468 if (!ErrMsg.empty()) 469 return 0; 470 471 return new UbigraphViz(Stream.take(), Dir, Filename); 472} 473 474void UbigraphViz::AddEdge(ExplodedNode* Src, ExplodedNode* Dst) { 475 476 assert (Src != Dst && "Self-edges are not allowed."); 477 478 // Lookup the Src. If it is a new node, it's a root. 479 VMap::iterator SrcI= M.find(Src); 480 unsigned SrcID; 481 482 if (SrcI == M.end()) { 483 M[Src] = SrcID = Cntr++; 484 *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n"; 485 } 486 else 487 SrcID = SrcI->second; 488 489 // Lookup the Dst. 490 VMap::iterator DstI= M.find(Dst); 491 unsigned DstID; 492 493 if (DstI == M.end()) { 494 M[Dst] = DstID = Cntr++; 495 *Out << "('vertex', " << DstID << ")\n"; 496 } 497 else { 498 // We have hit DstID before. Change its style to reflect a cache hit. 499 DstID = DstI->second; 500 *Out << "('change_vertex_style', " << DstID << ", 1)\n"; 501 } 502 503 // Add the edge. 504 *Out << "('edge', " << SrcID << ", " << DstID 505 << ", ('arrow','true'), ('oriented', 'true'))\n"; 506} 507 508UbigraphViz::UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir, 509 llvm::sys::Path& filename) 510 : Out(out), Dir(dir), Filename(filename), Cntr(0) { 511 512 *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n"; 513 *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66')," 514 " ('size', '1.5'))\n"; 515} 516 517UbigraphViz::~UbigraphViz() { 518 Out.reset(0); 519 llvm::errs() << "Running 'ubiviz' program... "; 520 std::string ErrMsg; 521 llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz"); 522 std::vector<const char*> args; 523 args.push_back(Ubiviz.c_str()); 524 args.push_back(Filename.c_str()); 525 args.push_back(0); 526 527 if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) { 528 llvm::errs() << "Error viewing graph: " << ErrMsg << "\n"; 529 } 530 531 // Delete the directory. 532 Dir.eraseFromDisk(true); 533} 534