summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2014-11-19 00:24:11 +0000
committerKostya Serebryany <kcc@google.com>2014-11-19 00:24:11 +0000
commit2f47b9dc1b11a9ede29dc35fabd21463adbbfdc6 (patch)
tree758a11101a617bebb540afddd2b2a241bf4ae2ba
parent9369be1fcdcf0826559350708ecc91a73f9be4a9 (diff)
[asan] initial support for experimental basic-block tracing; also add tests for various levels of -fsanitize-coverage
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@222291 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_libcdep.cc97
-rw-r--r--test/asan/TestCases/Linux/coverage-levels.cc20
-rw-r--r--test/asan/TestCases/Linux/coverage-tracing.cc22
3 files changed, 139 insertions, 0 deletions
diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
index d7da6c9b1..bd98adb4f 100644
--- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
@@ -69,6 +69,10 @@ class CoverageData {
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 @@ class CoverageData {
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 @@ void CoverageData::Init() {
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 @@ static int CovOpenFile(bool packed, const char* name) {
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 @@ void CoverageData::DumpCallerCalleePairs() {
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 @@ static void CovDump() {
if (cov_fd >= 0)
internal_close(cov_fd);
coverage_data.DumpCallerCalleePairs();
+ coverage_data.DumpTrace();
#endif // !SANITIZER_WINDOWS
}
@@ -478,4 +566,13 @@ SANITIZER_INTERFACE_ATTRIBUTE
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"
diff --git a/test/asan/TestCases/Linux/coverage-levels.cc b/test/asan/TestCases/Linux/coverage-levels.cc
new file mode 100644
index 000000000..748ef1f08
--- /dev/null
+++ b/test/asan/TestCases/Linux/coverage-levels.cc
@@ -0,0 +1,20 @@
+// 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: %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: %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
+//
+// REQUIRES: asan-64-bits
+
+volatile int sink;
+int main(int argc, char **argv) {
+ if (argc == 0)
+ sink = 0;
+}
+
+// CHECK1: 1 PCs written
+// CHECK2: 2 PCs written
+// CHECK3: 3 PCs written
diff --git a/test/asan/TestCases/Linux/coverage-tracing.cc b/test/asan/TestCases/Linux/coverage-tracing.cc
new file mode 100644
index 000000000..89ab0d283
--- /dev/null
+++ b/test/asan/TestCases/Linux/coverage-tracing.cc
@@ -0,0 +1,22 @@
+// Test -mllvm -sanitizer-coverage-experimental-tracing
+//
+// RUN: %clangxx_asan -O1 -fsanitize-coverage=1 -mllvm -sanitizer-coverage-experimental-tracing %s -o %t
+// RUN: rm -rf %T/coverage-tracing
+// RUN: mkdir -p %T/coverage-tracing
+// RUN: ASAN_OPTIONS=coverage=1:coverage_dir=%T/coverage-tracing:verbosity=1 %run %t 1 2 3 4 2>&1 | FileCheck %s
+// RUN: rm -rf %T/coverage-tracing
+//
+// REQUIRES: asan-64-bits
+
+volatile int sink;
+int main(int argc, char **argv) {
+ volatile int i = 0;
+ do {
+ sink = 0;
+ i++;
+ } while (i < argc);
+ return 0;
+}
+
+// CHECK: CovDump: Trace: {{[3-9]}} PCs written
+// CHECK: CovDump: Trace: {{[6-9]}} Events written