summaryrefslogtreecommitdiffstats
path: root/src/common/perf_counters.h
blob: c5f69aa7ce31abcb911c86d29f586f951f79faf3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2011 New Dream Network
 * Copyright (C) 2017 OVH
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software 
 * Foundation.  See file COPYING.
 * 
 */


#ifndef CEPH_COMMON_PERF_COUNTERS_H
#define CEPH_COMMON_PERF_COUNTERS_H

#include <string>
#include <vector>
#include <memory>
#include <atomic>
#include <cstdint>

#include "common/perf_histogram.h"
#include "include/utime.h"
#include "include/common_fwd.h"
#include "common/ceph_mutex.h"
#include "common/ceph_time.h"

namespace TOPNSPC::common {
  class CephContext;
  class PerfCountersBuilder;
  class PerfCounters;
}

enum perfcounter_type_d : uint8_t
{
  PERFCOUNTER_NONE = 0,
  PERFCOUNTER_TIME = 0x1,       // float (measuring seconds)
  PERFCOUNTER_U64 = 0x2,        // integer (note: either TIME or U64 *must* be set)
  PERFCOUNTER_LONGRUNAVG = 0x4, // paired counter + sum (time)
  PERFCOUNTER_COUNTER = 0x8,    // counter (vs gauge)
  PERFCOUNTER_HISTOGRAM = 0x10, // histogram (vector) of values
};

enum unit_t : uint8_t
{
  UNIT_BYTES,
  UNIT_NONE
};

/* Class for constructing a PerfCounters object.
 *
 * This class performs some validation that the parameters we have supplied are
 * correct in create_perf_counters().
 *
 * In the future, we will probably get rid of the first/last arguments, since
 * PerfCountersBuilder can deduce them itself.
 */
namespace TOPNSPC::common {
class PerfCountersBuilder
{
public:
  PerfCountersBuilder(CephContext *cct, const std::string &name,
		    int first, int last);
  ~PerfCountersBuilder();

  // prio values: higher is better, and higher values get included in
  // 'ceph daemonperf' (and similar) results.
  // Use of priorities enables us to add large numbers of counters
  // internally without necessarily overwhelming consumers.
  enum {
    PRIO_CRITICAL = 10,
    // 'interesting' is the default threshold for `daemonperf` output
    PRIO_INTERESTING = 8,
    // `useful` is the default threshold for transmission to ceph-mgr
    // and inclusion in prometheus/influxdb plugin output
    PRIO_USEFUL = 5,
    PRIO_UNINTERESTING = 2,
    PRIO_DEBUGONLY = 0,
  };
  void add_u64(int key, const char *name,
	       const char *description=NULL, const char *nick = NULL,
	       int prio=0, int unit=UNIT_NONE);
  void add_u64_counter(int key, const char *name,
		       const char *description=NULL,
		       const char *nick = NULL,
		       int prio=0, int unit=UNIT_NONE);
  void add_u64_avg(int key, const char *name,
		   const char *description=NULL,
		   const char *nick = NULL,
		   int prio=0, int unit=UNIT_NONE);
  void add_time(int key, const char *name,
		const char *description=NULL,
		const char *nick = NULL,
		int prio=0);
  void add_time_avg(int key, const char *name,
		    const char *description=NULL,
		    const char *nick = NULL,
		    int prio=0);
  void add_u64_counter_histogram(
    int key, const char* name,
    PerfHistogramCommon::axis_config_d x_axis_config,
    PerfHistogramCommon::axis_config_d y_axis_config,
    const char *description=NULL,
    const char* nick = NULL,
    int prio=0, int unit=UNIT_NONE);

  void set_prio_default(int prio_)
  {
    prio_default = prio_;
  }

  PerfCounters* create_perf_counters();
private:
  PerfCountersBuilder(const PerfCountersBuilder &rhs);
  PerfCountersBuilder& operator=(const PerfCountersBuilder &rhs);
  void add_impl(int idx, const char *name,
                const char *description, const char *nick, int prio, int ty, int unit=UNIT_NONE,
                std::unique_ptr<PerfHistogram<>> histogram = nullptr);

  PerfCounters *m_perf_counters;

  int prio_default = 0;
};

/*
 * A PerfCounters object is usually associated with a single subsystem.
 * It contains counters which we modify to track performance and throughput
 * over time. 
 *
 * PerfCounters can track several different types of values:
 * 1) integer values & counters
 * 2) floating-point values & counters
 * 3) floating-point averages
 * 4) 2D histograms of quantized value pairs
 *
 * The difference between values, counters and histograms is in how they are initialized
 * and accessed. For a counter, use the inc(counter, amount) function (note
 * that amount defaults to 1 if you don't set it). For a value, use the
 * set(index, value) function. For histogram use the hinc(value1, value2) function.
 * (For time, use the tinc and tset variants.)
 *
 * If for some reason you would like to reset your counters, you can do so using
 * the set functions even if they are counters, and you can also
 * increment your values if for some reason you wish to.
 *
 * For the time average, it returns the current value and
 * the "avgcount" member when read off. avgcount is incremented when you call
 * tinc. Calling tset on an average is an error and will assert out.
 */
class PerfCounters
{
public:
  /** Represents a PerfCounters data element. */
  struct perf_counter_data_any_d {
    perf_counter_data_any_d()
      : name(NULL),
        description(NULL),
        nick(NULL),
	 type(PERFCOUNTER_NONE),
	 unit(UNIT_NONE)
    {}
    perf_counter_data_any_d(const perf_counter_data_any_d& other)
      : name(other.name),
        description(other.description),
        nick(other.nick),
	 type(other.type),
	 unit(other.unit),
	 u64(other.u64.load()) {
      auto a = other.read_avg();
      u64 = a.first;
      avgcount = a.second;
      avgcount2 = a.second;
      if (other.histogram) {
        histogram.reset(new PerfHistogram<>(*other.histogram));
      }
    }

    const char *name;
    const char *description;
    const char *nick;
    uint8_t prio = 0;
    enum perfcounter_type_d type;
    enum unit_t unit;
    std::atomic<uint64_t> u64 = { 0 };
    std::atomic<uint64_t> avgcount = { 0 };
    std::atomic<uint64_t> avgcount2 = { 0 };
    std::unique_ptr<PerfHistogram<>> histogram;

    void reset()
    {
      if (type != PERFCOUNTER_U64) {
	    u64 = 0;
	    avgcount = 0;
	    avgcount2 = 0;
      }
      if (histogram) {
        histogram->reset();
      }
    }

    // read <sum, count> safely by making sure the post- and pre-count
    // are identical; in other words the whole loop needs to be run
    // without any intervening calls to inc, set, or tinc.
    std::pair<uint64_t,uint64_t> read_avg() const {
      uint64_t sum, count;
      do {
	count = avgcount2;
	sum = u64;
      } while (avgcount != count);
      return { sum, count };
    }
  };

  template <typename T>
  struct avg_tracker {
    std::pair<uint64_t, T> last;
    std::pair<uint64_t, T> cur;
    avg_tracker() : last(0, 0), cur(0, 0) {}
    T current_avg() const {
      if (cur.first == last.first)
        return 0;
      return (cur.second - last.second) / (cur.first - last.first);
    }
    void consume_next(const std::pair<uint64_t, T> &next) {
      last = cur;
      cur = next;
    }
  };

  ~PerfCounters();

  void inc(int idx, uint64_t v = 1);
  void dec(int idx, uint64_t v = 1);
  void set(int idx, uint64_t v);
  uint64_t get(int idx) const;

  void tset(int idx, utime_t v);
  void tinc(int idx, utime_t v);
  void tinc(int idx, ceph::timespan v);
  utime_t tget(int idx) const;

  void hinc(int idx, int64_t x, int64_t y);

  void reset();
  void dump_formatted(ceph::Formatter *f, bool schema,
                      const std::string &counter = "") const {
    dump_formatted_generic(f, schema, false, counter);
  }
  void dump_formatted_histograms(ceph::Formatter *f, bool schema,
                                 const std::string &counter = "") const {
    dump_formatted_generic(f, schema, true, counter);
  }
  std::pair<uint64_t, uint64_t> get_tavg_ns(int idx) const;

  const std::string& get_name() const;
  void set_name(std::string s) {
    m_name = s;
  }

  /// adjust priority values by some value
  void set_prio_adjust(int p) {
    prio_adjust = p;
  }

  int get_adjusted_priority(int p) const {
    return std::max(std::min(p + prio_adjust,
                             (int)PerfCountersBuilder::PRIO_CRITICAL),
                    0);
  }

private:
  PerfCounters(CephContext *cct, const std::string &name,
	     int lower_bound, int upper_bound);
  PerfCounters(const PerfCounters &rhs);
  PerfCounters& operator=(const PerfCounters &rhs);
  void dump_formatted_generic(ceph::Formatter *f, bool schema, bool histograms,
                              const std::string &counter = "") const;

  typedef std::vector<perf_counter_data_any_d> perf_counter_data_vec_t;

  CephContext *m_cct;
  int m_lower_bound;
  int m_upper_bound;
  std::string m_name;

  int prio_adjust = 0;

#if !defined(WITH_SEASTAR) || defined(WITH_ALIEN)
  const std::string m_lock_name;
  /** Protects m_data */
  ceph::mutex m_lock;
#endif

  perf_counter_data_vec_t m_data;

  friend class PerfCountersBuilder;
  friend class PerfCountersCollectionImpl;
};

class SortPerfCountersByName {
public:
  bool operator()(const PerfCounters* lhs, const PerfCounters* rhs) const {
    return (lhs->get_name() < rhs->get_name());
  }
};

typedef std::set <PerfCounters*, SortPerfCountersByName> perf_counters_set_t;

/*
 * PerfCountersCollectionImp manages PerfCounters objects for a Ceph process.
 */
class PerfCountersCollectionImpl
{
public:
  PerfCountersCollectionImpl();
  ~PerfCountersCollectionImpl();
  void add(PerfCounters *l);
  void remove(PerfCounters *l);
  void clear();
  bool reset(const std::string &name);

  void dump_formatted(ceph::Formatter *f, bool schema,
                      const std::string &logger = "",
                      const std::string &counter = "") const {
    dump_formatted_generic(f, schema, false, logger, counter);
  }

  void dump_formatted_histograms(ceph::Formatter *f, bool schema,
                                 const std::string &logger = "",
                                 const std::string &counter = "") const {
    dump_formatted_generic(f, schema, true, logger, counter);
  }

  // A reference to a perf_counter_data_any_d, with an accompanying
  // pointer to the enclosing PerfCounters, in order that the consumer
  // can see the prio_adjust
  class PerfCounterRef
  {
    public:
    PerfCounters::perf_counter_data_any_d *data;
    PerfCounters *perf_counters;
  };
  typedef std::map<std::string,
          PerfCounterRef> CounterMap;

  void with_counters(std::function<void(const CounterMap &)>) const;

private:
  void dump_formatted_generic(ceph::Formatter *f, bool schema, bool histograms,
                              const std::string &logger = "",
                              const std::string &counter = "") const;

  perf_counters_set_t m_loggers;

  CounterMap by_path; 
};


class PerfGuard {
  const ceph::real_clock::time_point start;
  PerfCounters* const counters;
  const int event;

public:
  PerfGuard(PerfCounters* const counters,
            const int event)
  : start(ceph::real_clock::now()),
    counters(counters),
    event(event) {
  }

  ~PerfGuard() {
    counters->tinc(event, ceph::real_clock::now() - start);
  }
};

}
#endif