tuktuk updated this revision to Diff 255032.
tuktuk added a comment.

Thank you for your interest in this feature! It is unfortunate indeed that the 
patch was not merged when accepted, so here is an update that matches the 
current status of the code base.

Dear reviewers, can you please make sure that this time, the patch gets merged 
if accepted?
Also, this time I was unable to run the new test that this patch adds from 
within the test system, because of my limited understanding; so I gave up and 
followed the test myself, command by command.
Am I missing an additional step for test integration, after adding the test 
file?
Thanks a lot.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D63616/new/

https://reviews.llvm.org/D63616

Files:
  clang/docs/SanitizerCoverage.rst
  clang/include/clang/Basic/CodeGenOptions.h
  clang/include/clang/Basic/DiagnosticDriverKinds.td
  clang/include/clang/Driver/Options.td
  clang/include/clang/Driver/SanitizerArgs.h
  clang/lib/CodeGen/BackendUtil.cpp
  clang/lib/Driver/SanitizerArgs.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  
compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_whitelist_blacklist.cc
  llvm/include/llvm/Transforms/Instrumentation/SanitizerCoverage.h
  llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp

Index: llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
===================================================================
--- llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
+++ llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
@@ -35,6 +35,8 @@
 #include "llvm/InitializePasses.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/VirtualFileSystem.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/Instrumentation.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
@@ -185,8 +187,11 @@
 class ModuleSanitizerCoverage {
 public:
   ModuleSanitizerCoverage(
-      const SanitizerCoverageOptions &Options = SanitizerCoverageOptions())
-      : Options(OverrideFromCL(Options)) {}
+      const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(),
+      const SpecialCaseList *Whitelist = nullptr,
+      const SpecialCaseList *Blacklist = nullptr)
+      : Options(OverrideFromCL(Options)), Whitelist(Whitelist),
+        Blacklist(Blacklist) {}
   bool instrumentModule(Module &M, DomTreeCallback DTCallback,
                         PostDomTreeCallback PDTCallback);
 
@@ -249,18 +254,32 @@
   SmallVector<GlobalValue *, 20> GlobalsToAppendToCompilerUsed;
 
   SanitizerCoverageOptions Options;
+
+  const SpecialCaseList *Whitelist;
+  const SpecialCaseList *Blacklist;
 };
 
 class ModuleSanitizerCoverageLegacyPass : public ModulePass {
 public:
   ModuleSanitizerCoverageLegacyPass(
-      const SanitizerCoverageOptions &Options = SanitizerCoverageOptions())
+      const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(),
+      const std::vector<std::string> &WhitelistFiles =
+          std::vector<std::string>(),
+      const std::vector<std::string> &BlacklistFiles =
+          std::vector<std::string>())
       : ModulePass(ID), Options(Options) {
+    if (WhitelistFiles.size() > 0)
+      Whitelist = SpecialCaseList::createOrDie(WhitelistFiles,
+                                               *vfs::getRealFileSystem());
+    if (BlacklistFiles.size() > 0)
+      Blacklist = SpecialCaseList::createOrDie(BlacklistFiles,
+                                               *vfs::getRealFileSystem());
     initializeModuleSanitizerCoverageLegacyPassPass(
         *PassRegistry::getPassRegistry());
   }
   bool runOnModule(Module &M) override {
-    ModuleSanitizerCoverage ModuleSancov(Options);
+    ModuleSanitizerCoverage ModuleSancov(Options, Whitelist.get(),
+                                         Blacklist.get());
     auto DTCallback = [this](Function &F) -> const DominatorTree * {
       return &this->getAnalysis<DominatorTreeWrapperPass>(F).getDomTree();
     };
@@ -281,6 +300,9 @@
 
 private:
   SanitizerCoverageOptions Options;
+
+  std::unique_ptr<SpecialCaseList> Whitelist;
+  std::unique_ptr<SpecialCaseList> Blacklist;
 };
 
 } // namespace
@@ -360,6 +382,12 @@
     Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
   if (Options.CoverageType == SanitizerCoverageOptions::SCK_None)
     return false;
+  if (Whitelist &&
+      !Whitelist->inSection("coverage", "src", M.getSourceFileName()))
+    return false;
+  if (Blacklist &&
+      Blacklist->inSection("coverage", "src", M.getSourceFileName()))
+    return false;
   C = &(M.getContext());
   DL = &M.getDataLayout();
   CurModule = &M;
@@ -589,6 +617,10 @@
   if (F.hasPersonalityFn() &&
       isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn())))
     return;
+  if (Whitelist && !Whitelist->inSection("coverage", "fun", F.getName()))
+    return;
+  if (Blacklist && Blacklist->inSection("coverage", "fun", F.getName()))
+    return;
   if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge)
     SplitAllCriticalEdges(F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests());
   SmallVector<Instruction *, 8> IndirCalls;
@@ -943,6 +975,9 @@
                     "Pass for instrumenting coverage on functions", false,
                     false)
 ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass(
-    const SanitizerCoverageOptions &Options) {
-  return new ModuleSanitizerCoverageLegacyPass(Options);
+    const SanitizerCoverageOptions &Options,
+    const std::vector<std::string> &WhitelistFiles,
+    const std::vector<std::string> &BlacklistFiles) {
+  return new ModuleSanitizerCoverageLegacyPass(Options, WhitelistFiles,
+                                               BlacklistFiles);
 }
Index: llvm/include/llvm/Transforms/Instrumentation/SanitizerCoverage.h
===================================================================
--- llvm/include/llvm/Transforms/Instrumentation/SanitizerCoverage.h
+++ llvm/include/llvm/Transforms/Instrumentation/SanitizerCoverage.h
@@ -40,7 +40,10 @@
 
 // Insert SanitizerCoverage instrumentation.
 ModulePass *createModuleSanitizerCoverageLegacyPassPass(
-    const SanitizerCoverageOptions &Options = SanitizerCoverageOptions());
+    const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(),
+    const std::vector<std::string> &WhitelistFiles = std::vector<std::string>(),
+    const std::vector<std::string> &BlacklistFiles =
+        std::vector<std::string>());
 
 } // namespace llvm
 
Index: compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_whitelist_blacklist.cc
===================================================================
--- /dev/null
+++ compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_whitelist_blacklist.cc
@@ -0,0 +1,116 @@
+// Tests -fsanitize-coverage-whitelist=whitelist.txt and
+// -fsanitize-coverage-blacklist=blacklist.txt with libFuzzer-like coverage
+// options
+
+// REQUIRES: has_sancovcc,stable-runtime
+// XFAIL: tsan
+
+// RUN: echo -e "src:*\nfun:*"     > wl_all.txt
+// RUN: echo -e ""                 > wl_none.txt
+// RUN: echo -e "src:%s\nfun:*"    > wl_file.txt
+// RUN: echo -e "src:*\nfun:*bar*" > wl_bar.txt
+// RUN: echo -e "src:*\nfun:*foo*" > wl_foo.txt
+// RUN: echo -e "src:*"            > bl_all.txt
+// RUN: echo -e ""                 > bl_none.txt
+// RUN: echo -e "src:%s"           > bl_file.txt
+// RUN: echo -e "fun:*foo*"        > bl_foo.txt
+// RUN: echo -e "fun:*bar*"        > bl_bar.txt
+
+// Check inline-8bit-counters
+// RUN: echo 'section "__sancov_cntrs"'                                                                             >  patterns.txt
+// RUN: echo '%[0-9]\+ = load i8, i8\* getelementptr inbounds (\[[0-9]\+ x i8\], \[[0-9]\+ x i8\]\* @__sancov_gen_' >> patterns.txt
+// RUN: echo 'store i8 %[0-9]\+, i8\* getelementptr inbounds (\[[0-9]\+ x i8\], \[[0-9]\+ x i8\]\* @__sancov_gen_'  >> patterns.txt
+
+// Check indirect-calls
+// RUN: echo 'call void @__sanitizer_cov_trace_pc_indir'                                                            >> patterns.txt
+
+// Check trace-cmp
+// RUN: echo 'call void @__sanitizer_cov_trace_cmp4'                                                                >> patterns.txt
+
+// Check pc-table
+// RUN: echo 'section "__sancov_pcs"'                                                                               >> patterns.txt
+
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table                                                                                      2>&1 |     grep -f patterns.txt | count 14
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_all.txt                                             2>&1 |     grep -f patterns.txt | count 14
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_none.txt                                            2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_file.txt                                            2>&1 |     grep -f patterns.txt | count 14
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_foo.txt                                             2>&1 |     grep -f patterns.txt | count 9
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_bar.txt                                             2>&1 |     grep -f patterns.txt | count 5
+
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table                                           -fsanitize-coverage-blacklist=bl_all.txt   2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_all.txt  -fsanitize-coverage-blacklist=bl_all.txt   2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_none.txt -fsanitize-coverage-blacklist=bl_all.txt   2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_file.txt -fsanitize-coverage-blacklist=bl_all.txt   2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_foo.txt  -fsanitize-coverage-blacklist=bl_all.txt   2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_bar.txt  -fsanitize-coverage-blacklist=bl_all.txt   2>&1 | not grep -f patterns.txt
+
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table                                           -fsanitize-coverage-blacklist=bl_none.txt  2>&1 |     grep -f patterns.txt | count 14
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_all.txt  -fsanitize-coverage-blacklist=bl_none.txt  2>&1 |     grep -f patterns.txt | count 14
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_none.txt -fsanitize-coverage-blacklist=bl_none.txt  2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_file.txt -fsanitize-coverage-blacklist=bl_none.txt  2>&1 |     grep -f patterns.txt | count 14
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_foo.txt  -fsanitize-coverage-blacklist=bl_none.txt  2>&1 |     grep -f patterns.txt | count 9
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_bar.txt  -fsanitize-coverage-blacklist=bl_none.txt  2>&1 |     grep -f patterns.txt | count 5
+
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table                                           -fsanitize-coverage-blacklist=bl_file.txt  2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_all.txt  -fsanitize-coverage-blacklist=bl_file.txt  2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_none.txt -fsanitize-coverage-blacklist=bl_file.txt  2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_file.txt -fsanitize-coverage-blacklist=bl_file.txt  2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_foo.txt  -fsanitize-coverage-blacklist=bl_file.txt  2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_bar.txt  -fsanitize-coverage-blacklist=bl_file.txt  2>&1 | not grep -f patterns.txt
+
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table                                           -fsanitize-coverage-blacklist=bl_foo.txt   2>&1 |     grep -f patterns.txt | count 5
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_all.txt  -fsanitize-coverage-blacklist=bl_foo.txt   2>&1 |     grep -f patterns.txt | count 5
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_none.txt -fsanitize-coverage-blacklist=bl_foo.txt   2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_file.txt -fsanitize-coverage-blacklist=bl_foo.txt   2>&1 |     grep -f patterns.txt | count 5
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_foo.txt  -fsanitize-coverage-blacklist=bl_foo.txt   2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_bar.txt  -fsanitize-coverage-blacklist=bl_foo.txt   2>&1 |     grep -f patterns.txt | count 5
+
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table                                           -fsanitize-coverage-blacklist=bl_bar.txt   2>&1 |     grep -f patterns.txt | count 9
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_all.txt  -fsanitize-coverage-blacklist=bl_bar.txt   2>&1 |     grep -f patterns.txt | count 9
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_none.txt -fsanitize-coverage-blacklist=bl_bar.txt   2>&1 | not grep -f patterns.txt
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_file.txt -fsanitize-coverage-blacklist=bl_bar.txt   2>&1 |     grep -f patterns.txt | count 9
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_foo.txt  -fsanitize-coverage-blacklist=bl_bar.txt   2>&1 |     grep -f patterns.txt | count 9
+// RUN: %clangxx -O0 %s -S -o - -emit-llvm -fsanitize-coverage=inline-8bit-counters,indirect-calls,trace-cmp,pc-table -fsanitize-coverage-whitelist=wl_bar.txt  -fsanitize-coverage-blacklist=bl_bar.txt   2>&1 | not grep -f patterns.txt
+
+// RUN: rm wl_*.txt
+// RUN: rm bl_*.txt
+// RUN: rm patterns.txt
+
+// foo has 3 instrumentation points, 0 indirect call, 1 comparison point
+
+// Expected results with patterns.txt when foo gets instrumentation with
+// libFuzzer-like coverage options: 9 lines
+//   inline-8bit-counters ->
+//     @__sancov_gen_XX = private global [3 x i8] zeroinitializer, section "__sancov_cntrs"...
+//     %XX = load i8, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__sancov_gen_, i64 0, i64 0)...
+//     %XX = load i8, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__sancov_gen_, i64 0, i64 1)...
+//     %XX = load i8, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__sancov_gen_, i64 0, i64 2)...
+//     store i8 %XX, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__sancov_gen_, i64 0, i64 0)...
+//     store i8 %XX, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__sancov_gen_, i64 0, i64 1)...
+//     store i8 %XX, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__sancov_gen_, i64 0, i64 2)...
+//   trace-cmp ->
+//     call void @__sanitizer_cov_trace_cmp4(i32 %XX, i32 %XX)
+//   pc-table ->
+//     @__sancov_gen_XX = private constant [6 x i64*] ..., section "__sancov_pcs"...
+
+bool foo(int *a, int *b) {
+  if (*a == *b) {
+    return true;
+  }
+  return false;
+}
+
+// bar has 1 instrumentation point, 1 indirect call, 0 comparison point
+
+// Expected results with patterns.txt when bar gets instrumentation with
+// libFuzzer-like coverage options: 5 lines
+//   inline-8bit-counters ->
+//     @__sancov_gen_XX = private global [1 x i8] zeroinitializer, section "__sancov_cntrs"...
+//     %XX = load i8, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @__sancov_gen_.2, i64 0, i64 0), ...
+//     store i8 %XX, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @__sancov_gen_.2, i64 0, i64 0), ...
+//   indirect-calls ->
+//     call void @__sanitizer_cov_trace_pc_indir(i64 %XX)
+//   pc-table ->
+//     @__sancov_gen_XX = private constant [2 x i64*] ..., section "__sancov_pcs"...
+
+void bar(void (*f)()) { f(); }
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -1178,6 +1178,10 @@
   Opts.SanitizeCoveragePCTable = Args.hasArg(OPT_fsanitize_coverage_pc_table);
   Opts.SanitizeCoverageStackDepth =
       Args.hasArg(OPT_fsanitize_coverage_stack_depth);
+  Opts.SanitizeCoverageWhitelistFiles =
+      Args.getAllArgValues(OPT_fsanitize_coverage_whitelist);
+  Opts.SanitizeCoverageBlacklistFiles =
+      Args.getAllArgValues(OPT_fsanitize_coverage_blacklist);
   Opts.SanitizeMemoryTrackOrigins =
       getLastArgIntValue(Args, OPT_fsanitize_memory_track_origins_EQ, 0, Diags);
   Opts.SanitizeMemoryUseAfterDtor =
Index: clang/lib/Driver/SanitizerArgs.cpp
===================================================================
--- clang/lib/Driver/SanitizerArgs.cpp
+++ clang/lib/Driver/SanitizerArgs.cpp
@@ -150,6 +150,40 @@
   }
 }
 
+/// Parse -f(no-)?sanitize-(coverage-)?(white|black)list argument's values,
+/// diagnosing any invalid file paths and validating special case list format.
+static void parseSpecialCaseListArg(const Driver &D,
+                                    const llvm::opt::ArgList &Args,
+                                    std::vector<std::string> &SCLFiles,
+                                    llvm::opt::OptSpecifier SCLOptionID,
+                                    llvm::opt::OptSpecifier NoSCLOptionID,
+                                    unsigned MalformedSCLErrorDiagID) {
+  for (const auto *Arg : Args) {
+    // Match -fsanitize-(coverage-)?(white|black)list.
+    if (Arg->getOption().matches(SCLOptionID)) {
+      Arg->claim();
+      std::string SCLPath = Arg->getValue();
+      if (D.getVFS().exists(SCLPath)) {
+        SCLFiles.push_back(SCLPath);
+      } else {
+        D.Diag(clang::diag::err_drv_no_such_file) << SCLPath;
+      }
+      // Match -fno-sanitize-blacklist.
+    } else if (Arg->getOption().matches(NoSCLOptionID)) {
+      Arg->claim();
+      SCLFiles.clear();
+    }
+  }
+  // Validate special case list format.
+  {
+    std::string BLError;
+    std::unique_ptr<llvm::SpecialCaseList> SCL(
+        llvm::SpecialCaseList::create(SCLFiles, D.getVFS(), BLError));
+    if (!SCL.get())
+      D.Diag(MalformedSCLErrorDiagID) << BLError;
+  }
+}
+
 /// Sets group bits for every group that has at least one representative already
 /// enabled in \p Kinds.
 static SanitizerMask setGroupBits(SanitizerMask Kinds) {
@@ -560,37 +594,18 @@
   // Setup blacklist files.
   // Add default blacklist from resource directory.
   addDefaultBlacklists(D, Kinds, SystemBlacklistFiles);
-  // Parse -f(no-)sanitize-blacklist options.
-  for (const auto *Arg : Args) {
-    if (Arg->getOption().matches(options::OPT_fsanitize_blacklist)) {
-      Arg->claim();
-      std::string BLPath = Arg->getValue();
-      if (D.getVFS().exists(BLPath)) {
-        UserBlacklistFiles.push_back(BLPath);
-      } else {
-        D.Diag(clang::diag::err_drv_no_such_file) << BLPath;
-      }
-    } else if (Arg->getOption().matches(options::OPT_fno_sanitize_blacklist)) {
-      Arg->claim();
-      UserBlacklistFiles.clear();
-      SystemBlacklistFiles.clear();
-    }
-  }
-  // Validate blacklists format.
-  {
-    std::string BLError;
-    std::unique_ptr<llvm::SpecialCaseList> SCL(
-        llvm::SpecialCaseList::create(UserBlacklistFiles, D.getVFS(), BLError));
-    if (!SCL.get())
-      D.Diag(clang::diag::err_drv_malformed_sanitizer_blacklist) << BLError;
-  }
-  {
-    std::string BLError;
-    std::unique_ptr<llvm::SpecialCaseList> SCL(llvm::SpecialCaseList::create(
-        SystemBlacklistFiles, D.getVFS(), BLError));
-    if (!SCL.get())
-      D.Diag(clang::diag::err_drv_malformed_sanitizer_blacklist) << BLError;
-  }
+
+  // Parse -f(no-)?sanitize-blacklist options.
+  // This also validates special case lists format.
+  // Here, OptSpecifier() acts as a never-matching command-line argument.
+  // So, there is no way to append to system blacklist but it can be cleared.
+  parseSpecialCaseListArg(D, Args, SystemBlacklistFiles, OptSpecifier(),
+                          options::OPT_fno_sanitize_blacklist,
+                          clang::diag::err_drv_malformed_sanitizer_blacklist);
+  parseSpecialCaseListArg(D, Args, UserBlacklistFiles,
+                          options::OPT_fsanitize_blacklist,
+                          options::OPT_fno_sanitize_blacklist,
+                          clang::diag::err_drv_malformed_sanitizer_blacklist);
 
   // Parse -f[no-]sanitize-memory-track-origins[=level] options.
   if (AllAddedKinds & SanitizerKind::Memory) {
@@ -742,6 +757,21 @@
       CoverageFeatures |= CoverageFunc;
   }
 
+  // Parse -fsanitize-coverage-(black|white)list options if coverage enabled.
+  // This also validates special case lists format.
+  // Here, OptSpecifier() acts as a never-matching command-line argument.
+  // So, there is no way to clear coverage lists but you can append to them.
+  if (CoverageFeatures) {
+    parseSpecialCaseListArg(
+        D, Args, CoverageWhitelistFiles,
+        options::OPT_fsanitize_coverage_whitelist, OptSpecifier(),
+        clang::diag::err_drv_malformed_sanitizer_coverage_whitelist);
+    parseSpecialCaseListArg(
+        D, Args, CoverageBlacklistFiles,
+        options::OPT_fsanitize_coverage_blacklist, OptSpecifier(),
+        clang::diag::err_drv_malformed_sanitizer_coverage_blacklist);
+  }
+
   SharedRuntime =
       Args.hasFlag(options::OPT_shared_libsan, options::OPT_static_libsan,
                    TC.getTriple().isAndroid() || TC.getTriple().isOSFuchsia() ||
@@ -868,6 +898,17 @@
   return Res;
 }
 
+static void addSpecialCaseListOpt(const llvm::opt::ArgList &Args,
+                                  llvm::opt::ArgStringList &CmdArgs,
+                                  const char* SCLOptFlag,
+                                  const std::vector<std::string>& SCLFiles) {
+  for (const auto &SCLPath : SCLFiles) {
+    SmallString<64> SCLOpt(SCLOptFlag);
+    SCLOpt += SCLPath;
+    CmdArgs.push_back(Args.MakeArgString(SCLOpt));
+  }
+}
+
 static void addIncludeLinkerOption(const ToolChain &TC,
                                    const llvm::opt::ArgList &Args,
                                    llvm::opt::ArgStringList &CmdArgs,
@@ -926,6 +967,8 @@
     if (CoverageFeatures & F.first)
       CmdArgs.push_back(F.second);
   }
+  addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-coverage-whitelist=", CoverageWhitelistFiles);
+  addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-coverage-blacklist=", CoverageBlacklistFiles);
 
   if (TC.getTriple().isOSWindows() && needsUbsanRt()) {
     // Instruct the code generator to embed linker directives in the object file
@@ -961,16 +1004,8 @@
     CmdArgs.push_back(
         Args.MakeArgString("-fsanitize-trap=" + toString(TrapSanitizers)));
 
-  for (const auto &BLPath : UserBlacklistFiles) {
-    SmallString<64> BlacklistOpt("-fsanitize-blacklist=");
-    BlacklistOpt += BLPath;
-    CmdArgs.push_back(Args.MakeArgString(BlacklistOpt));
-  }
-  for (const auto &BLPath : SystemBlacklistFiles) {
-    SmallString<64> BlacklistOpt("-fsanitize-system-blacklist=");
-    BlacklistOpt += BLPath;
-    CmdArgs.push_back(Args.MakeArgString(BlacklistOpt));
-  }
+  addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-blacklist=", UserBlacklistFiles);
+  addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-system-blacklist=", SystemBlacklistFiles);
 
   if (MsanTrackOrigins)
     CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins=" +
Index: clang/lib/CodeGen/BackendUtil.cpp
===================================================================
--- clang/lib/CodeGen/BackendUtil.cpp
+++ clang/lib/CodeGen/BackendUtil.cpp
@@ -233,7 +233,9 @@
       static_cast<const PassManagerBuilderWrapper &>(Builder);
   const CodeGenOptions &CGOpts = BuilderWrapper.getCGOpts();
   auto Opts = getSancovOptsFromCGOpts(CGOpts);
-  PM.add(createModuleSanitizerCoverageLegacyPassPass(Opts));
+  PM.add(createModuleSanitizerCoverageLegacyPassPass(
+      Opts, CGOpts.SanitizeCoverageWhitelistFiles,
+      CGOpts.SanitizeCoverageBlacklistFiles));
 }
 
 // Check if ASan should use GC-friendly instrumentation for globals.
Index: clang/include/clang/Driver/SanitizerArgs.h
===================================================================
--- clang/include/clang/Driver/SanitizerArgs.h
+++ clang/include/clang/Driver/SanitizerArgs.h
@@ -27,6 +27,8 @@
 
   std::vector<std::string> UserBlacklistFiles;
   std::vector<std::string> SystemBlacklistFiles;
+  std::vector<std::string> CoverageWhitelistFiles;
+  std::vector<std::string> CoverageBlacklistFiles;
   int CoverageFeatures = 0;
   int MsanTrackOrigins = 0;
   bool MsanUseAfterDtor = true;
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -1020,6 +1020,12 @@
       Group<f_clang_Group>, Flags<[CoreOption, DriverOption]>,
       HelpText<"Disable specified features of coverage instrumentation for "
                "Sanitizers">, Values<"func,bb,edge,indirect-calls,trace-bb,trace-cmp,trace-div,trace-gep,8bit-counters,trace-pc,trace-pc-guard,no-prune,inline-8bit-counters">;
+def fsanitize_coverage_whitelist : Joined<["-"], "fsanitize-coverage-whitelist=">,
+    Group<f_clang_Group>, Flags<[CoreOption, DriverOption]>,
+    HelpText<"Restrict sanitizer coverage instrumentation exclusively to modules and functions that match the provided special case list, except the blacklisted ones">;
+def fsanitize_coverage_blacklist : Joined<["-"], "fsanitize-coverage-blacklist=">,
+    Group<f_clang_Group>, Flags<[CoreOption, DriverOption]>,
+    HelpText<"Disable sanitizer coverage instrumentation for modules and functions that match the provided special case list, even the whitelisted ones">;
 def fsanitize_memory_track_origins_EQ : Joined<["-"], "fsanitize-memory-track-origins=">,
                                         Group<f_clang_Group>,
                                         HelpText<"Enable origins tracking in MemorySanitizer">;
Index: clang/include/clang/Basic/DiagnosticDriverKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -150,6 +150,10 @@
   "invalid argument '%0' to -%1">;
 def err_drv_malformed_sanitizer_blacklist : Error<
   "malformed sanitizer blacklist: '%0'">;
+def err_drv_malformed_sanitizer_coverage_whitelist : Error<
+  "malformed sanitizer coverage whitelist: '%0'">;
+def err_drv_malformed_sanitizer_coverage_blacklist : Error<
+  "malformed sanitizer coverage blacklist: '%0'">;
 def err_drv_duplicate_config : Error<
   "no more than one option '--config' is allowed">;
 def err_drv_config_file_not_exist : Error<
Index: clang/include/clang/Basic/CodeGenOptions.h
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.h
+++ clang/include/clang/Basic/CodeGenOptions.h
@@ -306,6 +306,16 @@
   /// List of dynamic shared object files to be loaded as pass plugins.
   std::vector<std::string> PassPlugins;
 
+  /// Path to whitelist file specifying which objects
+  /// (files, functions) should exclusively be instrumented
+  /// by sanitizer coverage pass.
+  std::vector<std::string> SanitizeCoverageWhitelistFiles;
+
+  /// Path to blacklist file specifying which objects
+  /// (files, functions) listed for instrumentation by sanitizer
+  /// coverage pass should actually not be instrumented.
+  std::vector<std::string> SanitizeCoverageBlacklistFiles;
+
 public:
   // Define accessors/mutators for code generation options of enumeration type.
 #define CODEGENOPT(Name, Bits, Default)
Index: clang/docs/SanitizerCoverage.rst
===================================================================
--- clang/docs/SanitizerCoverage.rst
+++ clang/docs/SanitizerCoverage.rst
@@ -289,6 +289,58 @@
   // for every non-constant array index.
   void __sanitizer_cov_trace_gep(uintptr_t Idx);
 
+Partially disabling instrumentation
+===================================
+
+It is sometimes useful to tell SanitizerCoverage to instrument only a subset of the
+functions in your target.
+With ``-fsanitize-coverage-whitelist=whitelist.txt``
+and ``-fsanitize-coverage-blacklist=blacklist.txt``,
+you can specify such a subset through the combination of a whitelist and a blacklist.
+
+SanitizerCoverage will only instrument functions that satisfy two conditions.
+First, the function should belong to a source file with a path that is both whitelisted
+and not blacklisted.
+Second, the function should have a mangled name that is both whitelisted and not blacklisted.
+
+The whitelist and blacklist format is similar to that of the sanitizer blacklist format.
+The default whitelist will match every source file and every function.
+The default blacklist will match no source file and no function.
+
+A common use case is to have the whitelist list folders or source files for which you want
+instrumentation and allow all function names, while the blacklist will opt out some specific
+files or functions that the whitelist loosely allowed.
+
+Here is an example whitelist:
+
+.. code-block:: none
+
+  # Enable instrumentation for a whole folder
+  src:bar/*
+  # Enable instrumentation for a specific source file
+  src:foo/a.cpp
+  # Enable instrumentation for all functions in those files
+  fun:*
+
+And an example blacklist:
+
+.. code-block:: none
+
+  # Disable instrumentation for a specific source file that the whitelist allowed
+  src:bar/b.cpp
+  # Disable instrumentation for a specific function that the whitelist allowed
+  fun:*myFunc*
+
+The use of ``*`` wildcards above is required because function names are matched after mangling.
+Without the wildcards, one would have to write the whole mangled name.
+
+Be careful that the paths of source files are matched exactly as they are provided on the clang
+command line.
+For example, the whitelist above would include file ``bar/b.cpp`` if the path was provided
+exactly like this, but would it would fail to include it with other ways to refer to the same
+file such as ``./bar/b.cpp``, or ``bar\b.cpp`` on Windows.
+So, please make sure to always double check that your lists are correctly applied.
+
 Default implementation
 ======================
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to