Skip to content

Commit 86ab322

Browse files
committed
[SanitizerCoverage] Add an option to gate the invocation of the tracing callbacks
Implement -sanitizer-coverage-gated-trace-callbacks to gate the invocation of the tracing callbacks based on the value of a global variable, which is stored in a specific section. When this option is enabled, the instrumentation will not call into the runtime-provided callbacks for tracing, thus only incurring in a trivial branch without going through a function call. It is up to the runtime to toggle the value of the global variable in order to enable tracing. This option is only supported for trace-pc-guard. Note: will add additional support for trace-cmp in a follow up PR. Patch by Filippo Bigarella rdar://101626834
1 parent 36d936a commit 86ab322

File tree

3 files changed

+103
-3
lines changed

3 files changed

+103
-3
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %clang %s -target arm64-apple-darwin -emit-llvm -S -fsanitize-coverage=trace-pc-guard -mllvm -sanitizer-coverage-gated-trace-callbacks=1 -o - | FileCheck %s --check-prefixes=CHECK,GATED
2+
// RUN: %clang %s -target arm64-apple-darwin -emit-llvm -S -fsanitize-coverage=trace-pc-guard -mllvm -sanitizer-coverage-gated-trace-callbacks=0 -o - | FileCheck %s --check-prefixes=CHECK,PLAIN
3+
// RUN: not %clang %s -target arm64-apple-darwin -emit-llvm -S -fsanitize-coverage=trace-pc -mllvm -sanitizer-coverage-gated-trace-callbacks=1 -o /dev/null 2>&1 | FileCheck %s --check-prefixes=INCOMPATIBLE
4+
// RUN: not %clang %s -target arm64-apple-darwin -emit-llvm -S -fsanitize-coverage=inline-8bit-counters -mllvm -sanitizer-coverage-gated-trace-callbacks=1 -o /dev/null 2>&1 | FileCheck %s --check-prefixes=INCOMPATIBLE
5+
// RUN: not %clang %s -target arm64-apple-darwin -emit-llvm -S -fsanitize-coverage=inline-bool-flag -mllvm -sanitizer-coverage-gated-trace-callbacks=1 -o /dev/null 2>&1 | FileCheck %s --check-prefixes=INCOMPATIBLE
6+
7+
// Verify that we do not emit the __sancov_gate section for "plain" trace-pc-guard
8+
// GATED: section "__DATA,__sancov_gate"
9+
// PLAIN-NOT: section "__DATA,__sancov_gate"
10+
11+
// Produce an error for all incompatible sanitizer coverage modes.
12+
// INCOMPATIBLE: error: 'sanitizer-coverage-gated-trace-callbacks' is only supported with trace-pc-guard
13+
14+
int x[10];
15+
16+
// CHECK: define{{.*}} void @foo
17+
void foo(int n, int m) {
18+
// COM: Verify that we're emitting the call to __sanitizer_cov_trace_pc_guard upon
19+
// COM: checking the value of __sancov_should_track.
20+
// GATED: [[VAL:%.*]] = load i64, {{.*}}@__sancov_should_track
21+
// GATED-NOT: [[VAL:%.*]] = load i64, i64* @__sancov_should_track
22+
// GATED-NEXT: [[CMP:%.*]] = icmp ne i64 [[VAL]], 0
23+
// GATED-NEXT: br i1 [[CMP]], label %[[L_TRUE:.*]], label %[[L_FALSE:.*]], !prof [[WEIGHTS:!.+]]
24+
// GATED: [[L_TRUE]]:
25+
// GATED-NEXT: call void @__sanitizer_cov_trace_pc_guard
26+
// GATED: br i1 [[CMP]], label %[[L_TRUE_2:.*]], label %[[L_FALSE_2:.*]]
27+
// GATED: [[L_TRUE_2]]:
28+
// GATED-NEXT: call void @__sanitizer_cov_trace_pc_guard
29+
// GATED: [[WEIGHTS]] = !{!"branch_weights", i32 1, i32 100000}
30+
31+
// COM: With the non-gated instrumentation, we should not emit the
32+
// COM: __sancov_should_track global.
33+
// PLAIN-NOT: __sancov_should_track
34+
// But we should still be emitting the calls to the callback.
35+
// PLAIN: call void @__sanitizer_cov_trace_pc_guard
36+
if (n) {
37+
x[n] = 42;
38+
if (m) {
39+
x[m] = 41;
40+
}
41+
}
42+
}

llvm/include/llvm/Transforms/Utils/Instrumentation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ struct SanitizerCoverageOptions {
161161
bool TraceLoads = false;
162162
bool TraceStores = false;
163163
bool CollectControlFlow = false;
164+
bool GatedCallbacks = false;
164165

165166
SanitizerCoverageOptions() = default;
166167
};

llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/Analysis/GlobalsModRef.h"
1717
#include "llvm/Analysis/PostDominators.h"
1818
#include "llvm/IR/Constant.h"
19+
#include "llvm/IR/Constants.h"
1920
#include "llvm/IR/DataLayout.h"
2021
#include "llvm/IR/Dominators.h"
2122
#include "llvm/IR/EHPersonalities.h"
@@ -28,6 +29,8 @@
2829
#include "llvm/IR/MDBuilder.h"
2930
#include "llvm/IR/Module.h"
3031
#include "llvm/IR/Type.h"
32+
#include "llvm/IR/ValueSymbolTable.h"
33+
#include "llvm/InitializePasses.h"
3134
#include "llvm/Support/CommandLine.h"
3235
#include "llvm/Support/SpecialCaseList.h"
3336
#include "llvm/Support/VirtualFileSystem.h"
@@ -82,8 +85,10 @@ const char SanCovCountersSectionName[] = "sancov_cntrs";
8285
const char SanCovBoolFlagSectionName[] = "sancov_bools";
8386
const char SanCovPCsSectionName[] = "sancov_pcs";
8487
const char SanCovCFsSectionName[] = "sancov_cfs";
88+
const char SanCovCallbackGateSectionName[] = "sancov_gate";
8589

8690
const char SanCovLowestStackName[] = "__sancov_lowest_stack";
91+
const char SanCovCallbackGateName[] = "__sancov_should_track";
8792

8893
static cl::opt<int> ClCoverageLevel(
8994
"sanitizer-coverage-level",
@@ -152,6 +157,12 @@ static cl::opt<bool>
152157
ClCollectCF("sanitizer-coverage-control-flow",
153158
cl::desc("collect control flow for each function"), cl::Hidden);
154159

160+
static cl::opt<bool> ClGatedCallbacks(
161+
"sanitizer-coverage-gated-trace-callbacks",
162+
cl::desc("Gate the invocation of the tracing callbacks on a global "
163+
"variable. Currently only supported for trace-pc-guard."),
164+
cl::Hidden, cl::init(false));
165+
155166
namespace {
156167

157168
SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) {
@@ -194,6 +205,7 @@ SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) {
194205
Options.StackDepth |= ClStackDepth;
195206
Options.TraceLoads |= ClLoadTracing;
196207
Options.TraceStores |= ClStoreTracing;
208+
Options.GatedCallbacks |= ClGatedCallbacks;
197209
if (!Options.TracePCGuard && !Options.TracePC &&
198210
!Options.Inline8bitCounters && !Options.StackDepth &&
199211
!Options.InlineBoolFlag && !Options.TraceLoads && !Options.TraceStores)
@@ -239,8 +251,9 @@ class ModuleSanitizerCoverage {
239251
const char *Section);
240252
GlobalVariable *CreatePCArray(Function &F, ArrayRef<BasicBlock *> AllBlocks);
241253
void CreateFunctionLocalArrays(Function &F, ArrayRef<BasicBlock *> AllBlocks);
254+
Value *CreateFunctionLocalGateCmp(IRBuilder<> &IRB);
242255
void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx,
243-
bool IsLeafFunc = true);
256+
Value *&FunctionGateCmp, bool IsLeafFunc = true);
244257
Function *CreateInitCallsForSections(Module &M, const char *CtorName,
245258
const char *InitFunctionName, Type *Ty,
246259
const char *Section);
@@ -265,6 +278,7 @@ class ModuleSanitizerCoverage {
265278
FunctionCallee SanCovTraceGepFunction;
266279
FunctionCallee SanCovTraceSwitchFunction;
267280
GlobalVariable *SanCovLowestStack;
281+
GlobalVariable *SanCovCallbackGate;
268282
Type *PtrTy, *IntptrTy, *Int64Ty, *Int32Ty, *Int16Ty, *Int8Ty, *Int1Ty;
269283
Module *CurModule;
270284
std::string CurModuleUniqueId;
@@ -478,6 +492,23 @@ bool ModuleSanitizerCoverage::instrumentModule() {
478492
if (Options.StackDepth && !SanCovLowestStack->isDeclaration())
479493
SanCovLowestStack->setInitializer(Constant::getAllOnesValue(IntptrTy));
480494

495+
if (Options.GatedCallbacks) {
496+
if (!Options.TracePCGuard) {
497+
C->emitError(StringRef("'") + ClGatedCallbacks.ArgStr +
498+
"' is only supported with trace-pc-guard");
499+
return true;
500+
}
501+
502+
SanCovCallbackGate = cast<GlobalVariable>(
503+
M.getOrInsertGlobal(SanCovCallbackGateName, Int64Ty));
504+
SanCovCallbackGate->setSection(
505+
getSectionName(SanCovCallbackGateSectionName));
506+
SanCovCallbackGate->setInitializer(Constant::getNullValue(Int64Ty));
507+
SanCovCallbackGate->setLinkage(GlobalVariable::LinkOnceAnyLinkage);
508+
SanCovCallbackGate->setVisibility(GlobalVariable::HiddenVisibility);
509+
appendToCompilerUsed(M, SanCovCallbackGate);
510+
}
511+
481512
SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy);
482513
SanCovTracePCGuard =
483514
M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, PtrTy);
@@ -777,13 +808,22 @@ void ModuleSanitizerCoverage::CreateFunctionLocalArrays(
777808
FunctionPCsArray = CreatePCArray(F, AllBlocks);
778809
}
779810

811+
Value *ModuleSanitizerCoverage::CreateFunctionLocalGateCmp(IRBuilder<> &IRB) {
812+
auto Load = IRB.CreateLoad(Int64Ty, SanCovCallbackGate);
813+
Load->setNoSanitizeMetadata();
814+
auto Cmp = IRB.CreateIsNotNull(Load);
815+
Cmp->setName("sancov gate cmp");
816+
return Cmp;
817+
}
818+
780819
bool ModuleSanitizerCoverage::InjectCoverage(Function &F,
781820
ArrayRef<BasicBlock *> AllBlocks,
782821
bool IsLeafFunc) {
783822
if (AllBlocks.empty()) return false;
784823
CreateFunctionLocalArrays(F, AllBlocks);
824+
Value *FunctionGateCmp = nullptr;
785825
for (size_t i = 0, N = AllBlocks.size(); i < N; i++)
786-
InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc);
826+
InjectCoverageAtBlock(F, *AllBlocks[i], i, FunctionGateCmp, IsLeafFunc);
787827
return true;
788828
}
789829

@@ -946,6 +986,7 @@ void ModuleSanitizerCoverage::InjectTraceForCmp(
946986

947987
void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
948988
size_t Idx,
989+
Value *&FunctionGateCmp,
949990
bool IsLeafFunc) {
950991
BasicBlock::iterator IP = BB.getFirstInsertionPt();
951992
bool IsEntryBB = &BB == &F.getEntryBlock();
@@ -971,7 +1012,23 @@ void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
9711012
IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy),
9721013
ConstantInt::get(IntptrTy, Idx * 4)),
9731014
PtrTy);
974-
IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge();
1015+
if (Options.GatedCallbacks) {
1016+
if (!FunctionGateCmp) {
1017+
// Create this in the entry block
1018+
assert(IsEntryBB);
1019+
FunctionGateCmp = CreateFunctionLocalGateCmp(IRB);
1020+
}
1021+
// Set the branch weights in order to minimize the price paid when the
1022+
// gate is turned off, allowing the default enablement of this
1023+
// instrumentation with as little of a performance cost as possible
1024+
auto Weights = MDBuilder(*C).createBranchWeights(1, 100000);
1025+
auto ThenTerm =
1026+
SplitBlockAndInsertIfThen(FunctionGateCmp, &*IP, false, Weights);
1027+
IRBuilder<> ThenIRB(ThenTerm);
1028+
ThenIRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge();
1029+
} else {
1030+
IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge();
1031+
}
9751032
}
9761033
if (Options.Inline8bitCounters) {
9771034
auto CounterPtr = IRB.CreateGEP(

0 commit comments

Comments
 (0)