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