diff options
author | Peter Collingbourne <peter@pcc.me.uk> | 2016-01-16 00:31:29 +0000 |
---|---|---|
committer | Peter Collingbourne <peter@pcc.me.uk> | 2016-01-16 00:31:29 +0000 |
commit | 4198b77a82619d06267267bc8944a47ef1db9fbe (patch) | |
tree | e57779d0420e3e635ab7c2027e8fd3faffd5df95 /lib/stats | |
parent | 7ca8db56e3d172876b58aa095f0624ca51d2b4c1 (diff) | |
download | compiler-rt-4198b77a82619d06267267bc8944a47ef1db9fbe.tar.gz |
Introduce stats and stats_client libraries.
This is part of a new statistics gathering feature for the sanitizers.
See clang/docs/SanitizerStats.rst for further info and docs.
Differential Revision: http://reviews.llvm.org/D16176
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@257972 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/stats')
-rw-r--r-- | lib/stats/CMakeLists.txt | 27 | ||||
-rw-r--r-- | lib/stats/stats.cc | 136 | ||||
-rw-r--r-- | lib/stats/stats.h | 43 | ||||
-rw-r--r-- | lib/stats/stats_client.cc | 83 |
4 files changed, 289 insertions, 0 deletions
diff --git a/lib/stats/CMakeLists.txt b/lib/stats/CMakeLists.txt new file mode 100644 index 000000000..086ee090a --- /dev/null +++ b/lib/stats/CMakeLists.txt @@ -0,0 +1,27 @@ +include_directories(..) + +add_custom_target(stats) + +if(APPLE) + set(STATS_LIB_FLAVOR SHARED) +else() + set(STATS_LIB_FLAVOR STATIC) +endif() + +add_compiler_rt_runtime(clang_rt.stats + ${STATS_LIB_FLAVOR} + ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH} + OS ${SANITIZER_COMMON_SUPPORTED_OS} + SOURCES stats.cc + OBJECT_LIBS RTSanitizerCommon + RTSanitizerCommonLibc + CFLAGS ${SANITIZER_COMMON_CFLAGS} + PARENT_TARGET stats) + +add_compiler_rt_runtime(clang_rt.stats_client + STATIC + ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH} + OS ${SANITIZER_COMMON_SUPPORTED_OS} + SOURCES stats_client.cc + CFLAGS ${SANITIZER_COMMON_CFLAGS} + PARENT_TARGET stats) diff --git a/lib/stats/stats.cc b/lib/stats/stats.cc new file mode 100644 index 000000000..6b937f1c8 --- /dev/null +++ b/lib/stats/stats.cc @@ -0,0 +1,136 @@ +//===-- stats.cc ----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Sanitizer statistics gathering. Manages statistics for a process and is +// responsible for writing the report file. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#if SANITIZER_POSIX +#include "sanitizer_common/sanitizer_posix.h" +#endif +#include "sanitizer_common/sanitizer_symbolizer.h" +#include "stats/stats.h" +#if SANITIZER_POSIX +#include <signal.h> +#endif + +using namespace __sanitizer; + +namespace { + +InternalMmapVectorNoCtor<StatModule **> modules; +StaticSpinMutex modules_mutex; + +fd_t stats_fd; + +void WriteLE(fd_t fd, uptr val) { + char chars[sizeof(uptr)]; + for (unsigned i = 0; i != sizeof(uptr); ++i) { + chars[i] = val >> (i * 8); + } + WriteToFile(fd, chars, sizeof(uptr)); +} + +void OpenStatsFile(const char *path_env) { + InternalScopedBuffer<char> path(kMaxPathLength); + SubstituteForFlagValue(path_env, path.data(), kMaxPathLength); + + error_t err; + stats_fd = OpenFile(path.data(), WrOnly, &err); + if (stats_fd == kInvalidFd) { + Report("stats: failed to open %s for writing (reason: %d)\n", path.data(), + err); + return; + } + char sizeof_uptr = sizeof(uptr); + WriteToFile(stats_fd, &sizeof_uptr, 1); +} + +void WriteModuleReport(StatModule **smodp) { + CHECK(smodp); + const char *path_env = GetEnv("SANITIZER_STATS_PATH"); + if (!path_env || stats_fd == kInvalidFd) + return; + if (!stats_fd) + OpenStatsFile(path_env); + LoadedModule *mod = Symbolizer::GetOrInit()->FindModuleForAddress( + reinterpret_cast<uptr>(smodp)); + WriteToFile(stats_fd, mod->full_name(), + internal_strlen(mod->full_name()) + 1); + for (StatModule *smod = *smodp; smod; smod = smod->next) { + for (u32 i = 0; i != smod->size; ++i) { + StatInfo *s = &smod->infos[i]; + if (!s->addr) + continue; + WriteLE(stats_fd, s->addr - mod->base_address()); + WriteLE(stats_fd, s->data); + } + } + WriteLE(stats_fd, 0); + WriteLE(stats_fd, 0); +} + +} // namespace + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +unsigned __sanitizer_stats_register(StatModule **mod) { + SpinMutexLock l(&modules_mutex); + modules.push_back(mod); + return modules.size() - 1; +} + +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_stats_unregister(unsigned index) { + SpinMutexLock l(&modules_mutex); + WriteModuleReport(modules[index]); + modules[index] = 0; +} + +namespace { + +void WriteFullReport() { + SpinMutexLock l(&modules_mutex); + for (StatModule **mod : modules) { + if (!mod) + continue; + WriteModuleReport(mod); + } + if (stats_fd != 0 && stats_fd != kInvalidFd) { + CloseFile(stats_fd); + stats_fd = kInvalidFd; + } +} + +#if SANITIZER_POSIX +void USR2Handler(int sig) { + WriteFullReport(); +} +#endif + +struct WriteReportOnExitOrSignal { + WriteReportOnExitOrSignal() { +#if SANITIZER_POSIX + struct sigaction sigact; + internal_memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = USR2Handler; + internal_sigaction(SIGUSR2, &sigact, nullptr); +#endif + } + + ~WriteReportOnExitOrSignal() { + WriteFullReport(); + } +} wr; + +} // namespace diff --git a/lib/stats/stats.h b/lib/stats/stats.h new file mode 100644 index 000000000..619470677 --- /dev/null +++ b/lib/stats/stats.h @@ -0,0 +1,43 @@ +//===-- stats.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Data definitions for sanitizer statistics gathering. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_STATS_STATS_H +#define SANITIZER_STATS_STATS_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +namespace __sanitizer { + +// Number of bits in data that are used for the sanitizer kind. Needs to match +// llvm::kSanitizerStatKindBits in +// llvm/include/llvm/Transforms/Utils/SanitizerStats.h +enum { kKindBits = 3 }; + +struct StatInfo { + uptr addr; + uptr data; +}; + +struct StatModule { + StatModule *next; + u32 size; + StatInfo infos[1]; +}; + +inline uptr CountFromData(uptr data) { + return data & ((1ull << (sizeof(uptr) * 8 - kKindBits)) - 1); +} + +} + +#endif diff --git a/lib/stats/stats_client.cc b/lib/stats/stats_client.cc new file mode 100644 index 000000000..fa4b2d909 --- /dev/null +++ b/lib/stats/stats_client.cc @@ -0,0 +1,83 @@ +//===-- stats_client.cc ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Sanitizer statistics gathering. Manages statistics for a module (executable +// or DSO) and registers statistics with the process. +// +// This is linked into each individual modle and cannot directly use functions +// declared in sanitizer_common. +// +//===----------------------------------------------------------------------===// + +#ifdef _WIN32 +#include <windows.h> +#else +#include <dlfcn.h> +#endif +#include <stdint.h> +#include <stdio.h> + +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "stats/stats.h" + +using namespace __sanitizer; + +namespace { + +void *LookupSymbolFromMain(const char *name) { +#ifdef _WIN32 + return reinterpret_cast<void *>(GetProcAddress(GetModuleHandle(0), name)); +#else + return dlsym(RTLD_DEFAULT, name); +#endif +} + +StatModule *list; + +struct RegisterSanStats { + unsigned module_id; + + RegisterSanStats() { + typedef unsigned (*reg_func_t)(StatModule **); + reg_func_t reg_func = reinterpret_cast<reg_func_t>( + LookupSymbolFromMain("__sanitizer_stats_register")); + if (reg_func) + module_id = reg_func(&list); + } + + ~RegisterSanStats() { + typedef void (*unreg_func_t)(unsigned); + unreg_func_t unreg_func = reinterpret_cast<unreg_func_t>( + LookupSymbolFromMain("__sanitizer_stats_unregister")); + if (unreg_func) + unreg_func(module_id); + } +} reg; + +} + +extern "C" void __sanitizer_stat_init(StatModule *mod) { + mod->next = list; + list = mod; +} + +extern "C" void __sanitizer_stat_report(StatInfo *s) { + s->addr = GET_CALLER_PC(); +#if defined(_WIN64) && !defined(__clang__) + uptr old_data = InterlockedIncrement64(reinterpret_cast<LONG64 *>(&s->data)); +#elif defined(_WIN32) && !defined(__clang__) + uptr old_data = InterlockedIncrement(&s->data); +#else + uptr old_data = __sync_fetch_and_add(&s->data, 1); +#endif + + // Overflow check. + if (CountFromData(old_data + 1) == 0) + Trap(); +} |