From: Ali Maredia Date: Thu, 20 Oct 2022 03:15:56 +0000 (-0400) Subject: common: created labeled perf counters instead of normal ones X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=8ccc1b2b0371a30af6234ee5cb97d3098d4180ff;p=ceph.git common: created labeled perf counters instead of normal ones Signed-off-by: Ali Maredia --- diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index fc32906193cbb..edfdb56ab79d6 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -76,6 +76,8 @@ set(common_srcs hostname.cc ipaddr.cc iso_8601.cc + labeled_perf_counters.cc + labeled_perf_counters_collection.cc mempool.cc mime.c numa.cc diff --git a/src/common/ceph_context.cc b/src/common/ceph_context.cc index eeee83618819c..a0eb181dac796 100644 --- a/src/common/ceph_context.cc +++ b/src/common/ceph_context.cc @@ -104,7 +104,7 @@ PerfCountersCollectionImpl* CephContext::get_perfcounters_collection() return _perf_counters_collection.get_perf_collection(); } -PerfCountersCollectionImpl* CephContext::get_labeledperfcounters_collection() +LabeledPerfCountersCollectionImpl* CephContext::get_labeledperfcounters_collection() { return _labeled_perf_counters_collection.get_perf_collection(); } @@ -748,7 +748,7 @@ CephContext::CephContext(uint32_t module_type_, _conf.add_observer(_lockdep_obs); #endif _perf_counters_collection = new PerfCountersCollection(this); - _labeled_perf_counters_collection = new PerfCountersCollection(this); + _labeled_perf_counters_collection = new LabeledPerfCountersCollection(this); _admin_socket = new AdminSocket(this); _heartbeat_map = new HeartbeatMap(this); @@ -947,7 +947,7 @@ PerfCountersCollection *CephContext::get_perfcounters_collection() return _perf_counters_collection; } -PerfCountersCollection *CephContext::get_labeledperfcounters_collection() +LabeledPerfCountersCollection *CephContext::get_labeledperfcounters_collection() { return _labeled_perf_counters_collection; } diff --git a/src/common/ceph_context.h b/src/common/ceph_context.h index 90eb926721211..e317a50cd16fa 100644 --- a/src/common/ceph_context.h +++ b/src/common/ceph_context.h @@ -41,9 +41,9 @@ #include "common/config_proxy.h" #include "include/spinlock.h" #include "common/perf_counters_collection.h" +#include "common/labeled_perf_counters_collection.h" #endif - #include "crush/CrushLocation.h" class AdminSocket; @@ -170,8 +170,8 @@ public: /* Get the PerfCountersCollection of this CephContext */ PerfCountersCollection *get_perfcounters_collection(); - /* Get the PerfCountersCollection of this CephContext */ - PerfCountersCollection *get_labeledperfcounters_collection(); + /* Get the LabeledPerfCountersCollection of this CephContext */ + LabeledPerfCountersCollection *get_labeledperfcounters_collection(); ceph::HeartbeatMap *get_heartbeat_map() { return _heartbeat_map; @@ -329,7 +329,7 @@ private: /* The collection of profiling loggers associated with this context */ PerfCountersCollection *_perf_counters_collection; - PerfCountersCollection *_labeled_perf_counters_collection; + LabeledPerfCountersCollection *_labeled_perf_counters_collection; md_config_obs_t *_perf_counters_conf_obs; diff --git a/src/common/labeled_perf_counters.cc b/src/common/labeled_perf_counters.cc new file mode 100644 index 0000000000000..ed3945306c493 --- /dev/null +++ b/src/common/labeled_perf_counters.cc @@ -0,0 +1,593 @@ +// -*- 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. + * + */ + +#include "common/labeled_perf_counters.h" +#include "common/dout.h" +#include "common/valgrind.h" +#include "include/common_fwd.h" + +using std::ostringstream; +using std::make_pair; +using std::pair; + +namespace TOPNSPC::common { +LabeledPerfCountersCollectionImpl::LabeledPerfCountersCollectionImpl() +{ +} + +LabeledPerfCountersCollectionImpl::~LabeledPerfCountersCollectionImpl() +{ + clear(); +} + +void LabeledPerfCountersCollectionImpl::add(LabeledPerfCounters *l) +{ + // make sure the name is unique + labeled_perf_counters_set_t::iterator i; + i = m_loggers.find(l); + while (i != m_loggers.end()) { + ostringstream ss; + ss << l->get_name() << "-" << (void*)l; + l->set_name(ss.str()); + i = m_loggers.find(l); + } + + m_loggers.insert(l); + + for (unsigned int i = 0; i < l->m_data.size(); ++i) { + LabeledPerfCounters::perf_counter_data_any_d &data = l->m_data[i]; + + std::string path = l->get_name(); + path += "."; + path += data.name; + + by_path[path] = {&data, l}; + } +} + +void LabeledPerfCountersCollectionImpl::remove(LabeledPerfCounters *l) +{ + for (unsigned int i = 0; i < l->m_data.size(); ++i) { + LabeledPerfCounters::perf_counter_data_any_d &data = l->m_data[i]; + + std::string path = l->get_name(); + path += "."; + path += data.name; + + by_path.erase(path); + } + + labeled_perf_counters_set_t::iterator i = m_loggers.find(l); + ceph_assert(i != m_loggers.end()); + m_loggers.erase(i); +} + +void LabeledPerfCountersCollectionImpl::clear() +{ + labeled_perf_counters_set_t::iterator i = m_loggers.begin(); + labeled_perf_counters_set_t::iterator i_end = m_loggers.end(); + for (; i != i_end; ) { + delete *i; + m_loggers.erase(i++); + } + + by_path.clear(); +} + +bool LabeledPerfCountersCollectionImpl::reset(const std::string &name) +{ + bool result = false; + labeled_perf_counters_set_t::iterator i = m_loggers.begin(); + labeled_perf_counters_set_t::iterator i_end = m_loggers.end(); + + if (!strcmp(name.c_str(), "all")) { + while (i != i_end) { + (*i)->reset(); + ++i; + } + result = true; + } else { + while (i != i_end) { + if (!name.compare((*i)->get_name())) { + (*i)->reset(); + result = true; + break; + } + ++i; + } + } + + return result; +} + + +/** + * Serialize current values of performance counters. Optionally + * output the schema instead, or filter output to a particular + * LabeledPerfCounters or particular named counter. + * + * @param logger name of subsystem logger, e.g. "mds_cache", may be empty + * @param counter name of counter within subsystem, e.g. "num_strays", + * may be empty. + * @param schema if true, output schema instead of current data. + * @param histograms if true, dump histogram values, + * if false dump all non-histogram counters + */ +void LabeledPerfCountersCollectionImpl::dump_formatted_generic( + Formatter *f, + bool schema, + bool histograms, + const std::string &logger, + const std::string &counter) const +{ + f->open_object_section("perfcounter_collection"); + + for (labeled_perf_counters_set_t::iterator l = m_loggers.begin(); + l != m_loggers.end(); ++l) { + // Optionally filter on logger name, pass through counter filter + if (logger.empty() || (*l)->get_name() == logger) { + (*l)->dump_formatted_generic(f, schema, histograms, counter); + } + } + f->close_section(); +} + +void LabeledPerfCountersCollectionImpl::with_counters(std::function fn) const +{ + fn(by_path); +} + +// --------------------------- + +LabeledPerfCounters::~LabeledPerfCounters() +{ +} + +void LabeledPerfCounters::inc(int idx, uint64_t amt) +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return; +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + if (!(data.type & PERFCOUNTER_U64)) + return; + if (data.type & PERFCOUNTER_LONGRUNAVG) { + data.avgcount++; + data.u64 += amt; + data.avgcount2++; + } else { + data.u64 += amt; + } + data.accessed = true; +} + +void LabeledPerfCounters::dec(int idx, uint64_t amt) +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return; +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + ceph_assert(!(data.type & PERFCOUNTER_LONGRUNAVG)); + if (!(data.type & PERFCOUNTER_U64)) + return; + data.u64 -= amt; +} + +void LabeledPerfCounters::set(int idx, uint64_t amt) +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return; +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + if (!(data.type & PERFCOUNTER_U64)) + return; + + ANNOTATE_BENIGN_RACE_SIZED(&data.u64, sizeof(data.u64), + "perf counter atomic"); + if (data.type & PERFCOUNTER_LONGRUNAVG) { + data.avgcount++; + data.u64 = amt; + data.avgcount2++; + } else { + data.u64 = amt; + } +} + +uint64_t LabeledPerfCounters::get(int idx) const +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return 0; +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + const perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + if (!(data.type & PERFCOUNTER_U64)) + return 0; + return data.u64; +} + +void LabeledPerfCounters::tinc(int idx, utime_t amt) +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return; +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + if (!(data.type & PERFCOUNTER_TIME)) + return; + if (data.type & PERFCOUNTER_LONGRUNAVG) { + data.avgcount++; + data.u64 += amt.to_nsec(); + data.avgcount2++; + } else { + data.u64 += amt.to_nsec(); + } +} + +void LabeledPerfCounters::tinc(int idx, ceph::timespan amt) +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return; +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + if (!(data.type & PERFCOUNTER_TIME)) + return; + if (data.type & PERFCOUNTER_LONGRUNAVG) { + data.avgcount++; + data.u64 += amt.count(); + data.avgcount2++; + } else { + data.u64 += amt.count(); + } +} + +void LabeledPerfCounters::tset(int idx, utime_t amt) +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return; +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + if (!(data.type & PERFCOUNTER_TIME)) + return; + data.u64 = amt.to_nsec(); + if (data.type & PERFCOUNTER_LONGRUNAVG) + ceph_abort(); +} + +utime_t LabeledPerfCounters::tget(int idx) const +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return utime_t(); +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + const perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + if (!(data.type & PERFCOUNTER_TIME)) + return utime_t(); + uint64_t v = data.u64; + return utime_t(v / 1000000000ull, v % 1000000000ull); +} + +void LabeledPerfCounters::hinc(int idx, int64_t x, int64_t y) +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return; +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + + perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + ceph_assert(data.type == (PERFCOUNTER_HISTOGRAM | PERFCOUNTER_COUNTER | PERFCOUNTER_U64)); + ceph_assert(data.histogram); + + data.histogram->inc(x, y); +} + +pair LabeledPerfCounters::get_tavg_ns(int idx) const +{ +#ifndef WITH_SEASTAR + if (!m_cct->_conf->perf) + return make_pair(0, 0); +#endif + + ceph_assert(idx > m_lower_bound); + ceph_assert(idx < m_upper_bound); + const perf_counter_data_any_d& data(m_data[idx - m_lower_bound - 1]); + if (!(data.type & PERFCOUNTER_TIME)) + return make_pair(0, 0); + if (!(data.type & PERFCOUNTER_LONGRUNAVG)) + return make_pair(0, 0); + pair a = data.read_avg(); + return make_pair(a.second, a.first); +} + +void LabeledPerfCounters::reset() +{ + perf_counter_data_vec_t::iterator d = m_data.begin(); + perf_counter_data_vec_t::iterator d_end = m_data.end(); + + while (d != d_end) { + d->reset(); + ++d; + } +} + +void LabeledPerfCounters::dump_formatted_generic(Formatter *f, bool schema, + bool histograms, const std::string &counter) const +{ + f->open_object_section(m_name.c_str()); + + for (perf_counter_data_vec_t::const_iterator d = m_data.begin(); + d != m_data.end(); ++d) { + if (!counter.empty() && counter != d->name) { + // Optionally filter on counter name + continue; + } + + // Switch between normal and histogram view + bool is_histogram = (d->type & PERFCOUNTER_HISTOGRAM) != 0; + if (is_histogram != histograms) { + continue; + } + + if (schema) { + f->open_object_section(d->name); + // we probably should not have exposed this raw field (with bit + // values), but existing plugins rely on it so we're stuck with + // it. + f->dump_int("type", d->type); + + if (d->type & PERFCOUNTER_COUNTER) { + f->dump_string("metric_type", "counter"); + } else { + f->dump_string("metric_type", "gauge"); + } + + if (d->type & PERFCOUNTER_LONGRUNAVG) { + if (d->type & PERFCOUNTER_TIME) { + f->dump_string("value_type", "real-integer-pair"); + } else { + f->dump_string("value_type", "integer-integer-pair"); + } + } else if (d->type & PERFCOUNTER_HISTOGRAM) { + if (d->type & PERFCOUNTER_TIME) { + f->dump_string("value_type", "real-2d-histogram"); + } else { + f->dump_string("value_type", "integer-2d-histogram"); + } + } else { + if (d->type & PERFCOUNTER_TIME) { + f->dump_string("value_type", "real"); + } else { + f->dump_string("value_type", "integer"); + } + } + + f->dump_string("description", d->description ? d->description : ""); + if (d->nick != NULL) { + f->dump_string("nick", d->nick); + } else { + f->dump_string("nick", ""); + } + f->dump_int("priority", get_adjusted_priority(d->prio)); + + if (d->unit == UNIT_NONE) { + f->dump_string("units", "none"); + } else if (d->unit == UNIT_BYTES) { + f->dump_string("units", "bytes"); + } + f->close_section(); + } else { + if (d->type & PERFCOUNTER_LONGRUNAVG) { + f->open_object_section(d->name); + pair a = d->read_avg(); + if (d->type & PERFCOUNTER_U64) { + f->dump_unsigned("avgcount", a.second); + f->dump_unsigned("sum", a.first); + } else if (d->type & PERFCOUNTER_TIME) { + f->dump_unsigned("avgcount", a.second); + f->dump_format_unquoted("sum", "%" PRId64 ".%09" PRId64, + a.first / 1000000000ull, + a.first % 1000000000ull); + uint64_t count = a.second; + uint64_t sum_ns = a.first; + if (count) { + uint64_t avg_ns = sum_ns / count; + f->dump_format_unquoted("avgtime", "%" PRId64 ".%09" PRId64, + avg_ns / 1000000000ull, + avg_ns % 1000000000ull); + } else { + f->dump_format_unquoted("avgtime", "%" PRId64 ".%09" PRId64, 0, 0); + } + } else { + ceph_abort(); + } + f->close_section(); + } else if (d->type & PERFCOUNTER_HISTOGRAM) { + ceph_assert(d->type == (PERFCOUNTER_HISTOGRAM | PERFCOUNTER_COUNTER | PERFCOUNTER_U64)); + ceph_assert(d->histogram); + f->open_object_section(d->name); + d->histogram->dump_formatted(f); + f->close_section(); + } else { + if(d->accessed) { + uint64_t v = d->u64; + if (d->type & PERFCOUNTER_U64) { + f->dump_unsigned(d->name, v); + } else if (d->type & PERFCOUNTER_TIME) { + f->dump_format_unquoted(d->name, "%" PRId64 ".%09" PRId64, + v / 1000000000ull, + v % 1000000000ull); + } else { + ceph_abort(); + } + } + } + } + } + f->close_section(); +} + +const std::string &LabeledPerfCounters::get_name() const +{ + return m_name; +} + +LabeledPerfCounters::LabeledPerfCounters(CephContext *cct, const std::string &name, + int lower_bound, int upper_bound) + : m_cct(cct), + m_lower_bound(lower_bound), + m_upper_bound(upper_bound), + m_name(name) +#if !defined(WITH_SEASTAR) || defined(WITH_ALIEN) + , + m_lock_name(std::string("LabeledPerfCounters::") + name.c_str()), + m_lock(ceph::make_mutex(m_lock_name)) +#endif +{ + m_data.resize(upper_bound - lower_bound - 1); +} + +LabeledPerfCountersBuilder::LabeledPerfCountersBuilder(CephContext *cct, const std::string &name, + int first, int last) + : m_perf_counters(new LabeledPerfCounters(cct, name, first, last)) +{ +} + +LabeledPerfCountersBuilder::~LabeledPerfCountersBuilder() +{ + if (m_perf_counters) + delete m_perf_counters; + m_perf_counters = NULL; +} + +void LabeledPerfCountersBuilder::add_u64_counter( + int idx, const char *name, + const char *description, const char *nick, int prio, int unit) +{ + add_impl(idx, name, description, nick, prio, + PERFCOUNTER_U64 | PERFCOUNTER_COUNTER, unit); +} + +void LabeledPerfCountersBuilder::add_u64( + int idx, const char *name, + const char *description, const char *nick, int prio, int unit) +{ + add_impl(idx, name, description, nick, prio, PERFCOUNTER_U64, unit); +} + +void LabeledPerfCountersBuilder::add_u64_avg( + int idx, const char *name, + const char *description, const char *nick, int prio, int unit) +{ + add_impl(idx, name, description, nick, prio, + PERFCOUNTER_U64 | PERFCOUNTER_LONGRUNAVG, unit); +} + +void LabeledPerfCountersBuilder::add_time( + int idx, const char *name, + const char *description, const char *nick, int prio) +{ + add_impl(idx, name, description, nick, prio, PERFCOUNTER_TIME); +} + +void LabeledPerfCountersBuilder::add_time_avg( + int idx, const char *name, + const char *description, const char *nick, int prio) +{ + add_impl(idx, name, description, nick, prio, + PERFCOUNTER_TIME | PERFCOUNTER_LONGRUNAVG); +} + +void LabeledPerfCountersBuilder::add_u64_counter_histogram( + int idx, const char *name, + PerfHistogramCommon::axis_config_d x_axis_config, + PerfHistogramCommon::axis_config_d y_axis_config, + const char *description, const char *nick, int prio, int unit) +{ + add_impl(idx, name, description, nick, prio, + PERFCOUNTER_U64 | PERFCOUNTER_HISTOGRAM | PERFCOUNTER_COUNTER, unit, + std::unique_ptr>{new PerfHistogram<>{x_axis_config, y_axis_config}}); +} + +void LabeledPerfCountersBuilder::add_impl( + int idx, const char *name, + const char *description, const char *nick, int prio, int ty, int unit, + std::unique_ptr> histogram) +{ + ceph_assert(idx > m_perf_counters->m_lower_bound); + ceph_assert(idx < m_perf_counters->m_upper_bound); + LabeledPerfCounters::perf_counter_data_vec_t &vec(m_perf_counters->m_data); + LabeledPerfCounters::perf_counter_data_any_d + &data(vec[idx - m_perf_counters->m_lower_bound - 1]); + ceph_assert(data.type == PERFCOUNTER_NONE); + data.name = name; + data.description = description; + // nick must be <= 4 chars + if (nick) { + ceph_assert(strlen(nick) <= 4); + } + data.nick = nick; + data.prio = prio ? prio : prio_default; + data.type = (enum perfcounter_type_d)ty; + data.unit = (enum unit_t) unit; + data.histogram = std::move(histogram); +} + +LabeledPerfCounters *LabeledPerfCountersBuilder::create_perf_counters() +{ + LabeledPerfCounters::perf_counter_data_vec_t::const_iterator d = m_perf_counters->m_data.begin(); + LabeledPerfCounters::perf_counter_data_vec_t::const_iterator d_end = m_perf_counters->m_data.end(); + for (; d != d_end; ++d) { + ceph_assert(d->type != PERFCOUNTER_NONE); + ceph_assert(d->type & (PERFCOUNTER_U64 | PERFCOUNTER_TIME)); + } + + LabeledPerfCounters *ret = m_perf_counters; + m_perf_counters = NULL; + return ret; +} + +} diff --git a/src/common/labeled_perf_counters.h b/src/common/labeled_perf_counters.h new file mode 100644 index 0000000000000..d35bfbe98d9f8 --- /dev/null +++ b/src/common/labeled_perf_counters.h @@ -0,0 +1,349 @@ +// -*- 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_LABELED_PERF_COUNTERS_H +#define CEPH_COMMON_LABELED_PERF_COUNTERS_H + +#include +#include +#include +#include +#include + +#include "common/perf_histogram.h" +#include "common/perf_counters.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 LabeledPerfCountersBuilder; + class LabeledPerfCounters; +} + +/* Class for constructing a LabeledPerfCounters 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 LabeledPerfCountersBuilder +{ +public: + LabeledPerfCountersBuilder(CephContext *cct, const std::string &name, + int first, int last); + ~LabeledPerfCountersBuilder(); + + // 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_; + } + + LabeledPerfCounters* create_perf_counters(); +private: + LabeledPerfCountersBuilder(const PerfCountersBuilder &rhs); + LabeledPerfCountersBuilder& 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> histogram = nullptr); + + LabeledPerfCounters *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 LabeledPerfCounters +{ +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 u64 = { 0 }; + std::atomic avgcount = { 0 }; + std::atomic avgcount2 = { 0 }; + std::unique_ptr> histogram; + bool accessed = false; + + 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. + std::pair read_avg() const { + uint64_t sum, count; + do { + count = avgcount2; + sum = u64; + } while (avgcount != count); + return { sum, count }; + } + }; + + template + struct avg_tracker { + std::pair last; + std::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 std::pair &next) { + last = cur; + cur = next; + } + }; + + ~LabeledPerfCounters(); + + 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 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: + LabeledPerfCounters(CephContext *cct, const std::string &name, + int lower_bound, int upper_bound); + LabeledPerfCounters(const PerfCounters &rhs); + LabeledPerfCounters& 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_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 LabeledPerfCountersBuilder; + friend class LabeledPerfCountersCollectionImpl; +}; + +class SortLabeledPerfCountersByName { +public: + bool operator()(const LabeledPerfCounters* lhs, const LabeledPerfCounters* rhs) const { + return (lhs->get_name() < rhs->get_name()); + } +}; + +typedef std::set labeled_perf_counters_set_t; + +/* + * PerfCountersCollectionImp manages PerfCounters objects for a Ceph process. + */ +class LabeledPerfCountersCollectionImpl +{ +public: + LabeledPerfCountersCollectionImpl(); + ~LabeledPerfCountersCollectionImpl(); + void add(LabeledPerfCounters *l); + void remove(LabeledPerfCounters *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 LabeledPerfCounterRef + { + public: + LabeledPerfCounters::perf_counter_data_any_d *data; + LabeledPerfCounters *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 = "") const; + + labeled_perf_counters_set_t m_loggers; + + CounterMap by_path; +}; + +} +#endif diff --git a/src/common/labeled_perf_counters_collection.cc b/src/common/labeled_perf_counters_collection.cc new file mode 100644 index 0000000000000..3da236bad8451 --- /dev/null +++ b/src/common/labeled_perf_counters_collection.cc @@ -0,0 +1,55 @@ +#include "common/labeled_perf_counters_collection.h" +#include "common/ceph_mutex.h" +#include "common/ceph_context.h" + +namespace ceph::common { +LabeledPerfCountersCollection::LabeledPerfCountersCollection(CephContext *cct) + : m_cct(cct), + m_lock(ceph::make_mutex("PerfCountersCollection")) +{ +} +LabeledPerfCountersCollection::~LabeledPerfCountersCollection() +{ + clear(); +} +void LabeledPerfCountersCollection::add(LabeledPerfCounters *l) +{ + std::lock_guard lck(m_lock); + perf_impl.add(l); +} +void LabeledPerfCountersCollection::remove(LabeledPerfCounters *l) +{ + std::lock_guard lck(m_lock); + perf_impl.remove(l); +} +void LabeledPerfCountersCollection::clear() +{ + std::lock_guard lck(m_lock); + perf_impl.clear(); +} +bool LabeledPerfCountersCollection::reset(const std::string &name) +{ + std::lock_guard lck(m_lock); + return perf_impl.reset(name); +} +void LabeledPerfCountersCollection::dump_formatted(ceph::Formatter *f, bool schema, + const std::string &logger, + const std::string &counter) +{ + std::lock_guard lck(m_lock); + perf_impl.dump_formatted(f,schema,logger,counter); +} +void LabeledPerfCountersCollection::dump_formatted_histograms(ceph::Formatter *f, bool schema, + const std::string &logger, + const std::string &counter) +{ + std::lock_guard lck(m_lock); + perf_impl.dump_formatted_histograms(f,schema,logger,counter); +} +void LabeledPerfCountersCollection::with_counters(std::function fn) const +{ + std::lock_guard lck(m_lock); + perf_impl.with_counters(fn); +} + +} diff --git a/src/common/labeled_perf_counters_collection.h b/src/common/labeled_perf_counters_collection.h new file mode 100644 index 0000000000000..6d2166b94d8bb --- /dev/null +++ b/src/common/labeled_perf_counters_collection.h @@ -0,0 +1,33 @@ +#pragma once + +#include "common/labeled_perf_counters.h" +#include "common/ceph_mutex.h" +#include "include/common_fwd.h" + +namespace ceph::common { +class LabeledPerfCountersCollection +{ + CephContext *m_cct; + + /** Protects perf_impl->m_loggers */ + mutable ceph::mutex m_lock; + LabeledPerfCountersCollectionImpl perf_impl; +public: + LabeledPerfCountersCollection(CephContext *cct); + ~LabeledPerfCountersCollection(); + void add(LabeledPerfCounters *l); + void remove(LabeledPerfCounters *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 = ""); + void dump_formatted_histograms(ceph::Formatter *f, bool schema, + const std::string &logger = "", + const std::string &counter = ""); + + void with_counters(std::function) const; + friend class PerfCountersCollectionTest; +}; +} diff --git a/src/common/perf_counters.cc b/src/common/perf_counters.cc index 420ff928c06ca..b4259254ee954 100644 --- a/src/common/perf_counters.cc +++ b/src/common/perf_counters.cc @@ -175,6 +175,7 @@ void PerfCounters::inc(int idx, uint64_t amt) } else { data.u64 += amt; } + data.accessed = true; } void PerfCounters::dec(int idx, uint64_t amt) @@ -451,16 +452,18 @@ void PerfCounters::dump_formatted_generic(Formatter *f, bool schema, d->histogram->dump_formatted(f); f->close_section(); } else { - uint64_t v = d->u64; - if (d->type & PERFCOUNTER_U64) { - f->dump_unsigned(d->name, v); - } else if (d->type & PERFCOUNTER_TIME) { - f->dump_format_unquoted(d->name, "%" PRId64 ".%09" PRId64, - v / 1000000000ull, - v % 1000000000ull); - } else { - ceph_abort(); - } + if(d->accessed) { + uint64_t v = d->u64; + if (d->type & PERFCOUNTER_U64) { + f->dump_unsigned(d->name, v); + } else if (d->type & PERFCOUNTER_TIME) { + f->dump_format_unquoted(d->name, "%" PRId64 ".%09" PRId64, + v / 1000000000ull, + v % 1000000000ull); + } else { + ceph_abort(); + } + } } } } diff --git a/src/common/perf_counters.h b/src/common/perf_counters.h index c5f69aa7ce31a..559b31e07cf9d 100644 --- a/src/common/perf_counters.h +++ b/src/common/perf_counters.h @@ -189,6 +189,7 @@ public: std::atomic avgcount = { 0 }; std::atomic avgcount2 = { 0 }; std::unique_ptr> histogram; + bool accessed = false; void reset() { diff --git a/src/common/perf_counters_cache.h b/src/common/perf_counters_cache.h index 872e41436a6f2..f11af43cd27be 100644 --- a/src/common/perf_counters_cache.h +++ b/src/common/perf_counters_cache.h @@ -60,7 +60,8 @@ public: plb.add_u64_counter(l_rgw_metrics_get_b, "get_b", "Size of gets", NULL, 8, UNIT_NONE); PerfCounters *counters = plb.create_perf_counters(); - cct->get_labeledperfcounters_collection()->add(counters); + //cct->get_labeledperfcounters_collection()->add(counters); + cct->get_perfcounters_collection()->add(counters); ref->perfcounters_instance = counters; //ref->collection = cct->get_perfcounters_collection(); //ref->collection->add(counters); diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 5f51d7e3aa89d..bc1daa5003b93 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -2323,7 +2323,7 @@ void RGWGetObj::execute(optional_yield y) } labels = ceph::perf_counters::cache_key("z_rgw", {{"Bucket", s->bucket_name}, {"User", s->user->get_display_name()}}); - //ldpp_dout(this, 20) << "labels for perf counters cache: " << labels << dendl; + ldpp_dout(this, 20) << "labels for perf counters cache for l_rgw_metrics_get_b: " << labels << dendl; perf_counters_cache->add(labels); perf_counters_cache->inc(labels, l_rgw_metrics_get_b, s->obj_size); @@ -4173,7 +4173,7 @@ void RGWPutObj::execute(optional_yield y) s->object->set_obj_size(ofs); std::string labels = ceph::perf_counters::cache_key("z_rgw", {{"Bucket", s->bucket_name}, {"User", s->user->get_display_name()}}); - //ldpp_dout(this, 20) << "labels for perf counters cache: " << labels << dendl; + ldpp_dout(this, 20) << "labels for perf counters cache for l_rgw_metrics_put_b: " << labels << dendl; perf_counters_cache->add(labels); perf_counters_cache->inc(labels, l_rgw_metrics_put_b, s->obj_size);