2008-07-02 08:03:09 +08:00
|
|
|
//===--- 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.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2014-01-04 01:23:10 +08:00
|
|
|
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
|
2015-01-14 19:29:14 +08:00
|
|
|
#include "ModelInjector.h"
|
2008-07-02 08:03:09 +08:00
|
|
|
#include "clang/AST/ASTConsumer.h"
|
2014-01-07 19:51:46 +08:00
|
|
|
#include "clang/AST/DataRecursiveASTVisitor.h"
|
2008-07-02 08:03:09 +08:00
|
|
|
#include "clang/AST/Decl.h"
|
2009-12-16 13:29:59 +08:00
|
|
|
#include "clang/AST/DeclCXX.h"
|
2008-07-02 08:03:09 +08:00
|
|
|
#include "clang/AST/DeclObjC.h"
|
2009-11-05 10:41:58 +08:00
|
|
|
#include "clang/AST/ParentMap.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/Analysis/Analyses/LiveVariables.h"
|
2009-11-05 10:41:58 +08:00
|
|
|
#include "clang/Analysis/CFG.h"
|
2012-03-09 07:16:38 +08:00
|
|
|
#include "clang/Analysis/CallGraph.h"
|
2015-01-14 19:29:14 +08:00
|
|
|
#include "clang/Analysis/CodeInjector.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/Basic/FileManager.h"
|
|
|
|
#include "clang/Basic/SourceManager.h"
|
2015-01-14 19:29:14 +08:00
|
|
|
#include "clang/Frontend/CompilerInstance.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/Lex/Preprocessor.h"
|
2010-12-24 03:38:26 +08:00
|
|
|
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
|
2011-02-10 09:03:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
|
2011-02-10 09:03:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
|
2012-03-09 07:16:38 +08:00
|
|
|
#include "llvm/ADT/DepthFirstIterator.h"
|
2013-01-02 18:28:36 +08:00
|
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
2012-04-13 06:36:48 +08:00
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
2012-02-28 05:33:16 +08:00
|
|
|
#include "llvm/ADT/Statistic.h"
|
2013-06-26 14:13:06 +08:00
|
|
|
#include "llvm/Support/FileSystem.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#include "llvm/Support/Program.h"
|
|
|
|
#include "llvm/Support/Timer.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2014-03-09 19:36:40 +08:00
|
|
|
#include <memory>
|
2012-03-14 03:32:13 +08:00
|
|
|
#include <queue>
|
|
|
|
|
2008-07-02 08:03:09 +08:00
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2012-03-09 07:16:38 +08:00
|
|
|
using llvm::SmallPtrSet;
|
2008-07-02 08:03:09 +08:00
|
|
|
|
2014-04-22 11:17:02 +08:00
|
|
|
#define DEBUG_TYPE "AnalysisConsumer"
|
|
|
|
|
2014-09-05 08:14:57 +08:00
|
|
|
static std::unique_ptr<ExplodedNode::Auditor> CreateUbiViz();
|
2008-12-22 09:52:37 +08:00
|
|
|
|
2012-03-10 05:14:01 +08:00
|
|
|
STATISTIC(NumFunctionTopLevel, "The # of functions at top level.");
|
2012-12-08 05:51:47 +08:00
|
|
|
STATISTIC(NumFunctionsAnalyzed,
|
2012-12-18 04:08:54 +08:00
|
|
|
"The # of functions and blocks analyzed (as top level "
|
|
|
|
"with inlining turned on).");
|
2012-04-03 10:05:47 +08:00
|
|
|
STATISTIC(NumBlocksInAnalyzedFunctions,
|
2012-12-18 04:08:54 +08:00
|
|
|
"The # of basic blocks in the analyzed functions.");
|
2012-04-03 10:05:47 +08:00
|
|
|
STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");
|
2012-07-06 04:44:02 +08:00
|
|
|
STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");
|
2012-03-09 07:16:38 +08:00
|
|
|
|
2009-07-28 06:13:39 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2011-09-27 09:43:33 +08:00
|
|
|
// Special PathDiagnosticConsumers.
|
2009-07-28 06:13:39 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-08-16 09:06:30 +08:00
|
|
|
void ento::createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
|
|
|
|
PathDiagnosticConsumers &C,
|
|
|
|
const std::string &prefix,
|
|
|
|
const Preprocessor &PP) {
|
2012-12-19 09:35:35 +08:00
|
|
|
createHTMLDiagnosticConsumer(AnalyzerOpts, C,
|
|
|
|
llvm::sys::path::parent_path(prefix), PP);
|
|
|
|
createPlistDiagnosticConsumer(AnalyzerOpts, C, prefix, PP);
|
2009-07-28 06:13:39 +08:00
|
|
|
}
|
|
|
|
|
2013-08-16 09:06:30 +08:00
|
|
|
void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
|
|
|
|
PathDiagnosticConsumers &C,
|
|
|
|
const std::string &Prefix,
|
|
|
|
const clang::Preprocessor &PP) {
|
|
|
|
llvm_unreachable("'text' consumer should be enabled on ClangDiags");
|
|
|
|
}
|
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
namespace {
|
|
|
|
class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer {
|
|
|
|
DiagnosticsEngine &Diag;
|
2013-08-16 09:06:30 +08:00
|
|
|
bool IncludePath;
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
public:
|
2013-08-16 09:06:30 +08:00
|
|
|
ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag)
|
|
|
|
: Diag(Diag), IncludePath(false) {}
|
2015-04-11 10:00:23 +08:00
|
|
|
~ClangDiagPathDiagConsumer() override {}
|
2014-03-15 12:29:04 +08:00
|
|
|
StringRef getName() const override { return "ClangDiags"; }
|
2013-08-16 09:06:30 +08:00
|
|
|
|
2014-03-15 12:29:04 +08:00
|
|
|
bool supportsLogicalOpControlFlow() const override { return true; }
|
|
|
|
bool supportsCrossFileDiagnostics() const override { return true; }
|
2013-08-16 09:06:30 +08:00
|
|
|
|
2014-03-15 12:29:04 +08:00
|
|
|
PathGenerationScheme getGenerationScheme() const override {
|
2013-08-16 09:06:30 +08:00
|
|
|
return IncludePath ? Minimal : None;
|
|
|
|
}
|
|
|
|
|
|
|
|
void enablePaths() {
|
|
|
|
IncludePath = true;
|
|
|
|
}
|
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
|
2014-03-15 12:29:04 +08:00
|
|
|
FilesMade *filesMade) override {
|
2013-12-24 01:59:59 +08:00
|
|
|
unsigned WarnID = Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
|
|
|
|
unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0");
|
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
for (std::vector<const PathDiagnostic*>::iterator I = Diags.begin(),
|
|
|
|
E = Diags.end(); I != E; ++I) {
|
|
|
|
const PathDiagnostic *PD = *I;
|
2013-12-24 01:59:59 +08:00
|
|
|
SourceLocation WarnLoc = PD->getLocation().asLocation();
|
2014-03-06 21:23:30 +08:00
|
|
|
Diag.Report(WarnLoc, WarnID) << PD->getShortDescription()
|
|
|
|
<< PD->path.back()->getRanges();
|
2013-08-16 09:06:30 +08:00
|
|
|
|
|
|
|
if (!IncludePath)
|
|
|
|
continue;
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
|
2013-08-16 09:06:30 +08:00
|
|
|
PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true);
|
|
|
|
for (PathPieces::const_iterator PI = FlatPath.begin(),
|
|
|
|
PE = FlatPath.end();
|
|
|
|
PI != PE; ++PI) {
|
|
|
|
SourceLocation NoteLoc = (*PI)->getLocation().asLocation();
|
2014-03-06 21:23:30 +08:00
|
|
|
Diag.Report(NoteLoc, NoteID) << (*PI)->getString()
|
|
|
|
<< (*PI)->getRanges();
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2008-07-02 08:03:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AnalysisConsumer declaration.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2014-02-04 02:37:50 +08:00
|
|
|
class AnalysisConsumer : public AnalysisASTConsumer,
|
2013-12-20 10:02:58 +08:00
|
|
|
public DataRecursiveASTVisitor<AnalysisConsumer> {
|
2012-10-11 01:55:40 +08:00
|
|
|
enum {
|
|
|
|
AM_None = 0,
|
|
|
|
AM_Syntax = 0x1,
|
|
|
|
AM_Path = 0x2
|
2012-03-14 03:32:00 +08:00
|
|
|
};
|
2012-10-11 01:55:40 +08:00
|
|
|
typedef unsigned AnalysisMode;
|
2012-03-14 03:32:00 +08:00
|
|
|
|
|
|
|
/// Mode of the analyzes while recursively visiting Decls.
|
|
|
|
AnalysisMode RecVisitorMode;
|
|
|
|
/// Bug Reporter to use while recursively visiting Decls.
|
|
|
|
BugReporter *RecVisitorBR;
|
|
|
|
|
2009-11-11 14:28:42 +08:00
|
|
|
public:
|
2011-08-13 07:37:29 +08:00
|
|
|
ASTContext *Ctx;
|
2009-11-11 14:28:42 +08:00
|
|
|
const Preprocessor &PP;
|
|
|
|
const std::string OutDir;
|
2012-08-31 12:36:05 +08:00
|
|
|
AnalyzerOptionsRef Opts;
|
2011-08-17 05:24:21 +08:00
|
|
|
ArrayRef<std::string> Plugins;
|
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
2014-08-27 23:14:15 +08:00
|
|
|
CodeInjector *Injector;
|
2009-08-03 11:27:37 +08:00
|
|
|
|
2012-04-13 06:36:48 +08:00
|
|
|
/// \brief 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
|
|
|
|
/// working with a PCH file.
|
|
|
|
SetOfDecls LocalTUDecls;
|
2012-04-27 08:38:33 +08:00
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
// Set of PathDiagnosticConsumers. Owned by AnalysisManager.
|
|
|
|
PathDiagnosticConsumers PathConsumers;
|
2009-08-03 11:27:37 +08:00
|
|
|
|
2009-11-11 14:28:42 +08:00
|
|
|
StoreManagerCreator CreateStoreMgr;
|
|
|
|
ConstraintManagerCreator CreateConstraintMgr;
|
2008-07-02 08:03:09 +08:00
|
|
|
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<CheckerManager> checkerMgr;
|
|
|
|
std::unique_ptr<AnalysisManager> Mgr;
|
2009-08-03 11:13:46 +08:00
|
|
|
|
2012-03-06 04:53:59 +08:00
|
|
|
/// Time the analyzes time of each translation unit.
|
|
|
|
static llvm::Timer* TUTotalTimer;
|
|
|
|
|
2012-03-30 13:48:10 +08:00
|
|
|
/// The information about analyzed functions shared throughout the
|
|
|
|
/// translation unit.
|
|
|
|
FunctionSummariesTy FunctionSummaries;
|
|
|
|
|
2009-11-11 14:28:42 +08:00
|
|
|
AnalysisConsumer(const Preprocessor& pp,
|
|
|
|
const std::string& outdir,
|
2012-08-31 12:36:05 +08:00
|
|
|
AnalyzerOptionsRef opts,
|
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
2014-08-27 23:14:15 +08:00
|
|
|
ArrayRef<std::string> plugins,
|
|
|
|
CodeInjector *injector)
|
|
|
|
: RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(pp),
|
|
|
|
OutDir(outdir), Opts(opts), Plugins(plugins), Injector(injector) {
|
2009-11-11 14:28:42 +08:00
|
|
|
DigestAnalyzerOptions();
|
2012-08-31 12:36:05 +08:00
|
|
|
if (Opts->PrintStats) {
|
2012-03-06 04:53:59 +08:00
|
|
|
llvm::EnableStatistics();
|
|
|
|
TUTotalTimer = new llvm::Timer("Analyzer Total Time");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-11 10:00:23 +08:00
|
|
|
~AnalysisConsumer() override {
|
2012-08-31 12:36:05 +08:00
|
|
|
if (Opts->PrintStats)
|
2012-03-06 04:53:59 +08:00
|
|
|
delete TUTotalTimer;
|
2009-11-11 14:28:42 +08:00
|
|
|
}
|
2009-07-30 17:11:52 +08:00
|
|
|
|
2009-11-11 14:28:42 +08:00
|
|
|
void DigestAnalyzerOptions() {
|
2014-02-04 02:37:50 +08:00
|
|
|
if (Opts->AnalysisDiagOpt != PD_NONE) {
|
|
|
|
// Create the PathDiagnosticConsumer.
|
|
|
|
ClangDiagPathDiagConsumer *clangDiags =
|
|
|
|
new ClangDiagPathDiagConsumer(PP.getDiagnostics());
|
|
|
|
PathConsumers.push_back(clangDiags);
|
|
|
|
|
|
|
|
if (Opts->AnalysisDiagOpt == PD_TEXT) {
|
|
|
|
clangDiags->enablePaths();
|
|
|
|
|
|
|
|
} else if (!OutDir.empty()) {
|
|
|
|
switch (Opts->AnalysisDiagOpt) {
|
|
|
|
default:
|
|
|
|
#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \
|
|
|
|
case PD_##NAME: \
|
2014-07-05 11:08:06 +08:00
|
|
|
CREATEFN(*Opts.get(), PathConsumers, OutDir, PP); \
|
2014-02-04 02:37:50 +08:00
|
|
|
break;
|
2012-08-31 12:35:58 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Analyses.def"
|
2014-02-04 02:37:50 +08:00
|
|
|
}
|
2009-07-30 17:11:52 +08:00
|
|
|
}
|
2009-11-11 14:28:42 +08:00
|
|
|
}
|
2009-07-30 17:11:52 +08:00
|
|
|
|
2009-11-11 14:28:42 +08:00
|
|
|
// Create the analyzer component creators.
|
2012-08-31 12:36:05 +08:00
|
|
|
switch (Opts->AnalysisStoreOpt) {
|
2011-02-15 02:13:17 +08:00
|
|
|
default:
|
2011-09-23 13:06:16 +08:00
|
|
|
llvm_unreachable("Unknown store manager.");
|
2009-07-30 17:11:52 +08:00
|
|
|
#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \
|
2011-02-15 02:13:17 +08:00
|
|
|
case NAME##Model: CreateStoreMgr = CREATEFN; break;
|
2012-08-31 12:35:58 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Analyses.def"
|
2009-11-11 14:28:42 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-31 12:36:05 +08:00
|
|
|
switch (Opts->AnalysisConstraintsOpt) {
|
2011-02-15 02:13:17 +08:00
|
|
|
default:
|
2013-02-08 07:29:20 +08:00
|
|
|
llvm_unreachable("Unknown constraint manager.");
|
2009-07-30 17:11:52 +08:00
|
|
|
#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \
|
2011-02-15 02:13:17 +08:00
|
|
|
case NAME##Model: CreateConstraintMgr = CREATEFN; break;
|
2012-08-31 12:35:58 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Analyses.def"
|
2009-07-30 17:11:52 +08:00
|
|
|
}
|
2009-11-11 14:28:42 +08:00
|
|
|
}
|
2010-02-15 03:08:51 +08:00
|
|
|
|
2013-02-02 08:30:02 +08:00
|
|
|
void DisplayFunction(const Decl *D, AnalysisMode Mode,
|
|
|
|
ExprEngine::InliningModes IMode) {
|
2012-08-31 12:36:05 +08:00
|
|
|
if (!Opts->AnalyzerDisplayProgress)
|
2009-11-11 14:28:42 +08:00
|
|
|
return;
|
2010-02-15 03:08:51 +08:00
|
|
|
|
2009-12-08 06:06:12 +08:00
|
|
|
SourceManager &SM = Mgr->getASTContext().getSourceManager();
|
|
|
|
PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
|
2010-11-12 15:15:47 +08:00
|
|
|
if (Loc.isValid()) {
|
2012-03-14 03:32:00 +08:00
|
|
|
llvm::errs() << "ANALYZE";
|
2012-10-11 01:55:40 +08:00
|
|
|
|
|
|
|
if (Mode == AM_Syntax)
|
|
|
|
llvm::errs() << " (Syntax)";
|
2013-02-02 08:30:02 +08:00
|
|
|
else if (Mode == AM_Path) {
|
|
|
|
llvm::errs() << " (Path, ";
|
|
|
|
switch (IMode) {
|
2013-03-27 02:57:58 +08:00
|
|
|
case ExprEngine::Inline_Minimal:
|
|
|
|
llvm::errs() << " Inline_Minimal";
|
2013-02-02 08:30:02 +08:00
|
|
|
break;
|
2013-03-27 02:57:58 +08:00
|
|
|
case ExprEngine::Inline_Regular:
|
|
|
|
llvm::errs() << " Inline_Regular";
|
2013-02-02 08:30:02 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
llvm::errs() << ")";
|
|
|
|
}
|
2012-10-11 01:55:40 +08:00
|
|
|
else
|
|
|
|
assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!");
|
|
|
|
|
2012-03-14 03:32:00 +08:00
|
|
|
llvm::errs() << ": " << Loc.getFilename();
|
2010-11-12 15:15:47 +08:00
|
|
|
if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
|
|
|
|
const NamedDecl *ND = cast<NamedDecl>(D);
|
2011-10-15 02:45:37 +08:00
|
|
|
llvm::errs() << ' ' << *ND << '\n';
|
2010-11-12 15:15:47 +08:00
|
|
|
}
|
|
|
|
else if (isa<BlockDecl>(D)) {
|
|
|
|
llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:"
|
|
|
|
<< Loc.getColumn() << '\n';
|
|
|
|
}
|
|
|
|
else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
|
|
|
|
Selector S = MD->getSelector();
|
|
|
|
llvm::errs() << ' ' << S.getAsString();
|
|
|
|
}
|
2010-10-23 06:08:29 +08:00
|
|
|
}
|
2009-11-11 14:28:42 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2014-03-15 12:29:04 +08:00
|
|
|
void Initialize(ASTContext &Context) override {
|
2009-11-11 14:28:42 +08:00
|
|
|
Ctx = &Context;
|
2014-08-30 04:11:03 +08:00
|
|
|
checkerMgr = createCheckerManager(*Opts, PP.getLangOpts(), Plugins,
|
|
|
|
PP.getDiagnostics());
|
|
|
|
|
|
|
|
Mgr = llvm::make_unique<AnalysisManager>(
|
|
|
|
*Ctx, PP.getDiagnostics(), PP.getLangOpts(), PathConsumers,
|
|
|
|
CreateStoreMgr, CreateConstraintMgr, checkerMgr.get(), *Opts, Injector);
|
2009-11-11 14:28:42 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-04-13 06:36:48 +08:00
|
|
|
/// \brief Store the top level decls in the set to be processed later on.
|
|
|
|
/// (Doing this pre-processing avoids deserialization of data from PCH.)
|
2014-03-15 12:29:04 +08:00
|
|
|
bool HandleTopLevelDecl(DeclGroupRef D) override;
|
|
|
|
void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override;
|
2012-04-13 06:36:48 +08:00
|
|
|
|
2014-03-15 12:29:04 +08:00
|
|
|
void HandleTranslationUnit(ASTContext &C) override;
|
2011-01-21 01:09:48 +08:00
|
|
|
|
2012-12-08 05:51:47 +08:00
|
|
|
/// \brief Determine which inlining mode should be used when this function is
|
2013-03-27 02:57:58 +08:00
|
|
|
/// analyzed. This allows to redefine the default inlining policies when
|
|
|
|
/// analyzing a given function.
|
2012-12-08 05:51:47 +08:00
|
|
|
ExprEngine::InliningModes
|
2014-08-30 04:01:38 +08:00
|
|
|
getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited);
|
2012-12-08 05:51:47 +08:00
|
|
|
|
2012-04-13 06:36:48 +08:00
|
|
|
/// \brief 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.
|
2012-10-11 01:55:37 +08:00
|
|
|
void HandleDeclsCallGraph(const unsigned LocalTUDeclsSize);
|
2012-03-14 03:32:00 +08:00
|
|
|
|
|
|
|
/// \brief 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
|
|
|
|
/// set of functions which should be considered analyzed after analyzing the
|
|
|
|
/// given root function.
|
2012-04-13 06:36:48 +08:00
|
|
|
void HandleCode(Decl *D, AnalysisMode Mode,
|
2013-03-27 02:57:58 +08:00
|
|
|
ExprEngine::InliningModes IMode = ExprEngine::Inline_Minimal,
|
2014-05-27 10:45:47 +08:00
|
|
|
SetOfConstDecls *VisitedCallees = nullptr);
|
2012-03-14 03:32:00 +08:00
|
|
|
|
2012-12-08 05:51:47 +08:00
|
|
|
void RunPathSensitiveChecks(Decl *D,
|
|
|
|
ExprEngine::InliningModes IMode,
|
|
|
|
SetOfConstDecls *VisitedCallees);
|
2012-04-13 06:36:48 +08:00
|
|
|
void ActionExprEngine(Decl *D, bool ObjCGCEnabled,
|
2012-12-08 05:51:47 +08:00
|
|
|
ExprEngine::InliningModes IMode,
|
2012-04-13 06:36:48 +08:00
|
|
|
SetOfConstDecls *VisitedCallees);
|
2012-03-14 03:32:00 +08:00
|
|
|
|
|
|
|
/// Visitors for the RecursiveASTVisitor.
|
2012-05-12 07:15:18 +08:00
|
|
|
bool shouldWalkTypesOfTypeLocs() const { return false; }
|
2012-03-14 03:32:00 +08:00
|
|
|
|
|
|
|
/// Handle callbacks for arbitrary Decls.
|
|
|
|
bool VisitDecl(Decl *D) {
|
2012-10-11 01:55:40 +08:00
|
|
|
AnalysisMode Mode = getModeForDecl(D, RecVisitorMode);
|
|
|
|
if (Mode & AM_Syntax)
|
|
|
|
checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR);
|
2012-03-14 03:32:00 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitFunctionDecl(FunctionDecl *FD) {
|
|
|
|
IdentifierInfo *II = FD->getIdentifier();
|
|
|
|
if (II && II->getName().startswith("__inline"))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// We skip function template definitions, as their semantics is
|
|
|
|
// only determined when they are instantiated.
|
|
|
|
if (FD->isThisDeclarationADefinition() &&
|
|
|
|
!FD->isDependentContext()) {
|
2012-12-08 05:51:47 +08:00
|
|
|
assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
|
2012-03-14 03:32:00 +08:00
|
|
|
HandleCode(FD, RecVisitorMode);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
|
2012-12-08 05:51:47 +08:00
|
|
|
if (MD->isThisDeclarationADefinition()) {
|
|
|
|
assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
|
2012-03-14 03:32:00 +08:00
|
|
|
HandleCode(MD, RecVisitorMode);
|
2012-12-08 05:51:47 +08:00
|
|
|
}
|
2012-03-14 03:32:00 +08:00
|
|
|
return true;
|
|
|
|
}
|
2012-12-21 09:19:15 +08:00
|
|
|
|
|
|
|
bool VisitBlockDecl(BlockDecl *BD) {
|
|
|
|
if (BD->hasBody()) {
|
|
|
|
assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
|
|
|
|
HandleCode(BD, RecVisitorMode);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2012-04-13 06:36:48 +08:00
|
|
|
|
2015-04-11 10:00:23 +08:00
|
|
|
void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) override {
|
2014-02-04 02:37:50 +08:00
|
|
|
PathConsumers.push_back(Consumer);
|
|
|
|
}
|
|
|
|
|
2012-04-13 06:36:48 +08:00
|
|
|
private:
|
|
|
|
void storeTopLevelDecls(DeclGroupRef DG);
|
|
|
|
|
|
|
|
/// \brief Check if we should skip (not analyze) the given function.
|
2012-10-11 01:55:40 +08:00
|
|
|
AnalysisMode getModeForDecl(Decl *D, AnalysisMode Mode);
|
2012-04-13 06:36:48 +08:00
|
|
|
|
2009-11-11 14:28:42 +08:00
|
|
|
};
|
2008-07-02 08:03:09 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
2012-03-14 03:32:00 +08:00
|
|
|
|
2008-07-02 08:03:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AnalysisConsumer implementation.
|
|
|
|
//===----------------------------------------------------------------------===//
|
2014-05-27 10:45:47 +08:00
|
|
|
llvm::Timer* AnalysisConsumer::TUTotalTimer = nullptr;
|
2008-07-02 08:03:09 +08:00
|
|
|
|
2012-04-13 06:36:48 +08:00
|
|
|
bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) {
|
|
|
|
storeTopLevelDecls(DG);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
|
|
|
|
storeTopLevelDecls(DG);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
|
|
|
|
for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) {
|
|
|
|
|
|
|
|
// Skip ObjCMethodDecl, wait for the objc container to avoid
|
|
|
|
// analyzing twice.
|
|
|
|
if (isa<ObjCMethodDecl>(*I))
|
|
|
|
continue;
|
|
|
|
|
2012-04-27 12:54:28 +08:00
|
|
|
LocalTUDecls.push_back(*I);
|
2012-04-13 06:36:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-15 03:08:17 +08:00
|
|
|
static bool shouldSkipFunction(const Decl *D,
|
2014-04-24 00:39:41 +08:00
|
|
|
const SetOfConstDecls &Visited,
|
|
|
|
const SetOfConstDecls &VisitedAsTopLevel) {
|
2012-12-15 03:08:17 +08:00
|
|
|
if (VisitedAsTopLevel.count(D))
|
2012-12-08 05:51:47 +08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
// We want to re-analyse the functions as top level in the following cases:
|
2012-08-31 07:42:02 +08:00
|
|
|
// - The 'init' methods should be reanalyzed because
|
|
|
|
// ObjCNonNilReturnValueChecker assumes that '[super init]' never returns
|
2012-12-08 05:51:47 +08:00
|
|
|
// 'nil' and unless we analyze the 'init' functions as top level, we will
|
|
|
|
// not catch errors within defensive code.
|
2012-08-31 07:42:02 +08:00
|
|
|
// - We want to reanalyze all ObjC methods as top level to report Retain
|
|
|
|
// Count naming convention errors more aggressively.
|
2012-12-15 03:08:17 +08:00
|
|
|
if (isa<ObjCMethodDecl>(D))
|
2012-08-31 07:42:02 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// Otherwise, if we visited the function before, do not reanalyze it.
|
2012-12-15 03:08:17 +08:00
|
|
|
return Visited.count(D);
|
2012-08-31 07:42:02 +08:00
|
|
|
}
|
|
|
|
|
2012-12-08 05:51:47 +08:00
|
|
|
ExprEngine::InliningModes
|
2012-12-15 03:08:17 +08:00
|
|
|
AnalysisConsumer::getInliningModeForFunction(const Decl *D,
|
2014-04-24 00:39:41 +08:00
|
|
|
const SetOfConstDecls &Visited) {
|
2012-12-08 05:51:47 +08:00
|
|
|
// We want to reanalyze all ObjC methods as top level to report Retain
|
2013-03-27 02:57:58 +08:00
|
|
|
// Count naming convention errors more aggressively. But we should tune down
|
2012-12-08 05:51:47 +08:00
|
|
|
// inlining when reanalyzing an already inlined function.
|
2012-12-15 03:08:17 +08:00
|
|
|
if (Visited.count(D)) {
|
|
|
|
assert(isa<ObjCMethodDecl>(D) &&
|
2012-12-08 05:51:47 +08:00
|
|
|
"We are only reanalyzing ObjCMethods.");
|
2012-12-15 03:08:17 +08:00
|
|
|
const ObjCMethodDecl *ObjCM = cast<ObjCMethodDecl>(D);
|
2012-12-08 05:51:47 +08:00
|
|
|
if (ObjCM->getMethodFamily() != OMF_init)
|
2013-03-27 02:57:58 +08:00
|
|
|
return ExprEngine::Inline_Minimal;
|
2012-12-08 05:51:47 +08:00
|
|
|
}
|
|
|
|
|
2013-03-27 02:57:58 +08:00
|
|
|
return ExprEngine::Inline_Regular;
|
2012-12-08 05:51:47 +08:00
|
|
|
}
|
|
|
|
|
2012-10-11 01:55:37 +08:00
|
|
|
void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
|
2012-12-22 01:27:01 +08:00
|
|
|
// Build the Call Graph by adding all the top level declarations to the graph.
|
2012-06-01 02:07:55 +08:00
|
|
|
// Note: CallGraph can trigger deserialization of more items from a pch
|
|
|
|
// (though HandleInterestingDecl); triggering additions to LocalTUDecls.
|
|
|
|
// We rely on random access to add the initially processed Decls to CG.
|
2012-12-22 01:27:01 +08:00
|
|
|
CallGraph CG;
|
2012-06-01 02:07:55 +08:00
|
|
|
for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
|
2012-05-31 07:14:48 +08:00
|
|
|
CG.addToCallGraph(LocalTUDecls[i]);
|
|
|
|
}
|
2012-03-09 07:16:38 +08:00
|
|
|
|
2012-12-22 01:27:01 +08:00
|
|
|
// Walk over all of the call graph nodes in topological order, so that we
|
|
|
|
// analyze parents before the children. Skip the functions inlined into
|
|
|
|
// the previously processed functions. Use external Visited set to identify
|
|
|
|
// inlined functions. The topological order allows the "do not reanalyze
|
|
|
|
// previously inlined function" performance heuristic to be triggered more
|
|
|
|
// often.
|
2012-12-15 03:08:17 +08:00
|
|
|
SetOfConstDecls Visited;
|
|
|
|
SetOfConstDecls VisitedAsTopLevel;
|
2012-12-22 01:27:01 +08:00
|
|
|
llvm::ReversePostOrderTraversal<clang::CallGraph*> RPOT(&CG);
|
|
|
|
for (llvm::ReversePostOrderTraversal<clang::CallGraph*>::rpo_iterator
|
|
|
|
I = RPOT.begin(), E = RPOT.end(); I != E; ++I) {
|
|
|
|
NumFunctionTopLevel++;
|
2012-07-03 04:21:48 +08:00
|
|
|
|
2012-12-22 01:27:01 +08:00
|
|
|
CallGraphNode *N = *I;
|
2012-12-15 03:08:17 +08:00
|
|
|
Decl *D = N->getDecl();
|
2012-12-22 01:27:01 +08:00
|
|
|
|
|
|
|
// Skip the abstract root node.
|
|
|
|
if (!D)
|
|
|
|
continue;
|
2012-12-15 03:08:17 +08:00
|
|
|
|
2012-03-14 03:32:13 +08:00
|
|
|
// Skip the functions which have been processed already or previously
|
|
|
|
// inlined.
|
2012-12-15 03:08:17 +08:00
|
|
|
if (shouldSkipFunction(D, Visited, VisitedAsTopLevel))
|
2012-03-14 03:32:13 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// Analyze the function.
|
2012-04-13 06:36:48 +08:00
|
|
|
SetOfConstDecls VisitedCallees;
|
2012-12-08 05:51:47 +08:00
|
|
|
|
2012-12-15 03:08:17 +08:00
|
|
|
HandleCode(D, AM_Path, getInliningModeForFunction(D, Visited),
|
2014-05-27 10:45:47 +08:00
|
|
|
(Mgr->options.InliningMode == All ? nullptr : &VisitedCallees));
|
2012-03-14 03:32:13 +08:00
|
|
|
|
|
|
|
// Add the visited callees to the global visited set.
|
2012-04-27 08:38:33 +08:00
|
|
|
for (SetOfConstDecls::iterator I = VisitedCallees.begin(),
|
|
|
|
E = VisitedCallees.end(); I != E; ++I) {
|
2012-12-15 03:08:17 +08:00
|
|
|
Visited.insert(*I);
|
2012-03-14 03:32:13 +08:00
|
|
|
}
|
2012-12-15 03:08:17 +08:00
|
|
|
VisitedAsTopLevel.insert(D);
|
2012-03-09 07:16:38 +08:00
|
|
|
}
|
2011-08-28 05:28:09 +08:00
|
|
|
}
|
|
|
|
|
2011-01-21 01:09:48 +08:00
|
|
|
void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
|
2012-03-14 03:32:00 +08:00
|
|
|
// Don't run the actions if an error has occurred with parsing the file.
|
|
|
|
DiagnosticsEngine &Diags = PP.getDiagnostics();
|
|
|
|
if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
|
|
|
|
return;
|
|
|
|
|
2014-08-30 04:01:38 +08:00
|
|
|
// Don't analyze if the user explicitly asked for no checks to be performed
|
|
|
|
// on this file.
|
|
|
|
if (Opts->DisableAllChecks)
|
|
|
|
return;
|
|
|
|
|
2012-01-08 00:49:46 +08:00
|
|
|
{
|
2012-03-06 04:53:59 +08:00
|
|
|
if (TUTotalTimer) TUTotalTimer->startTimer();
|
|
|
|
|
2012-01-08 00:49:46 +08:00
|
|
|
// Introduce a scope to destroy BR before Mgr.
|
|
|
|
BugReporter BR(*Mgr);
|
|
|
|
TranslationUnitDecl *TU = C.getTranslationUnitDecl();
|
|
|
|
checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
|
2012-03-14 03:32:00 +08:00
|
|
|
|
|
|
|
// 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.
|
2012-10-11 01:55:40 +08:00
|
|
|
RecVisitorMode = AM_Syntax;
|
|
|
|
if (!Mgr->shouldInlineCall())
|
|
|
|
RecVisitorMode |= AM_Path;
|
2012-03-14 03:32:00 +08:00
|
|
|
RecVisitorBR = &BR;
|
2012-04-13 06:36:48 +08:00
|
|
|
|
|
|
|
// Process all the top level declarations.
|
2012-04-27 08:38:33 +08:00
|
|
|
//
|
2012-04-27 12:54:28 +08:00
|
|
|
// 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.
|
2012-06-01 02:07:55 +08:00
|
|
|
const unsigned LocalTUDeclsSize = LocalTUDecls.size();
|
|
|
|
for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
|
2012-04-27 12:54:28 +08:00
|
|
|
TraverseDecl(LocalTUDecls[i]);
|
2012-04-27 08:38:33 +08:00
|
|
|
}
|
2012-03-14 03:32:00 +08:00
|
|
|
|
|
|
|
if (Mgr->shouldInlineCall())
|
2012-10-11 01:55:37 +08:00
|
|
|
HandleDeclsCallGraph(LocalTUDeclsSize);
|
2009-09-10 13:44:00 +08:00
|
|
|
|
2012-01-08 00:49:46 +08:00
|
|
|
// After all decls handled, run checkers on the entire TranslationUnit.
|
|
|
|
checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
|
2012-03-14 03:32:00 +08:00
|
|
|
|
2014-05-27 10:45:47 +08:00
|
|
|
RecVisitorBR = nullptr;
|
2012-01-08 00:49:46 +08:00
|
|
|
}
|
2011-05-05 11:41:17 +08:00
|
|
|
|
2011-09-26 08:51:36 +08:00
|
|
|
// Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
|
2009-08-02 13:43:14 +08:00
|
|
|
// FIXME: This should be replaced with something that doesn't rely on
|
2011-09-26 08:51:36 +08:00
|
|
|
// side-effects in PathDiagnosticConsumer's destructor. This is required when
|
2009-12-15 17:32:42 +08:00
|
|
|
// used with option -disable-free.
|
2014-07-19 09:06:45 +08:00
|
|
|
Mgr.reset();
|
2012-03-06 04:53:59 +08:00
|
|
|
|
|
|
|
if (TUTotalTimer) TUTotalTimer->stopTimer();
|
2012-04-05 10:10:21 +08:00
|
|
|
|
|
|
|
// Count how many basic blocks we have not covered.
|
|
|
|
NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks();
|
|
|
|
if (NumBlocksInAnalyzedFunctions > 0)
|
|
|
|
PercentReachableBlocks =
|
|
|
|
(FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
|
|
|
|
NumBlocksInAnalyzedFunctions;
|
|
|
|
|
2008-07-03 12:29:21 +08:00
|
|
|
}
|
|
|
|
|
2012-03-09 07:16:38 +08:00
|
|
|
static std::string getFunctionName(const Decl *D) {
|
|
|
|
if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) {
|
|
|
|
return ID->getSelector().getAsString();
|
|
|
|
}
|
|
|
|
if (const FunctionDecl *ND = dyn_cast<FunctionDecl>(D)) {
|
|
|
|
IdentifierInfo *II = ND->getIdentifier();
|
|
|
|
if (II)
|
|
|
|
return II->getName();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2012-10-11 01:55:40 +08:00
|
|
|
AnalysisConsumer::AnalysisMode
|
|
|
|
AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
|
2012-08-31 12:36:05 +08:00
|
|
|
if (!Opts->AnalyzeSpecificFunction.empty() &&
|
|
|
|
getFunctionName(D) != Opts->AnalyzeSpecificFunction)
|
2012-10-11 01:55:40 +08:00
|
|
|
return AM_None;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-10-11 01:55:40 +08:00
|
|
|
// Unless -analyze-all is specified, treat decls differently depending on
|
|
|
|
// where they came from:
|
|
|
|
// - Main source file: run both path-sensitive and non-path-sensitive checks.
|
|
|
|
// - Header files: run non-path-sensitive checks only.
|
|
|
|
// - System headers: don't run any checks.
|
2010-06-15 08:55:40 +08:00
|
|
|
SourceManager &SM = Ctx->getSourceManager();
|
2015-08-21 05:27:35 +08:00
|
|
|
const Stmt *Body = D->getBody();
|
|
|
|
SourceLocation SL = Body ? Body->getLocStart() : D->getLocation();
|
2015-06-27 01:42:58 +08:00
|
|
|
SL = SM.getExpansionLoc(SL);
|
|
|
|
|
2015-02-05 09:02:47 +08:00
|
|
|
if (!Opts->AnalyzeAll && !SM.isWrittenInMainFile(SL)) {
|
2012-10-11 01:55:40 +08:00
|
|
|
if (SL.isInvalid() || SM.isInSystemHeader(SL))
|
|
|
|
return AM_None;
|
|
|
|
return Mode & ~AM_Path;
|
|
|
|
}
|
2012-03-14 03:31:54 +08:00
|
|
|
|
2012-10-11 01:55:40 +08:00
|
|
|
return Mode;
|
2012-03-14 03:31:54 +08:00
|
|
|
}
|
|
|
|
|
2012-03-14 03:32:00 +08:00
|
|
|
void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
|
2012-12-08 05:51:47 +08:00
|
|
|
ExprEngine::InliningModes IMode,
|
2012-04-13 06:36:48 +08:00
|
|
|
SetOfConstDecls *VisitedCallees) {
|
2012-12-21 09:19:15 +08:00
|
|
|
if (!D->hasBody())
|
|
|
|
return;
|
2012-10-11 01:55:40 +08:00
|
|
|
Mode = getModeForDecl(D, Mode);
|
|
|
|
if (Mode == AM_None)
|
2009-09-09 23:08:12 +08:00
|
|
|
return;
|
2008-07-02 08:03:09 +08:00
|
|
|
|
2013-02-02 08:30:02 +08:00
|
|
|
DisplayFunction(D, Mode, IMode);
|
2012-07-06 04:44:02 +08:00
|
|
|
CFG *DeclCFG = Mgr->getCFG(D);
|
|
|
|
if (DeclCFG) {
|
|
|
|
unsigned CFGSize = DeclCFG->size();
|
|
|
|
MaxCFGSize = MaxCFGSize < CFGSize ? CFGSize : MaxCFGSize;
|
|
|
|
}
|
|
|
|
|
2011-10-24 09:32:45 +08:00
|
|
|
// Clear the AnalysisManager of old AnalysisDeclContexts.
|
2009-10-21 05:39:41 +08:00
|
|
|
Mgr->ClearContexts();
|
2011-02-18 05:39:24 +08:00
|
|
|
BugReporter BR(*Mgr);
|
2012-12-21 09:19:15 +08:00
|
|
|
|
|
|
|
if (Mode & AM_Syntax)
|
|
|
|
checkerMgr->runCheckersOnASTBody(D, *Mgr, BR);
|
|
|
|
if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) {
|
|
|
|
RunPathSensitiveChecks(D, IMode, VisitedCallees);
|
2013-03-27 02:57:58 +08:00
|
|
|
if (IMode != ExprEngine::Inline_Minimal)
|
2012-12-21 09:19:15 +08:00
|
|
|
NumFunctionsAnalyzed++;
|
|
|
|
}
|
2008-07-02 08:03:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2011-03-01 03:49:17 +08:00
|
|
|
// Path-sensitive checking.
|
2008-07-02 08:03:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2012-03-10 05:14:01 +08:00
|
|
|
void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
|
2012-12-08 05:51:47 +08:00
|
|
|
ExprEngine::InliningModes IMode,
|
2012-04-13 06:36:48 +08:00
|
|
|
SetOfConstDecls *VisitedCallees) {
|
2011-10-08 06:21:02 +08:00
|
|
|
// Construct the analysis engine. First check if the CFG is valid.
|
2009-09-19 06:29:35 +08:00
|
|
|
// FIXME: Inter-procedural analysis will need to handle invalid CFGs.
|
2012-03-10 05:14:01 +08:00
|
|
|
if (!Mgr->getCFG(D))
|
2010-02-15 03:08:51 +08:00
|
|
|
return;
|
2012-03-10 05:14:01 +08:00
|
|
|
|
2012-07-03 04:21:52 +08:00
|
|
|
// See if the LiveVariables analysis scales.
|
|
|
|
if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
|
|
|
|
return;
|
|
|
|
|
2012-12-08 05:51:47 +08:00
|
|
|
ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,IMode);
|
2010-02-15 03:08:51 +08:00
|
|
|
|
2008-08-28 06:31:43 +08:00
|
|
|
// Set the graph auditor.
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<ExplodedNode::Auditor> Auditor;
|
2012-08-31 03:26:53 +08:00
|
|
|
if (Mgr->options.visualizeExplodedGraphWithUbiGraph) {
|
2014-09-05 08:14:57 +08:00
|
|
|
Auditor = CreateUbiViz();
|
2009-08-06 09:32:16 +08:00
|
|
|
ExplodedNode::SetAuditor(Auditor.get());
|
2008-08-28 06:31:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-07-02 08:44:58 +08:00
|
|
|
// Execute the worklist algorithm.
|
2012-04-28 09:58:08 +08:00
|
|
|
Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D),
|
2013-01-31 03:12:39 +08:00
|
|
|
Mgr->options.getMaxNodesPerTopLevelFunction());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-28 06:31:43 +08:00
|
|
|
// Release the auditor (if any) so that it doesn't monitor the graph
|
|
|
|
// created BugReporter.
|
2014-05-27 10:45:47 +08:00
|
|
|
ExplodedNode::SetAuditor(nullptr);
|
2009-03-11 09:42:29 +08:00
|
|
|
|
2008-07-03 00:49:11 +08:00
|
|
|
// Visualize the exploded graph.
|
2012-08-31 03:26:53 +08:00
|
|
|
if (Mgr->options.visualizeExplodedGraphWithGraphViz)
|
2012-08-31 03:26:43 +08:00
|
|
|
Eng.ViewGraph(Mgr->options.TrimGraph);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-11 09:42:29 +08:00
|
|
|
// Display warnings.
|
|
|
|
Eng.getBugReporter().FlushReports();
|
2008-07-03 00:35:50 +08:00
|
|
|
}
|
|
|
|
|
2012-04-13 06:36:48 +08:00
|
|
|
void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
|
2012-12-08 05:51:47 +08:00
|
|
|
ExprEngine::InliningModes IMode,
|
2012-04-13 06:36:48 +08:00
|
|
|
SetOfConstDecls *Visited) {
|
2011-09-02 13:55:19 +08:00
|
|
|
|
2012-03-11 15:00:24 +08:00
|
|
|
switch (Mgr->getLangOpts().getGC()) {
|
2011-09-02 13:55:19 +08:00
|
|
|
case LangOptions::NonGC:
|
2012-12-08 05:51:47 +08:00
|
|
|
ActionExprEngine(D, false, IMode, Visited);
|
2011-09-02 13:55:19 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LangOptions::GCOnly:
|
2012-12-08 05:51:47 +08:00
|
|
|
ActionExprEngine(D, true, IMode, Visited);
|
2011-09-02 13:55:19 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LangOptions::HybridGC:
|
2012-12-08 05:51:47 +08:00
|
|
|
ActionExprEngine(D, false, IMode, Visited);
|
|
|
|
ActionExprEngine(D, true, IMode, Visited);
|
2011-09-02 13:55:19 +08:00
|
|
|
break;
|
|
|
|
}
|
2008-07-02 08:44:58 +08:00
|
|
|
}
|
|
|
|
|
2008-07-02 08:03:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AnalysisConsumer creation.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2014-08-11 03:56:51 +08:00
|
|
|
std::unique_ptr<AnalysisASTConsumer>
|
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
2014-08-27 23:14:15 +08:00
|
|
|
ento::CreateAnalysisConsumer(CompilerInstance &CI) {
|
2011-08-17 05:24:21 +08:00
|
|
|
// Disable the effects of '-Werror' when using the AnalysisConsumer.
|
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
2014-08-27 23:14:15 +08:00
|
|
|
CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false);
|
|
|
|
|
|
|
|
AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
|
|
|
|
bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
|
2009-02-17 12:27:41 +08:00
|
|
|
|
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
2014-08-27 23:14:15 +08:00
|
|
|
return llvm::make_unique<AnalysisConsumer>(
|
|
|
|
CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts,
|
|
|
|
CI.getFrontendOpts().Plugins,
|
|
|
|
hasModelPath ? new ModelInjector(CI) : nullptr);
|
2008-07-02 08:03:09 +08:00
|
|
|
}
|
|
|
|
|
2008-08-28 06:31:43 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Ubigraph Visualization. FIXME: Move to separate file.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
class UbigraphViz : public ExplodedNode::Auditor {
|
2014-03-08 04:03:18 +08:00
|
|
|
std::unique_ptr<raw_ostream> Out;
|
2013-06-26 22:33:23 +08:00
|
|
|
std::string Filename;
|
2008-08-28 06:31:43 +08:00
|
|
|
unsigned Cntr;
|
|
|
|
|
|
|
|
typedef llvm::DenseMap<void*,unsigned> VMap;
|
|
|
|
VMap M;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-28 06:31:43 +08:00
|
|
|
public:
|
2014-07-19 09:06:45 +08:00
|
|
|
UbigraphViz(std::unique_ptr<raw_ostream> Out, StringRef Filename);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2015-04-11 10:00:23 +08:00
|
|
|
~UbigraphViz() override;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2014-03-15 12:29:04 +08:00
|
|
|
void AddEdge(ExplodedNode *Src, ExplodedNode *Dst) override;
|
2008-08-28 06:31:43 +08:00
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-28 06:31:43 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
2014-09-05 08:14:57 +08:00
|
|
|
static std::unique_ptr<ExplodedNode::Auditor> CreateUbiViz() {
|
2013-06-26 14:13:06 +08:00
|
|
|
SmallString<128> P;
|
|
|
|
int FD;
|
2013-07-06 04:00:06 +08:00
|
|
|
llvm::sys::fs::createTemporaryFile("llvm_ubi", "", FD, P);
|
2015-03-10 15:33:23 +08:00
|
|
|
llvm::errs() << "Writing '" << P << "'.\n";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2014-07-19 09:06:45 +08:00
|
|
|
auto Stream = llvm::make_unique<llvm::raw_fd_ostream>(FD, true);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2014-09-05 08:14:57 +08:00
|
|
|
return llvm::make_unique<UbigraphViz>(std::move(Stream), P);
|
2008-08-28 06:31:43 +08:00
|
|
|
}
|
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-29 02:34:41 +08:00
|
|
|
assert (Src != Dst && "Self-edges are not allowed.");
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-28 06:31:43 +08:00
|
|
|
// Lookup the Src. If it is a new node, it's a root.
|
|
|
|
VMap::iterator SrcI= M.find(Src);
|
|
|
|
unsigned SrcID;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-28 06:31:43 +08:00
|
|
|
if (SrcI == M.end()) {
|
|
|
|
M[Src] = SrcID = Cntr++;
|
|
|
|
*Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SrcID = SrcI->second;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-08-28 06:31:43 +08:00
|
|
|
// Lookup the Dst.
|
|
|
|
VMap::iterator DstI= M.find(Dst);
|
|
|
|
unsigned DstID;
|
|
|
|
|
|
|
|
if (DstI == M.end()) {
|
|
|
|
M[Dst] = DstID = Cntr++;
|
|
|
|
*Out << "('vertex', " << DstID << ")\n";
|
|
|
|
}
|
2008-08-28 13:02:09 +08:00
|
|
|
else {
|
|
|
|
// We have hit DstID before. Change its style to reflect a cache hit.
|
2008-08-28 06:31:43 +08:00
|
|
|
DstID = DstI->second;
|
2008-08-28 13:02:09 +08:00
|
|
|
*Out << "('change_vertex_style', " << DstID << ", 1)\n";
|
|
|
|
}
|
2008-08-28 06:31:43 +08:00
|
|
|
|
|
|
|
// Add the edge.
|
2009-09-09 23:08:12 +08:00
|
|
|
*Out << "('edge', " << SrcID << ", " << DstID
|
2008-08-28 06:46:55 +08:00
|
|
|
<< ", ('arrow','true'), ('oriented', 'true'))\n";
|
2008-08-28 06:31:43 +08:00
|
|
|
}
|
|
|
|
|
2014-07-19 09:06:45 +08:00
|
|
|
UbigraphViz::UbigraphViz(std::unique_ptr<raw_ostream> Out, StringRef Filename)
|
|
|
|
: Out(std::move(Out)), Filename(Filename), Cntr(0) {
|
2008-08-28 13:02:09 +08:00
|
|
|
|
|
|
|
*Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n";
|
|
|
|
*Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66'),"
|
|
|
|
" ('size', '1.5'))\n";
|
|
|
|
}
|
|
|
|
|
2008-08-28 11:54:51 +08:00
|
|
|
UbigraphViz::~UbigraphViz() {
|
2014-07-19 09:06:45 +08:00
|
|
|
Out.reset();
|
2009-08-23 20:08:50 +08:00
|
|
|
llvm::errs() << "Running 'ubiviz' program... ";
|
2008-08-28 11:54:51 +08:00
|
|
|
std::string ErrMsg;
|
2014-11-04 09:30:55 +08:00
|
|
|
std::string Ubiviz;
|
|
|
|
if (auto Path = llvm::sys::findProgramByName("ubiviz"))
|
|
|
|
Ubiviz = *Path;
|
2008-08-28 11:54:51 +08:00
|
|
|
std::vector<const char*> args;
|
|
|
|
args.push_back(Ubiviz.c_str());
|
|
|
|
args.push_back(Filename.c_str());
|
2014-05-27 10:45:47 +08:00
|
|
|
args.push_back(nullptr);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2014-05-27 10:45:47 +08:00
|
|
|
if (llvm::sys::ExecuteAndWait(Ubiviz, &args[0], nullptr, nullptr, 0, 0,
|
|
|
|
&ErrMsg)) {
|
2009-08-23 20:08:50 +08:00
|
|
|
llvm::errs() << "Error viewing graph: " << ErrMsg << "\n";
|
2008-08-28 11:54:51 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-06-26 22:33:23 +08:00
|
|
|
// Delete the file.
|
|
|
|
llvm::sys::fs::remove(Filename);
|
2008-08-29 11:45:59 +08:00
|
|
|
}
|