summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Frontend
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2014-08-27 15:14:15 +0000
committerTed Kremenek <kremenek@apple.com>2014-08-27 15:14:15 +0000
commiteeccb30b942fbfd220990cbf52335559cdc04959 (patch)
tree641a7be8d79c887e0ca05c13703511208aa49dba /clang/lib/StaticAnalyzer/Frontend
parent5dc466b863a4d709747fbf73239ddbbcc47fa417 (diff)
downloadbcm5719-llvm-eeccb30b942fbfd220990cbf52335559cdc04959.tar.gz
bcm5719-llvm-eeccb30b942fbfd220990cbf52335559cdc04959.zip
Add support for the static analyzer to synthesize function implementations from external model files.
Currently the analyzer lazily models some functions using 'BodyFarm', which constructs a fake function implementation that the analyzer can simulate that approximates the semantics of the function when it is called. BodyFarm does this by constructing the AST for such definitions on-the-fly. One strength of BodyFarm is that all symbols and types referenced by synthesized function bodies are contextual adapted to the containing translation unit. The downside is that these ASTs are hardcoded in Clang's own source code. A more scalable model is to allow these models to be defined as source code in separate "model" files and have the analyzer use those definitions lazily when a function body is needed. Among other things, it will allow more customization of the analyzer for specific APIs and platforms. This patch provides the initial infrastructure for this feature. It extends BodyFarm to use an abstract API 'CodeInjector' that can be used to synthesize function bodies. That 'CodeInjector' is implemented using a new 'ModelInjector' in libFrontend, which lazily parses a model file and injects the ASTs into the current translation unit. Models are currently found by specifying a 'model-path' as an analyzer option; if no path is specified the CodeInjector is not used, thus defaulting to the current behavior in the analyzer. Models currently contain a single function definition, and can be found by finding the file <function name>.model. This is an initial starting point for something more rich, but it bootstraps this feature for future evolution. This patch was contributed by Gábor Horváth as part of his Google Summer of Code project. Some notes: - This introduces the notion of a "model file" into FrontendAction and the Preprocessor. This nomenclature is specific to the static analyzer, but possibly could be generalized. Essentially these are sources pulled in exogenously from the principal translation. Preprocessor gets a 'InitializeForModelFile' and 'FinalizeForModelFile' which could possibly be hoisted out of Preprocessor if Preprocessor exposed a new API to change the PragmaHandlers and some other internal pieces. This can be revisited. FrontendAction gets a 'isModelParsingAction()' predicate function used to allow a new FrontendAction to recycle the Preprocessor and ASTContext. This name could probably be made something more general (i.e., not tied to 'model files') at the expense of losing the intent of why it exists. This can be revisited. - This is a moderate sized patch; it has gone through some amount of offline code review. Most of the changes to the non-analyzer parts are fairly small, and would make little sense without the analyzer changes. - Most of the analyzer changes are plumbing, with the interesting behavior being introduced by ModelInjector.cpp and ModelConsumer.cpp. - The new functionality introduced by this change is off-by-default. It requires an analyzer config option to enable. llvm-svn: 216550
Diffstat (limited to 'clang/lib/StaticAnalyzer/Frontend')
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp29
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt2
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp15
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp42
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp119
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/ModelInjector.h75
6 files changed, 268 insertions, 14 deletions
diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index c390e9dbfa5..4e9ad71785a 100644
--- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -18,6 +18,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ParentMap.h"
+#include "clang/Analysis/CodeInjector.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CallGraph.h"
@@ -33,6 +34,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
+#include "clang/Frontend/CompilerInstance.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SmallPtrSet.h"
@@ -42,6 +44,7 @@
#include "llvm/Support/Program.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
+#include "ModelInjector.h"
#include <memory>
#include <queue>
@@ -157,6 +160,7 @@ public:
const std::string OutDir;
AnalyzerOptionsRef Opts;
ArrayRef<std::string> Plugins;
+ CodeInjector *Injector;
/// \brief Stores the declarations from the local translation unit.
/// Note, we pre-compute the local declarations at parse time as an
@@ -184,9 +188,10 @@ public:
AnalysisConsumer(const Preprocessor& pp,
const std::string& outdir,
AnalyzerOptionsRef opts,
- ArrayRef<std::string> plugins)
- : RecVisitorMode(0), RecVisitorBR(nullptr),
- Ctx(nullptr), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins) {
+ ArrayRef<std::string> plugins,
+ CodeInjector *injector)
+ : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(pp),
+ OutDir(outdir), Opts(opts), Plugins(plugins), Injector(injector) {
DigestAnalyzerOptions();
if (Opts->PrintStats) {
llvm::EnableStatistics();
@@ -286,6 +291,7 @@ public:
Ctx = &Context;
checkerMgr.reset(createCheckerManager(*Opts, PP.getLangOpts(), Plugins,
PP.getDiagnostics()));
+
Mgr.reset(new AnalysisManager(*Ctx,
PP.getDiagnostics(),
PP.getLangOpts(),
@@ -293,7 +299,8 @@ public:
CreateStoreMgr,
CreateConstraintMgr,
checkerMgr.get(),
- *Opts));
+ *Opts,
+ Injector));
}
/// \brief Store the top level decls in the set to be processed later on.
@@ -688,13 +695,17 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
//===----------------------------------------------------------------------===//
std::unique_ptr<AnalysisASTConsumer>
-ento::CreateAnalysisConsumer(const Preprocessor &pp, const std::string &outDir,
- AnalyzerOptionsRef opts,
- ArrayRef<std::string> plugins) {
+ento::CreateAnalysisConsumer(CompilerInstance &CI) {
// Disable the effects of '-Werror' when using the AnalysisConsumer.
- pp.getDiagnostics().setWarningsAsErrors(false);
+ CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false);
+
+ AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
+ bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
- return llvm::make_unique<AnalysisConsumer>(pp, outDir, opts, plugins);
+ return llvm::make_unique<AnalysisConsumer>(
+ CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts,
+ CI.getFrontendOpts().Plugins,
+ hasModelPath ? new ModelInjector(CI) : nullptr);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt
index 5349ed93e2c..76a83681a97 100644
--- a/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt
@@ -7,7 +7,9 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangStaticAnalyzerFrontend
AnalysisConsumer.cpp
CheckerRegistration.cpp
+ ModelConsumer.cpp
FrontendActions.cpp
+ ModelInjector.cpp
LINK_LIBS
clangAST
diff --git a/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
index 567102b7656..b33608042ce 100644
--- a/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
@@ -8,16 +8,21 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
-#include "clang/Frontend/CompilerInstance.h"
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
+#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h"
using namespace clang;
using namespace ento;
std::unique_ptr<ASTConsumer>
AnalysisAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
- return CreateAnalysisConsumer(CI.getPreprocessor(),
- CI.getFrontendOpts().OutputFile,
- CI.getAnalyzerOpts(),
- CI.getFrontendOpts().Plugins);
+ return CreateAnalysisConsumer(CI);
}
+ParseModelFileAction::ParseModelFileAction(llvm::StringMap<Stmt *> &Bodies)
+ : Bodies(Bodies) {}
+
+std::unique_ptr<ASTConsumer>
+ParseModelFileAction::CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) {
+ return llvm::make_unique<ModelConsumer>(Bodies);
+}
diff --git a/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
new file mode 100644
index 00000000000..360424bba19
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
@@ -0,0 +1,42 @@
+//===--- ModelConsumer.cpp - ASTConsumer for consuming model files --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief 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
+/// retrieval during the static analysis. The body of these functions will not
+/// be injected into the ASTUnit of the analyzed translation unit. It will be
+/// available through the BodyFarm which is utilized by the AnalysisDeclContext
+/// class.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclGroup.h"
+
+using namespace clang;
+using namespace ento;
+
+ModelConsumer::ModelConsumer(llvm::StringMap<Stmt *> &Bodies)
+ : Bodies(Bodies) {}
+
+bool ModelConsumer::HandleTopLevelDecl(DeclGroupRef D) {
+ for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) {
+
+ // Only interested in definitions.
+ const FunctionDecl *func = llvm::dyn_cast<FunctionDecl>(*I);
+ if (func && func->hasBody()) {
+ Bodies.insert(std::make_pair(func->getName(), func->getBody()));
+ }
+ }
+ return true;
+} \ No newline at end of file
diff --git a/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
new file mode 100644
index 00000000000..d5b623cd840
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
@@ -0,0 +1,119 @@
+//===-- ModelInjector.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ModelInjector.h"
+
+#include <string>
+#include <utility>
+
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
+#include "clang/Serialization/ASTReader.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/AST/Decl.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang;
+using namespace ento;
+
+ModelInjector::ModelInjector(CompilerInstance &CI) : CI(CI) {}
+
+Stmt *ModelInjector::getBody(const FunctionDecl *D) {
+ onBodySynthesis(D);
+ return Bodies[D->getName()];
+}
+
+Stmt *ModelInjector::getBody(const ObjCMethodDecl *D) {
+ onBodySynthesis(D);
+ return Bodies[D->getName()];
+}
+
+void ModelInjector::onBodySynthesis(const NamedDecl *D) {
+
+ // FIXME: what about overloads? Declarations can be used as keys but what
+ // about file name index? Mangled names may not be suitable for that either.
+ if (Bodies.count(D->getName()) != 0)
+ return;
+
+ SourceManager &SM = CI.getSourceManager();
+ FileID mainFileID = SM.getMainFileID();
+
+ AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
+ llvm::StringRef modelPath = analyzerOpts->Config["model-path"];
+
+ llvm::SmallString<128> fileName;
+
+ if (!modelPath.empty())
+ fileName =
+ llvm::StringRef(modelPath.str() + "/" + D->getName().str() + ".model");
+ else
+ fileName = llvm::StringRef(D->getName().str() + ".model");
+
+ if (!llvm::sys::fs::exists(fileName.str())) {
+ Bodies[D->getName()] = nullptr;
+ return;
+ }
+
+ IntrusiveRefCntPtr<CompilerInvocation> Invocation(
+ new CompilerInvocation(CI.getInvocation()));
+
+ FrontendOptions &FrontendOpts = Invocation->getFrontendOpts();
+ InputKind IK = IK_CXX; // FIXME
+ FrontendOpts.Inputs.clear();
+ FrontendOpts.Inputs.push_back(FrontendInputFile(fileName, IK));
+ FrontendOpts.DisableFree = true;
+
+ Invocation->getDiagnosticOpts().VerifyDiagnostics = 0;
+
+ // Modules are parsed by a separate CompilerInstance, so this code mimics that
+ // behavior for models
+ CompilerInstance Instance;
+ Instance.setInvocation(&*Invocation);
+ Instance.createDiagnostics(
+ new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()),
+ /*ShouldOwnClient=*/true);
+
+ Instance.getDiagnostics().setSourceManager(&SM);
+
+ Instance.setVirtualFileSystem(&CI.getVirtualFileSystem());
+
+ // The instance wants to take ownership, however DisableFree frontend option
+ // is set to true to avoid double free issues
+ Instance.setFileManager(&CI.getFileManager());
+ Instance.setSourceManager(&SM);
+ Instance.setPreprocessor(&CI.getPreprocessor());
+ Instance.setASTContext(&CI.getASTContext());
+
+ Instance.getPreprocessor().InitializeForModelFile();
+
+ ParseModelFileAction parseModelFile(Bodies);
+
+ const unsigned ThreadStackSize = 8 << 20;
+ llvm::CrashRecoveryContext CRC;
+
+ CRC.RunSafelyOnThread([&]() { Instance.ExecuteAction(parseModelFile); },
+ ThreadStackSize);
+
+ Instance.getPreprocessor().FinalizeForModelFile();
+
+ Instance.resetAndLeakSourceManager();
+ Instance.resetAndLeakFileManager();
+ Instance.resetAndLeakPreprocessor();
+
+ // 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
+ // is done.
+ SM.setMainFileID(mainFileID);
+}
diff --git a/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h b/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
new file mode 100644
index 00000000000..9241bd0d4fe
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
@@ -0,0 +1,75 @@
+//===-- ModelInjector.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief 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.
+///
+/// Model files allow definitions of functions to be lazily constituted for functions
+/// which lack bodies in the original source code. This allows the analyzer
+/// to more precisely analyze code that calls such functions, analyzing the
+/// artificial definitions (which typically approximate the semantics of the
+/// called function) when called by client code. These definitions are
+/// reconstituted lazily, on-demand, by the static analyzer engine.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H
+#define LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H
+
+#include <map>
+#include <vector>
+#include <memory>
+
+#include "clang/Analysis/CodeInjector.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+
+class CompilerInstance;
+class ASTUnit;
+class ASTReader;
+class NamedDecl;
+class Module;
+
+namespace ento {
+class ModelInjector : public CodeInjector {
+public:
+ ModelInjector(CompilerInstance &CI);
+ Stmt *getBody(const FunctionDecl *D);
+ Stmt *getBody(const ObjCMethodDecl *D);
+
+private:
+ /// \brief 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
+ /// looked up. If no model were synthesized yet for a function with that name
+ /// it will create a new compiler instance to parse the model file using the
+ /// ASTContext, Preprocessor, SourceManager of the original compiler instance.
+ /// The former resources are shared between the two compiler instance, so the
+ /// newly created instance have to "leak" these objects, since they are owned
+ /// by the original instance.
+ ///
+ /// The model-path should be either an absolute path or relative to the
+ /// working directory of the compiler.
+ void onBodySynthesis(const NamedDecl *D);
+
+ CompilerInstance &CI;
+
+ // FIXME: double memoization is redundant, with memoization both here and in
+ // BodyFarm.
+ llvm::StringMap<Stmt *> Bodies;
+};
+}
+}
+
+#endif \ No newline at end of file
OpenPOWER on IntegriCloud