diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 |
commit | 486754660bb926339aefcf012a3f848592babb8b (patch) | |
tree | ecdbc446c9876f4f120f701c243373cd3cb43db3 /lib/StaticAnalyzer/Frontend | |
parent | 55e6d896ad333f07bb3b1ba487df214fc268a4ab (diff) |
Notes
Diffstat (limited to 'lib/StaticAnalyzer/Frontend')
-rw-r--r-- | lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | 174 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/CMakeLists.txt | 3 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp | 13 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/ModelConsumer.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/ModelInjector.cpp | 6 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/ModelInjector.h | 4 |
6 files changed, 123 insertions, 79 deletions
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index fccea9ee53bf..44abde5da6d1 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -22,6 +22,7 @@ #include "clang/Analysis/CallGraph.h" #include "clang/Analysis/CodeInjector.h" #include "clang/Basic/SourceManager.h" +#include "clang/CrossTU/CrossTranslationUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" @@ -57,6 +58,8 @@ STATISTIC(NumFunctionsAnalyzed, "with inlining turned on)."); STATISTIC(NumBlocksInAnalyzedFunctions, "The # of basic blocks in the analyzed functions."); +STATISTIC(NumVisitedBlocksInAnalyzedFunctions, + "The # of visited basic blocks in the analyzed functions."); STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); @@ -70,7 +73,7 @@ void ento::createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, const Preprocessor &PP) { createHTMLDiagnosticConsumer(AnalyzerOpts, C, llvm::sys::path::parent_path(prefix), PP); - createPlistDiagnosticConsumer(AnalyzerOpts, C, prefix, PP); + createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP); } void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, @@ -161,6 +164,8 @@ class AnalysisConsumer : public AnalysisASTConsumer, /// Bug Reporter to use while recursively visiting Decls. BugReporter *RecVisitorBR; + std::vector<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns; + public: ASTContext *Ctx; const Preprocessor &PP; @@ -168,8 +173,9 @@ public: AnalyzerOptionsRef Opts; ArrayRef<std::string> Plugins; CodeInjector *Injector; + cross_tu::CrossTranslationUnitContext CTU; - /// \brief Stores the declarations from the local translation unit. + /// Stores the declarations from the local translation unit. /// Note, we pre-compute the local declarations at parse time as an /// optimization to make sure we do not deserialize everything from disk. /// The local declaration to all declarations ratio might be very small when @@ -186,28 +192,31 @@ public: std::unique_ptr<AnalysisManager> Mgr; /// Time the analyzes time of each translation unit. - static llvm::Timer* TUTotalTimer; + std::unique_ptr<llvm::TimerGroup> AnalyzerTimers; + std::unique_ptr<llvm::Timer> TUTotalTimer; /// The information about analyzed functions shared throughout the /// translation unit. FunctionSummariesTy FunctionSummaries; - AnalysisConsumer(const Preprocessor &pp, const std::string &outdir, + AnalysisConsumer(CompilerInstance &CI, const std::string &outdir, AnalyzerOptionsRef opts, ArrayRef<std::string> plugins, CodeInjector *injector) - : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(pp), - OutDir(outdir), Opts(std::move(opts)), Plugins(plugins), - Injector(injector) { + : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), + PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)), + Plugins(plugins), Injector(injector), CTU(CI) { DigestAnalyzerOptions(); - if (Opts->PrintStats) { - llvm::EnableStatistics(false); - TUTotalTimer = new llvm::Timer("time", "Analyzer Total Time"); + if (Opts->PrintStats || Opts->shouldSerializeStats()) { + AnalyzerTimers = llvm::make_unique<llvm::TimerGroup>( + "analyzer", "Analyzer timers"); + TUTotalTimer = llvm::make_unique<llvm::Timer>( + "time", "Analyzer total time", *AnalyzerTimers); + llvm::EnableStatistics(/* PrintOnExit= */ false); } } ~AnalysisConsumer() override { if (Opts->PrintStats) { - delete TUTotalTimer; llvm::PrintStatistics(); } } @@ -286,32 +295,33 @@ public: void Initialize(ASTContext &Context) override { Ctx = &Context; - checkerMgr = createCheckerManager(*Opts, PP.getLangOpts(), Plugins, - PP.getDiagnostics()); + checkerMgr = + createCheckerManager(*Opts, PP.getLangOpts(), Plugins, + CheckerRegistrationFns, PP.getDiagnostics()); Mgr = llvm::make_unique<AnalysisManager>( *Ctx, PP.getDiagnostics(), PP.getLangOpts(), PathConsumers, CreateStoreMgr, CreateConstraintMgr, checkerMgr.get(), *Opts, Injector); } - /// \brief Store the top level decls in the set to be processed later on. + /// Store the top level decls in the set to be processed later on. /// (Doing this pre-processing avoids deserialization of data from PCH.) bool HandleTopLevelDecl(DeclGroupRef D) override; void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override; void HandleTranslationUnit(ASTContext &C) override; - /// \brief Determine which inlining mode should be used when this function is + /// Determine which inlining mode should be used when this function is /// analyzed. This allows to redefine the default inlining policies when /// analyzing a given function. ExprEngine::InliningModes getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited); - /// \brief Build the call graph for all the top level decls of this TU and + /// Build the call graph for all the top level decls of this TU and /// use it to define the order in which the functions should be visited. void HandleDeclsCallGraph(const unsigned LocalTUDeclsSize); - /// \brief Run analyzes(syntax or path sensitive) on the given function. + /// Run analyzes(syntax or path sensitive) on the given function. /// \param Mode - determines if we are requesting syntax only or path /// sensitive only analysis. /// \param VisitedCallees - The output parameter, which is populated with the @@ -378,13 +388,20 @@ public: PathConsumers.push_back(Consumer); } + void AddCheckerRegistrationFn(std::function<void(CheckerRegistry&)> Fn) override { + CheckerRegistrationFns.push_back(std::move(Fn)); + } + private: void storeTopLevelDecls(DeclGroupRef DG); std::string getFunctionName(const Decl *D); - /// \brief Check if we should skip (not analyze) the given function. + /// Check if we should skip (not analyze) the given function. AnalysisMode getModeForDecl(Decl *D, AnalysisMode Mode); + void runAnalysisOnTranslationUnit(ASTContext &C); + /// Print \p S to stderr if \c Opts->AnalyzerDisplayProgress is set. + void reportAnalyzerProgress(StringRef S); }; } // end anonymous namespace @@ -392,8 +409,6 @@ private: //===----------------------------------------------------------------------===// // AnalysisConsumer implementation. //===----------------------------------------------------------------------===// -llvm::Timer* AnalysisConsumer::TUTotalTimer = nullptr; - bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) { storeTopLevelDecls(DG); return true; @@ -508,68 +523,90 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) { } } +static bool isBisonFile(ASTContext &C) { + const SourceManager &SM = C.getSourceManager(); + FileID FID = SM.getMainFileID(); + StringRef Buffer = SM.getBuffer(FID)->getBuffer(); + if (Buffer.startswith("/* A Bison parser, made by")) + return true; + return false; +} + +void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) { + BugReporter BR(*Mgr); + TranslationUnitDecl *TU = C.getTranslationUnitDecl(); + checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); + + // Run the AST-only checks using the order in which functions are defined. + // If inlining is not turned on, use the simplest function order for path + // sensitive analyzes as well. + RecVisitorMode = AM_Syntax; + if (!Mgr->shouldInlineCall()) + RecVisitorMode |= AM_Path; + RecVisitorBR = &BR; + + // Process all the top level declarations. + // + // Note: TraverseDecl may modify LocalTUDecls, but only by appending more + // entries. Thus we don't use an iterator, but rely on LocalTUDecls + // random access. By doing so, we automatically compensate for iterators + // possibly being invalidated, although this is a bit slower. + const unsigned LocalTUDeclsSize = LocalTUDecls.size(); + for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { + TraverseDecl(LocalTUDecls[i]); + } + + if (Mgr->shouldInlineCall()) + HandleDeclsCallGraph(LocalTUDeclsSize); + + // After all decls handled, run checkers on the entire TranslationUnit. + checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); + + RecVisitorBR = nullptr; +} + +void AnalysisConsumer::reportAnalyzerProgress(StringRef S) { + if (Opts->AnalyzerDisplayProgress) + llvm::errs() << S; +} + void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { + // Don't run the actions if an error has occurred with parsing the file. DiagnosticsEngine &Diags = PP.getDiagnostics(); if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) return; - // Don't analyze if the user explicitly asked for no checks to be performed - // on this file. - if (Opts->DisableAllChecks) - return; - - { - if (TUTotalTimer) TUTotalTimer->startTimer(); - - // Introduce a scope to destroy BR before Mgr. - BugReporter BR(*Mgr); - TranslationUnitDecl *TU = C.getTranslationUnitDecl(); - checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); - - // Run the AST-only checks using the order in which functions are defined. - // If inlining is not turned on, use the simplest function order for path - // sensitive analyzes as well. - RecVisitorMode = AM_Syntax; - if (!Mgr->shouldInlineCall()) - RecVisitorMode |= AM_Path; - RecVisitorBR = &BR; - - // Process all the top level declarations. - // - // Note: TraverseDecl may modify LocalTUDecls, but only by appending more - // entries. Thus we don't use an iterator, but rely on LocalTUDecls - // random access. By doing so, we automatically compensate for iterators - // possibly being invalidated, although this is a bit slower. - const unsigned LocalTUDeclsSize = LocalTUDecls.size(); - for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { - TraverseDecl(LocalTUDecls[i]); - } + if (TUTotalTimer) TUTotalTimer->startTimer(); - if (Mgr->shouldInlineCall()) - HandleDeclsCallGraph(LocalTUDeclsSize); + if (isBisonFile(C)) { + reportAnalyzerProgress("Skipping bison-generated file\n"); + } else if (Opts->DisableAllChecks) { - // After all decls handled, run checkers on the entire TranslationUnit. - checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); - - RecVisitorBR = nullptr; + // Don't analyze if the user explicitly asked for no checks to be performed + // on this file. + reportAnalyzerProgress("All checks are disabled using a supplied option\n"); + } else { + // Otherwise, just run the analysis. + runAnalysisOnTranslationUnit(C); } - // Explicitly destroy the PathDiagnosticConsumer. This will flush its output. - // FIXME: This should be replaced with something that doesn't rely on - // side-effects in PathDiagnosticConsumer's destructor. This is required when - // used with option -disable-free. - Mgr.reset(); - if (TUTotalTimer) TUTotalTimer->stopTimer(); // Count how many basic blocks we have not covered. NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks(); + NumVisitedBlocksInAnalyzedFunctions = + FunctionSummaries.getTotalNumVisitedBasicBlocks(); if (NumBlocksInAnalyzedFunctions > 0) PercentReachableBlocks = (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) / NumBlocksInAnalyzedFunctions; + // Explicitly destroy the PathDiagnosticConsumer. This will flush its output. + // FIXME: This should be replaced with something that doesn't rely on + // side-effects in PathDiagnosticConsumer's destructor. This is required when + // used with option -disable-free. + Mgr.reset(); } std::string AnalysisConsumer::getFunctionName(const Decl *D) { @@ -648,7 +685,7 @@ AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) { SourceLocation SL = Body ? Body->getLocStart() : D->getLocation(); SL = SM.getExpansionLoc(SL); - if (!Opts->AnalyzeAll && !SM.isWrittenInMainFile(SL)) { + if (!Opts->AnalyzeAll && !Mgr->isInCodeFile(SL)) { if (SL.isInvalid() || SM.isInSystemHeader(SL)) return AM_None; return Mode & ~AM_Path; @@ -704,7 +741,8 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>()) return; - ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,IMode); + ExprEngine Eng(CTU, *Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries, + IMode); // Set the graph auditor. std::unique_ptr<ExplodedNode::Auditor> Auditor; @@ -762,7 +800,7 @@ ento::CreateAnalysisConsumer(CompilerInstance &CI) { bool hasModelPath = analyzerOpts->Config.count("model-path") > 0; return llvm::make_unique<AnalysisConsumer>( - CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts, + CI, CI.getFrontendOpts().OutputFile, analyzerOpts, CI.getFrontendOpts().Plugins, hasModelPath ? new ModelInjector(CI) : nullptr); } @@ -852,9 +890,9 @@ UbigraphViz::~UbigraphViz() { std::string Ubiviz; if (auto Path = llvm::sys::findProgramByName("ubiviz")) Ubiviz = *Path; - const char *args[] = {Ubiviz.c_str(), Filename.c_str(), nullptr}; + std::array<StringRef, 2> Args{{Ubiviz, Filename}}; - if (llvm::sys::ExecuteAndWait(Ubiviz, &args[0], nullptr, {}, 0, 0, &ErrMsg)) { + if (llvm::sys::ExecuteAndWait(Ubiviz, Args, llvm::None, {}, 0, 0, &ErrMsg)) { llvm::errs() << "Error viewing graph: " << ErrMsg << "\n"; } diff --git a/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/lib/StaticAnalyzer/Frontend/CMakeLists.txt index e3ca91aec9cd..ff0a6e19fc97 100644 --- a/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ b/lib/StaticAnalyzer/Frontend/CMakeLists.txt @@ -7,14 +7,15 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangStaticAnalyzerFrontend AnalysisConsumer.cpp CheckerRegistration.cpp - ModelConsumer.cpp FrontendActions.cpp + ModelConsumer.cpp ModelInjector.cpp LINK_LIBS clangAST clangAnalysis clangBasic + clangCrossTU clangFrontend clangLex clangStaticAnalyzerCheckers diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index 6792f89876cd..a260c2d85b11 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -111,16 +111,21 @@ getCheckerOptList(const AnalyzerOptions &opts) { return checkerOpts; } -std::unique_ptr<CheckerManager> -ento::createCheckerManager(AnalyzerOptions &opts, const LangOptions &langOpts, - ArrayRef<std::string> plugins, - DiagnosticsEngine &diags) { +std::unique_ptr<CheckerManager> ento::createCheckerManager( + AnalyzerOptions &opts, const LangOptions &langOpts, + ArrayRef<std::string> plugins, + ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns, + DiagnosticsEngine &diags) { std::unique_ptr<CheckerManager> checkerMgr( new CheckerManager(langOpts, opts)); SmallVector<CheckerOptInfo, 8> checkerOpts = getCheckerOptList(opts); ClangCheckerRegistry allCheckers(plugins, &diags); + + for (const auto &Fn : checkerRegistrationFns) + Fn(allCheckers); + allCheckers.initializeManager(*checkerMgr, checkerOpts); allCheckers.validateCheckerOptions(opts, diags); checkerMgr->finishedCheckerRegistration(); diff --git a/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp b/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp index a65a5ee0a451..60825ef7411d 100644 --- a/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file implements an ASTConsumer for consuming model files. +/// This file implements an ASTConsumer for consuming model files. /// /// This ASTConsumer handles the AST of a parsed model file. All top level /// function definitions will be collected from that model file for later diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp index cdb1ed9b3815..c43d30440c8f 100644 --- a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp +++ b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -10,6 +10,7 @@ #include "ModelInjector.h" #include "clang/AST/Decl.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/Stack.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" @@ -95,11 +96,10 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) { ParseModelFileAction parseModelFile(Bodies); - const unsigned ThreadStackSize = 8 << 20; llvm::CrashRecoveryContext CRC; CRC.RunSafelyOnThread([&]() { Instance.ExecuteAction(parseModelFile); }, - ThreadStackSize); + DesiredStackSize); Instance.getPreprocessor().FinalizeForModelFile(); @@ -109,7 +109,7 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) { // The preprocessor enters to the main file id when parsing is started, so // the main file id is changed to the model file during parsing and it needs - // to be reseted to the former main file id after parsing of the model file + // to be reset to the former main file id after parsing of the model file // is done. SM.setMainFileID(mainFileID); } diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.h b/lib/StaticAnalyzer/Frontend/ModelInjector.h index 98a5f69d68e8..b1b6de9ef9d9 100644 --- a/lib/StaticAnalyzer/Frontend/ModelInjector.h +++ b/lib/StaticAnalyzer/Frontend/ModelInjector.h @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file defines the clang::ento::ModelInjector class which implements the +/// This file defines the clang::ento::ModelInjector class which implements the /// clang::CodeInjector interface. This class is responsible for injecting /// function definitions that were synthesized from model files. /// @@ -43,7 +43,7 @@ public: Stmt *getBody(const ObjCMethodDecl *D) override; private: - /// \brief Synthesize a body for a declaration + /// Synthesize a body for a declaration /// /// This method first looks up the appropriate model file based on the /// model-path configuration option and the name of the declaration that is |