]>
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 | ||
45 | /* | |
46 | * A PerfCounters object is usually associated with a single subsystem. | |
47 | * It contains counters which we modify to track performance and throughput | |
48 | * over time. | |
49 | * | |
50 | * PerfCounters can track several different types of values: | |
51 | * 1) integer values & counters | |
52 | * 2) floating-point values & counters | |
53 | * 3) floating-point averages | |
54 | * 4) 2D histograms of quantized value pairs | |
55 | * | |
56 | * The difference between values, counters and histograms is in how they are initialized | |
57 | * and accessed. For a counter, use the inc(counter, amount) function (note | |
58 | * that amount defaults to 1 if you don't set it). For a value, use the | |
59 | * set(index, value) function. For histogram use the hinc(value1, value2) function. | |
60 | * (For time, use the tinc and tset variants.) | |
61 | * | |
62 | * If for some reason you would like to reset your counters, you can do so using | |
63 | * the set functions even if they are counters, and you can also | |
64 | * increment your values if for some reason you wish to. | |
65 | * | |
66 | * For the time average, it returns the current value and | |
67 | * the "avgcount" member when read off. avgcount is incremented when you call | |
68 | * tinc. Calling tset on an average is an error and will assert out. | |
69 | */ | |
70 | class PerfCounters | |
71 | { | |
72 | public: | |
73 | /** Represents a PerfCounters data element. */ | |
74 | struct perf_counter_data_any_d { | |
75 | perf_counter_data_any_d() | |
76 | : name(NULL), | |
77 | description(NULL), | |
78 | nick(NULL), | |
31f18b77 | 79 | type(PERFCOUNTER_NONE) |
7c673cae FG |
80 | {} |
81 | perf_counter_data_any_d(const perf_counter_data_any_d& other) | |
82 | : name(other.name), | |
83 | description(other.description), | |
84 | nick(other.nick), | |
85 | type(other.type), | |
31f18b77 | 86 | u64(other.u64.load()) { |
7c673cae | 87 | pair<uint64_t,uint64_t> a = other.read_avg(); |
31f18b77 FG |
88 | u64 = a.first; |
89 | avgcount = a.second; | |
90 | avgcount2 = a.second; | |
7c673cae FG |
91 | if (other.histogram) { |
92 | histogram.reset(new PerfHistogram<>(*other.histogram)); | |
93 | } | |
94 | } | |
95 | ||
96 | const char *name; | |
97 | const char *description; | |
98 | const char *nick; | |
99 | int prio = 0; | |
100 | enum perfcounter_type_d type; | |
31f18b77 FG |
101 | std::atomic<uint64_t> u64 = { 0 }; |
102 | std::atomic<uint64_t> avgcount = { 0 }; | |
103 | std::atomic<uint64_t> avgcount2 = { 0 }; | |
7c673cae FG |
104 | std::unique_ptr<PerfHistogram<>> histogram; |
105 | ||
106 | void reset() | |
107 | { | |
108 | if (type != PERFCOUNTER_U64) { | |
31f18b77 FG |
109 | u64 = 0; |
110 | avgcount = 0; | |
111 | avgcount2 = 0; | |
7c673cae FG |
112 | } |
113 | if (histogram) { | |
114 | histogram->reset(); | |
115 | } | |
116 | } | |
117 | ||
118 | // read <sum, count> safely by making sure the post- and pre-count | |
119 | // are identical; in other words the whole loop needs to be run | |
120 | // without any intervening calls to inc, set, or tinc. | |
121 | pair<uint64_t,uint64_t> read_avg() const { | |
122 | uint64_t sum, count; | |
123 | do { | |
31f18b77 FG |
124 | count = avgcount; |
125 | sum = u64; | |
126 | } while (avgcount2 != count); | |
7c673cae FG |
127 | return make_pair(sum, count); |
128 | } | |
129 | }; | |
130 | ||
131 | template <typename T> | |
132 | struct avg_tracker { | |
133 | pair<uint64_t, T> last; | |
134 | pair<uint64_t, T> cur; | |
135 | avg_tracker() : last(0, 0), cur(0, 0) {} | |
c07f9fc5 | 136 | T current_avg() const { |
7c673cae | 137 | if (cur.first == last.first) |
c07f9fc5 | 138 | return 0; |
7c673cae FG |
139 | return (cur.second - last.second) / (cur.first - last.first); |
140 | } | |
141 | void consume_next(const pair<uint64_t, T> &next) { | |
142 | last = cur; | |
143 | cur = next; | |
144 | } | |
145 | }; | |
146 | ||
147 | ~PerfCounters(); | |
148 | ||
149 | void inc(int idx, uint64_t v = 1); | |
150 | void dec(int idx, uint64_t v = 1); | |
151 | void set(int idx, uint64_t v); | |
152 | uint64_t get(int idx) const; | |
153 | ||
154 | void tset(int idx, utime_t v); | |
155 | void tinc(int idx, utime_t v, uint32_t avgcount = 1); | |
156 | void tinc(int idx, ceph::timespan v, uint32_t avgcount = 1); | |
157 | utime_t tget(int idx) const; | |
158 | ||
159 | void hinc(int idx, int64_t x, int64_t y); | |
160 | ||
161 | void reset(); | |
162 | void dump_formatted(ceph::Formatter *f, bool schema, | |
163 | const std::string &counter = "") { | |
164 | dump_formatted_generic(f, schema, false, counter); | |
165 | } | |
166 | void dump_formatted_histograms(ceph::Formatter *f, bool schema, | |
167 | const std::string &counter = "") { | |
168 | dump_formatted_generic(f, schema, true, counter); | |
169 | } | |
170 | pair<uint64_t, uint64_t> get_tavg_ms(int idx) const; | |
171 | ||
172 | const std::string& get_name() const; | |
173 | void set_name(std::string s) { | |
174 | m_name = s; | |
175 | } | |
176 | ||
177 | /// adjust priority values by some value | |
178 | void set_prio_adjust(int p) { | |
179 | prio_adjust = p; | |
180 | } | |
181 | ||
182 | private: | |
183 | PerfCounters(CephContext *cct, const std::string &name, | |
184 | int lower_bound, int upper_bound); | |
185 | PerfCounters(const PerfCounters &rhs); | |
186 | PerfCounters& operator=(const PerfCounters &rhs); | |
187 | void dump_formatted_generic(ceph::Formatter *f, bool schema, bool histograms, | |
188 | const std::string &counter = ""); | |
189 | ||
190 | typedef std::vector<perf_counter_data_any_d> perf_counter_data_vec_t; | |
191 | ||
192 | CephContext *m_cct; | |
193 | int m_lower_bound; | |
194 | int m_upper_bound; | |
195 | std::string m_name; | |
196 | const std::string m_lock_name; | |
197 | ||
198 | int prio_adjust = 0; | |
199 | ||
200 | /** Protects m_data */ | |
201 | mutable Mutex m_lock; | |
202 | ||
203 | perf_counter_data_vec_t m_data; | |
204 | ||
205 | friend class PerfCountersBuilder; | |
206 | friend class PerfCountersCollection; | |
207 | }; | |
208 | ||
209 | class SortPerfCountersByName { | |
210 | public: | |
211 | bool operator()(const PerfCounters* lhs, const PerfCounters* rhs) const { | |
212 | return (lhs->get_name() < rhs->get_name()); | |
213 | } | |
214 | }; | |
215 | ||
216 | typedef std::set <PerfCounters*, SortPerfCountersByName> perf_counters_set_t; | |
217 | ||
218 | /* | |
219 | * PerfCountersCollection manages PerfCounters objects for a Ceph process. | |
220 | */ | |
221 | class PerfCountersCollection | |
222 | { | |
223 | public: | |
224 | PerfCountersCollection(CephContext *cct); | |
225 | ~PerfCountersCollection(); | |
226 | void add(class PerfCounters *l); | |
227 | void remove(class PerfCounters *l); | |
228 | void clear(); | |
229 | bool reset(const std::string &name); | |
230 | ||
231 | void dump_formatted(ceph::Formatter *f, bool schema, | |
232 | const std::string &logger = "", | |
233 | const std::string &counter = "") { | |
234 | dump_formatted_generic(f, schema, false, logger, counter); | |
235 | } | |
236 | ||
237 | void dump_formatted_histograms(ceph::Formatter *f, bool schema, | |
238 | const std::string &logger = "", | |
239 | const std::string &counter = "") { | |
240 | dump_formatted_generic(f, schema, true, logger, counter); | |
241 | } | |
242 | ||
243 | typedef std::map<std::string, | |
244 | PerfCounters::perf_counter_data_any_d *> CounterMap; | |
245 | ||
246 | void with_counters(std::function<void(const CounterMap &)>) const; | |
247 | ||
248 | private: | |
249 | void dump_formatted_generic(ceph::Formatter *f, bool schema, bool histograms, | |
250 | const std::string &logger = "", | |
251 | const std::string &counter = ""); | |
252 | ||
253 | CephContext *m_cct; | |
254 | ||
255 | /** Protects m_loggers */ | |
256 | mutable Mutex m_lock; | |
257 | ||
258 | perf_counters_set_t m_loggers; | |
259 | ||
260 | std::map<std::string, PerfCounters::perf_counter_data_any_d *> by_path; | |
261 | ||
262 | friend class PerfCountersCollectionTest; | |
263 | }; | |
264 | ||
265 | /* Class for constructing a PerfCounters object. | |
266 | * | |
267 | * This class performs some validation that the parameters we have supplied are | |
268 | * correct in create_perf_counters(). | |
269 | * | |
270 | * In the future, we will probably get rid of the first/last arguments, since | |
271 | * PerfCountersBuilder can deduce them itself. | |
272 | */ | |
273 | class PerfCountersBuilder | |
274 | { | |
275 | public: | |
276 | PerfCountersBuilder(CephContext *cct, const std::string &name, | |
277 | int first, int last); | |
278 | ~PerfCountersBuilder(); | |
279 | ||
280 | // prio values: higher is better, and higher values get included in | |
281 | // 'ceph daemonperf' (and similar) results. | |
282 | enum { | |
283 | PRIO_CRITICAL = 10, | |
284 | PRIO_INTERESTING = 8, | |
285 | PRIO_USEFUL = 5, | |
286 | PRIO_UNINTERESTING = 2, | |
287 | PRIO_DEBUGONLY = 0, | |
288 | }; | |
289 | void add_u64(int key, const char *name, | |
290 | const char *description=NULL, const char *nick = NULL, | |
291 | int prio=0); | |
292 | void add_u64_counter(int key, const char *name, | |
293 | const char *description=NULL, | |
294 | const char *nick = NULL, | |
295 | int prio=0); | |
296 | void add_u64_avg(int key, const char *name, | |
297 | const char *description=NULL, | |
298 | const char *nick = NULL, | |
299 | int prio=0); | |
300 | void add_time(int key, const char *name, | |
301 | const char *description=NULL, | |
302 | const char *nick = NULL, | |
303 | int prio=0); | |
304 | void add_time_avg(int key, const char *name, | |
305 | const char *description=NULL, | |
306 | const char *nick = NULL, | |
307 | int prio=0); | |
31f18b77 FG |
308 | void add_u64_counter_histogram( |
309 | int key, const char* name, | |
310 | PerfHistogramCommon::axis_config_d x_axis_config, | |
311 | PerfHistogramCommon::axis_config_d y_axis_config, | |
312 | const char *description=NULL, | |
313 | const char* nick = NULL, | |
314 | int prio=0); | |
315 | ||
7c673cae FG |
316 | PerfCounters* create_perf_counters(); |
317 | private: | |
318 | PerfCountersBuilder(const PerfCountersBuilder &rhs); | |
319 | PerfCountersBuilder& operator=(const PerfCountersBuilder &rhs); | |
320 | void add_impl(int idx, const char *name, | |
321 | const char *description, const char *nick, int prio, int ty, | |
322 | unique_ptr<PerfHistogram<>> histogram = nullptr); | |
323 | ||
324 | PerfCounters *m_perf_counters; | |
325 | }; | |
326 | ||
327 | #endif |