| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/statistics.h |
| Date: | 2025-11-02 02:35:35 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 39 | 40 | 97.5% |
| Branches: | 39 | 78 | 50.0% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * This file is part of the CernVM File System. | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef CVMFS_STATISTICS_H_ | ||
| 6 | #define CVMFS_STATISTICS_H_ | ||
| 7 | |||
| 8 | #include <pthread.h> | ||
| 9 | #include <stdint.h> | ||
| 10 | |||
| 11 | #include <map> | ||
| 12 | #include <string> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include "util/atomic.h" | ||
| 16 | |||
| 17 | #ifdef CVMFS_NAMESPACE_GUARD | ||
| 18 | namespace CVMFS_NAMESPACE_GUARD { | ||
| 19 | #endif | ||
| 20 | |||
| 21 | |||
| 22 | namespace perf { | ||
| 23 | |||
| 24 | /** | ||
| 25 | * A wrapper around an atomic 64bit signed integer. | ||
| 26 | */ | ||
| 27 | class Counter { | ||
| 28 | public: | ||
| 29 | 278129 | Counter() { atomic_init64(&counter_); } | |
| 30 | 3893849 | void Inc() { atomic_inc64(&counter_); } | |
| 31 | 1194 | void Dec() { atomic_dec64(&counter_); } | |
| 32 | 2411 | int64_t Get() { return atomic_read64(&counter_); } | |
| 33 | 16272 | void Set(const int64_t val) { atomic_write64(&counter_, val); } | |
| 34 | 42641 | int64_t Xadd(const int64_t delta) { return atomic_xadd64(&counter_, delta); } | |
| 35 | |||
| 36 | std::string Print(); | ||
| 37 | std::string PrintK(); | ||
| 38 | std::string PrintKi(); | ||
| 39 | std::string PrintM(); | ||
| 40 | std::string PrintMi(); | ||
| 41 | std::string PrintRatio(Counter divider); | ||
| 42 | std::string ToString(); | ||
| 43 | |||
| 44 | private: | ||
| 45 | atomic_int64 counter_; | ||
| 46 | }; | ||
| 47 | |||
| 48 | // perf::Func(Counter) is more clear to read in the code | ||
| 49 | 1140 | inline void Dec(class Counter *counter) { counter->Dec(); } | |
| 50 | 3893822 | inline void Inc(class Counter *counter) { counter->Inc(); } | |
| 51 | 42614 | inline int64_t Xadd(class Counter *counter, const int64_t delta) { | |
| 52 | 42614 | return counter->Xadd(delta); | |
| 53 | } | ||
| 54 | |||
| 55 | |||
| 56 | /** | ||
| 57 | * A collection of Counter objects with a name and a description. Counters in | ||
| 58 | * a Statistics class have a name and a description. Thread-safe. | ||
| 59 | */ | ||
| 60 | class Statistics { | ||
| 61 | public: | ||
| 62 | enum PrintOptions { | ||
| 63 | kPrintSimple = 0, | ||
| 64 | kPrintHeader | ||
| 65 | }; | ||
| 66 | |||
| 67 | Statistics(); | ||
| 68 | ~Statistics(); | ||
| 69 | Statistics *Fork(); | ||
| 70 | Counter *Register(const std::string &name, const std::string &desc); | ||
| 71 | Counter *Lookup(const std::string &name) const; | ||
| 72 | std::string LookupDesc(const std::string &name); | ||
| 73 | std::string PrintList(const PrintOptions print_options); | ||
| 74 | std::string PrintJSON(); | ||
| 75 | void SnapshotCounters(std::map<std::string, int64_t> *counters, | ||
| 76 | uint64_t *timestamp_ns); | ||
| 77 | |||
| 78 | private: | ||
| 79 | Statistics(const Statistics &other); | ||
| 80 | Statistics &operator=(const Statistics &other); | ||
| 81 | struct CounterInfo { | ||
| 82 | 278075 | explicit CounterInfo(const std::string &desc) : desc(desc) { | |
| 83 | 278075 | atomic_init32(&refcnt); | |
| 84 | 278075 | atomic_inc32(&refcnt); | |
| 85 | 278075 | } | |
| 86 | atomic_int32 refcnt; | ||
| 87 | Counter counter; | ||
| 88 | std::string desc; | ||
| 89 | }; | ||
| 90 | std::map<std::string, CounterInfo *> counters_; | ||
| 91 | mutable pthread_mutex_t *lock_; | ||
| 92 | }; | ||
| 93 | |||
| 94 | |||
| 95 | /** | ||
| 96 | * Allows multiple instance of the same class to use the same Statistics | ||
| 97 | * instance. The "name_major" part is used to construct counter names in the | ||
| 98 | * form name_major.<provided name> | ||
| 99 | */ | ||
| 100 | class StatisticsTemplate { | ||
| 101 | public: | ||
| 102 | 12045 | StatisticsTemplate(const std::string &name_major, Statistics *statistics) | |
| 103 | 12045 | : name_major_(name_major), statistics_(statistics) { } | |
| 104 | 4181 | StatisticsTemplate(const std::string &name_sub, | |
| 105 | const StatisticsTemplate &statistics) | ||
| 106 |
1/2✓ Branch 2 taken 4181 times.
✗ Branch 3 not taken.
|
4181 | : name_major_(statistics.name_major_ + "." + name_sub) |
| 107 | 4181 | , statistics_(statistics.statistics_) { } | |
| 108 | |||
| 109 | 138512 | Counter *RegisterTemplated(const std::string &name_minor, | |
| 110 | const std::string &desc) { | ||
| 111 |
2/4✓ Branch 2 taken 138512 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 138512 times.
✗ Branch 6 not taken.
|
138512 | return statistics_->Register(name_major_ + "." + name_minor, desc); |
| 112 | } | ||
| 113 | |||
| 114 | 280 | Counter *RegisterOrLookupTemplated(const std::string &name_minor, | |
| 115 | const std::string &desc) { | ||
| 116 |
2/4✓ Branch 2 taken 280 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 280 times.
✗ Branch 6 not taken.
|
280 | Counter *result = statistics_->Lookup(name_major_ + "." + name_minor); |
| 117 |
1/2✓ Branch 0 taken 280 times.
✗ Branch 1 not taken.
|
280 | if (result == NULL) { |
| 118 | 280 | return RegisterTemplated(name_minor, desc); | |
| 119 | } | ||
| 120 | ✗ | return result; | |
| 121 | } | ||
| 122 | |||
| 123 | Statistics *statistics() { return statistics_; } | ||
| 124 | |||
| 125 | private: | ||
| 126 | std::string name_major_; | ||
| 127 | Statistics *statistics_; | ||
| 128 | }; | ||
| 129 | |||
| 130 | |||
| 131 | struct FsCounters { | ||
| 132 | perf::Counter *n_files_added; | ||
| 133 | perf::Counter *n_files_removed; | ||
| 134 | perf::Counter *n_files_changed; | ||
| 135 | perf::Counter *n_directories_added; | ||
| 136 | perf::Counter *n_directories_removed; | ||
| 137 | perf::Counter *n_directories_changed; | ||
| 138 | perf::Counter *n_symlinks_added; | ||
| 139 | perf::Counter *n_symlinks_removed; | ||
| 140 | perf::Counter *n_symlinks_changed; | ||
| 141 | perf::Counter *sz_added_bytes; | ||
| 142 | perf::Counter *sz_removed_bytes; | ||
| 143 | |||
| 144 | 56 | explicit FsCounters(StatisticsTemplate statistics) { | |
| 145 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | n_files_added = statistics.RegisterTemplated("n_files_added", |
| 146 | "Number of files added"); | ||
| 147 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | n_files_removed = statistics.RegisterTemplated("n_files_removed", |
| 148 | "Number of files removed"); | ||
| 149 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | n_files_changed = statistics.RegisterTemplated("n_files_changed", |
| 150 | "Number of files changed"); | ||
| 151 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | n_directories_added = statistics.RegisterTemplated( |
| 152 | "n_directories_added", "Number of directories added"); | ||
| 153 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | n_directories_removed = statistics.RegisterTemplated( |
| 154 | "n_directories_removed", "Number of directories removed"); | ||
| 155 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | n_directories_changed = statistics.RegisterTemplated( |
| 156 | "n_directories_changed", "Number of directories changed"); | ||
| 157 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | n_symlinks_added = statistics.RegisterTemplated("n_symlinks_added", |
| 158 | "Number of symlinks added"); | ||
| 159 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | n_symlinks_removed = statistics.RegisterTemplated( |
| 160 | "n_symlinks_removed", "Number of symlinks removed"); | ||
| 161 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | n_symlinks_changed = statistics.RegisterTemplated( |
| 162 | "n_symlinks_changed", "Number of symlinks changed"); | ||
| 163 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | sz_added_bytes = statistics.RegisterTemplated("sz_added_bytes", |
| 164 | "Number of bytes added"); | ||
| 165 |
3/6✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 56 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 56 times.
✗ Branch 10 not taken.
|
56 | sz_removed_bytes = statistics.RegisterTemplated("sz_removed_bytes", |
| 166 | "Number of bytes removed"); | ||
| 167 | 56 | } | |
| 168 | }; // FsCounters | ||
| 169 | |||
| 170 | |||
| 171 | /** | ||
| 172 | * Keeps track of events over time. Can be used to query the number of events | ||
| 173 | * between now and a point in time in the past. The time range should be | ||
| 174 | * smaller than capacity_s seconds. Uses a monotonic clock. Not thread-safe. | ||
| 175 | */ | ||
| 176 | class Recorder { | ||
| 177 | public: | ||
| 178 | Recorder(uint32_t resolution_s, uint32_t capacity_s); | ||
| 179 | |||
| 180 | void Tick(); | ||
| 181 | void TickAt(uint64_t timestamp); | ||
| 182 | uint64_t GetNoTicks(uint32_t retrospect_s) const; | ||
| 183 | |||
| 184 | 388 | uint32_t capacity_s() const { return capacity_s_; } | |
| 185 | uint32_t resolution_s() const { return resolution_s_; } | ||
| 186 | |||
| 187 | private: | ||
| 188 | /** | ||
| 189 | * Records number of ticks (events) per unit of resolution. A ring buffer. | ||
| 190 | * Entries older than capacity_s get overwritten by new events. | ||
| 191 | */ | ||
| 192 | std::vector<uint32_t> bins_; | ||
| 193 | |||
| 194 | /** | ||
| 195 | * When the most recent tick occurred. | ||
| 196 | */ | ||
| 197 | uint64_t last_timestamp_; | ||
| 198 | |||
| 199 | /** | ||
| 200 | * Time window in seconds that the recorder is supposed to remember. | ||
| 201 | */ | ||
| 202 | uint32_t capacity_s_; | ||
| 203 | |||
| 204 | /** | ||
| 205 | * Size of the bins for the tick counters. | ||
| 206 | */ | ||
| 207 | uint32_t resolution_s_; | ||
| 208 | |||
| 209 | /** | ||
| 210 | * Shorthand for bins_.size(), constant during lifetime of the recorder. | ||
| 211 | */ | ||
| 212 | unsigned no_bins_; | ||
| 213 | }; | ||
| 214 | |||
| 215 | |||
| 216 | /** | ||
| 217 | * Writes to multiple recorders. Recorders with coarsed-grained resolution and | ||
| 218 | * a large capacity are combined with precise recorders with shorter capacity. | ||
| 219 | * Preferred recorders should be added first because GetNoTicks will use the | ||
| 220 | * first recorder with a capacity >= retrospect_s (or the last recorder). | ||
| 221 | */ | ||
| 222 | class MultiRecorder { | ||
| 223 | public: | ||
| 224 | void Tick(); | ||
| 225 | void TickAt(uint64_t timestamp); | ||
| 226 | uint64_t GetNoTicks(uint32_t retrospect_s) const; | ||
| 227 | |||
| 228 | void AddRecorder(uint32_t resolution_s, uint32_t capacity_s); | ||
| 229 | |||
| 230 | private: | ||
| 231 | std::vector<Recorder> recorders_; | ||
| 232 | }; | ||
| 233 | |||
| 234 | } // namespace perf | ||
| 235 | |||
| 236 | #ifdef CVMFS_NAMESPACE_GUARD | ||
| 237 | } // namespace CVMFS_NAMESPACE_GUARD | ||
| 238 | #endif | ||
| 239 | |||
| 240 | #endif // CVMFS_STATISTICS_H_ | ||
| 241 |