13cab2bb3Spatrick //===-- stats.h -------------------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick 
93cab2bb3Spatrick #ifndef SCUDO_STATS_H_
103cab2bb3Spatrick #define SCUDO_STATS_H_
113cab2bb3Spatrick 
123cab2bb3Spatrick #include "atomic_helpers.h"
133cab2bb3Spatrick #include "list.h"
143cab2bb3Spatrick #include "mutex.h"
153cab2bb3Spatrick 
163cab2bb3Spatrick #include <string.h>
173cab2bb3Spatrick 
183cab2bb3Spatrick namespace scudo {
193cab2bb3Spatrick 
203cab2bb3Spatrick // Memory allocator statistics
213cab2bb3Spatrick enum StatType { StatAllocated, StatFree, StatMapped, StatCount };
223cab2bb3Spatrick 
233cab2bb3Spatrick typedef uptr StatCounters[StatCount];
243cab2bb3Spatrick 
253cab2bb3Spatrick // Per-thread stats, live in per-thread cache. We use atomics so that the
263cab2bb3Spatrick // numbers themselves are consistent. But we don't use atomic_{add|sub} or a
273cab2bb3Spatrick // lock, because those are expensive operations , and we only care for the stats
283cab2bb3Spatrick // to be "somewhat" correct: eg. if we call GlobalStats::get while a thread is
293cab2bb3Spatrick // LocalStats::add'ing, this is OK, we will still get a meaningful number.
303cab2bb3Spatrick class LocalStats {
313cab2bb3Spatrick public:
init()32*d89ec533Spatrick   void init() {
33*d89ec533Spatrick     for (uptr I = 0; I < StatCount; I++)
34*d89ec533Spatrick       DCHECK_EQ(get(static_cast<StatType>(I)), 0U);
35*d89ec533Spatrick   }
363cab2bb3Spatrick 
add(StatType I,uptr V)373cab2bb3Spatrick   void add(StatType I, uptr V) {
383cab2bb3Spatrick     V += atomic_load_relaxed(&StatsArray[I]);
393cab2bb3Spatrick     atomic_store_relaxed(&StatsArray[I], V);
403cab2bb3Spatrick   }
413cab2bb3Spatrick 
sub(StatType I,uptr V)423cab2bb3Spatrick   void sub(StatType I, uptr V) {
433cab2bb3Spatrick     V = atomic_load_relaxed(&StatsArray[I]) - V;
443cab2bb3Spatrick     atomic_store_relaxed(&StatsArray[I], V);
453cab2bb3Spatrick   }
463cab2bb3Spatrick 
set(StatType I,uptr V)473cab2bb3Spatrick   void set(StatType I, uptr V) { atomic_store_relaxed(&StatsArray[I], V); }
483cab2bb3Spatrick 
get(StatType I)493cab2bb3Spatrick   uptr get(StatType I) const { return atomic_load_relaxed(&StatsArray[I]); }
503cab2bb3Spatrick 
51*d89ec533Spatrick   LocalStats *Next = nullptr;
52*d89ec533Spatrick   LocalStats *Prev = nullptr;
533cab2bb3Spatrick 
543cab2bb3Spatrick private:
55*d89ec533Spatrick   atomic_uptr StatsArray[StatCount] = {};
563cab2bb3Spatrick };
573cab2bb3Spatrick 
583cab2bb3Spatrick // Global stats, used for aggregation and querying.
593cab2bb3Spatrick class GlobalStats : public LocalStats {
603cab2bb3Spatrick public:
init()61*d89ec533Spatrick   void init() { LocalStats::init(); }
623cab2bb3Spatrick 
link(LocalStats * S)633cab2bb3Spatrick   void link(LocalStats *S) {
643cab2bb3Spatrick     ScopedLock L(Mutex);
653cab2bb3Spatrick     StatsList.push_back(S);
663cab2bb3Spatrick   }
673cab2bb3Spatrick 
unlink(LocalStats * S)683cab2bb3Spatrick   void unlink(LocalStats *S) {
693cab2bb3Spatrick     ScopedLock L(Mutex);
703cab2bb3Spatrick     StatsList.remove(S);
713cab2bb3Spatrick     for (uptr I = 0; I < StatCount; I++)
723cab2bb3Spatrick       add(static_cast<StatType>(I), S->get(static_cast<StatType>(I)));
733cab2bb3Spatrick   }
743cab2bb3Spatrick 
get(uptr * S)753cab2bb3Spatrick   void get(uptr *S) const {
763cab2bb3Spatrick     ScopedLock L(Mutex);
773cab2bb3Spatrick     for (uptr I = 0; I < StatCount; I++)
783cab2bb3Spatrick       S[I] = LocalStats::get(static_cast<StatType>(I));
793cab2bb3Spatrick     for (const auto &Stats : StatsList) {
803cab2bb3Spatrick       for (uptr I = 0; I < StatCount; I++)
813cab2bb3Spatrick         S[I] += Stats.get(static_cast<StatType>(I));
823cab2bb3Spatrick     }
833cab2bb3Spatrick     // All stats must be non-negative.
843cab2bb3Spatrick     for (uptr I = 0; I < StatCount; I++)
853cab2bb3Spatrick       S[I] = static_cast<sptr>(S[I]) >= 0 ? S[I] : 0;
863cab2bb3Spatrick   }
873cab2bb3Spatrick 
lock()88*d89ec533Spatrick   void lock() { Mutex.lock(); }
unlock()89*d89ec533Spatrick   void unlock() { Mutex.unlock(); }
90*d89ec533Spatrick 
disable()91*d89ec533Spatrick   void disable() { lock(); }
enable()92*d89ec533Spatrick   void enable() { unlock(); }
933cab2bb3Spatrick 
943cab2bb3Spatrick private:
953cab2bb3Spatrick   mutable HybridMutex Mutex;
963cab2bb3Spatrick   DoublyLinkedList<LocalStats> StatsList;
973cab2bb3Spatrick };
983cab2bb3Spatrick 
993cab2bb3Spatrick } // namespace scudo
1003cab2bb3Spatrick 
1013cab2bb3Spatrick #endif // SCUDO_STATS_H_
102