Directory: | cvmfs/ |
---|---|
File: | cvmfs/statistics.h |
Date: | 2025-06-22 02:36:02 |
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 | 364807 | Counter() { atomic_init64(&counter_); } | |
30 | 4413765 | void Inc() { atomic_inc64(&counter_); } | |
31 | 1739 | void Dec() { atomic_dec64(&counter_); } | |
32 | 1958 | int64_t Get() { return atomic_read64(&counter_); } | |
33 | 19489 | void Set(const int64_t val) { atomic_write64(&counter_, val); } | |
34 | 52915 | 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 | 1659 | inline void Dec(class Counter *counter) { counter->Dec(); } | |
50 | 4413725 | inline void Inc(class Counter *counter) { counter->Inc(); } | |
51 | 52875 | inline int64_t Xadd(class Counter *counter, const int64_t delta) { | |
52 | 52875 | 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 | 364727 | explicit CounterInfo(const std::string &desc) : desc(desc) { | |
83 | 364727 | atomic_init32(&refcnt); | |
84 | 364727 | atomic_inc32(&refcnt); | |
85 | 364727 | } | |
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 | 13956 | StatisticsTemplate(const std::string &name_major, Statistics *statistics) | |
103 | 13956 | : name_major_(name_major), statistics_(statistics) { } | |
104 | 5882 | StatisticsTemplate(const std::string &name_sub, | |
105 | const StatisticsTemplate &statistics) | ||
106 |
1/2✓ Branch 2 taken 5882 times.
✗ Branch 3 not taken.
|
5882 | : name_major_(statistics.name_major_ + "." + name_sub) |
107 | 5882 | , statistics_(statistics.statistics_) { } | |
108 | |||
109 | 178404 | Counter *RegisterTemplated(const std::string &name_minor, | |
110 | const std::string &desc) { | ||
111 |
2/4✓ Branch 2 taken 178404 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 178404 times.
✗ Branch 6 not taken.
|
178404 | return statistics_->Register(name_major_ + "." + name_minor, desc); |
112 | } | ||
113 | |||
114 | 490 | Counter *RegisterOrLookupTemplated(const std::string &name_minor, | |
115 | const std::string &desc) { | ||
116 |
2/4✓ Branch 2 taken 490 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 490 times.
✗ Branch 6 not taken.
|
490 | Counter *result = statistics_->Lookup(name_major_ + "." + name_minor); |
117 |
1/2✓ Branch 0 taken 490 times.
✗ Branch 1 not taken.
|
490 | if (result == NULL) { |
118 | 490 | 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 | 98 | explicit FsCounters(StatisticsTemplate statistics) { | |
145 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | n_files_added = statistics.RegisterTemplated("n_files_added", |
146 | "Number of files added"); | ||
147 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | n_files_removed = statistics.RegisterTemplated("n_files_removed", |
148 | "Number of files removed"); | ||
149 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | n_files_changed = statistics.RegisterTemplated("n_files_changed", |
150 | "Number of files changed"); | ||
151 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | n_directories_added = statistics.RegisterTemplated( |
152 | "n_directories_added", "Number of directories added"); | ||
153 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | n_directories_removed = statistics.RegisterTemplated( |
154 | "n_directories_removed", "Number of directories removed"); | ||
155 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | n_directories_changed = statistics.RegisterTemplated( |
156 | "n_directories_changed", "Number of directories changed"); | ||
157 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | n_symlinks_added = statistics.RegisterTemplated("n_symlinks_added", |
158 | "Number of symlinks added"); | ||
159 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | n_symlinks_removed = statistics.RegisterTemplated( |
160 | "n_symlinks_removed", "Number of symlinks removed"); | ||
161 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | n_symlinks_changed = statistics.RegisterTemplated( |
162 | "n_symlinks_changed", "Number of symlinks changed"); | ||
163 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | sz_added_bytes = statistics.RegisterTemplated("sz_added_bytes", |
164 | "Number of bytes added"); | ||
165 |
3/6✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 98 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 98 times.
✗ Branch 10 not taken.
|
98 | sz_removed_bytes = statistics.RegisterTemplated("sz_removed_bytes", |
166 | "Number of bytes removed"); | ||
167 | 98 | } | |
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 | 424 | 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 |