r236790 - [SanitizerCoverage] Implement user-friendly -fsanitize-coverage= flags.

Alexey Samsonov vonosmas at gmail.com
Thu May 7 15:34:06 PDT 2015


Author: samsonov
Date: Thu May  7 17:34:06 2015
New Revision: 236790

URL: http://llvm.org/viewvc/llvm-project?rev=236790&view=rev
Log:
[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

Modified:
    cfe/trunk/include/clang/Driver/Options.td
    cfe/trunk/include/clang/Driver/SanitizerArgs.h
    cfe/trunk/lib/CodeGen/BackendUtil.cpp
    cfe/trunk/lib/Driver/SanitizerArgs.cpp
    cfe/trunk/test/Driver/fsanitize-coverage.c

Modified: cfe/trunk/include/clang/Driver/Options.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Options.td?rev=236790&r1=236789&r2=236790&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/Options.td (original)
+++ cfe/trunk/include/clang/Driver/Options.td Thu May  7 17:34:06 2015
@@ -530,9 +530,14 @@ def fno_sanitize_blacklist : Flag<["-"],
                              Group<f_clang_Group>,
                              HelpText<"Don't use blacklist file for sanitizers">;
 def fsanitize_coverage
-    : Joined<["-"], "fsanitize-coverage=">,
+    : CommaJoined<["-"], "fsanitize-coverage=">,
       Group<f_clang_Group>, Flags<[CoreOption]>,
       HelpText<"Specify the type of coverage instrumentation for Sanitizers">;
+def fno_sanitize_coverage
+    : CommaJoined<["-"], "fno-sanitize-coverage=">,
+      Group<f_clang_Group>, Flags<[CoreOption]>,
+      HelpText<"Disable specified features of coverage instrumentation for "
+               "Sanitizers">;
 def fsanitize_memory_track_origins_EQ : Joined<["-"], "fsanitize-memory-track-origins=">,
                                         Group<f_clang_Group>, Flags<[CC1Option]>,
                                         HelpText<"Enable origins tracking in MemorySanitizer">;

Modified: cfe/trunk/include/clang/Driver/SanitizerArgs.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/SanitizerArgs.h?rev=236790&r1=236789&r2=236790&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/SanitizerArgs.h (original)
+++ cfe/trunk/include/clang/Driver/SanitizerArgs.h Thu May  7 17:34:06 2015
@@ -25,7 +25,7 @@ class SanitizerArgs {
   SanitizerSet RecoverableSanitizers;
 
   std::vector<std::string> BlacklistFiles;
-  int SanitizeCoverage;
+  int CoverageFeatures;
   int MsanTrackOrigins;
   int AsanFieldPadding;
   bool AsanZeroBaseShadow;

Modified: cfe/trunk/lib/CodeGen/BackendUtil.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/BackendUtil.cpp?rev=236790&r1=236789&r2=236790&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/BackendUtil.cpp (original)
+++ cfe/trunk/lib/CodeGen/BackendUtil.cpp Thu May  7 17:34:06 2015
@@ -313,7 +313,9 @@ void EmitAssemblyHelper::CreatePasses()
                            addBoundsCheckingPass);
   }
 
-  if (CodeGenOpts.SanitizeCoverageType) {
+  if (CodeGenOpts.SanitizeCoverageType ||
+      CodeGenOpts.SanitizeCoverageIndirectCalls ||
+      CodeGenOpts.SanitizeCoverageTraceCmp) {
     PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast,
                            addSanitizerCoveragePass);
     PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,

Modified: cfe/trunk/lib/Driver/SanitizerArgs.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/SanitizerArgs.cpp?rev=236790&r1=236789&r2=236790&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/SanitizerArgs.cpp (original)
+++ cfe/trunk/lib/Driver/SanitizerArgs.cpp Thu May  7 17:34:06 2015
@@ -50,6 +50,16 @@ enum SanitizeKind : uint64_t {
   LegacyFsanitizeRecoverMask = Undefined | Integer,
   NeedsLTO = CFI,
 };
+
+enum CoverageFeature {
+  CoverageFunc = 1 << 0,
+  CoverageBB = 1 << 1,
+  CoverageEdge = 1 << 2,
+  CoverageIndirCall = 1 << 3,
+  CoverageTraceBB = 1 << 4,
+  CoverageTraceCmp = 1 << 5,
+  Coverage8bitCounters = 1 << 6,
+};
 }
 
 /// Returns true if set of \p Sanitizers contain at least one sanitizer from
@@ -88,6 +98,10 @@ static uint64_t parseValue(const char *V
 static uint64_t parseArgValues(const Driver &D, const llvm::opt::Arg *A,
                                bool DiagnoseErrors);
 
+/// Parse -f(no-)?sanitize-coverage= flag values, diagnosing any invalid
+/// components. Returns OR of members of \c CoverageFeature enumeration.
+static int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A);
+
 /// Produce an argument string from ArgList \p Args, which shows how it
 /// provides some sanitizer kind from \p Mask. For example, the argument list
 /// "-fsanitize=thread,vptr -fsanitize=address" with mask \c NeedsUbsanRt
@@ -181,7 +195,7 @@ void SanitizerArgs::clear() {
   Sanitizers.clear();
   RecoverableSanitizers.clear();
   BlacklistFiles.clear();
-  SanitizeCoverage = 0;
+  CoverageFeatures = 0;
   MsanTrackOrigins = 0;
   AsanFieldPadding = 0;
   AsanZeroBaseShadow = false;
@@ -393,16 +407,70 @@ SanitizerArgs::SanitizerArgs(const ToolC
     }
   }
 
-  // Parse -fsanitize-coverage=N. Currently one of asan/msan/lsan is required.
+  // Parse -f(no-)?sanitize-coverage flags if coverage is supported by the
+  // enabled sanitizers.
   if (Kinds & SanitizeKind::SupportsCoverage) {
-    if (Arg *A = Args.getLastArg(options::OPT_fsanitize_coverage)) {
-      StringRef S = A->getValue();
-      // Legal values are 0..4.
-      if (S.getAsInteger(0, SanitizeCoverage) || SanitizeCoverage < 0 ||
-          SanitizeCoverage > 4)
-        D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
+    for (const auto *Arg : Args) {
+      if (Arg->getOption().matches(options::OPT_fsanitize_coverage)) {
+        Arg->claim();
+        int LegacySanitizeCoverage;
+        if (Arg->getNumValues() == 1 &&
+            !StringRef(Arg->getValue(0))
+                 .getAsInteger(0, LegacySanitizeCoverage) &&
+            LegacySanitizeCoverage >= 0 && LegacySanitizeCoverage <= 4) {
+          // TODO: Add deprecation notice for this form.
+          switch (LegacySanitizeCoverage) {
+          case 0:
+            CoverageFeatures = 0;
+            break;
+          case 1:
+            CoverageFeatures = CoverageFunc;
+            break;
+          case 2:
+            CoverageFeatures = CoverageBB;
+            break;
+          case 3:
+            CoverageFeatures = CoverageEdge;
+            break;
+          case 4:
+            CoverageFeatures = CoverageEdge | CoverageIndirCall;
+            break;
+          }
+          continue;
+        }
+        CoverageFeatures |= parseCoverageFeatures(D, Arg);
+      } else if (Arg->getOption().matches(options::OPT_fno_sanitize_coverage)) {
+        Arg->claim();
+        CoverageFeatures &= ~parseCoverageFeatures(D, Arg);
+      }
     }
   }
+  // Choose at most one coverage type: function, bb, or edge.
+  if ((CoverageFeatures & CoverageFunc) && (CoverageFeatures & CoverageBB))
+    D.Diag(clang::diag::err_drv_argument_not_allowed_with)
+        << "-fsanitize-coverage=func"
+        << "-fsanitize-coverage=bb";
+  if ((CoverageFeatures & CoverageFunc) && (CoverageFeatures & CoverageEdge))
+    D.Diag(clang::diag::err_drv_argument_not_allowed_with)
+        << "-fsanitize-coverage=func"
+        << "-fsanitize-coverage=edge";
+  if ((CoverageFeatures & CoverageBB) && (CoverageFeatures & CoverageEdge))
+    D.Diag(clang::diag::err_drv_argument_not_allowed_with)
+        << "-fsanitize-coverage=bb"
+        << "-fsanitize-coverage=edge";
+  // Basic block tracing and 8-bit counters require some type of coverage
+  // enabled.
+  int CoverageTypes = CoverageFunc | CoverageBB | CoverageEdge;
+  if ((CoverageFeatures & CoverageTraceBB) &&
+      !(CoverageFeatures & CoverageTypes))
+    D.Diag(clang::diag::err_drv_argument_only_allowed_with)
+        << "-fsanitize-coverage=trace-bb"
+        << "-fsanitize-coverage=(func|bb|edge)";
+  if ((CoverageFeatures & Coverage8bitCounters) &&
+      !(CoverageFeatures & CoverageTypes))
+    D.Diag(clang::diag::err_drv_argument_only_allowed_with)
+        << "-fsanitize-coverage=8bit-counters"
+        << "-fsanitize-coverage=(func|bb|edge)";
 
   if (Kinds & SanitizeKind::Address) {
     AsanSharedRuntime =
@@ -482,14 +550,21 @@ void SanitizerArgs::addArgs(const llvm::
   if (AsanFieldPadding)
     CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" +
                                          llvm::utostr(AsanFieldPadding)));
-  if (SanitizeCoverage) {
-    int CoverageType = std::min(SanitizeCoverage, 3);
-    CmdArgs.push_back(Args.MakeArgString("-fsanitize-coverage-type=" +
-                                         llvm::utostr(CoverageType)));
-    if (SanitizeCoverage == 4)
-      CmdArgs.push_back(
-          Args.MakeArgString("-fsanitize-coverage-indirect-calls"));
+  // Translate available CoverageFeatures to corresponding clang-cc1 flags.
+  std::pair<int, const char *> CoverageFlags[] = {
+    std::make_pair(CoverageFunc, "-fsanitize-coverage-type=1"),
+    std::make_pair(CoverageBB, "-fsanitize-coverage-type=2"),
+    std::make_pair(CoverageEdge, "-fsanitize-coverage-type=3"),
+    std::make_pair(CoverageIndirCall, "-fsanitize-coverage-indirect-calls"),
+    std::make_pair(CoverageTraceBB, "-fsanitize-coverage-trace-bb"),
+    std::make_pair(CoverageTraceCmp, "-fsanitize-coverage-trace-cmp"),
+    std::make_pair(Coverage8bitCounters, "-fsanitize-coverage-8bit-counters")};
+  for (auto F : CoverageFlags) {
+    if (CoverageFeatures & F.first)
+      CmdArgs.push_back(Args.MakeArgString(F.second));
   }
+
+
   // MSan: Workaround for PR16386.
   // ASan: This is mainly to help LSan with cases such as
   // https://code.google.com/p/address-sanitizer/issues/detail?id=373
@@ -543,6 +618,29 @@ uint64_t parseArgValues(const Driver &D,
   return Kinds;
 }
 
+int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A) {
+  assert(A->getOption().matches(options::OPT_fsanitize_coverage) ||
+         A->getOption().matches(options::OPT_fno_sanitize_coverage));
+  int Features = 0;
+  for (int i = 0, n = A->getNumValues(); i != n; ++i) {
+    const char *Value = A->getValue(i);
+    int F = llvm::StringSwitch<int>(Value)
+        .Case("func", CoverageFunc)
+        .Case("bb", CoverageBB)
+        .Case("edge", CoverageEdge)
+        .Case("indirect-calls", CoverageIndirCall)
+        .Case("trace-bb", CoverageTraceBB)
+        .Case("trace-cmp", CoverageTraceCmp)
+        .Case("8bit-counters", Coverage8bitCounters)
+        .Default(0);
+    if (F == 0)
+      D.Diag(clang::diag::err_drv_unsupported_option_argument)
+          << A->getOption().getName() << Value;
+    Features |= F;
+  }
+  return Features;
+}
+
 std::string lastArgumentForMask(const Driver &D, const llvm::opt::ArgList &Args,
                                 uint64_t Mask) {
   for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(),

Modified: cfe/trunk/test/Driver/fsanitize-coverage.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/fsanitize-coverage.c?rev=236790&r1=236789&r2=236790&view=diff
==============================================================================
--- cfe/trunk/test/Driver/fsanitize-coverage.c (original)
+++ cfe/trunk/test/Driver/fsanitize-coverage.c Thu May  7 17:34:06 2015
@@ -1,6 +1,8 @@
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=0 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-0
-// RUN: %clang -target x86_64-linux-gnu -fsanitize=address                       %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-0
+// 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
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-0
 // CHECK-SANITIZE-COVERAGE-0-NOT: fsanitize-coverage-type
+
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=memory -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=leak -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
@@ -8,15 +10,54 @@
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=bool -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=dataflow -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-1
 // CHECK-SANITIZE-COVERAGE-1: fsanitize-coverage-type=1
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=2 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-2
+// CHECK-SANITIZE-COVERAGE-2: fsanitize-coverage-type=2
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=3 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-3
+// CHECK-SANITIZE-COVERAGE-3: fsanitize-coverage-type=3
+
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=4 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-4
 // CHECK-SANITIZE-COVERAGE-4: fsanitize-coverage-type=3
 // CHECK-SANITIZE-COVERAGE-4: fsanitize-coverage-indirect-calls
+
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=5 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-5
-// CHECK-SANITIZE-COVERAGE-5: error: invalid value '5' in '-fsanitize-coverage=5'
+// CHECK-SANITIZE-COVERAGE-5: error: unsupported argument '5' to option 'fsanitize-coverage='
+
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=thread   -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-UNUSED
 // RUN: %clang -target x86_64-linux-gnu                     -fsanitize-coverage=1 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SANITIZE-COVERAGE-UNUSED
 // CHECK-SANITIZE-COVERAGE-UNUSED: argument unused during compilation: '-fsanitize-coverage=1'
 
+// 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
+// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-type=3
+// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-indirect-calls
+// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-trace-bb
+// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-trace-cmp
+// CHECK-SANITIZE-COVERAGE-FEATURES: -fsanitize-coverage-8bit-counters
+
+// 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
+// CHECK-MASK: -fsanitize-coverage-type=1
+// CHECK-MASK: -fsanitize-coverage-trace-cmp
+// CHECK-MASK-NOT: -fsanitize-coverage-
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=foobar %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID-VALUE
+// CHECK-INVALID-VALUE: error: unsupported argument 'foobar' to option 'fsanitize-coverage='
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=func -fsanitize-coverage=edge %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE
+// CHECK-INCOMPATIBLE: error: invalid argument '-fsanitize-coverage=func' not allowed with '-fsanitize-coverage=edge'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=8bit-counters %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MISSING-TYPE
+// CHECK-MISSING-TYPE: error: invalid argument '-fsanitize-coverage=8bit-counters' only allowed with '-fsanitize-coverage=(func|bb|edge)'
+
+// 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
+// CHECK-NO-TYPE-NECESSARY-NOT: error:
+// CHECK-NO-TYPE-NECESSARY: -fsanitize-coverage-indirect-calls
+// CHECK-NO-TYPE-NECESSARY: -fsanitize-coverage-trace-cmp
+
+// 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
+// CHECK-EXTEND-LEGACY: -fsanitize-coverage-type=1
+// CHECK-EXTEND-LEGACY: -fsanitize-coverage-trace-cmp
+
 // RUN: %clang_cl -fsanitize=address -fsanitize-coverage=1 -c -### -- %s 2>&1 | FileCheck %s -check-prefix=CLANG-CL-COVERAGE
 // CLANG-CL-COVERAGE-NOT: error:
 // CLANG-CL-COVERAGE-NOT: warning:





More information about the cfe-commits mailing list