[compiler-rt] r224789 - [asan] change the coverage collection scheme so that we can easily emit coverage for the entire process as a single bit set, and if coverage_bitset=1 actually emit that bitset

Kostya Serebryany kcc at google.com
Tue Dec 23 14:32:18 PST 2014


Author: kcc
Date: Tue Dec 23 16:32:17 2014
New Revision: 224789

URL: http://llvm.org/viewvc/llvm-project?rev=224789&view=rev
Log:
[asan] change the coverage collection scheme so that we can easily emit coverage for the entire process as a single bit set, and if coverage_bitset=1 actually emit that bitset

Modified:
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h
    compiler-rt/trunk/test/asan/TestCases/Linux/coverage-fork.cc
    compiler-rt/trunk/test/asan/TestCases/Linux/coverage-levels.cc

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_libcdep.cc?rev=224789&r1=224788&r2=224789&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_libcdep.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_libcdep.cc Tue Dec 23 16:32:17 2014
@@ -12,14 +12,16 @@
 //
 // Compiler instrumentation:
 // For every interesting basic block the compiler injects the following code:
-// if (Guard) {
+// if (Guard < 0) {
 //    __sanitizer_cov(&Guard);
 // }
+// At the module start up time __sanitizer_cov_module_init sets the guards
+// to consecutive negative numbers (-1, -2, -3, ...).
 // It's fine to call __sanitizer_cov more than once for a given block.
 //
 // Run-time:
 //  - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC).
-//    and atomically set Guard to 1.
+//    and atomically set Guard to -Guard.
 //  - __sanitizer_cov_dump: dump the coverage data to disk.
 //  For every module of the current process that has coverage data
 //  this will create a file module_name.PID.sancov. The file format is simple:
@@ -65,14 +67,16 @@ class CoverageData {
   void BeforeFork();
   void AfterFork(int child_pid);
   void Extend(uptr npcs);
-  void Add(uptr pc, u8 *guard);
+  void Add(uptr pc, u32 *guard);
   void IndirCall(uptr caller, uptr callee, uptr callee_cache[],
                  uptr cache_size);
   void DumpCallerCalleePairs();
   void DumpTrace();
 
   ALWAYS_INLINE
-  void TraceBasicaBlock(uptr *cache);
+  void TraceBasicBlock(uptr *cache);
+
+  void InitializeGuards(s32 **guards, uptr n);
 
   uptr *data();
   uptr size();
@@ -149,12 +153,11 @@ void CoverageData::Init() {
   pc_array = reinterpret_cast<uptr *>(
       MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit"));
   pc_fd = kInvalidFd;
+  atomic_store(&pc_array_index, 0, memory_order_relaxed);
   if (common_flags()->coverage_direct) {
     atomic_store(&pc_array_size, 0, memory_order_relaxed);
-    atomic_store(&pc_array_index, 0, memory_order_relaxed);
   } else {
     atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed);
-    atomic_store(&pc_array_index, 0, memory_order_relaxed);
   }
 
   cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie(
@@ -230,15 +233,27 @@ void CoverageData::Extend(uptr npcs) {
   atomic_store(&pc_array_size, size, memory_order_release);
 }
 
+void CoverageData::InitializeGuards(s32 **guards, uptr n) {
+  for (uptr i = 0; i < n; i++) {
+    uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed);
+    *guards[i] = -static_cast<s32>(idx + 1);
+  }
+}
+
 // Atomically add the pc to the vector. The atomically set the guard to 1.
 // If the function is called more than once for a given PC it will
 // be inserted multiple times, which is fine.
-void CoverageData::Add(uptr pc, u8 *guard) {
-  // Set the guard.
-  atomic_uint8_t *atomic_guard = reinterpret_cast<atomic_uint8_t*>(guard);
-  atomic_store(atomic_guard, 1, memory_order_relaxed);
+void CoverageData::Add(uptr pc, u32 *guard) {
+  atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard);
+  s32 guard_value = atomic_load(atomic_guard, memory_order_relaxed);
+  if (guard_value >= 0) return;
+
+  atomic_store(atomic_guard, -guard_value, memory_order_relaxed);
   if (!pc_array) return;
-  uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed);
+
+  uptr idx = -guard_value - 1;
+  if (idx >= atomic_load(&pc_array_index, memory_order_acquire))
+    return;  // May happen after fork when pc_array_index becomes 0.
   CHECK_LT(idx * sizeof(uptr),
            atomic_load(&pc_array_size, memory_order_acquire));
   pc_array[idx] = pc;
@@ -338,18 +353,20 @@ static void CovWritePacked(int pid, cons
 // If packed = true and name == 0: <pid>.<sancov>.<packed>.
 // If packed = true and name != 0: <name>.<sancov>.<packed> (name is
 // user-supplied).
-static int CovOpenFile(bool packed, const char* name) {
+static int CovOpenFile(bool packed, const char *name,
+                       const char *extension = "sancov") {
   InternalScopedString path(kMaxPathLength);
   if (!packed) {
     CHECK(name);
-    path.append("%s/%s.%zd.sancov", common_flags()->coverage_dir, name,
-                internal_getpid());
+    path.append("%s/%s.%zd.%s", common_flags()->coverage_dir, name,
+                internal_getpid(), extension);
   } else {
     if (!name)
-      path.append("%s/%zd.sancov.packed", common_flags()->coverage_dir,
-                  internal_getpid());
+      path.append("%s/%zd.%s.packed", common_flags()->coverage_dir,
+                  internal_getpid(), extension);
     else
-      path.append("%s/%s.sancov.packed", common_flags()->coverage_dir, name);
+      path.append("%s/%s.%s.packed", common_flags()->coverage_dir, name,
+                  extension);
   }
   uptr fd = OpenFile(path.data(), true);
   if (internal_iserror(fd)) {
@@ -434,7 +451,7 @@ void CoverageData::DumpCallerCalleePairs
 // Record the current PC into the event buffer.
 // Every event is a u32 value (index in tr_pc_array_index) so we compute
 // it once and then cache in the provided 'cache' storage.
-void CoverageData::TraceBasicaBlock(uptr *cache) {
+void CoverageData::TraceBasicBlock(uptr *cache) {
   CHECK(common_flags()->coverage);
   uptr idx = *cache;
   if (!idx) {
@@ -450,12 +467,33 @@ void CoverageData::TraceBasicaBlock(uptr
   tr_event_array_index++;
 }
 
+static void CovDumpAsBitSet() {
+  if (!common_flags()->coverage_bitset) return;
+  if (!coverage_data.size()) return;
+  int fd = CovOpenFile(/* packed */false, "combined", "bitset-sancov");
+  if (fd < 0) return;
+  uptr n = coverage_data.size();
+  uptr n_set_bits = 0;
+  InternalScopedBuffer<char> out(n);
+  for (uptr i = 0; i < n; i++) {
+    uptr pc = coverage_data.data()[i];
+    out[i] = pc ? '1' : '0';
+    if (pc)
+      n_set_bits++;
+  }
+  internal_write(fd, out.data(), n);
+  internal_close(fd);
+  VReport(1, " CovDump: bitset of %zd bits written, %zd bits are set\n", n,
+          n_set_bits);
+}
+
 // Dump the coverage on disk.
 static void CovDump() {
   if (!common_flags()->coverage || common_flags()->coverage_direct) return;
 #if !SANITIZER_WINDOWS
   if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
     return;
+  CovDumpAsBitSet();
   uptr size = coverage_data.size();
   InternalMmapVector<u32> offsets(size);
   uptr *vb = coverage_data.data();
@@ -539,7 +577,7 @@ void CovAfterFork(int child_pid) {
 }  // namespace __sanitizer
 
 extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u8 *guard) {
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) {
   coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
                     guard);
 }
@@ -552,8 +590,11 @@ SANITIZER_INTERFACE_ATTRIBUTE void __san
 SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
   coverage_data.Init();
 }
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(uptr npcs) {
-  if (!common_flags()->coverage || !common_flags()->coverage_direct) return;
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(s32 **guards,
+                                                               uptr npcs) {
+  coverage_data.InitializeGuards(guards, npcs);
+  if (!common_flags()->coverage || !common_flags()->coverage_direct)
+    return;
   if (SANITIZER_ANDROID) {
     // dlopen/dlclose interceptors do not work on Android, so we rely on
     // Extend() calls to update .sancov.map.
@@ -572,10 +613,10 @@ uptr __sanitizer_get_total_unique_covera
 
 SANITIZER_INTERFACE_ATTRIBUTE
 void __sanitizer_cov_trace_func_enter(uptr *cache) {
-  coverage_data.TraceBasicaBlock(cache);
+  coverage_data.TraceBasicBlock(cache);
 }
 SANITIZER_INTERFACE_ATTRIBUTE
 void __sanitizer_cov_trace_basic_block(uptr *cache) {
-  coverage_data.TraceBasicaBlock(cache);
+  coverage_data.TraceBasicBlock(cache);
 }
 }  // extern "C"

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc?rev=224789&r1=224788&r2=224789&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.cc Tue Dec 23 16:32:17 2014
@@ -63,6 +63,7 @@ void CommonFlags::SetDefaults() {
   legacy_pthread_cond = false;
   intercept_tls_get_addr = false;
   coverage = false;
+  coverage_bitset = false;
   coverage_direct = SANITIZER_ANDROID;
   coverage_dir = ".";
   full_address_space = false;
@@ -150,6 +151,9 @@ void CommonFlags::ParseFromString(const
   ParseFlag(str, &coverage, "coverage",
       "If set, coverage information will be dumped at program shutdown (if the "
       "coverage instrumentation was enabled at compile time).");
+  ParseFlag(str, &coverage_bitset, "coverage_bitset",
+      "If set (and if 'coverage' is set too), the coverage information "
+      "will also be dumped as a bitset to a separate file.");
   ParseFlag(str, &coverage_direct, "coverage_direct",
             "If set, coverage information will be dumped directly to a memory "
             "mapped file. This way data is not lost even if the process is "

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h?rev=224789&r1=224788&r2=224789&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_flags.h Tue Dec 23 16:32:17 2014
@@ -56,6 +56,7 @@ struct CommonFlags {
   uptr mmap_limit_mb;
   uptr hard_rss_limit_mb;
   bool coverage;
+  bool coverage_bitset;
   bool coverage_direct;
   const char *coverage_dir;
   bool full_address_space;

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h?rev=224789&r1=224788&r2=224789&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_internal_defs.h Tue Dec 23 16:32:17 2014
@@ -120,7 +120,7 @@ extern "C" {
 
   SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
   SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init();
-  SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u8 *guard);
+  SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard);
   SANITIZER_INTERFACE_ATTRIBUTE
   void __sanitizer_annotate_contiguous_container(const void *beg,
                                                  const void *end,

Modified: compiler-rt/trunk/test/asan/TestCases/Linux/coverage-fork.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/coverage-fork.cc?rev=224789&r1=224788&r2=224789&view=diff
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Linux/coverage-fork.cc (original)
+++ compiler-rt/trunk/test/asan/TestCases/Linux/coverage-fork.cc Tue Dec 23 16:32:17 2014
@@ -33,6 +33,7 @@ int main(int argc, char **argv) {
 }
 
 // CHECK-DAG: Child PID: [[ChildPID:[0-9]+]]
-// CHECK-DAG: [[ChildPID]].sancov: 1 PCs written
+// Coverage in the child (after fork,before exec) is not expected to work.
+// (disabled): [[ChildPID]].sancov: 1 PCs written
 // CHECK-DAG: Parent PID: [[ParentPID:[0-9]+]]
 // CHECK-DAG: [[ParentPID]].sancov: 3 PCs written

Modified: compiler-rt/trunk/test/asan/TestCases/Linux/coverage-levels.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/coverage-levels.cc?rev=224789&r1=224788&r2=224789&view=diff
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Linux/coverage-levels.cc (original)
+++ compiler-rt/trunk/test/asan/TestCases/Linux/coverage-levels.cc Tue Dec 23 16:32:17 2014
@@ -1,11 +1,14 @@
 // Test various levels of coverage
 //
 // RUN: %clangxx_asan -O1 -fsanitize-coverage=1  %s -o %t
-// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1
+// RUN: ASAN_OPTIONS=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1
 // RUN: %clangxx_asan -O1 -fsanitize-coverage=2  %s -o %t
-// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2
+// RUN: ASAN_OPTIONS=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2
 // RUN: %clangxx_asan -O1 -fsanitize-coverage=3  %s -o %t
-// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3
+// RUN: ASAN_OPTIONS=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3
+
+// RUN: ASAN_OPTIONS=coverage=1:coverage_bitset=0:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET
 //
 // REQUIRES: asan-64-bits
 
@@ -15,6 +18,10 @@ int main(int argc, char **argv) {
     sink = 0;
 }
 
+// CHECK1: CovDump: bitset of 1 bits written, 1 bits are set
 // CHECK1:  1 PCs written
+// CHECK2: CovDump: bitset of 3 bits written, 2 bits are set
 // CHECK2:  2 PCs written
+// CHECK3: CovDump: bitset of 4 bits written, 3 bits are set
 // CHECK3:  3 PCs written
+// CHECK3_NOBITSET-NOT: bitset of





More information about the llvm-commits mailing list