| //===-- 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 |