]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/perf_counters.h
import 15.2.0 Octopus source
[ceph.git] / ceph / src / common / perf_counters.h
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
20 #include <string>
21 #include <vector>
22 #include <memory>
23 #include <atomic>
24 #include <cstdint>
25
26 #include "common/perf_histogram.h"
27 #include "include/utime.h"
28 #include "include/common_fwd.h"
29 #include "common/ceph_mutex.h"
30 #include "common/ceph_time.h"
31
32 namespace TOPNSPC::common {
33 class CephContext;
34 class PerfCountersBuilder;
35 class PerfCounters;
36 }
37
38 enum perfcounter_type_d : uint8_t
39 {
40 PERFCOUNTER_NONE = 0,
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)
44 PERFCOUNTER_COUNTER = 0x8, // counter (vs gauge)
45 PERFCOUNTER_HISTOGRAM = 0x10, // histogram (vector) of values
46 };
47
48 enum unit_t : uint8_t
49 {
50 UNIT_BYTES,
51 UNIT_NONE
52 };
53
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 */
62 namespace TOPNSPC::common {
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,
86 int prio=0, int unit=UNIT_NONE);
87 void add_u64_counter(int key, const char *name,
88 const char *description=NULL,
89 const char *nick = NULL,
90 int prio=0, int unit=UNIT_NONE);
91 void add_u64_avg(int key, const char *name,
92 const char *description=NULL,
93 const char *nick = NULL,
94 int prio=0, int unit=UNIT_NONE);
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,
109 int prio=0, int unit=UNIT_NONE);
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,
121 const char *description, const char *nick, int prio, int ty, int unit=UNIT_NONE,
122 std::unique_ptr<PerfHistogram<>> histogram = nullptr);
123
124 PerfCounters *m_perf_counters;
125
126 int prio_default = 0;
127 };
128
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),
163 type(PERFCOUNTER_NONE),
164 unit(UNIT_NONE)
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),
170 type(other.type),
171 unit(other.unit),
172 u64(other.u64.load()) {
173 auto a = other.read_avg();
174 u64 = a.first;
175 avgcount = a.second;
176 avgcount2 = a.second;
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;
185 uint8_t prio = 0;
186 enum perfcounter_type_d type;
187 enum unit_t unit;
188 std::atomic<uint64_t> u64 = { 0 };
189 std::atomic<uint64_t> avgcount = { 0 };
190 std::atomic<uint64_t> avgcount2 = { 0 };
191 std::unique_ptr<PerfHistogram<>> histogram;
192
193 void reset()
194 {
195 if (type != PERFCOUNTER_U64) {
196 u64 = 0;
197 avgcount = 0;
198 avgcount2 = 0;
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.
208 std::pair<uint64_t,uint64_t> read_avg() const {
209 uint64_t sum, count;
210 do {
211 count = avgcount2;
212 sum = u64;
213 } while (avgcount != count);
214 return { sum, count };
215 }
216 };
217
218 template <typename T>
219 struct avg_tracker {
220 std::pair<uint64_t, T> last;
221 std::pair<uint64_t, T> cur;
222 avg_tracker() : last(0, 0), cur(0, 0) {}
223 T current_avg() const {
224 if (cur.first == last.first)
225 return 0;
226 return (cur.second - last.second) / (cur.first - last.first);
227 }
228 void consume_next(const std::pair<uint64_t, T> &next) {
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);
242 void tinc(int idx, utime_t v);
243 void tinc(int idx, ceph::timespan v);
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,
250 const std::string &counter = "") const {
251 dump_formatted_generic(f, schema, false, counter);
252 }
253 void dump_formatted_histograms(ceph::Formatter *f, bool schema,
254 const std::string &counter = "") const {
255 dump_formatted_generic(f, schema, true, counter);
256 }
257 std::pair<uint64_t, uint64_t> get_tavg_ns(int idx) const;
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
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
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,
281 const std::string &counter = "") const;
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;
289
290 int prio_adjust = 0;
291
292 #if !defined(WITH_SEASTAR) || defined(WITH_ALIEN)
293 const std::string m_lock_name;
294 /** Protects m_data */
295 ceph::mutex m_lock;
296 #endif
297
298 perf_counter_data_vec_t m_data;
299
300 friend class PerfCountersBuilder;
301 friend class PerfCountersCollectionImpl;
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 /*
314 * PerfCountersCollectionImp manages PerfCounters objects for a Ceph process.
315 */
316 class PerfCountersCollectionImpl
317 {
318 public:
319 PerfCountersCollectionImpl();
320 ~PerfCountersCollectionImpl();
321 void add(PerfCounters *l);
322 void remove(PerfCounters *l);
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 = "",
328 const std::string &counter = "") const {
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 = "",
334 const std::string &counter = "") const {
335 dump_formatted_generic(f, schema, true, logger, counter);
336 }
337
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 };
347 typedef std::map<std::string,
348 PerfCounterRef> CounterMap;
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 = "",
355 const std::string &counter = "") const;
356
357 perf_counters_set_t m_loggers;
358
359 CounterMap by_path;
360 };
361
362
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
381 }
382 #endif