summaryrefslogtreecommitdiff
path: root/lib/stats/stats.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stats/stats.cpp')
-rw-r--r--lib/stats/stats.cpp136
1 files changed, 136 insertions, 0 deletions
diff --git a/lib/stats/stats.cpp b/lib/stats/stats.cpp
new file mode 100644
index 000000000000..da254fdb5a52
--- /dev/null
+++ b/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