[asan] initial support for experimental basic-block tracing; also add tests for various levels of -fsanitize-coverage 

llvm-svn: 222291
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
index d7da6c9..bd98adb 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
@@ -69,6 +69,10 @@
   void IndirCall(uptr caller, uptr callee, uptr callee_cache[],
                  uptr cache_size);
   void DumpCallerCalleePairs();
+  void DumpTrace();
+
+  ALWAYS_INLINE
+  void TraceBasicaBlock(uptr *cache);
 
   uptr *data();
   uptr size();
@@ -98,6 +102,26 @@
   atomic_uintptr_t cc_array_index;
   atomic_uintptr_t cc_array_size;
 
+  // Tracing (tr) pc and event arrays, their size and current index.
+  // We record all events (basic block entries) in a global buffer of u32
+  // values. Each such value is an index in the table of TracedPc objects.
+  // So far the tracing is highly experimental:
+  //   - not thread-safe;
+  //   - does not support long traces;
+  //   - not tuned for performance.
+  struct TracedPc {
+    uptr pc;
+    const char *module_name;
+    uptr module_offset;
+  };
+  static const uptr kTrEventArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 30);
+  u32 *tr_event_array;
+  uptr tr_event_array_size;
+  uptr tr_event_array_index;
+  static const uptr kTrPcArrayMaxSize    = FIRST_32_SECOND_64(1 << 22, 1 << 27);
+  TracedPc *tr_pc_array;
+  uptr tr_pc_array_size;
+  uptr tr_pc_array_index;
 
   StaticSpinMutex mu;
 
@@ -137,6 +161,17 @@
       sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array"));
   atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
   atomic_store(&cc_array_index, 0, memory_order_relaxed);
+
+  tr_event_array = reinterpret_cast<u32 *>(
+      MmapNoReserveOrDie(sizeof(tr_event_array[0]) * kTrEventArrayMaxSize,
+                         "CovInit::tr_event_array"));
+  tr_event_array_size = kTrEventArrayMaxSize;
+  tr_event_array_index = 0;
+
+  tr_pc_array = reinterpret_cast<TracedPc *>(MmapNoReserveOrDie(
+      sizeof(tr_pc_array[0]) * kTrEventArrayMaxSize, "CovInit::tr_pc_array"));
+  tr_pc_array_size = kTrEventArrayMaxSize;
+  tr_pc_array_index = 0;
 }
 
 void CoverageData::ReInit() {
@@ -322,6 +357,39 @@
   return fd;
 }
 
+// Dump trace PCs and trace events into two separate files.
+void CoverageData::DumpTrace() {
+  uptr max_idx = tr_event_array_index;
+  if (!max_idx) return;
+  auto sym = Symbolizer::GetOrInit();
+  if (!sym)
+    return;
+  InternalScopedString out(32 << 20);
+  for (uptr i = 0; i < max_idx; i++) {
+    u32 pc_idx = tr_event_array[i];
+    TracedPc *t = &tr_pc_array[pc_idx];
+    if (!t->module_name) {
+      const char *module_name = "<unknown>";
+      uptr module_address = 0;
+      sym->GetModuleNameAndOffsetForPC(t->pc, &module_name, &module_address);
+      t->module_name = internal_strdup(module_name);
+      t->module_offset = module_address;
+      out.append("%s 0x%zx\n", t->module_name, t->module_offset);
+    }
+  }
+  int fd = CovOpenFile(false, "trace-points");
+  if (fd < 0) return;
+  internal_write(fd, out.data(), out.length());
+  internal_close(fd);
+
+  fd = CovOpenFile(false, "trace-events");
+  if (fd < 0) return;
+  internal_write(fd, tr_event_array, max_idx * sizeof(tr_event_array[0]));
+  internal_close(fd);
+  VReport(1, " CovDump: Trace: %zd PCs written\n", tr_pc_array_index);
+  VReport(1, " CovDump: Trace: %zd Events written\n", tr_event_array_index);
+}
+
 // This function dumps the caller=>callee pairs into a file as a sequence of
 // lines like "module_name offset".
 void CoverageData::DumpCallerCalleePairs() {
@@ -361,6 +429,25 @@
   VReport(1, " CovDump: %zd caller-callee pairs written\n", total);
 }
 
+// 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) {
+  CHECK(common_flags()->coverage);
+  uptr idx = *cache;
+  if (!idx) {
+    CHECK_LT(tr_pc_array_index, kTrPcArrayMaxSize);
+    idx = tr_pc_array_index++;
+    TracedPc *t = &tr_pc_array[idx];
+    t->pc = GET_CALLER_PC();
+    *cache = idx;
+    CHECK_LT(idx, 1U << 31);
+  }
+  CHECK_LT(tr_event_array_index, tr_event_array_size);
+  tr_event_array[tr_event_array_index] = static_cast<u32>(idx);
+  tr_event_array_index++;
+}
+
 // Dump the coverage on disk.
 static void CovDump() {
   if (!common_flags()->coverage || common_flags()->coverage_direct) return;
@@ -417,6 +504,7 @@
   if (cov_fd >= 0)
     internal_close(cov_fd);
   coverage_data.DumpCallerCalleePairs();
+  coverage_data.DumpTrace();
 #endif  // !SANITIZER_WINDOWS
 }
 
@@ -478,4 +566,13 @@
 uptr __sanitizer_get_total_unique_coverage() {
   return atomic_load(&coverage_counter, memory_order_relaxed);
 }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_func_enter(uptr *cache) {
+  coverage_data.TraceBasicaBlock(cache);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_basic_block(uptr *cache) {
+  coverage_data.TraceBasicaBlock(cache);
+}
 }  // extern "C"