// -*- 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 #include #include #include #include #include "common/perf_histogram.h" #include "include/utime.h" #include "common/Mutex.h" #include "common/ceph_time.h" class CephContext; class PerfCountersBuilder; 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 guage) PERFCOUNTER_HISTOGRAM = 0x10, // histogram (vector) of values }; /* 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. */ 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); void add_u64_counter(int key, const char *name, const char *description=NULL, const char *nick = NULL, int prio=0); void add_u64_avg(int key, const char *name, const char *description=NULL, const char *nick = NULL, int prio=0); 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); 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, unique_ptr> 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) {} perf_counter_data_any_d(const perf_counter_data_any_d& other) : name(other.name), description(other.description), nick(other.nick), type(other.type), u64(other.u64.load()) { pair 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; std::atomic u64 = { 0 }; std::atomic avgcount = { 0 }; std::atomic avgcount2 = { 0 }; std::unique_ptr> histogram; void reset() { if (type != PERFCOUNTER_U64) { u64 = 0; avgcount = 0; avgcount2 = 0; } if (histogram) { histogram->reset(); } } // read 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. pair read_avg() const { uint64_t sum, count; do { count = avgcount; sum = u64; } while (avgcount2 != count); return make_pair(sum, count); } }; template struct avg_tracker { pair last; pair 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 pair &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, uint32_t avgcount = 1); void tinc(int idx, ceph::timespan v, uint32_t avgcount = 1); 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 = "") { dump_formatted_generic(f, schema, false, counter); } void dump_formatted_histograms(ceph::Formatter *f, bool schema, const std::string &counter = "") { dump_formatted_generic(f, schema, true, counter); } pair get_tavg_ms(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 = ""); typedef std::vector perf_counter_data_vec_t; CephContext *m_cct; int m_lower_bound; int m_upper_bound; std::string m_name; const std::string m_lock_name; int prio_adjust = 0; /** Protects m_data */ mutable Mutex m_lock; perf_counter_data_vec_t m_data; friend class PerfCountersBuilder; friend class PerfCountersCollection; }; class SortPerfCountersByName { public: bool operator()(const PerfCounters* lhs, const PerfCounters* rhs) const { return (lhs->get_name() < rhs->get_name()); } }; typedef std::set perf_counters_set_t; /* * PerfCountersCollection manages PerfCounters objects for a Ceph process. */ class PerfCountersCollection { public: PerfCountersCollection(CephContext *cct); ~PerfCountersCollection(); void add(class PerfCounters *l); void remove(class 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 = "") { 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 = "") { 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 CounterMap; void with_counters(std::function) const; private: void dump_formatted_generic(ceph::Formatter *f, bool schema, bool histograms, const std::string &logger = "", const std::string &counter = ""); CephContext *m_cct; /** Protects m_loggers */ mutable Mutex m_lock; perf_counters_set_t m_loggers; CounterMap by_path; friend class PerfCountersCollectionTest; }; #endif