diff options
author | Eli Friedman <eli.friedman@gmail.com> | 2009-05-19 21:10:40 +0000 |
---|---|---|
committer | Eli Friedman <eli.friedman@gmail.com> | 2009-05-19 21:10:40 +0000 |
commit | 0ec78fa2c92b714de41cc87cfa79e4877ab5479e (patch) | |
tree | 08d8f593cbff7e9497f2e21c625815ed830483d0 /lib/Frontend/AnalysisConsumer.cpp | |
parent | dc241b42c7588f99027b035a09b71557a6db219e (diff) |
Move AnalysisConsumer.h and Analyses.def from tools/clang-cc to
include/clang/Frontend, and move AnalysisConsumer.cpp from
tools/clang-cc to lib/Frontend.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@72135 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Frontend/AnalysisConsumer.cpp')
-rw-r--r-- | lib/Frontend/AnalysisConsumer.cpp | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/lib/Frontend/AnalysisConsumer.cpp b/lib/Frontend/AnalysisConsumer.cpp new file mode 100644 index 0000000000..02a8710b16 --- /dev/null +++ b/lib/Frontend/AnalysisConsumer.cpp @@ -0,0 +1,659 @@ +//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// "Meta" ASTConsumer for running different source analyses. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/AnalysisConsumer.h" +#include "clang/Frontend/PathDiagnosticClients.h" +#include "clang/Frontend/ManagerRegistry.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "llvm/Support/Compiler.h" +#include "llvm/ADT/OwningPtr.h" +#include "clang/AST/CFG.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/AST/ParentMap.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/LocalCheckers.h" +#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" +#include "llvm/System/Program.h" + +using namespace clang; + +static ExplodedNodeImpl::Auditor* CreateUbiViz(); + +//===----------------------------------------------------------------------===// +// Basic type definitions. +//===----------------------------------------------------------------------===// + +namespace { + class AnalysisManager; + typedef void (*CodeAction)(AnalysisManager& Mgr); +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// AnalysisConsumer declaration. +//===----------------------------------------------------------------------===// + +namespace { + + class VISIBILITY_HIDDEN AnalysisConsumer : public ASTConsumer { + typedef std::vector<CodeAction> Actions; + Actions FunctionActions; + Actions ObjCMethodActions; + Actions ObjCImplementationActions; + Actions TranslationUnitActions; + + public: + const LangOptions& LOpts; + Diagnostic &Diags; + ASTContext* Ctx; + Preprocessor* PP; + PreprocessorFactory* PPF; + const std::string OutDir; + AnalyzerOptions Opts; + llvm::OwningPtr<PathDiagnosticClient> PD; + + AnalysisConsumer(Diagnostic &diags, Preprocessor* pp, + PreprocessorFactory* ppf, + const LangOptions& lopts, + const std::string& outdir, + const AnalyzerOptions& opts) + : LOpts(lopts), Diags(diags), + Ctx(0), PP(pp), PPF(ppf), + OutDir(outdir), Opts(opts) {} + + void addCodeAction(CodeAction action) { + FunctionActions.push_back(action); + ObjCMethodActions.push_back(action); + } + + void addObjCImplementationAction(CodeAction action) { + ObjCImplementationActions.push_back(action); + } + + void addTranslationUnitAction(CodeAction action) { + TranslationUnitActions.push_back(action); + } + + virtual void Initialize(ASTContext &Context) { + Ctx = &Context; + } + + virtual void HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) + HandleTopLevelSingleDecl(*I); + } + + void HandleTopLevelSingleDecl(Decl *D); + virtual void HandleTranslationUnit(ASTContext &C); + + void HandleCode(Decl* D, Stmt* Body, Actions& actions); + }; + + + class VISIBILITY_HIDDEN AnalysisManager : public BugReporterData { + Decl* D; Stmt* Body; + + enum AnalysisScope { ScopeTU, ScopeDecl } AScope; + + AnalysisConsumer& C; + bool DisplayedFunction; + + llvm::OwningPtr<CFG> cfg; + llvm::OwningPtr<LiveVariables> liveness; + llvm::OwningPtr<ParentMap> PM; + + // Configurable components creators. + StoreManagerCreator CreateStoreMgr; + ConstraintManagerCreator CreateConstraintMgr; + + public: + AnalysisManager(AnalysisConsumer& c, Decl* d, Stmt* b, bool displayProgress) + : D(d), Body(b), AScope(ScopeDecl), C(c), + DisplayedFunction(!displayProgress) { + setManagerCreators(); + } + + AnalysisManager(AnalysisConsumer& c, bool displayProgress) + : D(0), Body(0), AScope(ScopeTU), C(c), + DisplayedFunction(!displayProgress) { + setManagerCreators(); + } + + Decl* getCodeDecl() const { + assert (AScope == ScopeDecl); + return D; + } + + Stmt* getBody() const { + assert (AScope == ScopeDecl); + return Body; + } + + StoreManagerCreator getStoreManagerCreator() { + return CreateStoreMgr; + }; + + ConstraintManagerCreator getConstraintManagerCreator() { + return CreateConstraintMgr; + } + + virtual CFG* getCFG() { + if (!cfg) cfg.reset(CFG::buildCFG(getBody())); + return cfg.get(); + } + + virtual ParentMap& getParentMap() { + if (!PM) + PM.reset(new ParentMap(getBody())); + return *PM.get(); + } + + virtual ASTContext& getContext() { + return *C.Ctx; + } + + virtual SourceManager& getSourceManager() { + return getContext().getSourceManager(); + } + + virtual Diagnostic& getDiagnostic() { + return C.Diags; + } + + const LangOptions& getLangOptions() const { + return C.LOpts; + } + + virtual PathDiagnosticClient* getPathDiagnosticClient() { + if (C.PD.get() == 0 && !C.OutDir.empty()) { + switch (C.Opts.AnalysisDiagOpt) { + default: +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE)\ +case PD_##NAME: C.PD.reset(CREATEFN(C.OutDir, C.PP, C.PPF)); break; +#include "Analyses.def" + } + } + return C.PD.get(); + } + + virtual LiveVariables* getLiveVariables() { + if (!liveness) { + CFG* c = getCFG(); + if (!c) return 0; + + liveness.reset(new LiveVariables(getContext(), *c)); + liveness->runOnCFG(*c); + liveness->runOnAllBlocks(*c, 0, true); + } + + return liveness.get(); + } + + bool shouldVisualizeGraphviz() const { return C.Opts.VisualizeEGDot; } + + bool shouldVisualizeUbigraph() const { return C.Opts.VisualizeEGUbi; } + + bool shouldVisualize() const { + return C.Opts.VisualizeEGDot || C.Opts.VisualizeEGUbi; + } + + bool shouldTrimGraph() const { return C.Opts.TrimGraph; } + + bool shouldPurgeDead() const { return C.Opts.PurgeDead; } + + bool shouldEagerlyAssume() const { return C.Opts.EagerlyAssume; } + + void DisplayFunction() { + + if (DisplayedFunction) + return; + + DisplayedFunction = true; + + // FIXME: Is getCodeDecl() always a named decl? + if (isa<FunctionDecl>(getCodeDecl()) || + isa<ObjCMethodDecl>(getCodeDecl())) { + NamedDecl *ND = cast<NamedDecl>(getCodeDecl()); + SourceManager &SM = getContext().getSourceManager(); + llvm::cerr << "ANALYZE: " + << SM.getPresumedLoc(ND->getLocation()).getFilename() + << ' ' << ND->getNameAsString() << '\n'; + } + } + + private: + /// Set configurable analyzer components creators. First check if there are + /// components registered at runtime. Otherwise fall back to builtin + /// components. + void setManagerCreators() { + if (ManagerRegistry::StoreMgrCreator != 0) { + CreateStoreMgr = ManagerRegistry::StoreMgrCreator; + } + else { + switch (C.Opts.AnalysisStoreOpt) { + default: + assert(0 && "Unknown store manager."); +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateStoreMgr = CREATEFN; break; +#include "Analyses.def" + } + } + + if (ManagerRegistry::ConstraintMgrCreator != 0) + CreateConstraintMgr = ManagerRegistry::ConstraintMgrCreator; + else { + switch (C.Opts.AnalysisConstraintsOpt) { + default: + assert(0 && "Unknown store manager."); +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateConstraintMgr = CREATEFN; break; +#include "Analyses.def" + } + } + + + // Some DiagnosticClients should be created all the time instead of + // lazily. Create those now. + switch (C.Opts.AnalysisDiagOpt) { + default: break; +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE)\ +case PD_##NAME: if (AUTOCREATE) getPathDiagnosticClient(); break; +#include "Analyses.def" + } + } + + }; + +} // end anonymous namespace + +namespace llvm { + template <> struct FoldingSetTrait<CodeAction> { + static inline void Profile(CodeAction X, FoldingSetNodeID& ID) { + ID.AddPointer(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(X))); + } + }; +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer implementation. +//===----------------------------------------------------------------------===// + +void AnalysisConsumer::HandleTopLevelSingleDecl(Decl *D) { + switch (D->getKind()) { + case Decl::Function: { + FunctionDecl* FD = cast<FunctionDecl>(D); + + if (Opts.AnalyzeSpecificFunction.size() > 0 && + Opts.AnalyzeSpecificFunction != FD->getIdentifier()->getName()) + break; + + Stmt* Body = FD->getBody(*Ctx); + if (Body) HandleCode(FD, Body, FunctionActions); + break; + } + + case Decl::ObjCMethod: { + ObjCMethodDecl* MD = cast<ObjCMethodDecl>(D); + + if (Opts.AnalyzeSpecificFunction.size() > 0 && + Opts.AnalyzeSpecificFunction != MD->getSelector().getAsString()) + return; + + Stmt* Body = MD->getBody(); + if (Body) HandleCode(MD, Body, ObjCMethodActions); + break; + } + + default: + break; + } +} + +void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { + + if(!TranslationUnitActions.empty()) { + AnalysisManager mgr(*this, Opts.AnalyzerDisplayProgress); + for (Actions::iterator I = TranslationUnitActions.begin(), + E = TranslationUnitActions.end(); I != E; ++I) + (*I)(mgr); + } + + if (!ObjCImplementationActions.empty()) { + TranslationUnitDecl *TUD = C.getTranslationUnitDecl(); + + for (DeclContext::decl_iterator I = TUD->decls_begin(C), + E = TUD->decls_end(C); + I != E; ++I) + if (ObjCImplementationDecl* ID = dyn_cast<ObjCImplementationDecl>(*I)) + HandleCode(ID, 0, ObjCImplementationActions); + } + + // Delete the PathDiagnosticClient here just in case the AnalysisConsumer + // object doesn't get released. This will cause any side-effects in the + // destructor of the PathDiagnosticClient to get executed. + PD.reset(); +} + +void AnalysisConsumer::HandleCode(Decl* D, Stmt* Body, Actions& actions) { + + // Don't run the actions if an error has occured with parsing the file. + if (Diags.hasErrorOccurred()) + return; + + // Don't run the actions on declarations in header files unless + // otherwise specified. + if (!Opts.AnalyzeAll && + !Ctx->getSourceManager().isFromMainFile(D->getLocation())) + return; + + // Create an AnalysisManager that will manage the state for analyzing + // this method/function. + AnalysisManager mgr(*this, D, Body, Opts.AnalyzerDisplayProgress); + + // Dispatch on the actions. + for (Actions::iterator I = actions.begin(), E = actions.end(); I != E; ++I) + (*I)(mgr); +} + +//===----------------------------------------------------------------------===// +// Analyses +//===----------------------------------------------------------------------===// + +static void ActionWarnDeadStores(AnalysisManager& mgr) { + if (LiveVariables* L = mgr.getLiveVariables()) { + BugReporter BR(mgr); + CheckDeadStores(*L, BR); + } +} + +static void ActionWarnUninitVals(AnalysisManager& mgr) { + if (CFG* c = mgr.getCFG()) + CheckUninitializedValues(*c, mgr.getContext(), mgr.getDiagnostic()); +} + + +static void ActionGRExprEngine(AnalysisManager& mgr, GRTransferFuncs* tf, + bool StandardWarnings = true) { + + + llvm::OwningPtr<GRTransferFuncs> TF(tf); + + // Display progress. + mgr.DisplayFunction(); + + // Construct the analysis engine. + LiveVariables* L = mgr.getLiveVariables(); + if (!L) return; + + GRExprEngine Eng(*mgr.getCFG(), *mgr.getCodeDecl(), mgr.getContext(), *L, mgr, + mgr.shouldPurgeDead(), mgr.shouldEagerlyAssume(), + mgr.getStoreManagerCreator(), + mgr.getConstraintManagerCreator()); + + Eng.setTransferFunctions(tf); + + if (StandardWarnings) { + Eng.RegisterInternalChecks(); + RegisterAppleChecks(Eng); + } + + // Set the graph auditor. + llvm::OwningPtr<ExplodedNodeImpl::Auditor> Auditor; + if (mgr.shouldVisualizeUbigraph()) { + Auditor.reset(CreateUbiViz()); + ExplodedNodeImpl::SetAuditor(Auditor.get()); + } + + // Execute the worklist algorithm. + Eng.ExecuteWorkList(); + + // Release the auditor (if any) so that it doesn't monitor the graph + // created BugReporter. + ExplodedNodeImpl::SetAuditor(0); + + // Visualize the exploded graph. + if (mgr.shouldVisualizeGraphviz()) + Eng.ViewGraph(mgr.shouldTrimGraph()); + + // Display warnings. + Eng.getBugReporter().FlushReports(); +} + +static void ActionCheckerCFRefAux(AnalysisManager& mgr, bool GCEnabled, + bool StandardWarnings) { + + GRTransferFuncs* TF = MakeCFRefCountTF(mgr.getContext(), + GCEnabled, + mgr.getLangOptions()); + + ActionGRExprEngine(mgr, TF, StandardWarnings); +} + +static void ActionCheckerCFRef(AnalysisManager& mgr) { + + switch (mgr.getLangOptions().getGCMode()) { + default: + assert (false && "Invalid GC mode."); + case LangOptions::NonGC: + ActionCheckerCFRefAux(mgr, false, true); + break; + + case LangOptions::GCOnly: + ActionCheckerCFRefAux(mgr, true, true); + break; + + case LangOptions::HybridGC: + ActionCheckerCFRefAux(mgr, false, true); + ActionCheckerCFRefAux(mgr, true, false); + break; + } +} + +static void ActionCheckerSimple(AnalysisManager& mgr) { + ActionGRExprEngine(mgr, MakeGRSimpleValsTF()); +} + +static void ActionDisplayLiveVariables(AnalysisManager& mgr) { + if (LiveVariables* L = mgr.getLiveVariables()) { + mgr.DisplayFunction(); + L->dumpBlockLiveness(mgr.getSourceManager()); + } +} + +static void ActionCFGDump(AnalysisManager& mgr) { + if (CFG* c = mgr.getCFG()) { + mgr.DisplayFunction(); + c->dump(); + } +} + +static void ActionCFGView(AnalysisManager& mgr) { + if (CFG* c = mgr.getCFG()) { + mgr.DisplayFunction(); + c->viewCFG(); + } +} + +static void ActionWarnObjCDealloc(AnalysisManager& mgr) { + if (mgr.getLangOptions().getGCMode() == LangOptions::GCOnly) + return; + + BugReporter BR(mgr); + + CheckObjCDealloc(cast<ObjCImplementationDecl>(mgr.getCodeDecl()), + mgr.getLangOptions(), BR); +} + +static void ActionWarnObjCUnusedIvars(AnalysisManager& mgr) { + BugReporter BR(mgr); + CheckObjCUnusedIvar(cast<ObjCImplementationDecl>(mgr.getCodeDecl()), BR); +} + +static void ActionWarnObjCMethSigs(AnalysisManager& mgr) { + BugReporter BR(mgr); + + CheckObjCInstMethSignature(cast<ObjCImplementationDecl>(mgr.getCodeDecl()), + BR); +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer creation. +//===----------------------------------------------------------------------===// + +ASTConsumer* clang::CreateAnalysisConsumer(Diagnostic &diags, Preprocessor* pp, + PreprocessorFactory* ppf, + const LangOptions& lopts, + const std::string& OutDir, + const AnalyzerOptions& Opts) { + + llvm::OwningPtr<AnalysisConsumer> C(new AnalysisConsumer(diags, pp, ppf, + lopts, OutDir, + Opts)); + + for (unsigned i = 0; i < Opts.AnalysisList.size(); ++i) + switch (Opts.AnalysisList[i]) { +#define ANALYSIS(NAME, CMD, DESC, SCOPE)\ + case NAME:\ + C->add ## SCOPE ## Action(&Action ## NAME);\ + break; +#include "Analyses.def" + default: break; + } + + // Last, disable the effects of '-Werror' when using the AnalysisConsumer. + diags.setWarningsAsErrors(false); + + return C.take(); +} + +//===----------------------------------------------------------------------===// +// Ubigraph Visualization. FIXME: Move to separate file. +//===----------------------------------------------------------------------===// + +namespace { + +class UbigraphViz : public ExplodedNodeImpl::Auditor { + llvm::OwningPtr<llvm::raw_ostream> Out; + llvm::sys::Path Dir, Filename; + unsigned Cntr; + + typedef llvm::DenseMap<void*,unsigned> VMap; + VMap M; + +public: + UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir, + llvm::sys::Path& filename); + + ~UbigraphViz(); + + virtual void AddEdge(ExplodedNodeImpl* Src, ExplodedNodeImpl* Dst); +}; + +} // end anonymous namespace + +static ExplodedNodeImpl::Auditor* CreateUbiViz() { + std::string ErrMsg; + + llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg); + if (!ErrMsg.empty()) + return 0; + + llvm::sys::Path Filename = Dir; + Filename.appendComponent("llvm_ubi"); + Filename.makeUnique(true,&ErrMsg); + + if (!ErrMsg.empty()) + return 0; + + llvm::cerr << "Writing '" << Filename << "'.\n"; + + llvm::OwningPtr<llvm::raw_fd_ostream> Stream; + std::string filename = Filename.toString(); + Stream.reset(new llvm::raw_fd_ostream(filename.c_str(), false, ErrMsg)); + + if (!ErrMsg.empty()) + return 0; + + return new UbigraphViz(Stream.take(), Dir, Filename); +} + +void UbigraphViz::AddEdge(ExplodedNodeImpl* Src, ExplodedNodeImpl* Dst) { + + assert (Src != Dst && "Self-edges are not allowed."); + + // Lookup the Src. If it is a new node, it's a root. + VMap::iterator SrcI= M.find(Src); + unsigned SrcID; + + if (SrcI == M.end()) { + M[Src] = SrcID = Cntr++; + *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n"; + } + else + SrcID = SrcI->second; + + // Lookup the Dst. + VMap::iterator DstI= M.find(Dst); + unsigned DstID; + + if (DstI == M.end()) { + M[Dst] = DstID = Cntr++; + *Out << "('vertex', " << DstID << ")\n"; + } + else { + // We have hit DstID before. Change its style to reflect a cache hit. + DstID = DstI->second; + *Out << "('change_vertex_style', " << DstID << ", 1)\n"; + } + + // Add the edge. + *Out << "('edge', " << SrcID << ", " << DstID + << ", ('arrow','true'), ('oriented', 'true'))\n"; +} + +UbigraphViz::UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir, + llvm::sys::Path& filename) + : Out(out), Dir(dir), Filename(filename), Cntr(0) { + + *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n"; + *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66')," + " ('size', '1.5'))\n"; +} + +UbigraphViz::~UbigraphViz() { + Out.reset(0); + llvm::cerr << "Running 'ubiviz' program... "; + std::string ErrMsg; + llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz"); + std::vector<const char*> args; + args.push_back(Ubiviz.c_str()); + args.push_back(Filename.c_str()); + args.push_back(0); + + if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) { + llvm::cerr << "Error viewing graph: " << ErrMsg << "\n"; + } + + // Delete the directory. + Dir.eraseFromDisk(true); +} |