From: Kostya Serebryany Date: Wed, 19 Nov 2014 00:24:11 +0000 (+0000) Subject: [asan] initial support for experimental basic-block tracing; also add tests for vario... X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c9d251e4d17c47504702d0a5b040ac20414f5ba7;p=platform%2Fupstream%2Fllvm.git [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 @@ 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( + 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(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 = ""; + 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(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/compiler-rt/test/asan/TestCases/Linux/coverage-levels.cc b/compiler-rt/test/asan/TestCases/Linux/coverage-levels.cc new file mode 100644 index 0000000..748ef1f --- /dev/null +++ b/compiler-rt/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/compiler-rt/test/asan/TestCases/Linux/coverage-tracing.cc b/compiler-rt/test/asan/TestCases/Linux/coverage-tracing.cc new file mode 100644 index 0000000..89ab0d2 --- /dev/null +++ b/compiler-rt/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