Skip to content

Commit dfa908c

Browse files
committedMay 7, 2015
[SanitizerCoverage] Implement user-friendly -fsanitize-coverage= flags.
Summary: Possible coverage levels are: * -fsanitize-coverage=func - function-level coverage * -fsanitize-coverage=bb - basic-block-level coverage * -fsanitize-coverage=edge - edge-level coverage Extra features are: * -fsanitize-coverage=indirect-calls - coverage for indirect calls * -fsanitize-coverage=trace-bb - tracing for basic blocks * -fsanitize-coverage=trace-cmp - tracing for cmp instructions * -fsanitize-coverage=8bit-counters - frequency counters Levels and features can be combined in comma-separated list, and can be disabled by subsequent -fno-sanitize-coverage= flags, e.g.: -fsanitize-coverage=bb,trace-bb,8bit-counters -fno-sanitize-coverage=trace-bb is equivalient to: -fsanitize-coverage=bb,8bit-counters Original semantics of -fsanitize-coverage flag is preserved: * -fsanitize-coverage=0 disables the coverage * -fsanitize-coverage=1 is a synonym for -fsanitize-coverage=func * -fsanitize-coverage=2 is a synonym for -fsanitize-coverage=bb * -fsanitize-coverage=3 is a synonym for -fsanitize-coverage=edge * -fsanitize-coverage=4 is a synonym for -fsanitize-coverage=edge,indirect-calls Driver tries to diagnose invalid flag usage, in particular: * At most one level (func,bb,edge) must be specified. * "trace-bb" and "8bit-counters" features require some level to be specified. See test case for more examples. Test Plan: regression test suite Reviewers: kcc Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D9577 llvm-svn: 236790
1 parent 9c70ea4 commit dfa908c

File tree

5 files changed

+166
-20
lines changed

5 files changed

+166
-20
lines changed
 

‎clang/include/clang/Driver/Options.td

+6-1
Original file line numberDiff line numberDiff line change
@@ -530,9 +530,14 @@ def fno_sanitize_blacklist : Flag<["-"], "fno-sanitize-blacklist">,
530530
Group<f_clang_Group>,
531531
HelpText<"Don't use blacklist file for sanitizers">;
532532
def fsanitize_coverage
533-
: Joined<["-"], "fsanitize-coverage=">,
533+
: CommaJoined<["-"], "fsanitize-coverage=">,
534534
Group<f_clang_Group>, Flags<[CoreOption]>,
535535
HelpText<"Specify the type of coverage instrumentation for Sanitizers">;
536+
def fno_sanitize_coverage
537+
: CommaJoined<["-"], "fno-sanitize-coverage=">,
538+
Group<f_clang_Group>, Flags<[CoreOption]>,
539+
HelpText<"Disable specified features of coverage instrumentation for "
540+
"Sanitizers">;
536541
def fsanitize_memory_track_origins_EQ : Joined<["-"], "fsanitize-memory-track-origins=">,
537542
Group<f_clang_Group>, Flags<[CC1Option]>,
538543
HelpText<"Enable origins tracking in MemorySanitizer">;

‎clang/include/clang/Driver/SanitizerArgs.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class SanitizerArgs {
2525
SanitizerSet RecoverableSanitizers;
2626

2727
std::vector<std::string> BlacklistFiles;
28-
int SanitizeCoverage;
28+
int CoverageFeatures;
2929
int MsanTrackOrigins;
3030
int AsanFieldPadding;
3131
bool AsanZeroBaseShadow;

‎clang/lib/CodeGen/BackendUtil.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,9 @@ void EmitAssemblyHelper::CreatePasses() {
313313
addBoundsCheckingPass);
314314
}
315315

316-
if (CodeGenOpts.SanitizeCoverageType) {
316+
if (CodeGenOpts.SanitizeCoverageType ||
317+
CodeGenOpts.SanitizeCoverageIndirectCalls ||
318+
CodeGenOpts.SanitizeCoverageTraceCmp) {
317319
PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast,
318320
addSanitizerCoveragePass);
319321
PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,

‎clang/lib/Driver/SanitizerArgs.cpp

+113-15
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ enum SanitizeKind : uint64_t {
5050
LegacyFsanitizeRecoverMask = Undefined | Integer,
5151
NeedsLTO = CFI,
5252
};
53+
54+
enum CoverageFeature {
55+
CoverageFunc = 1 << 0,
56+
CoverageBB = 1 << 1,
57+
CoverageEdge = 1 << 2,
58+
CoverageIndirCall = 1 << 3,
59+
CoverageTraceBB = 1 << 4,
60+
CoverageTraceCmp = 1 << 5,
61+
Coverage8bitCounters = 1 << 6,
62+
};
5363
}
5464

5565
/// Returns true if set of \p Sanitizers contain at least one sanitizer from
@@ -88,6 +98,10 @@ static uint64_t parseValue(const char *Value);
8898
static uint64_t parseArgValues(const Driver &D, const llvm::opt::Arg *A,
8999
bool DiagnoseErrors);
90100

101+
/// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
102+
/// components. Returns OR of members of \c CoverageFeature enumeration.
103+
static int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A);
104+
91105
/// Produce an argument string from ArgList \p Args, which shows how it
92106
/// provides some sanitizer kind from \p Mask. For example, the argument list
93107
/// "-fsanitize=thread,vptr -fsanitize=address" with mask \c NeedsUbsanRt
@@ -181,7 +195,7 @@ void SanitizerArgs::clear() {
181195
Sanitizers.clear();
182196
RecoverableSanitizers.clear();
183197
BlacklistFiles.clear();
184-
SanitizeCoverage = 0;
198+
CoverageFeatures = 0;
185199
MsanTrackOrigins = 0;
186200
AsanFieldPadding = 0;
187201
AsanZeroBaseShadow = false;
@@ -393,16 +407,70 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
393407
}
394408
}
395409

396-
// Parse -fsanitize-coverage=N. Currently one of asan/msan/lsan is required.
410+
// Parse -f(no-)?sanitize-coverage flags if coverage is supported by the
411+
// enabled sanitizers.
397412
if (Kinds & SanitizeKind::SupportsCoverage) {
398-
if (Arg *A = Args.getLastArg(options::OPT_fsanitize_coverage)) {
399-
StringRef S = A->getValue();
400-
// Legal values are 0..4.
401-
if (S.getAsInteger(0, SanitizeCoverage) || SanitizeCoverage < 0 ||
402-
SanitizeCoverage > 4)
403-
D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
413+
for (const auto *Arg : Args) {
414+
if (Arg->getOption().matches(options::OPT_fsanitize_coverage)) {
415+
Arg->claim();
416+
int LegacySanitizeCoverage;
417+
if (Arg->getNumValues() == 1 &&
418+
!StringRef(Arg->getValue(0))
419+
.getAsInteger(0, LegacySanitizeCoverage) &&
420+
LegacySanitizeCoverage >= 0 && LegacySanitizeCoverage <= 4) {
421+
// TODO: Add deprecation notice for this form.
422+
switch (LegacySanitizeCoverage) {
423+
case 0:
424+
CoverageFeatures = 0;
425+
break;
426+
case 1:
427+
CoverageFeatures = CoverageFunc;
428+
break;
429+
case 2:
430+
CoverageFeatures = CoverageBB;
431+
break;
432+
case 3:
433+
CoverageFeatures = CoverageEdge;
434+
break;
435+
case 4:
436+
CoverageFeatures = CoverageEdge | CoverageIndirCall;
437+
break;
438+
}
439+
continue;
440+
}
441+
CoverageFeatures |= parseCoverageFeatures(D, Arg);
442+
} else if (Arg->getOption().matches(options::OPT_fno_sanitize_coverage)) {
443+
Arg->claim();
444+
CoverageFeatures &= ~parseCoverageFeatures(D, Arg);
445+
}
404446
}
405447
}
448+
// Choose at most one coverage type: function, bb, or edge.
449+
if ((CoverageFeatures & CoverageFunc) && (CoverageFeatures & CoverageBB))
450+
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
451+
<< "-fsanitize-coverage=func"
452+
<< "-fsanitize-coverage=bb";
453+
if ((CoverageFeatures & CoverageFunc) && (CoverageFeatures & CoverageEdge))
454+
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
455+
<< "-fsanitize-coverage=func"
456+
<< "-fsanitize-coverage=edge";
457+
if ((CoverageFeatures & CoverageBB) && (CoverageFeatures & CoverageEdge))
458+
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
459+
<< "-fsanitize-coverage=bb"
460+
<< "-fsanitize-coverage=edge";
461+
// Basic block tracing and 8-bit counters require some type of coverage
462+
// enabled.
463+
int CoverageTypes = CoverageFunc | CoverageBB | CoverageEdge;
464+
if ((CoverageFeatures & CoverageTraceBB) &&
465+
!(CoverageFeatures & CoverageTypes))
466+
D.Diag(clang::diag::err_drv_argument_only_allowed_with)
467+
<< "-fsanitize-coverage=trace-bb"
468+
<< "-fsanitize-coverage=(func|bb|edge)";
469+
if ((CoverageFeatures & Coverage8bitCounters) &&
470+
!(CoverageFeatures & CoverageTypes))
471+
D.Diag(clang::diag::err_drv_argument_only_allowed_with)
472+
<< "-fsanitize-coverage=8bit-counters"
473+
<< "-fsanitize-coverage=(func|bb|edge)";
406474

407475
if (Kinds & SanitizeKind::Address) {
408476
AsanSharedRuntime =
@@ -482,14 +550,21 @@ void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args,
482550
if (AsanFieldPadding)
483551
CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" +
484552
llvm::utostr(AsanFieldPadding)));
485-
if (SanitizeCoverage) {
486-
int CoverageType = std::min(SanitizeCoverage, 3);
487-
CmdArgs.push_back(Args.MakeArgString("-fsanitize-coverage-type=" +
488-
llvm::utostr(CoverageType)));
489-
if (SanitizeCoverage == 4)
490-
CmdArgs.push_back(
491-
Args.MakeArgString("-fsanitize-coverage-indirect-calls"));
553+
// Translate available CoverageFeatures to corresponding clang-cc1 flags.
554+
std::pair<int, const char *> CoverageFlags[] = {
555+
std::make_pair(CoverageFunc, "-fsanitize-coverage-type=1"),
556+
std::make_pair(CoverageBB, "-fsanitize-coverage-type=2"),
557+
std::make_pair(CoverageEdge, "-fsanitize-coverage-type=3"),
558+
std::make_pair(CoverageIndirCall, "-fsanitize-coverage-indirect-calls"),
559+
std::make_pair(CoverageTraceBB, "-fsanitize-coverage-trace-bb"),
560+
std::make_pair(CoverageTraceCmp, "-fsanitize-coverage-trace-cmp"),
561+
std::make_pair(Coverage8bitCounters, "-fsanitize-coverage-8bit-counters")};
562+
for (auto F : CoverageFlags) {
563+
if (CoverageFeatures & F.first)
564+
CmdArgs.push_back(Args.MakeArgString(F.second));
492565
}
566+
567+
493568
// MSan: Workaround for PR16386.
494569
// ASan: This is mainly to help LSan with cases such as
495570
// https://code.google.com/p/address-sanitizer/issues/detail?id=373
@@ -543,6 +618,29 @@ uint64_t parseArgValues(const Driver &D, const llvm::opt::Arg *A,
543618
return Kinds;
544619
}
545620

621+
int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A) {
622+
assert(A->getOption().matches(options::OPT_fsanitize_coverage) ||
623+
A->getOption().matches(options::OPT_fno_sanitize_coverage));
624+
int Features = 0;
625+
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
626+
const char *Value = A->getValue(i);
627+
int F = llvm::StringSwitch<int>(Value)
628+
.Case("func", CoverageFunc)
629+
.Case("bb", CoverageBB)
630+
.Case("edge", CoverageEdge)
631+
.Case("indirect-calls", CoverageIndirCall)
632+
.Case("trace-bb", CoverageTraceBB)
633+
.Case("trace-cmp", CoverageTraceCmp)
634+
.Case("8bit-counters", Coverage8bitCounters)
635+
.Default(0);
636+
if (F == 0)
637+
D.Diag(clang::diag::err_drv_unsupported_option_argument)
638+
<< A->getOption().getName() << Value;
639+
Features |= F;
640+
}
641+
return Features;
642+
}
643+
546644
std::string lastArgumentForMask(const Driver &D, const llvm::opt::ArgList &Args,
547645
uint64_t Mask) {
548646
for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(),

‎clang/test/Driver/fsanitize-coverage.c

+43-2
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,63 @@
11
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-0
2-
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-0
2+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=edge -fsanitize-coverage=0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-0
3+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-0
34
// CHECK-SANITIZE-COVERAGE-0-NOT: fsanitize-coverage-type
5+
46
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
57
// RUN: %clang -target x86_64-linux-gnu -fsanitize=memory -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
68
// RUN: %clang -target x86_64-linux-gnu -fsanitize=leak -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
79
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
810
// RUN: %clang -target x86_64-linux-gnu -fsanitize=bool -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
911
// RUN: %clang -target x86_64-linux-gnu -fsanitize=dataflow -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
1012
// CHECK-SANITIZE-COVERAGE-1: fsanitize-coverage-type=1
13+
14+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=2 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-2
15+
// CHECK-SANITIZE-COVERAGE-2: fsanitize-coverage-type=2
16+
17+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=3 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-3
18+
// CHECK-SANITIZE-COVERAGE-3: fsanitize-coverage-type=3
19+
1120
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=4 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-4
1221
// CHECK-SANITIZE-COVERAGE-4: fsanitize-coverage-type=3
1322
// CHECK-SANITIZE-COVERAGE-4: fsanitize-coverage-indirect-calls
23+
1424
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-5
15-
// CHECK-SANITIZE-COVERAGE-5: error: invalid value '5' in '-fsanitize-coverage=5'
25+
// CHECK-SANITIZE-COVERAGE-5: error: unsupported argument '5' to option 'fsanitize-coverage='
26+
1627
// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-UNUSED
1728
// RUN: %clang -target x86_64-linux-gnu -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-UNUSED
1829
// CHECK-SANITIZE-COVERAGE-UNUSED: argument unused during compilation: '-fsanitize-coverage=1'
1930

31+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=edge,indirect-calls,trace-bb,trace-cmp,8bit-counters %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-FEATURES
32+
// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-type=3
33+
// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-indirect-calls
34+
// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-trace-bb
35+
// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-trace-cmp
36+
// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-8bit-counters
37+
38+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=func,edge,indirect-calls,trace-bb,trace-cmp -fno-sanitize-coverage=edge,indirect-calls,trace-bb %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MASK
39+
// CHECK-MASK: -fsanitize-coverage-type=1
40+
// CHECK-MASK: -fsanitize-coverage-trace-cmp
41+
// CHECK-MASK-NOT: -fsanitize-coverage-
42+
43+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=foobar %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID-VALUE
44+
// CHECK-INVALID-VALUE: error: unsupported argument 'foobar' to option 'fsanitize-coverage='
45+
46+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=func -fsanitize-coverage=edge %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE
47+
// CHECK-INCOMPATIBLE: error: invalid argument '-fsanitize-coverage=func' not allowed with '-fsanitize-coverage=edge'
48+
49+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=8bit-counters %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MISSING-TYPE
50+
// CHECK-MISSING-TYPE: error: invalid argument '-fsanitize-coverage=8bit-counters' only allowed with '-fsanitize-coverage=(func|bb|edge)'
51+
52+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=trace-cmp,indirect-calls %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-TYPE-NECESSARY
53+
// CHECK-NO-TYPE-NECESSARY-NOT: error:
54+
// CHECK-NO-TYPE-NECESSARY: -fsanitize-coverage-indirect-calls
55+
// CHECK-NO-TYPE-NECESSARY: -fsanitize-coverage-trace-cmp
56+
57+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=1 -fsanitize-coverage=trace-cmp %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-EXTEND-LEGACY
58+
// CHECK-EXTEND-LEGACY: -fsanitize-coverage-type=1
59+
// CHECK-EXTEND-LEGACY: -fsanitize-coverage-trace-cmp
60+
2061
// RUN: %clang_cl -fsanitize=address -fsanitize-coverage=1 -c -### -- %s 2>&1 | FileCheck %s -check-prefix=CLANG-CL-COVERAGE
2162
// CLANG-CL-COVERAGE-NOT: error:
2263
// CLANG-CL-COVERAGE-NOT: warning:

0 commit comments

Comments
 (0)
Please sign in to comment.