[sanitizer] add run-time a flag coverage_order_pcs. When true, the PCs are dumped...
authorKostya Serebryany <kcc@google.com>
Wed, 18 Mar 2015 00:23:44 +0000 (00:23 +0000)
committerKostya Serebryany <kcc@google.com>
Wed, 18 Mar 2015 00:23:44 +0000 (00:23 +0000)
llvm-svn: 232573

compiler-rt/lib/sanitizer_common/sanitizer_common.h
compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
compiler-rt/lib/sanitizer_common/scripts/sancov.py
compiler-rt/test/asan/TestCases/Linux/coverage-order-pcs.cc [new file with mode: 0644]

index 1b720aa..d55bfef 100644 (file)
@@ -445,6 +445,9 @@ class InternalMmapVectorNoCtor {
   const T *data() const {
     return data_;
   }
+  T *data() {
+    return data_;
+  }
   uptr capacity() const {
     return capacity_;
   }
index 29f8e49..6ef8bf4 100644 (file)
@@ -352,6 +352,32 @@ void CoverageData::InitializeGuards(s32 *guards, uptr n,
   UpdateModuleNameVec(caller_pc, range_beg, range_end);
 }
 
+static const uptr kBundleCounterBits = 16;
+
+// When coverage_order_pcs==true and SANITIZER_WORDSIZE==64
+// we insert the global counter into the first 16 bits of the PC.
+uptr BundlePcAndCounter(uptr pc, uptr counter) {
+  if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+    return pc;
+  static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1;
+  if (counter > kMaxCounter)
+    counter = kMaxCounter;
+  CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits));
+  return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits));
+}
+
+uptr UnbundlePc(uptr bundle) {
+  if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+    return bundle;
+  return (bundle << kBundleCounterBits) >> kBundleCounterBits;
+}
+
+uptr UnbundleCounter(uptr bundle) {
+  if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+    return 0;
+  return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits);
+}
+
 // If guard is negative, atomically set it to -guard and store the PC in
 // pc_array.
 void CoverageData::Add(uptr pc, u32 *guard) {
@@ -367,8 +393,8 @@ void CoverageData::Add(uptr pc, u32 *guard) {
     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;
-  atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+  uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+  pc_array[idx] = BundlePcAndCounter(pc, counter);
 }
 
 // Registers a pair caller=>callee.
@@ -555,7 +581,7 @@ void CoverageData::DumpTrace() {
   for (uptr i = 0, n = size(); i < n; i++) {
     const char *module_name = "<unknown>";
     uptr module_address = 0;
-    sym->GetModuleNameAndOffsetForPC(pc_array[i], &module_name,
+    sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name,
                                      &module_address);
     out.append("%s 0x%zx\n", module_name, module_address);
   }
@@ -681,7 +707,7 @@ void CoverageData::DumpAsBitSet() {
     CHECK_LE(r.beg, r.end);
     CHECK_LE(r.end, size());
     for (uptr i = r.beg; i < r.end; i++) {
-      uptr pc = data()[i];
+      uptr pc = UnbundlePc(pc_array[i]);
       out[i] = pc ? '1' : '0';
       if (pc)
         n_set_bits++;
@@ -711,12 +737,18 @@ void CoverageData::DumpOffsets() {
     CHECK_LE(r.end, size());
     const char *module_name = "<unknown>";
     for (uptr i = r.beg; i < r.end; i++) {
-      uptr pc = data()[i];
+      uptr pc = UnbundlePc(pc_array[i]);
+      uptr counter = UnbundleCounter(pc_array[i]);
       if (!pc) continue; // Not visited.
       uptr offset = 0;
       sym->GetModuleNameAndOffsetForPC(pc, &module_name, &offset);
-      offsets.push_back(offset);
+      offsets.push_back(BundlePcAndCounter(offset, counter));
     }
+
+    SortArray(offsets.data(), offsets.size());
+    for (uptr i = 0; i < offsets.size(); i++)
+      offsets[i] = UnbundlePc(offsets[i]);
+
     module_name = StripModuleName(r.name);
     if (cov_sandboxed) {
       if (cov_fd >= 0) {
index 983af07..884602f 100644 (file)
@@ -111,10 +111,12 @@ COMMON_FLAG(
     bool, coverage, false,
     "If set, coverage information will be dumped at program shutdown (if the "
     "coverage instrumentation was enabled at compile time).")
-// On by default, but works only if coverage == true.
 COMMON_FLAG(bool, coverage_pcs, true,
             "If set (and if 'coverage' is set too), the coverage information "
             "will be dumped as a set of PC offsets for every module.")
+COMMON_FLAG(bool, coverage_order_pcs, false,
+             "If true, the PCs will be dumped in the order they've"
+             " appeared during the execution.")
 COMMON_FLAG(bool, coverage_bitset, false,
             "If set (and if 'coverage' is set too), the coverage information "
             "will also be dumped as a bitset to a separate file.")
index 1614877..4435216 100755 (executable)
@@ -30,20 +30,23 @@ def ReadOneFile(path, bits):
     f.seek(0, 2)
     size = f.tell()
     f.seek(0, 0)
-    s = set(array.array(TypeCodeForBits(bits), f.read(size)))
+    s = array.array(TypeCodeForBits(bits), f.read(size))
   print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size * 8 / bits, path)
   return s
 
 def Merge(files, bits):
   s = set()
   for f in files:
-    s = s.union(ReadOneFile(f, bits))
+    s = s.union(set(ReadOneFile(f, bits)))
   print >> sys.stderr, "%s: %d files merged; %d PCs total" % \
     (prog_name, len(files), len(s))
   return sorted(s)
 
 def PrintFiles(files, bits):
-  s = Merge(files, bits)
+  if len(files) > 1:
+    s = Merge(files, bits)
+  else:  # If there is just on file, print the PCs in order.
+    s = ReadOneFile(files[0], bits)
   for i in s:
     print "0x%x" % i
 
diff --git a/compiler-rt/test/asan/TestCases/Linux/coverage-order-pcs.cc b/compiler-rt/test/asan/TestCases/Linux/coverage-order-pcs.cc
new file mode 100644 (file)
index 0000000..4377b8f
--- /dev/null
@@ -0,0 +1,56 @@
+// Test coverage_order_pcs=1 flag which orders the PCs by their appearance.
+// RUN: DIR=%T/coverage-order-pcs
+// RUN: rm -rf $DIR
+// RUN: mkdir $DIR
+// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t
+// RUN: ASAN_OPTIONS=coverage_dir=$DIR:coverage=1:coverage_order_pcs=0 %t
+// RUN: mv $DIR/*sancov $DIR/A
+
+// RUN: ASAN_OPTIONS=coverage_dir=$DIR:coverage=1:coverage_order_pcs=0 %t 1
+// RUN: mv $DIR/*sancov $DIR/B
+
+// RUN: ASAN_OPTIONS=coverage_dir=$DIR:coverage=1:coverage_order_pcs=1 %t
+// RUN: mv $DIR/*sancov $DIR/C
+
+// RUN: ASAN_OPTIONS=coverage_dir=$DIR:coverage=1:coverage_order_pcs=1 %t 1
+// RUN: mv $DIR/*sancov $DIR/D
+//
+// RUN: (%sancov print $DIR/A; %sancov print $DIR/B; %sancov print $DIR/C; %sancov print $DIR/D) | FileCheck %s
+//
+// RUN: rm -rf $DIR
+// Ordering works only in 64-bit mode for now.
+// REQUIRES: asan-64-bits
+#include <stdio.h>
+
+void foo() { fprintf(stderr, "FOO\n"); }
+void bar() { fprintf(stderr, "BAR\n"); }
+
+int main(int argc, char **argv) {
+  if (argc == 2) {
+    foo();
+    bar();
+  } else {
+    bar();
+    foo();
+  }
+}
+
+// Run A: no ordering
+// CHECK: [[FOO:0x[0-9a-f]*]]
+// CHECK-NEXT: [[BAR:0x[0-9a-f]*]]
+// CHECK-NEXT: [[MAIN:0x[0-9a-f]*]]
+//
+// Run B: still no ordering
+// CHECK-NEXT: [[FOO]]
+// CHECK-NEXT: [[BAR]]
+// CHECK-NEXT: [[MAIN]]
+//
+// Run C: MAIN, BAR, FOO
+// CHECK-NEXT: [[MAIN]]
+// CHECK-NEXT: [[BAR]]
+// CHECK-NEXT: [[FOO]]
+//
+// Run D: MAIN, FOO, BAR
+// CHECK-NEXT: [[MAIN]]
+// CHECK-NEXT: [[FOO]]
+// CHECK-NEXT: [[BAR]]