]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2011 New Dream Network | |
7 | * Copyright (C) 2017 OVH | |
8 | * | |
9 | * This is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License version 2.1, as published by the Free Software | |
12 | * Foundation. See file COPYING. | |
13 | * | |
14 | */ | |
15 | ||
16 | ||
17 | #ifndef CEPH_COMMON_PERF_COUNTERS_H | |
18 | #define CEPH_COMMON_PERF_COUNTERS_H | |
19 | ||
31f18b77 FG |
20 | #include <string> |
21 | #include <vector> | |
22 | #include <memory> | |
23 | #include <atomic> | |
24 | #include <cstdint> | |
25 | ||
7c673cae | 26 | #include "common/perf_histogram.h" |
7c673cae | 27 | #include "include/utime.h" |
7c673cae FG |
28 | #include "common/Mutex.h" |
29 | #include "common/ceph_time.h" | |
30 | ||
7c673cae FG |
31 | class CephContext; |
32 | class PerfCountersBuilder; | |
33 | ||
34 | enum perfcounter_type_d : uint8_t | |
35 | { | |
36 | PERFCOUNTER_NONE = 0, | |
31f18b77 FG |
37 | PERFCOUNTER_TIME = 0x1, // float (measuring seconds) |
38 | PERFCOUNTER_U64 = 0x2, // integer (note: either TIME or U64 *must* be set) | |
39 | PERFCOUNTER_LONGRUNAVG = 0x4, // paired counter + sum (time) | |
40 | PERFCOUNTER_COUNTER = 0x8, // counter (vs guage) | |
41 | PERFCOUNTER_HISTOGRAM = 0x10, // histogram (vector) of values | |
7c673cae FG |
42 | }; |
43 | ||
44 | ||
3efd9988 FG |
45 | /* Class for constructing a PerfCounters object. |
46 | * | |
47 | * This class performs some validation that the parameters we have supplied are | |
48 | * correct in create_perf_counters(). | |
49 | * | |
50 | * In the future, we will probably get rid of the first/last arguments, since | |
51 | * PerfCountersBuilder can deduce them itself. | |
52 | */ | |
53 | class PerfCountersBuilder | |
54 | { | |
55 | public: | |
56 | PerfCountersBuilder(CephContext *cct, const std::string &name, | |
57 | int first, int last); | |
58 | ~PerfCountersBuilder(); | |
59 | ||
60 | // prio values: higher is better, and higher values get included in | |
61 | // 'ceph daemonperf' (and similar) results. | |
62 | // Use of priorities enables us to add large numbers of counters | |
63 | // internally without necessarily overwhelming consumers. | |
64 | enum { | |
65 | PRIO_CRITICAL = 10, | |
66 | // 'interesting' is the default threshold for `daemonperf` output | |
67 | PRIO_INTERESTING = 8, | |
68 | // `useful` is the default threshold for transmission to ceph-mgr | |
69 | // and inclusion in prometheus/influxdb plugin output | |
70 | PRIO_USEFUL = 5, | |
71 | PRIO_UNINTERESTING = 2, | |
72 | PRIO_DEBUGONLY = 0, | |
73 | }; | |
74 | void add_u64(int key, const char *name, | |
75 | const char *description=NULL, const char *nick = NULL, | |
76 | int prio=0); | |
77 | void add_u64_counter(int key, const char *name, | |
78 | const char *description=NULL, | |
79 | const char *nick = NULL, | |
80 | int prio=0); | |
81 | void add_u64_avg(int key, const char *name, | |
82 | const char *description=NULL, | |
83 | const char *nick = NULL, | |
84 | int prio=0); | |
85 | void add_time(int key, const char *name, | |
86 | const char *description=NULL, | |
87 | const char *nick = NULL, | |
88 | int prio=0); | |
89 | void add_time_avg(int key, const char *name, | |
90 | const char *description=NULL, | |
91 | const char *nick = NULL, | |
92 | int prio=0); | |
93 | void add_u64_counter_histogram( | |
94 | int key, const char* name, | |
95 | PerfHistogramCommon::axis_config_d x_axis_config, | |
96 | PerfHistogramCommon::axis_config_d y_axis_config, | |
97 | const char *description=NULL, | |
98 | const char* nick = NULL, | |
99 | int prio=0); | |
100 | ||
101 | void set_prio_default(int prio_) | |
102 | { | |
103 | prio_default = prio_; | |
104 | } | |
105 | ||
106 | PerfCounters* create_perf_counters(); | |
107 | private: | |
108 | PerfCountersBuilder(const PerfCountersBuilder &rhs); | |
109 | PerfCountersBuilder& operator=(const PerfCountersBuilder &rhs); | |
110 | void add_impl(int idx, const char *name, | |
111 | const char *description, const char *nick, int prio, int ty, | |
112 | unique_ptr<PerfHistogram<>> histogram = nullptr); | |
113 | ||
114 | PerfCounters *m_perf_counters; | |
115 | ||
116 | int prio_default = 0; | |
117 | }; | |
118 | ||
7c673cae FG |
119 | /* |
120 | * A PerfCounters object is usually associated with a single subsystem. | |
121 | * It contains counters which we modify to track performance and throughput | |
122 | * over time. | |
123 | * | |
124 | * PerfCounters can track several different types of values: | |
125 | * 1) integer values & counters | |
126 | * 2) floating-point values & counters | |
127 | * 3) floating-point averages | |
128 | * 4) 2D histograms of quantized value pairs | |
129 | * | |
130 | * The difference between values, counters and histograms is in how they are initialized | |
131 | * and accessed. For a counter, use the inc(counter, amount) function (note | |
132 | * that amount defaults to 1 if you don't set it). For a value, use the | |
133 | * set(index, value) function. For histogram use the hinc(value1, value2) function. | |
134 | * (For time, use the tinc and tset variants.) | |
135 | * | |
136 | * If for some reason you would like to reset your counters, you can do so using | |
137 | * the set functions even if they are counters, and you can also | |
138 | * increment your values if for some reason you wish to. | |
139 | * | |
140 | * For the time average, it returns the current value and | |
141 | * the "avgcount" member when read off. avgcount is incremented when you call | |
142 | * tinc. Calling tset on an average is an error and will assert out. | |
143 | */ | |
144 | class PerfCounters | |
145 | { | |
146 | public: | |
147 | /** Represents a PerfCounters data element. */ | |
148 | struct perf_counter_data_any_d { | |
149 | perf_counter_data_any_d() | |
150 | : name(NULL), | |
151 | description(NULL), | |
152 | nick(NULL), | |
31f18b77 | 153 | type(PERFCOUNTER_NONE) |
7c673cae FG |
154 | {} |
155 | perf_counter_data_any_d(const perf_counter_data_any_d& other) | |
156 | : name(other.name), | |
157 | description(other.description), | |
158 | nick(other.nick), | |
159 | type(other.type), | |
31f18b77 | 160 | u64(other.u64.load()) { |
7c673cae | 161 | pair<uint64_t,uint64_t> a = other.read_avg(); |
31f18b77 FG |
162 | u64 = a.first; |
163 | avgcount = a.second; | |
164 | avgcount2 = a.second; | |
7c673cae FG |
165 | if (other.histogram) { |
166 | histogram.reset(new PerfHistogram<>(*other.histogram)); | |
167 | } | |
168 | } | |
169 | ||
170 | const char *name; | |
171 | const char *description; | |
172 | const char *nick; | |
3efd9988 | 173 | uint8_t prio = 0; |
7c673cae | 174 | enum perfcounter_type_d type; |
31f18b77 FG |
175 | std::atomic<uint64_t> u64 = { 0 }; |
176 | std::atomic<uint64_t> avgcount = { 0 }; | |
177 | std::atomic<uint64_t> avgcount2 = { 0 }; | |
7c673cae FG |
178 | std::unique_ptr<PerfHistogram<>> histogram; |
179 | ||
180 | void reset() | |
181 | { | |
182 | if (type != PERFCOUNTER_U64) { | |
31f18b77 FG |
183 | u64 = 0; |
184 | avgcount = 0; | |
185 | avgcount2 = 0; | |
7c673cae FG |
186 | } |
187 | if (histogram) { | |
188 | histogram->reset(); | |
189 | } | |
190 | } | |
191 | ||
192 | // read <sum, count> safely by making sure the post- and pre-count | |
193 | // are identical; in other words the whole loop needs to be run | |
194 | // without any intervening calls to inc, set, or tinc. | |
195 | pair<uint64_t,uint64_t> read_avg() const { | |
196 | uint64_t sum, count; | |
197 | do { | |
31f18b77 FG |
198 | count = avgcount; |
199 | sum = u64; | |
200 | } while (avgcount2 != count); | |
7c673cae FG |
201 | return make_pair(sum, count); |
202 | } | |
203 | }; | |
204 | ||
205 | template <typename T> | |
206 | struct avg_tracker { | |
207 | pair<uint64_t, T> last; | |
208 | pair<uint64_t, T> cur; | |
209 | avg_tracker() : last(0, 0), cur(0, 0) {} | |
c07f9fc5 | 210 | T current_avg() const { |
7c673cae | 211 | if (cur.first == last.first) |
c07f9fc5 | 212 | return 0; |
7c673cae FG |
213 | return (cur.second - last.second) / (cur.first - last.first); |
214 | } | |
215 | void consume_next(const pair<uint64_t, T> &next) { | |
216 | last = cur; | |
217 | cur = next; | |
218 | } | |
219 | }; | |
220 | ||
221 | ~PerfCounters(); | |
222 | ||
223 | void inc(int idx, uint64_t v = 1); | |
224 | void dec(int idx, uint64_t v = 1); | |
225 | void set(int idx, uint64_t v); | |
226 | uint64_t get(int idx) const; | |
227 | ||
228 | void tset(int idx, utime_t v); | |
229 | void tinc(int idx, utime_t v, uint32_t avgcount = 1); | |
230 | void tinc(int idx, ceph::timespan v, uint32_t avgcount = 1); | |
231 | utime_t tget(int idx) const; | |
232 | ||
233 | void hinc(int idx, int64_t x, int64_t y); | |
234 | ||
235 | void reset(); | |
236 | void dump_formatted(ceph::Formatter *f, bool schema, | |
237 | const std::string &counter = "") { | |
238 | dump_formatted_generic(f, schema, false, counter); | |
239 | } | |
240 | void dump_formatted_histograms(ceph::Formatter *f, bool schema, | |
241 | const std::string &counter = "") { | |
242 | dump_formatted_generic(f, schema, true, counter); | |
243 | } | |
244 | pair<uint64_t, uint64_t> get_tavg_ms(int idx) const; | |
245 | ||
246 | const std::string& get_name() const; | |
247 | void set_name(std::string s) { | |
248 | m_name = s; | |
249 | } | |
250 | ||
251 | /// adjust priority values by some value | |
252 | void set_prio_adjust(int p) { | |
253 | prio_adjust = p; | |
254 | } | |
255 | ||
3efd9988 FG |
256 | int get_adjusted_priority(int p) const { |
257 | return std::max(std::min(p + prio_adjust, | |
258 | (int)PerfCountersBuilder::PRIO_CRITICAL), | |
259 | 0); | |
260 | } | |
261 | ||
7c673cae FG |
262 | private: |
263 | PerfCounters(CephContext *cct, const std::string &name, | |
264 | int lower_bound, int upper_bound); | |
265 | PerfCounters(const PerfCounters &rhs); | |
266 | PerfCounters& operator=(const PerfCounters &rhs); | |
267 | void dump_formatted_generic(ceph::Formatter *f, bool schema, bool histograms, | |
268 | const std::string &counter = ""); | |
269 | ||
270 | typedef std::vector<perf_counter_data_any_d> perf_counter_data_vec_t; | |
271 | ||
272 | CephContext *m_cct; | |
273 | int m_lower_bound; | |
274 | int m_upper_bound; | |
275 | std::string m_name; | |
276 | const std::string m_lock_name; | |
277 | ||
278 | int prio_adjust = 0; | |
279 | ||
280 | /** Protects m_data */ | |
281 | mutable Mutex m_lock; | |
282 | ||
283 | perf_counter_data_vec_t m_data; | |
284 | ||
285 | friend class PerfCountersBuilder; | |
286 | friend class PerfCountersCollection; | |
287 | }; | |
288 | ||
289 | class SortPerfCountersByName { | |
290 | public: | |
291 | bool operator()(const PerfCounters* lhs, const PerfCounters* rhs) const { | |
292 | return (lhs->get_name() < rhs->get_name()); | |
293 | } | |
294 | }; | |
295 | ||
296 | typedef std::set <PerfCounters*, SortPerfCountersByName> perf_counters_set_t; | |
297 | ||
298 | /* | |
299 | * PerfCountersCollection manages PerfCounters objects for a Ceph process. | |
300 | */ | |
301 | class PerfCountersCollection | |
302 | { | |
303 | public: | |
304 | PerfCountersCollection(CephContext *cct); | |
305 | ~PerfCountersCollection(); | |
306 | void add(class PerfCounters *l); | |
307 | void remove(class PerfCounters *l); | |
308 | void clear(); | |
309 | bool reset(const std::string &name); | |
310 | ||
311 | void dump_formatted(ceph::Formatter *f, bool schema, | |
312 | const std::string &logger = "", | |
313 | const std::string &counter = "") { | |
314 | dump_formatted_generic(f, schema, false, logger, counter); | |
315 | } | |
316 | ||
317 | void dump_formatted_histograms(ceph::Formatter *f, bool schema, | |
318 | const std::string &logger = "", | |
319 | const std::string &counter = "") { | |
320 | dump_formatted_generic(f, schema, true, logger, counter); | |
321 | } | |
322 | ||
3efd9988 FG |
323 | // A reference to a perf_counter_data_any_d, with an accompanying |
324 | // pointer to the enclosing PerfCounters, in order that the consumer | |
325 | // can see the prio_adjust | |
326 | class PerfCounterRef | |
327 | { | |
328 | public: | |
329 | PerfCounters::perf_counter_data_any_d *data; | |
330 | PerfCounters *perf_counters; | |
331 | }; | |
7c673cae | 332 | typedef std::map<std::string, |
3efd9988 | 333 | PerfCounterRef> CounterMap; |
7c673cae FG |
334 | |
335 | void with_counters(std::function<void(const CounterMap &)>) const; | |
336 | ||
337 | private: | |
338 | void dump_formatted_generic(ceph::Formatter *f, bool schema, bool histograms, | |
339 | const std::string &logger = "", | |
340 | const std::string &counter = ""); | |
341 | ||
342 | CephContext *m_cct; | |
343 | ||
344 | /** Protects m_loggers */ | |
345 | mutable Mutex m_lock; | |
346 | ||
347 | perf_counters_set_t m_loggers; | |
348 | ||
3efd9988 | 349 | CounterMap by_path; |
7c673cae FG |
350 | |
351 | friend class PerfCountersCollectionTest; | |
352 | }; | |
353 | ||
7c673cae | 354 | |
7c673cae FG |
355 | |
356 | #endif |