diff options
Diffstat (limited to 'compiler-rt/lib/stats')
-rw-r--r-- | compiler-rt/lib/stats/stats.cpp | 136 | ||||
-rw-r--r-- | compiler-rt/lib/stats/stats.h | 42 | ||||
-rw-r--r-- | compiler-rt/lib/stats/stats_client.cpp | 83 |
3 files changed, 261 insertions, 0 deletions
diff --git a/compiler-rt/lib/stats/stats.cpp b/compiler-rt/lib/stats/stats.cpp new file mode 100644 index 000000000000..da254fdb5a52 --- /dev/null +++ b/compiler-rt/lib/stats/stats.cpp @@ -0,0 +1,136 @@ +//===-- stats.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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_file.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) { + InternalMmapVector<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); + const 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/compiler-rt/lib/stats/stats.h b/compiler-rt/lib/stats/stats.h new file mode 100644 index 000000000000..4b7514d61082 --- /dev/null +++ b/compiler-rt/lib/stats/stats.h @@ -0,0 +1,42 @@ +//===-- stats.h -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/compiler-rt/lib/stats/stats_client.cpp b/compiler-rt/lib/stats/stats_client.cpp new file mode 100644 index 000000000000..52eab8eea079 --- /dev/null +++ b/compiler-rt/lib/stats/stats_client.cpp @@ -0,0 +1,83 @@ +//===-- stats_client.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 +#define WIN32_LEAN_AND_MEAN +#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(); +} |