[compiler-rt] r220975 - [asan] run-time part of the caller-callee coverage instrumentation

Kostya Serebryany kcc at google.com
Fri Oct 31 10:19:11 PDT 2014


Author: kcc
Date: Fri Oct 31 12:19:11 2014
New Revision: 220975

URL: http://llvm.org/viewvc/llvm-project?rev=220975&view=rev
Log:
[asan] run-time part of the caller-callee coverage instrumentation

Added:
    compiler-rt/trunk/test/asan/TestCases/Linux/coverage-caller-callee.cc
Modified:
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_libcdep.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=220975&r1=220974&r2=220975&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_libcdep.cc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_libcdep.cc Fri Oct 31 12:19:11 2014
@@ -38,6 +38,7 @@
 #include "sanitizer_mutex.h"
 #include "sanitizer_procmaps.h"
 #include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
 #include "sanitizer_flags.h"
 
 atomic_uint32_t dump_once_guard;  // Ensure that CovDump runs only once.
@@ -63,6 +64,9 @@ class CoverageData {
   void AfterFork(int child_pid);
   void Extend(uptr npcs);
   void Add(uptr pc);
+  void IndirCall(uptr caller, uptr callee, uptr callee_cache[],
+                 uptr cache_size);
+  void DumpCallerCalleePairs();
 
   uptr *data();
   uptr size();
@@ -85,6 +89,14 @@ class CoverageData {
   uptr pc_array_mapped_size;
   // Descriptor of the file mapped pc array.
   int pc_fd;
+
+  // Caller-Callee (cc) array, size and current index.
+  static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
+  uptr **cc_array;
+  atomic_uintptr_t cc_array_index;
+  atomic_uintptr_t cc_array_size;
+
+
   StaticSpinMutex mu;
 
   void DirectOpen();
@@ -118,6 +130,11 @@ void CoverageData::Init() {
     atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed);
     atomic_store(&pc_array_index, 0, memory_order_relaxed);
   }
+
+  cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie(
+      sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array"));
+  atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
+  atomic_store(&cc_array_index, 0, memory_order_relaxed);
 }
 
 void CoverageData::ReInit() {
@@ -186,6 +203,38 @@ void CoverageData::Add(uptr pc) {
   pc_array[idx] = pc;
 }
 
+// Registers a pair caller=>callee.
+// When a given caller is seen for the first time, the callee_cache is added
+// to the global array cc_array, callee_cache[0] is set to caller and
+// callee_cache[1] is set to cache_size.
+// Then we are trying to add callee to callee_cache [2,cache_size) if it is
+// not there yet.
+// If the cache is full we drop the callee (may want to fix this later).
+void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[],
+                             uptr cache_size) {
+  if (!cc_array) return;
+  atomic_uintptr_t *atomic_callee_cache =
+      reinterpret_cast<atomic_uintptr_t *>(callee_cache);
+  uptr zero = 0;
+  if (atomic_compare_exchange_strong(&atomic_callee_cache[0], &zero, caller,
+                                     memory_order_seq_cst)) {
+    uptr idx = atomic_fetch_add(&cc_array_index, 1, memory_order_relaxed);
+    CHECK_LT(idx * sizeof(uptr),
+             atomic_load(&cc_array_size, memory_order_acquire));
+    callee_cache[1] = cache_size;
+    cc_array[idx] = callee_cache;
+  }
+  CHECK_EQ(atomic_load(&atomic_callee_cache[0], memory_order_relaxed), caller);
+  for (uptr i = 2; i < cache_size; i++) {
+    uptr was = 0;
+    if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee,
+                                       memory_order_seq_cst))
+      return;
+    if (was == callee)  // Already have this callee.
+      return;
+  }
+}
+
 uptr *CoverageData::data() {
   return pc_array;
 }
@@ -268,6 +317,45 @@ static int CovOpenFile(bool packed, cons
   return fd;
 }
 
+// This function dumps the caller=>callee pairs into a file as a sequence of
+// lines like "module_name offset".
+void CoverageData::DumpCallerCalleePairs() {
+  uptr max_idx = atomic_load(&cc_array_index, memory_order_relaxed);
+  if (!max_idx) return;
+  auto sym = Symbolizer::GetOrInit();
+  if (!sym)
+    return;
+  InternalScopedString out(4096 * 16);
+  uptr total = 0;
+  for (uptr i = 0; i < max_idx; i++) {
+    uptr *cc_cache = cc_array[i];
+    CHECK(cc_cache);
+    uptr caller = cc_cache[0];
+    uptr n_callees = cc_cache[1];
+    const char *caller_module_name = "<unknown>";
+    uptr caller_module_address = 0;
+    sym->GetModuleNameAndOffsetForPC(caller, &caller_module_name,
+                                     &caller_module_address);
+    for (uptr j = 2; j < n_callees; j++) {
+      uptr callee = cc_cache[j];
+      if (!callee) break;
+      total++;
+      const char *callee_module_name = "<unknown>";
+      uptr callee_module_address = 0;
+      sym->GetModuleNameAndOffsetForPC(callee, &callee_module_name,
+                                       &callee_module_address);
+      out.append("%s 0x%zx\n%s 0x%zx\n", caller_module_name,
+                 caller_module_address, callee_module_name,
+                 callee_module_address);
+    }
+  }
+  int fd = CovOpenFile(false, "caller-callee");
+  if (fd < 0) return;
+  internal_write(fd, out.data(), out.length());
+  internal_close(fd);
+  VReport(1, " CovDump: %zd caller-callee pairs written\n", total);
+}
+
 // Dump the coverage on disk.
 static void CovDump() {
   if (!common_flags()->coverage || common_flags()->coverage_direct) return;
@@ -324,6 +412,7 @@ static void CovDump() {
   }
   if (cov_fd >= 0)
     internal_close(cov_fd);
+  coverage_data.DumpCallerCalleePairs();
 #endif  // !SANITIZER_WINDOWS
 }
 
@@ -359,6 +448,11 @@ extern "C" {
 SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov() {
   coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()));
 }
+SANITIZER_INTERFACE_ATTRIBUTE void
+__sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) {
+  coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
+                          callee, callee_cache16, 16);
+}
 SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
 SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
   coverage_data.Init();

Added: compiler-rt/trunk/test/asan/TestCases/Linux/coverage-caller-callee.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/coverage-caller-callee.cc?rev=220975&view=auto
==============================================================================
--- compiler-rt/trunk/test/asan/TestCases/Linux/coverage-caller-callee.cc (added)
+++ compiler-rt/trunk/test/asan/TestCases/Linux/coverage-caller-callee.cc Fri Oct 31 12:19:11 2014
@@ -0,0 +1,74 @@
+// Test caller-callee coverage with large number of threads
+// and various numbers of callers and callees.
+
+// RUN: %clangxx_asan -mllvm -asan-coverage=4 %s -o %t
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 10 1 2>&1 | FileCheck %s --check-prefix=CHECK-10-1
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 9  2 2>&1 | FileCheck %s --check-prefix=CHECK-9-2
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 7  3 2>&1 | FileCheck %s --check-prefix=CHECK-7-3
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 17 1 2>&1 | FileCheck %s --check-prefix=CHECK-17-1
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 15 2 2>&1 | FileCheck %s --check-prefix=CHECK-15-2
+// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 18 3 2>&1 | FileCheck %s --check-prefix=CHECK-18-3
+// RUN: rm -f caller-callee*.sancov
+//
+// REQUIRES: asan-64-bits
+//
+// CHECK-10-1: CovDump: 10 caller-callee pairs written
+// CHECK-9-2: CovDump: 18 caller-callee pairs written
+// CHECK-7-3: CovDump: 21 caller-callee pairs written
+// CHECK-17-1: CovDump: 14 caller-callee pairs written
+// CHECK-15-2: CovDump: 28 caller-callee pairs written
+// CHECK-18-3: CovDump: 42 caller-callee pairs written
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+int P = 0;
+struct Foo {virtual void f() {if (P) printf("Foo::f()\n");}};
+struct Foo1 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo2 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo3 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo4 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo5 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo6 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo7 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo8 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo9 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo10 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo11 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo12 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo13 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo14 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo15 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo16 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo17 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo18 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+struct Foo19 : Foo {virtual void f() {if (P) printf("%d\n", __LINE__);}};
+
+Foo *foo[20] = {
+    new Foo,   new Foo1,  new Foo2,  new Foo3,  new Foo4,  new Foo5,  new Foo6,
+    new Foo7,  new Foo8,  new Foo9,  new Foo10, new Foo11, new Foo12, new Foo13,
+    new Foo14, new Foo15, new Foo16, new Foo17, new Foo18, new Foo19,
+};
+
+int n_functions = 10;
+int n_callers = 2;
+
+void *Thread(void *arg) {
+  if (n_callers >= 1) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f();
+  if (n_callers >= 2) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f();
+  if (n_callers >= 3) for (int i = 0; i < 2000; i++) foo[i % n_functions]->f();
+  return arg;
+}
+
+int main(int argc, char **argv) {
+  if (argc >= 2)
+    n_functions = atoi(argv[1]);
+  if (argc >= 3)
+    n_callers = atoi(argv[2]);
+  const int kNumThreads = 16;
+  pthread_t t[kNumThreads];
+  for (int i = 0; i < kNumThreads; i++)
+    pthread_create(&t[i], 0, Thread, 0);
+  for (int i = 0; i < kNumThreads; i++)
+    pthread_join(t[i], 0);
+}





More information about the llvm-commits mailing list