Directory: | cvmfs/ |
---|---|
File: | cvmfs/statistics.h |
Date: | 2025-02-09 02:34:19 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 43 | 44 | 97.7% |
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 | 8481 | Counter() { atomic_init64(&counter_); } | |
30 | 98023 | void Inc() { atomic_inc64(&counter_); } | |
31 | 39 | void Dec() { atomic_dec64(&counter_); } | |
32 | 69 | int64_t Get() { return atomic_read64(&counter_); } | |
33 | 461 | void Set(const int64_t val) { atomic_write64(&counter_, val); } | |
34 | 1273 | 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 | 37 | inline void Dec(class Counter *counter) { counter->Dec(); } | |
50 | 98022 | inline void Inc(class Counter *counter) { counter->Inc(); } | |
51 | 1272 | inline int64_t Xadd(class Counter *counter, const int64_t delta) { | |
52 | 1272 | 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 | 8479 | explicit CounterInfo(const std::string &desc) : desc(desc) { | |
83 | 8479 | atomic_init32(&refcnt); | |
84 | 8479 | atomic_inc32(&refcnt); | |
85 | 8479 | } | |
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 | 348 | StatisticsTemplate(const std::string &name_major, Statistics *statistics) | |
103 | 348 | : name_major_(name_major), statistics_(statistics) | |
104 | 348 | { } | |
105 | 122 | StatisticsTemplate(const std::string &name_sub, | |
106 | const StatisticsTemplate &statistics) | ||
107 |
1/2✓ Branch 2 taken 122 times.
✗ Branch 3 not taken.
|
122 | : name_major_(statistics.name_major_ + "." + name_sub) |
108 | 122 | , statistics_(statistics.statistics_) | |
109 | 122 | { } | |
110 | |||
111 | 4039 | Counter *RegisterTemplated(const std::string &name_minor, | |
112 | const std::string &desc) | ||
113 | { | ||
114 |
2/4✓ Branch 2 taken 4039 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4039 times.
✗ Branch 6 not taken.
|
4039 | return statistics_->Register(name_major_ + "." + name_minor, desc); |
115 | } | ||
116 | |||
117 | 10 | Counter *RegisterOrLookupTemplated(const std::string &name_minor, | |
118 | const std::string &desc) | ||
119 | { | ||
120 |
2/4✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
|
10 | Counter *result = statistics_->Lookup(name_major_ + "." + name_minor); |
121 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (result == NULL) { |
122 | 10 | return RegisterTemplated(name_minor, desc); | |
123 | } | ||
124 | ✗ | return result; | |
125 | } | ||
126 | |||
127 | Statistics *statistics() { return statistics_; } | ||
128 | |||
129 | private: | ||
130 | std::string name_major_; | ||
131 | Statistics *statistics_; | ||
132 | }; | ||
133 | |||
134 | |||
135 | struct FsCounters { | ||
136 | perf::Counter *n_files_added; | ||
137 | perf::Counter *n_files_removed; | ||
138 | perf::Counter *n_files_changed; | ||
139 | perf::Counter *n_directories_added; | ||
140 | perf::Counter *n_directories_removed; | ||
141 | perf::Counter *n_directories_changed; | ||
142 | perf::Counter *n_symlinks_added; | ||
143 | perf::Counter *n_symlinks_removed; | ||
144 | perf::Counter *n_symlinks_changed; | ||
145 | perf::Counter *sz_added_bytes; | ||
146 | perf::Counter *sz_removed_bytes; | ||
147 | |||
148 | 2 | explicit FsCounters(StatisticsTemplate statistics) { | |
149 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | n_files_added = statistics.RegisterTemplated("n_files_added", |
150 | "Number of files added"); | ||
151 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | n_files_removed = statistics.RegisterTemplated("n_files_removed", |
152 | "Number of files removed"); | ||
153 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | n_files_changed = statistics.RegisterTemplated("n_files_changed", |
154 | "Number of files changed"); | ||
155 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | n_directories_added = statistics.RegisterTemplated("n_directories_added", |
156 | "Number of directories added"); | ||
157 | 2 | n_directories_removed = | |
158 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | statistics.RegisterTemplated("n_directories_removed", |
159 | "Number of directories removed"); | ||
160 | 2 | n_directories_changed = | |
161 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | statistics.RegisterTemplated("n_directories_changed", |
162 | "Number of directories changed"); | ||
163 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | n_symlinks_added = statistics.RegisterTemplated("n_symlinks_added", |
164 | "Number of symlinks added"); | ||
165 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | n_symlinks_removed = statistics.RegisterTemplated("n_symlinks_removed", |
166 | "Number of symlinks removed"); | ||
167 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | n_symlinks_changed = statistics.RegisterTemplated("n_symlinks_changed", |
168 | "Number of symlinks changed"); | ||
169 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | sz_added_bytes = statistics.RegisterTemplated("sz_added_bytes", |
170 | "Number of bytes added"); | ||
171 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
|
2 | sz_removed_bytes = statistics.RegisterTemplated("sz_removed_bytes", |
172 | "Number of bytes removed"); | ||
173 | 2 | } | |
174 | }; // FsCounters | ||
175 | |||
176 | |||
177 | /** | ||
178 | * Keeps track of events over time. Can be used to query the number of events | ||
179 | * between now and a point in time in the past. The time range should be | ||
180 | * smaller than capacity_s seconds. Uses a monotonic clock. Not thread-safe. | ||
181 | */ | ||
182 | class Recorder { | ||
183 | public: | ||
184 | Recorder(uint32_t resolution_s, uint32_t capacity_s); | ||
185 | |||
186 | void Tick(); | ||
187 | void TickAt(uint64_t timestamp); | ||
188 | uint64_t GetNoTicks(uint32_t retrospect_s) const; | ||
189 | |||
190 | 13 | uint32_t capacity_s() const { return capacity_s_; } | |
191 | uint32_t resolution_s() const { return resolution_s_; } | ||
192 | |||
193 | private: | ||
194 | /** | ||
195 | * Records number of ticks (events) per unit of resolution. A ring buffer. | ||
196 | * Entries older than capacity_s get overwritten by new events. | ||
197 | */ | ||
198 | std::vector<uint32_t> bins_; | ||
199 | |||
200 | /** | ||
201 | * When the most recent tick occurred. | ||
202 | */ | ||
203 | uint64_t last_timestamp_; | ||
204 | |||
205 | /** | ||
206 | * Time window in seconds that the recorder is supposed to remember. | ||
207 | */ | ||
208 | uint32_t capacity_s_; | ||
209 | |||
210 | /** | ||
211 | * Size of the bins for the tick counters. | ||
212 | */ | ||
213 | uint32_t resolution_s_; | ||
214 | |||
215 | /** | ||
216 | * Shorthand for bins_.size(), constant during lifetime of the recorder. | ||
217 | */ | ||
218 | unsigned no_bins_; | ||
219 | }; | ||
220 | |||
221 | |||
222 | /** | ||
223 | * Writes to multiple recorders. Recorders with coarsed-grained resolution and | ||
224 | * a large capacity are combined with precise recorders with shorter capacity. | ||
225 | * Preferred recorders should be added first because GetNoTicks will use the | ||
226 | * first recorder with a capacity >= retrospect_s (or the last recorder). | ||
227 | */ | ||
228 | class MultiRecorder { | ||
229 | public: | ||
230 | void Tick(); | ||
231 | void TickAt(uint64_t timestamp); | ||
232 | uint64_t GetNoTicks(uint32_t retrospect_s) const; | ||
233 | |||
234 | void AddRecorder(uint32_t resolution_s, uint32_t capacity_s); | ||
235 | |||
236 | private: | ||
237 | std::vector<Recorder> recorders_; | ||
238 | }; | ||
239 | |||
240 | } // namespace perf | ||
241 | |||
242 | #ifdef CVMFS_NAMESPACE_GUARD | ||
243 | } // namespace CVMFS_NAMESPACE_GUARD | ||
244 | #endif | ||
245 | |||
246 | #endif // CVMFS_STATISTICS_H_ | ||
247 |