summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2013-11-14 13:28:17 +0000
committerKostya Serebryany <kcc@google.com>2013-11-14 13:28:17 +0000
commit68e16eb59c7f654cdb98a2811b3a42612b58a735 (patch)
treeb9847d967f60fe09b1262b0ae5e3b475a8c5d0a0
parentb9ac7162892b283f05b70b6d79eb92119712ff0d (diff)
downloadcompiler-rt-68e16eb59c7f654cdb98a2811b3a42612b58a735.tar.gz
[asan] Poor man's coverage that works with ASan (compiler-rt part)
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@194702 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/sanitizer/common_interface_defs.h4
-rw-r--r--lib/asan/asan_flags.h3
-rw-r--r--lib/asan/asan_rtl.cc5
-rw-r--r--lib/asan/lit_tests/TestCases/Linux/coverage.cc45
-rw-r--r--lib/sanitizer_common/CMakeLists.txt1
-rw-r--r--lib/sanitizer_common/sanitizer_common.cc11
-rw-r--r--lib/sanitizer_common/sanitizer_common.h5
-rw-r--r--lib/sanitizer_common/sanitizer_coverage.cc109
-rw-r--r--lib/sanitizer_common/sanitizer_internal_defs.h3
-rw-r--r--lib/tsan/rtl/tsan_symbolize.cc12
10 files changed, 186 insertions, 12 deletions
diff --git a/include/sanitizer/common_interface_defs.h b/include/sanitizer/common_interface_defs.h
index 741958f83..b9c0d7311 100644
--- a/include/sanitizer/common_interface_defs.h
+++ b/include/sanitizer/common_interface_defs.h
@@ -47,6 +47,10 @@ extern "C" {
void __sanitizer_unaligned_store32(void *p, uint32_t x);
void __sanitizer_unaligned_store64(void *p, uint64_t x);
+ // Record and dump coverage info.
+ void __sanitizer_cov(void *pc);
+ void __sanitizer_cov_dump();
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h
index 86eb3b6c1..89662f28b 100644
--- a/lib/asan/asan_flags.h
+++ b/lib/asan/asan_flags.h
@@ -83,6 +83,9 @@ struct Flags {
bool print_legend;
// If set, prints ASan exit stats even after program terminates successfully.
bool atexit;
+ // If set, coverage information will be dumped at shutdown time if the
+ // appropriate instrumentation was enabled.
+ bool coverage;
// By default, disable core dumper on 64-bit - it makes little sense
// to dump 16T+ core.
bool disable_core;
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index bd1ace747..11f05954d 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -120,6 +120,7 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
ParseFlag(str, &f->print_stats, "print_stats");
ParseFlag(str, &f->print_legend, "print_legend");
ParseFlag(str, &f->atexit, "atexit");
+ ParseFlag(str, &f->coverage, "coverage");
ParseFlag(str, &f->disable_core, "disable_core");
ParseFlag(str, &f->allow_reexec, "allow_reexec");
ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history");
@@ -161,6 +162,7 @@ void InitializeFlags(Flags *f, const char *env) {
f->print_stats = false;
f->print_legend = true;
f->atexit = false;
+ f->coverage = false;
f->disable_core = (SANITIZER_WORDSIZE == 64);
f->allow_reexec = true;
f->print_full_thread_history = true;
@@ -541,6 +543,9 @@ void __asan_init() {
if (flags()->atexit)
Atexit(asan_atexit);
+ if (flags()->coverage)
+ Atexit(__sanitizer_cov_dump);
+
// interceptors
InitTlsSize();
diff --git a/lib/asan/lit_tests/TestCases/Linux/coverage.cc b/lib/asan/lit_tests/TestCases/Linux/coverage.cc
new file mode 100644
index 000000000..4373e9b13
--- /dev/null
+++ b/lib/asan/lit_tests/TestCases/Linux/coverage.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %t.so -fPIC
+// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t -Wl,-R. %t.so
+// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1
+// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-main
+// RUN: %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-foo
+// RUN: %t bar 2>&1 | FileCheck %s --check-prefix=CHECK-bar
+// RUN: %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef SHARED
+void bar() { printf("bar\n"); }
+#else
+__attribute__((noinline))
+void foo() { printf("foo\n"); }
+extern void bar();
+
+int main(int argc, char **argv) {
+ fprintf(stderr, "PID: %d\n", getpid());
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "foo"))
+ foo();
+ if (!strcmp(argv[i], "bar"))
+ bar();
+ }
+}
+#endif
+
+// CHECK-main: PID: [[PID:[0-9]+]]
+// CHECK-main: [[PID]].sancov: 1 PCs written
+// CHECK-main-NOT: .so.[[PID]]
+//
+// CHECK-foo: PID: [[PID:[0-9]+]]
+// CHECK-foo: [[PID]].sancov: 2 PCs written
+// CHECK-foo-NOT: .so.[[PID]]
+//
+// CHECK-bar: PID: [[PID:[0-9]+]]
+// CHECK-bar: [[PID]].sancov: 1 PCs written
+// CHECK-bar: .so.[[PID]].sancov: 1 PCs written
+//
+// CHECK-foo-bar: PID: [[PID:[0-9]+]]
+// CHECK-foo-bar: [[PID]].sancov: 2 PCs written
+// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 4ec371958..84c1e67dc 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -4,6 +4,7 @@
set(SANITIZER_SOURCES
sanitizer_allocator.cc
sanitizer_common.cc
+ sanitizer_coverage.cc
sanitizer_flags.cc
sanitizer_libc.cc
sanitizer_libignore.cc
diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc
index 3af4d4fb6..7e870ff65 100644
--- a/lib/sanitizer_common/sanitizer_common.cc
+++ b/lib/sanitizer_common/sanitizer_common.cc
@@ -226,6 +226,17 @@ bool LoadedModule::containsAddress(uptr address) const {
return false;
}
+char *StripModuleName(const char *module) {
+ if (module == 0)
+ return 0;
+ const char *short_module_name = internal_strrchr(module, '/');
+ if (short_module_name)
+ short_module_name += 1;
+ else
+ short_module_name = module;
+ return internal_strdup(short_module_name);
+}
+
} // namespace __sanitizer
using namespace __sanitizer; // NOLINT
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index daf9892d9..cf8a12d65 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -180,6 +180,9 @@ void SleepForMillis(int millis);
u64 NanoTime();
int Atexit(void (*function)(void));
void SortArray(uptr *array, uptr size);
+// Strip the directories from the module name, return a new string allocated
+// with internal_strdup.
+char *StripModuleName(const char *module);
// Exit
void NORETURN Abort();
@@ -359,6 +362,8 @@ class InternalMmapVector {
return capacity_;
}
+ void clear() { size_ = 0; }
+
private:
void Resize(uptr new_capacity) {
CHECK_GT(new_capacity, 0);
diff --git a/lib/sanitizer_common/sanitizer_coverage.cc b/lib/sanitizer_common/sanitizer_coverage.cc
new file mode 100644
index 000000000..c5338e9c6
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_coverage.cc
@@ -0,0 +1,109 @@
+//===-- sanitizer_coverage.cc ---------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Sanitizer Coverage.
+// This file implements run-time support for a poor man's coverage tool.
+//
+// Compiler instrumentation:
+// For every function F the compiler injects the following code:
+// if (*Guard) {
+// __sanitizer_cov(&F);
+// *Guard = 1;
+// }
+// It's fine to call __sanitizer_cov more than once for a given function.
+//
+// Run-time:
+// - __sanitizer_cov(pc): record that we've executed a given PC.
+// - __sanitizer_cov_dump: dump the coverage data to disk.
+// For every module of the current process that has coverage data
+// this will create a file module_name.PID.sancov. The file format is simple:
+// it's just a sorted sequence of 4-byte offsets in the module.
+//
+// Eventually, this coverage implementation should be obsoleted by a more
+// powerful general purpose Clang/LLVM coverage instrumentation.
+// Consider this implementation as prototype.
+//
+// FIXME: support (or at least test with) dlclose.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_flags.h"
+
+struct CovData {
+ BlockingMutex mu;
+ InternalMmapVector<uptr> v;
+};
+
+static uptr cov_data_placeholder[sizeof(CovData) / sizeof(uptr)];
+COMPILER_CHECK(sizeof(cov_data_placeholder) >= sizeof(CovData));
+static CovData *cov_data = reinterpret_cast<CovData*>(cov_data_placeholder);
+
+namespace __sanitizer {
+
+// Simply add the pc into the vector under lock. If the function is called more
+// than once for a given PC it will be inserted multiple times, which is fine.
+static void CovAdd(uptr pc) {
+ BlockingMutexLock lock(&cov_data->mu);
+ cov_data->v.push_back(pc);
+}
+
+static inline bool CompareLess(const uptr &a, const uptr &b) {
+ return a < b;
+}
+
+// Dump the coverage on disk.
+void CovDump() {
+ BlockingMutexLock lock(&cov_data->mu);
+ InternalMmapVector<uptr> &v = cov_data->v;
+ InternalSort(&v, v.size(), CompareLess);
+ InternalMmapVector<u32> offsets(v.size());
+ const uptr *vb = v.data();
+ const uptr *ve = vb + v.size();
+ MemoryMappingLayout proc_maps(/*cache_enabled*/false);
+ uptr mb, me, off, prot;
+ InternalScopedBuffer<char> module(4096);
+ InternalScopedBuffer<char> path(4096 * 2);
+ for (int i = 0;
+ proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot);
+ i++) {
+ if ((prot & MemoryMappingLayout::kProtectionExecute) == 0)
+ continue;
+ if (vb >= ve) break;
+ if (mb <= *vb && *vb < me) {
+ offsets.clear();
+ const uptr *old_vb = vb;
+ CHECK_LE(off, *vb);
+ for (; vb < ve && *vb < me; vb++) {
+ uptr diff = *vb - (i ? mb : 0) + off;
+ CHECK_LE(diff, 0xffffffffU);
+ offsets.push_back(static_cast<u32>(diff));
+ }
+ char *module_name = StripModuleName(module.data());
+ internal_snprintf((char *)path.data(), path.size(), "%s.%zd.sancov",
+ module_name, internal_getpid());
+ InternalFree(module_name);
+ uptr fd = OpenFile(path.data(), true);
+ internal_write(fd, offsets.data(), offsets.size() * sizeof(u32));
+ internal_close(fd);
+ if (common_flags()->verbosity)
+ Report(" CovDump: %s: %zd PCs written\n", path.data(), vb - old_vb);
+ }
+ }
+}
+
+} // namespace __sanitizer
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(uptr pc) { CovAdd(pc); }
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
+} // extern "C"
diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h
index 189614c36..0f8367ff4 100644
--- a/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -110,6 +110,9 @@ extern "C" {
// the error message. This function can be overridden by the client.
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
void __sanitizer_report_error_summary(const char *error_summary);
+
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::uptr pc);
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
} // extern "C"
diff --git a/lib/tsan/rtl/tsan_symbolize.cc b/lib/tsan/rtl/tsan_symbolize.cc
index 24c747227..75acf1d84 100644
--- a/lib/tsan/rtl/tsan_symbolize.cc
+++ b/lib/tsan/rtl/tsan_symbolize.cc
@@ -42,18 +42,6 @@ ReportStack *NewReportStackEntry(uptr addr) {
return ent;
}
-// Strip module path to make output shorter.
-static char *StripModuleName(const char *module) {
- if (module == 0)
- return 0;
- const char *short_module_name = internal_strrchr(module, '/');
- if (short_module_name)
- short_module_name += 1;
- else
- short_module_name = module;
- return internal_strdup(short_module_name);
-}
-
static ReportStack *NewReportStackEntry(const AddressInfo &info) {
ReportStack *ent = NewReportStackEntry(info.address);
ent->module = StripModuleName(info.module);