1 //===-- stats.h -------------------------------------------------*- C++ -*-===//
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 #ifndef SCUDO_STATS_H_
10 #define SCUDO_STATS_H_
11 
12 #include "atomic_helpers.h"
13 #include "list.h"
14 #include "mutex.h"
15 
16 #include <string.h>
17 
18 namespace scudo {
19 
20 // Memory allocator statistics
21 enum StatType { StatAllocated, StatFree, StatMapped, StatCount };
22 
23 typedef uptr StatCounters[StatCount];
24 
25 // Per-thread stats, live in per-thread cache. We use atomics so that the
26 // numbers themselves are consistent. But we don't use atomic_{add|sub} or a
27 // lock, because those are expensive operations , and we only care for the stats
28 // to be "somewhat" correct: eg. if we call GlobalStats::get while a thread is
29 // LocalStats::add'ing, this is OK, we will still get a meaningful number.
30 class LocalStats {
31 public:
initLinkerInitialized()32   void initLinkerInitialized() {}
init()33   void init() { memset(this, 0, sizeof(*this)); }
34 
add(StatType I,uptr V)35   void add(StatType I, uptr V) {
36     V += atomic_load_relaxed(&StatsArray[I]);
37     atomic_store_relaxed(&StatsArray[I], V);
38   }
39 
sub(StatType I,uptr V)40   void sub(StatType I, uptr V) {
41     V = atomic_load_relaxed(&StatsArray[I]) - V;
42     atomic_store_relaxed(&StatsArray[I], V);
43   }
44 
set(StatType I,uptr V)45   void set(StatType I, uptr V) { atomic_store_relaxed(&StatsArray[I], V); }
46 
get(StatType I)47   uptr get(StatType I) const { return atomic_load_relaxed(&StatsArray[I]); }
48 
49   LocalStats *Next = nullptr;
50   LocalStats *Prev = nullptr;
51 
52 private:
53   atomic_uptr StatsArray[StatCount] = {};
54 };
55 
56 // Global stats, used for aggregation and querying.
57 class GlobalStats : public LocalStats {
58 public:
initLinkerInitialized()59   void initLinkerInitialized() {}
init()60   void init() {
61     LocalStats::init();
62     Mutex.init();
63     StatsList = {};
64     initLinkerInitialized();
65   }
66 
link(LocalStats * S)67   void link(LocalStats *S) {
68     ScopedLock L(Mutex);
69     StatsList.push_back(S);
70   }
71 
unlink(LocalStats * S)72   void unlink(LocalStats *S) {
73     ScopedLock L(Mutex);
74     StatsList.remove(S);
75     for (uptr I = 0; I < StatCount; I++)
76       add(static_cast<StatType>(I), S->get(static_cast<StatType>(I)));
77   }
78 
get(uptr * S)79   void get(uptr *S) const {
80     ScopedLock L(Mutex);
81     for (uptr I = 0; I < StatCount; I++)
82       S[I] = LocalStats::get(static_cast<StatType>(I));
83     for (const auto &Stats : StatsList) {
84       for (uptr I = 0; I < StatCount; I++)
85         S[I] += Stats.get(static_cast<StatType>(I));
86     }
87     // All stats must be non-negative.
88     for (uptr I = 0; I < StatCount; I++)
89       S[I] = static_cast<sptr>(S[I]) >= 0 ? S[I] : 0;
90   }
91 
lock()92   void lock() { Mutex.lock(); }
unlock()93   void unlock() { Mutex.unlock(); }
94 
disable()95   void disable() { lock(); }
enable()96   void enable() { unlock(); }
97 
98 private:
99   mutable HybridMutex Mutex;
100   DoublyLinkedList<LocalStats> StatsList;
101 };
102 
103 } // namespace scudo
104 
105 #endif // SCUDO_STATS_H_
106