1//===-- stats.cpp ---------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Sanitizer statistics gathering. Manages statistics for a process and is
10// responsible for writing the report file.
11//
12//===----------------------------------------------------------------------===//
13
14#include "sanitizer_common/sanitizer_common.h"
15#include "sanitizer_common/sanitizer_file.h"
16#include "sanitizer_common/sanitizer_internal_defs.h"
17#if SANITIZER_POSIX
18#include "sanitizer_common/sanitizer_posix.h"
19#endif
20#include "sanitizer_common/sanitizer_symbolizer.h"
21#include "stats/stats.h"
22#if SANITIZER_POSIX
23#include <signal.h>
24#endif
25
26using namespace __sanitizer;
27
28namespace {
29
30InternalMmapVectorNoCtor<StatModule **> modules;
31StaticSpinMutex modules_mutex;
32
33fd_t stats_fd;
34
35void WriteLE(fd_t fd, uptr val) {
36  char chars[sizeof(uptr)];
37  for (unsigned i = 0; i != sizeof(uptr); ++i) {
38    chars[i] = val >> (i * 8);
39  }
40  WriteToFile(fd, chars, sizeof(uptr));
41}
42
43void OpenStatsFile(const char *path_env) {
44  InternalMmapVector<char> path(kMaxPathLength);
45  SubstituteForFlagValue(path_env, path.data(), kMaxPathLength);
46
47  error_t err;
48  stats_fd = OpenFile(path.data(), WrOnly, &err);
49  if (stats_fd == kInvalidFd) {
50    Report("stats: failed to open %s for writing (reason: %d)\n", path.data(),
51           err);
52    return;
53  }
54  char sizeof_uptr = sizeof(uptr);
55  WriteToFile(stats_fd, &sizeof_uptr, 1);
56}
57
58void WriteModuleReport(StatModule **smodp) {
59  CHECK(smodp);
60  const char *path_env = GetEnv("SANITIZER_STATS_PATH");
61  if (!path_env || stats_fd == kInvalidFd)
62    return;
63  if (!stats_fd)
64    OpenStatsFile(path_env);
65  const LoadedModule *mod = Symbolizer::GetOrInit()->FindModuleForAddress(
66      reinterpret_cast<uptr>(smodp));
67  WriteToFile(stats_fd, mod->full_name(),
68              internal_strlen(mod->full_name()) + 1);
69  for (StatModule *smod = *smodp; smod; smod = smod->next) {
70    for (u32 i = 0; i != smod->size; ++i) {
71      StatInfo *s = &smod->infos[i];
72      if (!s->addr)
73        continue;
74      WriteLE(stats_fd, s->addr - mod->base_address());
75      WriteLE(stats_fd, s->data);
76    }
77  }
78  WriteLE(stats_fd, 0);
79  WriteLE(stats_fd, 0);
80}
81
82} // namespace
83
84extern "C"
85SANITIZER_INTERFACE_ATTRIBUTE
86unsigned __sanitizer_stats_register(StatModule **mod) {
87  SpinMutexLock l(&modules_mutex);
88  modules.push_back(mod);
89  return modules.size() - 1;
90}
91
92extern "C"
93SANITIZER_INTERFACE_ATTRIBUTE
94void __sanitizer_stats_unregister(unsigned index) {
95  SpinMutexLock l(&modules_mutex);
96  WriteModuleReport(modules[index]);
97  modules[index] = 0;
98}
99
100namespace {
101
102void WriteFullReport() {
103  SpinMutexLock l(&modules_mutex);
104  for (StatModule **mod : modules) {
105    if (!mod)
106      continue;
107    WriteModuleReport(mod);
108  }
109  if (stats_fd != 0 && stats_fd != kInvalidFd) {
110    CloseFile(stats_fd);
111    stats_fd = kInvalidFd;
112  }
113}
114
115#if SANITIZER_POSIX
116void USR2Handler(int sig) {
117  WriteFullReport();
118}
119#endif
120
121struct WriteReportOnExitOrSignal {
122  WriteReportOnExitOrSignal() {
123#if SANITIZER_POSIX
124    struct sigaction sigact;
125    internal_memset(&sigact, 0, sizeof(sigact));
126    sigact.sa_handler = USR2Handler;
127    internal_sigaction(SIGUSR2, &sigact, nullptr);
128#endif
129  }
130
131  ~WriteReportOnExitOrSignal() {
132    WriteFullReport();
133  }
134} wr;
135
136} // namespace
137