Skip to content

Commit f2ac4da

Browse files
committed
Introduce -fsanitize-stats flag.
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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@257971 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent c5f92b6 commit f2ac4da

28 files changed

+304
-22
lines changed

docs/SanitizerStats.rst

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
==============
2+
SanitizerStats
3+
==============
4+
5+
.. contents::
6+
:local:
7+
8+
Introduction
9+
============
10+
11+
The sanitizers support a simple mechanism for gathering profiling statistics
12+
to help understand the overhead associated with sanitizers.
13+
14+
How to build and run
15+
====================
16+
17+
SanitizerStats can currently only be used with :doc:`ControlFlowIntegrity`.
18+
In addition to ``-fsanitize=cfi*``, pass the ``-fsanitize-stats`` flag.
19+
This will cause the program to count the number of times that each control
20+
flow integrity check in the program fires.
21+
22+
At run time, set the ``SANITIZER_STATS_PATH`` environment variable to direct
23+
statistics output to a file. The file will be written on process exit.
24+
The following substitutions will be applied to the environment variable:
25+
26+
- ``%b`` -- The executable basename.
27+
- ``%p`` -- The process ID.
28+
29+
You can also send the ``SIGUSR2`` signal to a process to make it write
30+
sanitizer statistics immediately.
31+
32+
The ``sanstats`` program can be used to dump statistics. It takes as a
33+
command line argument the path to a statistics file produced by a program
34+
compiled with ``-fsanitize-stats``.
35+
36+
The output of ``sanstats`` is in four columns, separated by spaces. The first
37+
column is the file and line number of the call site. The second column is
38+
the function name. The third column is the type of statistic gathered (in
39+
this case, the type of control flow integrity check). The fourth column is
40+
the call count.
41+
42+
Example:
43+
44+
.. code-block:: console
45+
46+
$ cat -n vcall.cc
47+
1 struct A {
48+
2 virtual void f() {}
49+
3 };
50+
4
51+
5 __attribute__((noinline)) void g(A *a) {
52+
6 a->f();
53+
7 }
54+
8
55+
9 int main() {
56+
10 A a;
57+
11 g(&a);
58+
12 }
59+
$ clang++ -fsanitize=cfi -flto -fuse-ld=gold vcall.cc -fsanitize-stats -g
60+
$ SANITIZER_STATS_PATH=a.stats ./a.out
61+
$ sanstats a.stats
62+
vcall.cc:6 _Z1gP1A cfi-vcall 1

docs/UsersManual.rst

+5
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,11 @@ are listed below.
10381038
Enable simple code coverage in addition to certain sanitizers.
10391039
See :doc:`SanitizerCoverage` for more details.
10401040

1041+
**-f[no-]sanitize-stats**
1042+
1043+
Enable simple statistics gathering for the enabled sanitizers.
1044+
See :doc:`SanitizerStats` for more details.
1045+
10411046
.. option:: -fsanitize-undefined-trap-on-error
10421047

10431048
Deprecated alias for ``-fsanitize-trap=undefined``.

docs/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Using Clang as a Compiler
2828
DataFlowSanitizer
2929
LeakSanitizer
3030
SanitizerCoverage
31+
SanitizerStats
3132
SanitizerSpecialCaseList
3233
ControlFlowIntegrity
3334
SafeStack

include/clang/AST/ASTConsumer.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,10 @@ class ASTConsumer {
9494
/// The default implementation passes it to HandleTopLevelDecl.
9595
virtual void HandleImplicitImportDecl(ImportDecl *D);
9696

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

101102
/// \brief Handle a pragma that emits a mismatch identifier and value to the
102103
/// object file for the linker to work with. Currently, this only exists to

include/clang/Driver/CC1Options.td

+2
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ def vectorize_slp_aggressive : Flag<["-"], "vectorize-slp-aggressive">,
252252
HelpText<"Run the BB vectorization passes">;
253253
def dependent_lib : Joined<["--"], "dependent-lib=">,
254254
HelpText<"Add dependent library">;
255+
def linker_option : Joined<["--"], "linker-option=">,
256+
HelpText<"Add linker option">;
255257
def fsanitize_coverage_type : Joined<["-"], "fsanitize-coverage-type=">,
256258
HelpText<"Sanitizer coverage type">;
257259
def fsanitize_coverage_indirect_calls

include/clang/Driver/Options.td

+6
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,12 @@ def fsanitize_cfi_cross_dso : Flag<["-"], "fsanitize-cfi-cross-dso">,
644644
def fno_sanitize_cfi_cross_dso : Flag<["-"], "fno-sanitize-cfi-cross-dso">,
645645
Group<f_clang_Group>, Flags<[CC1Option]>,
646646
HelpText<"Disable control flow integrity (CFI) checks for cross-DSO calls.">;
647+
def fsanitize_stats : Flag<["-"], "fsanitize-stats">,
648+
Group<f_clang_Group>, Flags<[CC1Option]>,
649+
HelpText<"Enable sanitizer statistics gathering.">;
650+
def fno_sanitize_stats : Flag<["-"], "fno-sanitize-stats">,
651+
Group<f_clang_Group>, Flags<[CC1Option]>,
652+
HelpText<"Disable sanitizer statistics gathering.">;
647653
def funsafe_math_optimizations : Flag<["-"], "funsafe-math-optimizations">,
648654
Group<f_Group>;
649655
def fno_unsafe_math_optimizations : Flag<["-"], "fno-unsafe-math-optimizations">,

include/clang/Driver/SanitizerArgs.h

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class SanitizerArgs {
3636
bool AsanSharedRuntime = false;
3737
bool LinkCXXRuntimes = false;
3838
bool NeedPIE = false;
39+
bool Stats = false;
3940

4041
public:
4142
/// Parses the sanitizer arguments from an argument list.
@@ -56,6 +57,7 @@ class SanitizerArgs {
5657
}
5758
bool needsCfiRt() const;
5859
bool needsCfiDiagRt() const;
60+
bool needsStatsRt() const { return Stats; }
5961

6062
bool requiresPIE() const;
6163
bool needsUnwindTables() const;

include/clang/Frontend/CodeGenOptions.def

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ CODEGENOPT(SanitizeCoverageTraceCmp, 1, 0) ///< Enable cmp instruction tracing
134134
///< in sanitizer coverage.
135135
CODEGENOPT(SanitizeCoverage8bitCounters, 1, 0) ///< Use 8-bit frequency counters
136136
///< in sanitizer coverage.
137+
CODEGENOPT(SanitizeStats , 1, 0) ///< Collect statistics for sanitizers.
137138
CODEGENOPT(SimplifyLibCalls , 1, 1) ///< Set when -fbuiltin is enabled.
138139
CODEGENOPT(SoftFloat , 1, 0) ///< -soft-float.
139140
CODEGENOPT(StrictEnums , 1, 0) ///< Optimize based on strict enum definition.

include/clang/Frontend/CodeGenOptions.h

+3
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
164164
/// A list of dependent libraries.
165165
std::vector<std::string> DependentLibraries;
166166

167+
/// A list of linker options to embed in the object file.
168+
std::vector<std::string> LinkerOptions;
169+
167170
/// Name of the profile file to use as output for -fprofile-instr-generate
168171
/// and -fprofile-generate.
169172
std::string InstrProfileOutput;

include/clang/Frontend/MultiplexConsumer.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class MultiplexConsumer : public SemaConsumer {
4444
void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override;
4545
void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override;
4646
void HandleImplicitImportDecl(ImportDecl *D) override;
47-
void HandleLinkerOptionPragma(llvm::StringRef Opts) override;
47+
void HandleLinkerOption(llvm::StringRef Opts) override;
4848
void HandleDetectMismatch(llvm::StringRef Name,
4949
llvm::StringRef Value) override;
5050
void HandleDependentLibrary(llvm::StringRef Lib) override;

lib/CodeGen/CGClass.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "clang/Frontend/CodeGenOptions.h"
2727
#include "llvm/IR/Intrinsics.h"
2828
#include "llvm/IR/Metadata.h"
29+
#include "llvm/Transforms/Utils/SanitizerStats.h"
2930

3031
using namespace clang;
3132
using namespace CodeGen;
@@ -2551,6 +2552,22 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
25512552
return;
25522553

25532554
SanitizerScope SanScope(this);
2555+
llvm::SanitizerStatKind SSK;
2556+
switch (TCK) {
2557+
case CFITCK_VCall:
2558+
SSK = llvm::SanStat_CFI_VCall;
2559+
break;
2560+
case CFITCK_NVCall:
2561+
SSK = llvm::SanStat_CFI_NVCall;
2562+
break;
2563+
case CFITCK_DerivedCast:
2564+
SSK = llvm::SanStat_CFI_DerivedCast;
2565+
break;
2566+
case CFITCK_UnrelatedCast:
2567+
SSK = llvm::SanStat_CFI_UnrelatedCast;
2568+
break;
2569+
}
2570+
EmitSanitizerStatReport(SSK);
25542571

25552572
llvm::Metadata *MD =
25562573
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));

lib/CodeGen/CGExpr.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "llvm/IR/MDBuilder.h"
3333
#include "llvm/Support/ConvertUTF.h"
3434
#include "llvm/Support/MathExtras.h"
35+
#include "llvm/Transforms/Utils/SanitizerStats.h"
3536

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

38563858
llvm::Metadata *MD = CGM.CreateMetadataIdentifierForType(QualType(FnType, 0));
38573859
llvm::Value *BitSetName = llvm::MetadataAsValue::get(getLLVMContext(), MD);

lib/CodeGen/CodeGenAction.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ namespace clang {
214214
Gen->HandleVTable(RD);
215215
}
216216

217-
void HandleLinkerOptionPragma(llvm::StringRef Opts) override {
218-
Gen->HandleLinkerOptionPragma(Opts);
217+
void HandleLinkerOption(llvm::StringRef Opts) override {
218+
Gen->HandleLinkerOption(Opts);
219219
}
220220

221221
void HandleDetectMismatch(llvm::StringRef Name,

lib/CodeGen/CodeGenFunction.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -1956,3 +1956,12 @@ void CodeGenFunction::checkTargetFeatures(const CallExpr *E,
19561956
<< FD->getDeclName() << TargetDecl->getDeclName() << MissingFeature;
19571957
}
19581958
}
1959+
1960+
void CodeGenFunction::EmitSanitizerStatReport(llvm::SanitizerStatKind SSK) {
1961+
if (!CGM.getCodeGenOpts().SanitizeStats)
1962+
return;
1963+
1964+
llvm::IRBuilder<> IRB(Builder.GetInsertBlock(), Builder.GetInsertPoint());
1965+
IRB.SetCurrentDebugLocation(Builder.getCurrentDebugLocation());
1966+
CGM.getSanStats().create(IRB, SSK);
1967+
}

lib/CodeGen/CodeGenFunction.h

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "llvm/ADT/SmallVector.h"
3737
#include "llvm/IR/ValueHandle.h"
3838
#include "llvm/Support/Debug.h"
39+
#include "llvm/Transforms/Utils/SanitizerStats.h"
3940

4041
namespace llvm {
4142
class BasicBlock;
@@ -3187,6 +3188,8 @@ class CodeGenFunction : public CodeGenTypeCache {
31873188
Address EmitPointerWithAlignment(const Expr *Addr,
31883189
AlignmentSource *Source = nullptr);
31893190

3191+
void EmitSanitizerStatReport(llvm::SanitizerStatKind SSK);
3192+
31903193
private:
31913194
QualType getVarArgType(const Expr *Arg);
31923195

lib/CodeGen/CodeGenModule.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,8 @@ void CodeGenModule::Release() {
392392
if (CoverageMapping)
393393
CoverageMapping->emit();
394394
emitLLVMUsed();
395+
if (SanStats)
396+
SanStats->finish();
395397

396398
if (CodeGenOpts.Autolink &&
397399
(Context.getLangOpts().Modules || !LinkerOptionsMetadata.empty())) {
@@ -4066,3 +4068,10 @@ void CodeGenModule::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
40664068
Target.getTargetOpts().Features);
40674069
}
40684070
}
4071+
4072+
llvm::SanitizerStatReport &CodeGenModule::getSanStats() {
4073+
if (!SanStats)
4074+
SanStats = llvm::make_unique<llvm::SanitizerStatReport>(&getModule());
4075+
4076+
return *SanStats;
4077+
}

lib/CodeGen/CodeGenModule.h

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "llvm/ADT/StringMap.h"
3434
#include "llvm/IR/Module.h"
3535
#include "llvm/IR/ValueHandle.h"
36+
#include "llvm/Transforms/Utils/SanitizerStats.h"
3637

3738
namespace llvm {
3839
class Module;
@@ -289,6 +290,7 @@ class CodeGenModule : public CodeGenTypeCache {
289290
llvm::MDNode *NoObjCARCExceptionsMetadata;
290291
std::unique_ptr<llvm::IndexedInstrProfReader> PGOReader;
291292
InstrProfStats PGOStats;
293+
std::unique_ptr<llvm::SanitizerStatReport> SanStats;
292294

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

1134+
llvm::SanitizerStatReport &getSanStats();
1135+
11321136
private:
11331137
llvm::Constant *
11341138
GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D,

lib/CodeGen/ModuleBuilder.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,10 @@ namespace {
103103
PreprocessorOpts, CodeGenOpts,
104104
*M, Diags, CoverageInfo));
105105

106-
for (size_t i = 0, e = CodeGenOpts.DependentLibraries.size(); i < e; ++i)
107-
HandleDependentLibrary(CodeGenOpts.DependentLibraries[i]);
106+
for (auto &&Lib : CodeGenOpts.DependentLibraries)
107+
HandleDependentLibrary(Lib);
108+
for (auto &&Opt : CodeGenOpts.LinkerOptions)
109+
HandleLinkerOption(Opt);
108110
}
109111

110112
void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) override {
@@ -222,7 +224,7 @@ namespace {
222224
Builder->EmitVTable(RD);
223225
}
224226

225-
void HandleLinkerOptionPragma(llvm::StringRef Opts) override {
227+
void HandleLinkerOption(llvm::StringRef Opts) override {
226228
Builder->AppendLinkerOptions(Opts);
227229
}
228230

lib/Driver/SanitizerArgs.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
431431
NeedPIE |= CfiCrossDso;
432432
}
433433

434+
Stats = Args.hasFlag(options::OPT_fsanitize_stats,
435+
options::OPT_fno_sanitize_stats, false);
436+
434437
// Parse -f(no-)?sanitize-coverage flags if coverage is supported by the
435438
// enabled sanitizers.
436439
if (AllAddedKinds & SupportsCoverage) {
@@ -548,6 +551,20 @@ static std::string toString(const clang::SanitizerSet &Sanitizers) {
548551
return Res;
549552
}
550553

554+
static void addIncludeLinkerOption(const ToolChain &TC,
555+
const llvm::opt::ArgList &Args,
556+
llvm::opt::ArgStringList &CmdArgs,
557+
StringRef SymbolName) {
558+
SmallString<64> LinkerOptionFlag;
559+
LinkerOptionFlag = "--linker-option=/include:";
560+
if (TC.getTriple().getArch() == llvm::Triple::x86) {
561+
// Win32 mangles C function names with a '_' prefix.
562+
LinkerOptionFlag += '_';
563+
}
564+
LinkerOptionFlag += SymbolName;
565+
CmdArgs.push_back(Args.MakeArgString(LinkerOptionFlag));
566+
}
567+
551568
void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
552569
llvm::opt::ArgStringList &CmdArgs,
553570
types::ID InputType) const {
@@ -584,6 +601,9 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
584601
if (CfiCrossDso)
585602
CmdArgs.push_back(Args.MakeArgString("-fsanitize-cfi-cross-dso"));
586603

604+
if (Stats)
605+
CmdArgs.push_back(Args.MakeArgString("-fsanitize-stats"));
606+
587607
if (AsanFieldPadding)
588608
CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" +
589609
llvm::utostr(AsanFieldPadding)));
@@ -619,6 +639,18 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
619639
CmdArgs.push_back(Args.MakeArgString(
620640
"--dependent-lib=" + TC.getCompilerRT(Args, "ubsan_standalone_cxx")));
621641
}
642+
if (TC.getTriple().isOSWindows() && needsStatsRt()) {
643+
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" +
644+
TC.getCompilerRT(Args, "stats_client")));
645+
646+
// The main executable must export the stats runtime.
647+
// FIXME: Only exporting from the main executable (e.g. based on whether the
648+
// translation unit defines main()) would save a little space, but having
649+
// multiple copies of the runtime shouldn't hurt.
650+
CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" +
651+
TC.getCompilerRT(Args, "stats")));
652+
addIncludeLinkerOption(TC, Args, CmdArgs, "__sanitizer_stats_register");
653+
}
622654
}
623655

624656
SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,

lib/Driver/ToolChains.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,13 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args,
413413
AddLinkSanitizerLibArgs(Args, CmdArgs, "ubsan");
414414
if (Sanitize.needsTsanRt())
415415
AddLinkSanitizerLibArgs(Args, CmdArgs, "tsan");
416+
if (Sanitize.needsStatsRt()) {
417+
StringRef OS = isTargetMacOS() ? "osx" : "iossim";
418+
AddLinkRuntimeLib(Args, CmdArgs,
419+
(Twine("libclang_rt.stats_client_") + OS + ".a").str(),
420+
/*AlwaysLink=*/true);
421+
AddLinkSanitizerLibArgs(Args, CmdArgs, "stats");
422+
}
416423

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

0 commit comments

Comments
 (0)