Skip to content

Commit

Permalink
Introduce -fsanitize-stats flag.
Browse files Browse the repository at this point in the history
This is part of a new statistics gathering feature for the sanitizers.
See clang/docs/SanitizerStats.rst for further info and docs.

Differential Revision: http://reviews.llvm.org/D16175

llvm-svn: 257971
  • Loading branch information
pcc committed Jan 16, 2016
1 parent f0f5e87 commit dc13453
Show file tree
Hide file tree
Showing 28 changed files with 304 additions and 22 deletions.
62 changes: 62 additions & 0 deletions clang/docs/SanitizerStats.rst
@@ -0,0 +1,62 @@
==============
SanitizerStats
==============

.. contents::
:local:

Introduction
============

The sanitizers support a simple mechanism for gathering profiling statistics
to help understand the overhead associated with sanitizers.

How to build and run
====================

SanitizerStats can currently only be used with :doc:`ControlFlowIntegrity`.
In addition to ``-fsanitize=cfi*``, pass the ``-fsanitize-stats`` flag.
This will cause the program to count the number of times that each control
flow integrity check in the program fires.

At run time, set the ``SANITIZER_STATS_PATH`` environment variable to direct
statistics output to a file. The file will be written on process exit.
The following substitutions will be applied to the environment variable:

- ``%b`` -- The executable basename.
- ``%p`` -- The process ID.

You can also send the ``SIGUSR2`` signal to a process to make it write
sanitizer statistics immediately.

The ``sanstats`` program can be used to dump statistics. It takes as a
command line argument the path to a statistics file produced by a program
compiled with ``-fsanitize-stats``.

The output of ``sanstats`` is in four columns, separated by spaces. The first
column is the file and line number of the call site. The second column is
the function name. The third column is the type of statistic gathered (in
this case, the type of control flow integrity check). The fourth column is
the call count.

Example:

.. code-block:: console
$ cat -n vcall.cc
1 struct A {
2 virtual void f() {}
3 };
4
5 __attribute__((noinline)) void g(A *a) {
6 a->f();
7 }
8
9 int main() {
10 A a;
11 g(&a);
12 }
$ clang++ -fsanitize=cfi -flto -fuse-ld=gold vcall.cc -fsanitize-stats -g
$ SANITIZER_STATS_PATH=a.stats ./a.out
$ sanstats a.stats
vcall.cc:6 _Z1gP1A cfi-vcall 1
5 changes: 5 additions & 0 deletions clang/docs/UsersManual.rst
Expand Up @@ -1038,6 +1038,11 @@ are listed below.
Enable simple code coverage in addition to certain sanitizers.
See :doc:`SanitizerCoverage` for more details.

**-f[no-]sanitize-stats**

Enable simple statistics gathering for the enabled sanitizers.
See :doc:`SanitizerStats` for more details.

.. option:: -fsanitize-undefined-trap-on-error

Deprecated alias for ``-fsanitize-trap=undefined``.
Expand Down
1 change: 1 addition & 0 deletions clang/docs/index.rst
Expand Up @@ -28,6 +28,7 @@ Using Clang as a Compiler
DataFlowSanitizer
LeakSanitizer
SanitizerCoverage
SanitizerStats
SanitizerSpecialCaseList
ControlFlowIntegrity
SafeStack
Expand Down
7 changes: 4 additions & 3 deletions clang/include/clang/AST/ASTConsumer.h
Expand Up @@ -94,9 +94,10 @@ class ASTConsumer {
/// The default implementation passes it to HandleTopLevelDecl.
virtual void HandleImplicitImportDecl(ImportDecl *D);

/// \brief Handle a pragma that appends to Linker Options. Currently this
/// only exists to support Microsoft's #pragma comment(linker, "/foo").
virtual void HandleLinkerOptionPragma(llvm::StringRef Opts) {}
/// \brief Handle a pragma or command line flag that appends to Linker
/// Options. This exists to support Microsoft's
/// #pragma comment(linker, "/foo") and the frontend flag --linker-option=.
virtual void HandleLinkerOption(llvm::StringRef Opts) {}

/// \brief Handle a pragma that emits a mismatch identifier and value to the
/// object file for the linker to work with. Currently, this only exists to
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/CC1Options.td
Expand Up @@ -252,6 +252,8 @@ def vectorize_slp_aggressive : Flag<["-"], "vectorize-slp-aggressive">,
HelpText<"Run the BB vectorization passes">;
def dependent_lib : Joined<["--"], "dependent-lib=">,
HelpText<"Add dependent library">;
def linker_option : Joined<["--"], "linker-option=">,
HelpText<"Add linker option">;
def fsanitize_coverage_type : Joined<["-"], "fsanitize-coverage-type=">,
HelpText<"Sanitizer coverage type">;
def fsanitize_coverage_indirect_calls
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -644,6 +644,12 @@ def fsanitize_cfi_cross_dso : Flag<["-"], "fsanitize-cfi-cross-dso">,
def fno_sanitize_cfi_cross_dso : Flag<["-"], "fno-sanitize-cfi-cross-dso">,
Group<f_clang_Group>, Flags<[CC1Option]>,
HelpText<"Disable control flow integrity (CFI) checks for cross-DSO calls.">;
def fsanitize_stats : Flag<["-"], "fsanitize-stats">,
Group<f_clang_Group>, Flags<[CC1Option]>,
HelpText<"Enable sanitizer statistics gathering.">;
def fno_sanitize_stats : Flag<["-"], "fno-sanitize-stats">,
Group<f_clang_Group>, Flags<[CC1Option]>,
HelpText<"Disable sanitizer statistics gathering.">;
def funsafe_math_optimizations : Flag<["-"], "funsafe-math-optimizations">,
Group<f_Group>;
def fno_unsafe_math_optimizations : Flag<["-"], "fno-unsafe-math-optimizations">,
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/SanitizerArgs.h
Expand Up @@ -36,6 +36,7 @@ class SanitizerArgs {
bool AsanSharedRuntime = false;
bool LinkCXXRuntimes = false;
bool NeedPIE = false;
bool Stats = false;

public:
/// Parses the sanitizer arguments from an argument list.
Expand All @@ -56,6 +57,7 @@ class SanitizerArgs {
}
bool needsCfiRt() const;
bool needsCfiDiagRt() const;
bool needsStatsRt() const { return Stats; }

bool requiresPIE() const;
bool needsUnwindTables() const;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Frontend/CodeGenOptions.def
Expand Up @@ -134,6 +134,7 @@ CODEGENOPT(SanitizeCoverageTraceCmp, 1, 0) ///< Enable cmp instruction tracing
///< in sanitizer coverage.
CODEGENOPT(SanitizeCoverage8bitCounters, 1, 0) ///< Use 8-bit frequency counters
///< in sanitizer coverage.
CODEGENOPT(SanitizeStats , 1, 0) ///< Collect statistics for sanitizers.
CODEGENOPT(SimplifyLibCalls , 1, 1) ///< Set when -fbuiltin is enabled.
CODEGENOPT(SoftFloat , 1, 0) ///< -soft-float.
CODEGENOPT(StrictEnums , 1, 0) ///< Optimize based on strict enum definition.
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Frontend/CodeGenOptions.h
Expand Up @@ -164,6 +164,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// A list of dependent libraries.
std::vector<std::string> DependentLibraries;

/// A list of linker options to embed in the object file.
std::vector<std::string> LinkerOptions;

/// Name of the profile file to use as output for -fprofile-instr-generate
/// and -fprofile-generate.
std::string InstrProfileOutput;
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Frontend/MultiplexConsumer.h
Expand Up @@ -44,7 +44,7 @@ class MultiplexConsumer : public SemaConsumer {
void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override;
void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override;
void HandleImplicitImportDecl(ImportDecl *D) override;
void HandleLinkerOptionPragma(llvm::StringRef Opts) override;
void HandleLinkerOption(llvm::StringRef Opts) override;
void HandleDetectMismatch(llvm::StringRef Name,
llvm::StringRef Value) override;
void HandleDependentLibrary(llvm::StringRef Lib) override;
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/CodeGen/CGClass.cpp
Expand Up @@ -26,6 +26,7 @@
#include "clang/Frontend/CodeGenOptions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Metadata.h"
#include "llvm/Transforms/Utils/SanitizerStats.h"

using namespace clang;
using namespace CodeGen;
Expand Down Expand Up @@ -2551,6 +2552,22 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
return;

SanitizerScope SanScope(this);
llvm::SanitizerStatKind SSK;
switch (TCK) {
case CFITCK_VCall:
SSK = llvm::SanStat_CFI_VCall;
break;
case CFITCK_NVCall:
SSK = llvm::SanStat_CFI_NVCall;
break;
case CFITCK_DerivedCast:
SSK = llvm::SanStat_CFI_DerivedCast;
break;
case CFITCK_UnrelatedCast:
SSK = llvm::SanStat_CFI_UnrelatedCast;
break;
}
EmitSanitizerStatReport(SSK);

llvm::Metadata *MD =
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CGExpr.cpp
Expand Up @@ -32,6 +32,7 @@
#include "llvm/IR/MDBuilder.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Transforms/Utils/SanitizerStats.h"

using namespace clang;
using namespace CodeGen;
Expand Down Expand Up @@ -3852,6 +3853,7 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
if (SanOpts.has(SanitizerKind::CFIICall) &&
(!TargetDecl || !isa<FunctionDecl>(TargetDecl))) {
SanitizerScope SanScope(this);
EmitSanitizerStatReport(llvm::SanStat_CFI_ICall);

llvm::Metadata *MD = CGM.CreateMetadataIdentifierForType(QualType(FnType, 0));
llvm::Value *BitSetName = llvm::MetadataAsValue::get(getLLVMContext(), MD);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CodeGenAction.cpp
Expand Up @@ -214,8 +214,8 @@ namespace clang {
Gen->HandleVTable(RD);
}

void HandleLinkerOptionPragma(llvm::StringRef Opts) override {
Gen->HandleLinkerOptionPragma(Opts);
void HandleLinkerOption(llvm::StringRef Opts) override {
Gen->HandleLinkerOption(Opts);
}

void HandleDetectMismatch(llvm::StringRef Name,
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Expand Up @@ -1956,3 +1956,12 @@ void CodeGenFunction::checkTargetFeatures(const CallExpr *E,
<< FD->getDeclName() << TargetDecl->getDeclName() << MissingFeature;
}
}

void CodeGenFunction::EmitSanitizerStatReport(llvm::SanitizerStatKind SSK) {
if (!CGM.getCodeGenOpts().SanitizeStats)
return;

llvm::IRBuilder<> IRB(Builder.GetInsertBlock(), Builder.GetInsertPoint());
IRB.SetCurrentDebugLocation(Builder.getCurrentDebugLocation());
CGM.getSanStats().create(IRB, SSK);
}
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Expand Up @@ -36,6 +36,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Utils/SanitizerStats.h"

namespace llvm {
class BasicBlock;
Expand Down Expand Up @@ -3187,6 +3188,8 @@ class CodeGenFunction : public CodeGenTypeCache {
Address EmitPointerWithAlignment(const Expr *Addr,
AlignmentSource *Source = nullptr);

void EmitSanitizerStatReport(llvm::SanitizerStatKind SSK);

private:
QualType getVarArgType(const Expr *Arg);

Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Expand Up @@ -392,6 +392,8 @@ void CodeGenModule::Release() {
if (CoverageMapping)
CoverageMapping->emit();
emitLLVMUsed();
if (SanStats)
SanStats->finish();

if (CodeGenOpts.Autolink &&
(Context.getLangOpts().Modules || !LinkerOptionsMetadata.empty())) {
Expand Down Expand Up @@ -4066,3 +4068,10 @@ void CodeGenModule::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
Target.getTargetOpts().Features);
}
}

llvm::SanitizerStatReport &CodeGenModule::getSanStats() {
if (!SanStats)
SanStats = llvm::make_unique<llvm::SanitizerStatReport>(&getModule());

return *SanStats;
}
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Expand Up @@ -33,6 +33,7 @@
#include "llvm/ADT/StringMap.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Transforms/Utils/SanitizerStats.h"

namespace llvm {
class Module;
Expand Down Expand Up @@ -289,6 +290,7 @@ class CodeGenModule : public CodeGenTypeCache {
llvm::MDNode *NoObjCARCExceptionsMetadata;
std::unique_ptr<llvm::IndexedInstrProfReader> PGOReader;
InstrProfStats PGOStats;
std::unique_ptr<llvm::SanitizerStatReport> SanStats;

// A set of references that have only been seen via a weakref so far. This is
// used to remove the weak of the reference if we ever see a direct reference
Expand Down Expand Up @@ -1129,6 +1131,8 @@ class CodeGenModule : public CodeGenTypeCache {
/// \breif Get the declaration of std::terminate for the platform.
llvm::Constant *getTerminateFn();

llvm::SanitizerStatReport &getSanStats();

private:
llvm::Constant *
GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D,
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/CodeGen/ModuleBuilder.cpp
Expand Up @@ -103,8 +103,10 @@ namespace {
PreprocessorOpts, CodeGenOpts,
*M, Diags, CoverageInfo));

for (size_t i = 0, e = CodeGenOpts.DependentLibraries.size(); i < e; ++i)
HandleDependentLibrary(CodeGenOpts.DependentLibraries[i]);
for (auto &&Lib : CodeGenOpts.DependentLibraries)
HandleDependentLibrary(Lib);
for (auto &&Opt : CodeGenOpts.LinkerOptions)
HandleLinkerOption(Opt);
}

void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) override {
Expand Down Expand Up @@ -222,7 +224,7 @@ namespace {
Builder->EmitVTable(RD);
}

void HandleLinkerOptionPragma(llvm::StringRef Opts) override {
void HandleLinkerOption(llvm::StringRef Opts) override {
Builder->AppendLinkerOptions(Opts);
}

Expand Down
32 changes: 32 additions & 0 deletions clang/lib/Driver/SanitizerArgs.cpp
Expand Up @@ -431,6 +431,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
NeedPIE |= CfiCrossDso;
}

Stats = Args.hasFlag(options::OPT_fsanitize_stats,
options::OPT_fno_sanitize_stats, false);

// Parse -f(no-)?sanitize-coverage flags if coverage is supported by the
// enabled sanitizers.
if (AllAddedKinds & SupportsCoverage) {
Expand Down Expand Up @@ -548,6 +551,20 @@ static std::string toString(const clang::SanitizerSet &Sanitizers) {
return Res;
}

static void addIncludeLinkerOption(const ToolChain &TC,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
StringRef SymbolName) {
SmallString<64> LinkerOptionFlag;
LinkerOptionFlag = "--linker-option=/include:";
if (TC.getTriple().getArch() == llvm::Triple::x86) {
// Win32 mangles C function names with a '_' prefix.
LinkerOptionFlag += '_';
}
LinkerOptionFlag += SymbolName;
CmdArgs.push_back(Args.MakeArgString(LinkerOptionFlag));
}

void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
types::ID InputType) const {
Expand Down Expand Up @@ -584,6 +601,9 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
if (CfiCrossDso)
CmdArgs.push_back(Args.MakeArgString("-fsanitize-cfi-cross-dso"));

if (Stats)
CmdArgs.push_back(Args.MakeArgString("-fsanitize-stats"));

if (AsanFieldPadding)
CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" +
llvm::utostr(AsanFieldPadding)));
Expand Down Expand Up @@ -619,6 +639,18 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
CmdArgs.push_back(Args.MakeArgString(
"--dependent-lib=" + TC.getCompilerRT(Args, "ubsan_standalone_cxx")));
}
if (TC.getTriple().isOSWindows() && needsStatsRt()) {
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" +
TC.getCompilerRT(Args, "stats_client")));

// The main executable must export the stats runtime.
// FIXME: Only exporting from the main executable (e.g. based on whether the
// translation unit defines main()) would save a little space, but having
// multiple copies of the runtime shouldn't hurt.
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" +
TC.getCompilerRT(Args, "stats")));
addIncludeLinkerOption(TC, Args, CmdArgs, "__sanitizer_stats_register");
}
}

SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Driver/ToolChains.cpp
Expand Up @@ -413,6 +413,13 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args,
AddLinkSanitizerLibArgs(Args, CmdArgs, "ubsan");
if (Sanitize.needsTsanRt())
AddLinkSanitizerLibArgs(Args, CmdArgs, "tsan");
if (Sanitize.needsStatsRt()) {
StringRef OS = isTargetMacOS() ? "osx" : "iossim";
AddLinkRuntimeLib(Args, CmdArgs,
(Twine("libclang_rt.stats_client_") + OS + ".a").str(),
/*AlwaysLink=*/true);
AddLinkSanitizerLibArgs(Args, CmdArgs, "stats");
}

// Otherwise link libSystem, then the dynamic runtime library, and finally any
// target specific static runtime library.
Expand Down

0 comments on commit dc13453

Please sign in to comment.