diff options
author | Henning Baldersheim <balder@yahoo-inc.com> | 2016-11-20 03:04:28 +0100 |
---|---|---|
committer | Henning Baldersheim <balder@yahoo-inc.com> | 2016-11-20 03:12:50 +0100 |
commit | c982c1c6cecd4818cf767a700bbc56387a80b32c (patch) | |
tree | 4a7d5e7290668697431e86e01d7b5dd14111de62 /metrics | |
parent | 81b959e4472035f9c44b3f6c8e54a6cc41ec4259 (diff) |
Avoid including th ewhole world just to get some metrics.
Diffstat (limited to 'metrics')
36 files changed, 1526 insertions, 1253 deletions
diff --git a/metrics/src/tests/countmetrictest.cpp b/metrics/src/tests/countmetrictest.cpp index 9c99e6c686b..82eb65caf25 100644 --- a/metrics/src/tests/countmetrictest.cpp +++ b/metrics/src/tests/countmetrictest.cpp @@ -48,7 +48,7 @@ void CountMetricTest::testLongCountMetric() CPPUNIT_ASSERT_EQUAL(Double(84), Double(o.getDoubleValue("value"))); CPPUNIT_ASSERT_EQUAL(int64_t(84), o.getLongValue("value")); - (void) expected; +// (void) expected; } } diff --git a/metrics/src/tests/loadmetrictest.cpp b/metrics/src/tests/loadmetrictest.cpp index b0755fa46e9..3585f4adf32 100644 --- a/metrics/src/tests/loadmetrictest.cpp +++ b/metrics/src/tests/loadmetrictest.cpp @@ -2,6 +2,8 @@ #include <vespa/fastos/fastos.h> #include <vespa/metrics/loadmetric.h> #include <vespa/metrics/valuemetric.h> +#include <vespa/metrics/loadmetric.hpp> +#include <vespa/metrics/summetric.hpp> #include <vespa/vdstestlib/cppunit/macros.h> namespace metrics { diff --git a/metrics/src/tests/metricmanagertest.cpp b/metrics/src/tests/metricmanagertest.cpp index 31471cc40ed..d3349d2e91d 100644 --- a/metrics/src/tests/metricmanagertest.cpp +++ b/metrics/src/tests/metricmanagertest.cpp @@ -3,6 +3,7 @@ #include <vespa/fastos/fastos.h> #include <vespa/log/log.h> #include <vespa/metrics/metrics.h> +#include <vespa/metrics/summetric.hpp> #include <vespa/metrics/xmlwriter.h> #include <vespa/metrics/jsonwriter.h> #include <vespa/metrics/textwriter.h> diff --git a/metrics/src/tests/snapshottest.cpp b/metrics/src/tests/snapshottest.cpp index 800d9f8465a..626b53f7d90 100644 --- a/metrics/src/tests/snapshottest.cpp +++ b/metrics/src/tests/snapshottest.cpp @@ -1,10 +1,12 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/fastos/fastos.h> -#include <vespa/log/log.h> #include <vespa/metrics/metrics.h> +#include <vespa/metrics/loadmetric.hpp> +#include <vespa/metrics/summetric.hpp> #include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/log/log.h> LOG_SETUP(".test.snapshot"); namespace metrics { diff --git a/metrics/src/tests/stresstest.cpp b/metrics/src/tests/stresstest.cpp index f88c3a4944c..cb069f27300 100644 --- a/metrics/src/tests/stresstest.cpp +++ b/metrics/src/tests/stresstest.cpp @@ -1,9 +1,11 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/fastos/fastos.h> -#include <vespa/log/log.h> #include <vespa/metrics/metrics.h> +#include <vespa/metrics/loadmetric.hpp> +#include <vespa/metrics/summetric.hpp> #include <vespa/vdstestlib/cppunit/macros.h> +#include <vespa/log/log.h> LOG_SETUP(".metrics.test.stress"); namespace metrics { diff --git a/metrics/src/tests/summetrictest.cpp b/metrics/src/tests/summetrictest.cpp index fd4d67d7f3b..3b5812f363f 100644 --- a/metrics/src/tests/summetrictest.cpp +++ b/metrics/src/tests/summetrictest.cpp @@ -1,7 +1,8 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/fastos/fastos.h> -#include <vespa/vdstestlib/cppunit/macros.h> #include <vespa/metrics/metrics.h> +#include <vespa/metrics/summetric.hpp> +#include <vespa/vdstestlib/cppunit/macros.h> namespace metrics { diff --git a/metrics/src/tests/valuemetrictest.cpp b/metrics/src/tests/valuemetrictest.cpp index afe4221481e..e50d4c69dfb 100644 --- a/metrics/src/tests/valuemetrictest.cpp +++ b/metrics/src/tests/valuemetrictest.cpp @@ -2,6 +2,7 @@ #include <vespa/fastos/fastos.h> #include <vespa/vdstestlib/cppunit/macros.h> #include <vespa/vespalib/objects/floatingpointtype.h> +#include <vespa/vespalib/text/stringtokenizer.h> #include <vespa/metrics/jsonwriter.h> #include <vespa/metrics/metricmanager.h> #include <vespa/metrics/valuemetric.h> diff --git a/metrics/src/vespa/metrics/CMakeLists.txt b/metrics/src/vespa/metrics/CMakeLists.txt index 7a1813d7e18..eeb90553309 100644 --- a/metrics/src/vespa/metrics/CMakeLists.txt +++ b/metrics/src/vespa/metrics/CMakeLists.txt @@ -1,17 +1,23 @@ # Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. vespa_add_library(metrics SOURCES - metric.cpp countmetric.cpp - valuemetric.cpp - metrictimer.cpp - metricset.cpp + countmetricvalues.cpp + jsonwriter.cpp + loadmetric.cpp + metric.cpp + memoryconsumption.cpp metricmanager.cpp + metricset.cpp metricsnapshot.cpp - jsonwriter.cpp + metrictimer.cpp + metricvalueset.cpp + state_api_adapter.cpp + summetric.cpp textwriter.cpp + valuemetric.cpp + valuemetricvalues.cpp xmlwriter.cpp - state_api_adapter.cpp INSTALL lib64 DEPENDS ) diff --git a/metrics/src/vespa/metrics/countmetric.cpp b/metrics/src/vespa/metrics/countmetric.cpp index d152c09a242..9f5ae741b30 100644 --- a/metrics/src/vespa/metrics/countmetric.cpp +++ b/metrics/src/vespa/metrics/countmetric.cpp @@ -1,7 +1,7 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/fastos/fastos.h> -#include <vespa/metrics/countmetric.h> +#include "countmetric.hpp" #include <vespa/log/log.h> @@ -22,4 +22,6 @@ AbstractCountMetric::sendLogCountEvent( EV_COUNT(name.c_str(), value); } +template class CountMetric<uint64_t, true>; + } // metrics diff --git a/metrics/src/vespa/metrics/countmetric.h b/metrics/src/vespa/metrics/countmetric.h index c9eef00f6ea..65281fb5b3a 100644 --- a/metrics/src/vespa/metrics/countmetric.h +++ b/metrics/src/vespa/metrics/countmetric.h @@ -13,17 +13,13 @@ #pragma once -#include <sstream> #include <vespa/metrics/metric.h> -#include <vespa/metrics/metricvalueset.h> -#include <vespa/vespalib/util/jsonstream.h> -#include <atomic> +#include "countmetricvalues.h" namespace metrics { struct AbstractCountMetric : public Metric { - virtual bool visit(MetricVisitor& visitor, - bool tagAsAutoGenerated = false) const + virtual bool visit(MetricVisitor& visitor, bool tagAsAutoGenerated = false) const { return visitor.visitCountMetric(*this, tagAsAutoGenerated); } @@ -56,38 +52,7 @@ protected: template<typename T, bool SumOnAdd> class CountMetric : public AbstractCountMetric { - struct Values : public MetricValueClass { - T _value; - - struct AtomicImpl { - std::atomic<T> _value {0}; - }; - - void relaxedStoreInto(AtomicImpl& target) const noexcept { - target._value.store(_value, std::memory_order_relaxed); - } - - void relaxedLoadFrom(const AtomicImpl& source) noexcept { - _value = source._value.load(std::memory_order_relaxed); - } - - Values() : _value(0) {} - - std::string toString() const { - std::ostringstream ost; - ost << _value; - return ost.str(); - } - double getDoubleValue(const stringref &) const - { return static_cast<double>(_value); } - uint64_t getLongValue(const stringref &) const - { return static_cast<uint64_t>(_value); } - void output(const std::string&, std::ostream& out) const - { out << _value; } - void output(const std::string&, vespalib::JsonStream& stream) const - { stream << _value; } - bool inUse() const { return (_value != 0); } - }; + using Values = CountMetricValues<T>; MetricValueSet<Values> _values; enum Flag { LOG_IF_UNSET = 2 }; @@ -160,198 +125,9 @@ public: virtual void addToPart(Metric&) const; virtual void addToSnapshot(Metric&, std::vector<Metric::LP>&) const; - }; typedef CountMetric<uint64_t, true> LongCountMetric; -template <typename T, bool SumOnAdd> -CountMetric<T, SumOnAdd>::CountMetric(const String& name, const String& tags, - const String& desc, MetricSet* owner) - : AbstractCountMetric(name, tags, desc, owner), - _values() -{ - _values.setFlag(LOG_IF_UNSET); -} - -template <typename T, bool SumOnAdd> -CountMetric<T, SumOnAdd>::CountMetric(const String& name, Tags dimensions, - const String& desc, MetricSet* owner) - : AbstractCountMetric(name, std::move(dimensions), desc, owner), - _values() -{ - _values.setFlag(LOG_IF_UNSET); -} - -template <typename T, bool SumOnAdd> -CountMetric<T, SumOnAdd>::CountMetric(const CountMetric<T, SumOnAdd>& other, - CopyType copyType, MetricSet* owner) - : AbstractCountMetric(other, owner), - _values(other._values, copyType == CLONE ? other._values.size() : 1) -{ -} - -template <typename T, bool SumOnAdd> -CountMetric<T, SumOnAdd>& -CountMetric<T, SumOnAdd>::operator+=(const CountMetric<T, SumOnAdd>& other) -{ - T otherValues(other.getValue()); - bool overflow; - Values values; - do { - values = _values.getValues(); - overflow = (values._value + otherValues < values._value); - values._value += otherValues; - } while (!_values.setValues(values)); - if (overflow) { - _values.reset(); - std::ostringstream ost; - ost << "Overflow in metric " << getPath() << " op +=. Resetting it."; - logWarning(ost.str().c_str()); - } - return *this; -} - -template <typename T, bool SumOnAdd> -CountMetric<T, SumOnAdd>& -CountMetric<T, SumOnAdd>::operator-=(const CountMetric<T, SumOnAdd>& other) -{ - T otherValues(other.getValue()); - bool underflow; - Values values; - do { - values = _values.getValues(); - underflow = (values._value - otherValues > values._value); - values._value -= otherValues; - } while (!_values.setValues(values)); - if (underflow) { - _values.reset(); - std::ostringstream ost; - ost << "Underflow in metric " << getPath() << " op -=. Resetting it."; - logWarning(ost.str().c_str()); - } - return *this; -} - -template <typename T, bool SumOnAdd> -void -CountMetric<T, SumOnAdd>::set(T value) -{ - Values values; - values._value = value; - while (!_values.setValues(values)) {} -} - -template <typename T, bool SumOnAdd> -void -CountMetric<T, SumOnAdd>::inc(T value) -{ - bool overflow; - Values values; - do { - values = _values.getValues(); - overflow = (values._value + value < values._value); - values._value += value; - } while (!_values.setValues(values)); - if (overflow) { - _values.reset(); - std::ostringstream ost; - ost << "Overflow in metric " << getPath() << ". Resetting it."; - logWarning(ost.str().c_str()); - } -} - -template <typename T, bool SumOnAdd> -void -CountMetric<T, SumOnAdd>::dec(T value) -{ - bool underflow; - Values values; - do { - values = _values.getValues(); - underflow = (values._value - value > values._value); - values._value -= value; - } while (!_values.setValues(values)); - if (underflow) { - _values.reset(); - std::ostringstream ost; - ost << "Underflow in metric " << getPath() << ". Resetting it."; - logWarning(ost.str().c_str()); - } -} - -template <typename T, bool SumOnAdd> -void -CountMetric<T, SumOnAdd>::addToSnapshot( - Metric& other, std::vector<Metric::LP>&) const -{ - CountMetric<T, SumOnAdd>& o( - reinterpret_cast<CountMetric<T, SumOnAdd>&>(other)); - o.inc(_values.getValues()._value); -} - -template <typename T, bool SumOnAdd> -void -CountMetric<T, SumOnAdd>::addToPart(Metric& other) const -{ - CountMetric<T, SumOnAdd>& o( - reinterpret_cast<CountMetric<T, SumOnAdd>&>(other)); - if (SumOnAdd) { - o.inc(_values.getValues()._value); - } else { - o.set((_values.getValues()._value + o._values.getValues()._value) / 2); - } -} - -template <typename T, bool SumOnAdd> -bool -CountMetric<T, SumOnAdd>::logEvent(const String& fullName) const -{ - Values values(_values.getValues()); - if (!logIfUnset() && values._value == 0) return false; - sendLogCountEvent( - fullName, static_cast<uint64_t>(values._value)); - return true; -} - -template <typename T, bool SumOnAdd> -void -CountMetric<T, SumOnAdd>::print(std::ostream& out, bool verbose, - const std::string& indent, - uint64_t secondsPassed) const -{ - (void) indent; - Values values(_values.getValues()); - if (values._value == 0 && !verbose) return; - out << this->_name << (SumOnAdd ? " count=" : " value=") << values._value; - if (SumOnAdd) { - if (secondsPassed != 0) { - double avgDiff = values._value / ((double) secondsPassed); - out << " average_change_per_second=" << avgDiff; - } - } -} - -template <typename T, bool SumOnAdd> -void -CountMetric<T, SumOnAdd>::addMemoryUsage(MemoryConsumption& mc) const -{ - ++mc._countMetricCount; - mc._countMetricValues += _values.getMemoryUsageAllocatedInternally(); - mc._countMetricMeta += sizeof(CountMetric<T, SumOnAdd>) - - sizeof(Metric); - Metric::addMemoryUsage(mc); -} - -template <typename T, bool SumOnAdd> -void -CountMetric<T, SumOnAdd>::printDebug(std::ostream& out, - const std::string& indent) const -{ - Values values(_values.getValues()); - out << "count=" << values._value << " "; - Metric::printDebug(out, indent); -} - } // metrics diff --git a/metrics/src/vespa/metrics/countmetric.hpp b/metrics/src/vespa/metrics/countmetric.hpp new file mode 100644 index 00000000000..f2f7b953a31 --- /dev/null +++ b/metrics/src/vespa/metrics/countmetric.hpp @@ -0,0 +1,199 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include "countmetric.h" +#include "memoryconsumption.h" +#include <sstream> + +namespace metrics { + +template <typename T, bool SumOnAdd> +CountMetric<T, SumOnAdd>::CountMetric(const String& name, const String& tags, + const String& desc, MetricSet* owner) + : AbstractCountMetric(name, tags, desc, owner), + _values() +{ + _values.setFlag(LOG_IF_UNSET); +} + +template <typename T, bool SumOnAdd> +CountMetric<T, SumOnAdd>::CountMetric(const String& name, Tags dimensions, + const String& desc, MetricSet* owner) + : AbstractCountMetric(name, std::move(dimensions), desc, owner), + _values() +{ + _values.setFlag(LOG_IF_UNSET); +} + +template <typename T, bool SumOnAdd> +CountMetric<T, SumOnAdd>::CountMetric(const CountMetric<T, SumOnAdd>& other, + CopyType copyType, MetricSet* owner) + : AbstractCountMetric(other, owner), + _values(other._values, copyType == CLONE ? other._values.size() : 1) +{ +} + +template <typename T, bool SumOnAdd> +CountMetric<T, SumOnAdd>& +CountMetric<T, SumOnAdd>::operator+=(const CountMetric<T, SumOnAdd>& other) +{ + T otherValues(other.getValue()); + bool overflow; + Values values; + do { + values = _values.getValues(); + overflow = (values._value + otherValues < values._value); + values._value += otherValues; + } while (!_values.setValues(values)); + if (overflow) { + _values.reset(); + std::ostringstream ost; + ost << "Overflow in metric " << getPath() << " op +=. Resetting it."; + logWarning(ost.str().c_str()); + } + return *this; +} + +template <typename T, bool SumOnAdd> +CountMetric<T, SumOnAdd>& +CountMetric<T, SumOnAdd>::operator-=(const CountMetric<T, SumOnAdd>& other) +{ + T otherValues(other.getValue()); + bool underflow; + Values values; + do { + values = _values.getValues(); + underflow = (values._value - otherValues > values._value); + values._value -= otherValues; + } while (!_values.setValues(values)); + if (underflow) { + _values.reset(); + std::ostringstream ost; + ost << "Underflow in metric " << getPath() << " op -=. Resetting it."; + logWarning(ost.str().c_str()); + } + return *this; +} + +template <typename T, bool SumOnAdd> +void +CountMetric<T, SumOnAdd>::set(T value) +{ + Values values; + values._value = value; + while (!_values.setValues(values)) {} +} + +template <typename T, bool SumOnAdd> +void +CountMetric<T, SumOnAdd>::inc(T value) +{ + bool overflow; + Values values; + do { + values = _values.getValues(); + overflow = (values._value + value < values._value); + values._value += value; + } while (!_values.setValues(values)); + if (overflow) { + _values.reset(); + std::ostringstream ost; + ost << "Overflow in metric " << getPath() << ". Resetting it."; + logWarning(ost.str().c_str()); + } +} + +template <typename T, bool SumOnAdd> +void +CountMetric<T, SumOnAdd>::dec(T value) +{ + bool underflow; + Values values; + do { + values = _values.getValues(); + underflow = (values._value - value > values._value); + values._value -= value; + } while (!_values.setValues(values)); + if (underflow) { + _values.reset(); + std::ostringstream ost; + ost << "Underflow in metric " << getPath() << ". Resetting it."; + logWarning(ost.str().c_str()); + } +} + +template <typename T, bool SumOnAdd> +void +CountMetric<T, SumOnAdd>::addToSnapshot( + Metric& other, std::vector<Metric::LP>&) const +{ + CountMetric<T, SumOnAdd>& o( + reinterpret_cast<CountMetric<T, SumOnAdd>&>(other)); + o.inc(_values.getValues()._value); +} + +template <typename T, bool SumOnAdd> +void +CountMetric<T, SumOnAdd>::addToPart(Metric& other) const +{ + CountMetric<T, SumOnAdd>& o( + reinterpret_cast<CountMetric<T, SumOnAdd>&>(other)); + if (SumOnAdd) { + o.inc(_values.getValues()._value); + } else { + o.set((_values.getValues()._value + o._values.getValues()._value) / 2); + } +} + +template <typename T, bool SumOnAdd> +bool +CountMetric<T, SumOnAdd>::logEvent(const String& fullName) const +{ + Values values(_values.getValues()); + if (!logIfUnset() && values._value == 0) return false; + sendLogCountEvent( + fullName, static_cast<uint64_t>(values._value)); + return true; +} + +template <typename T, bool SumOnAdd> +void +CountMetric<T, SumOnAdd>::print(std::ostream& out, bool verbose, + const std::string& indent, + uint64_t secondsPassed) const +{ + (void) indent; + Values values(_values.getValues()); + if (values._value == 0 && !verbose) return; + out << this->_name << (SumOnAdd ? " count=" : " value=") << values._value; + if (SumOnAdd) { + if (secondsPassed != 0) { + double avgDiff = values._value / ((double) secondsPassed); + out << " average_change_per_second=" << avgDiff; + } + } +} + +template <typename T, bool SumOnAdd> +void +CountMetric<T, SumOnAdd>::addMemoryUsage(MemoryConsumption& mc) const +{ + ++mc._countMetricCount; + mc._countMetricValues += _values.getMemoryUsageAllocatedInternally(); + mc._countMetricMeta += sizeof(CountMetric<T, SumOnAdd>) + - sizeof(Metric); + Metric::addMemoryUsage(mc); +} + +template <typename T, bool SumOnAdd> +void +CountMetric<T, SumOnAdd>::printDebug(std::ostream& out, + const std::string& indent) const +{ + Values values(_values.getValues()); + out << "count=" << values._value << " "; + Metric::printDebug(out, indent); +} + +} // metrics + diff --git a/metrics/src/vespa/metrics/countmetricvalues.cpp b/metrics/src/vespa/metrics/countmetricvalues.cpp new file mode 100644 index 00000000000..6be0f6a72d1 --- /dev/null +++ b/metrics/src/vespa/metrics/countmetricvalues.cpp @@ -0,0 +1,9 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "countmetricvalues.hpp" + +namespace metrics { + +template class CountMetricValues<uint64_t>; + +} // metrics diff --git a/metrics/src/vespa/metrics/countmetricvalues.h b/metrics/src/vespa/metrics/countmetricvalues.h new file mode 100644 index 00000000000..210668972a9 --- /dev/null +++ b/metrics/src/vespa/metrics/countmetricvalues.h @@ -0,0 +1,47 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * \class CountMetric + * \ingroup metrics + * + * \brief Metric representing a count. + * + * A counter metric have the following properties: + * - It can never decrease, only increase. + * - Logs its value as a count event. + * - When summing counts, the counts are added together. + */ + +#pragma once + +#include <vespa/metrics/metricvalueset.h> +#include <atomic> + +namespace metrics { + +template <typename T> +struct CountMetricValues : public MetricValueClass { + T _value; + + struct AtomicImpl { + std::atomic<T> _value {0}; + }; + + void relaxedStoreInto(AtomicImpl& target) const noexcept { + target._value.store(_value, std::memory_order_relaxed); + } + + void relaxedLoadFrom(const AtomicImpl& source) noexcept { + _value = source._value.load(std::memory_order_relaxed); + } + + CountMetricValues() : _value(0) {} + + std::string toString() const; + double getDoubleValue(const stringref &) const override; + uint64_t getLongValue(const stringref &) const override; + void output(const std::string&, std::ostream& out) const override; + void output(const std::string&, vespalib::JsonStream& stream) const override; + bool inUse() const { return (_value != 0); } +}; + +} // metrics diff --git a/metrics/src/vespa/metrics/countmetricvalues.hpp b/metrics/src/vespa/metrics/countmetricvalues.hpp new file mode 100644 index 00000000000..3d94b0b23ee --- /dev/null +++ b/metrics/src/vespa/metrics/countmetricvalues.hpp @@ -0,0 +1,37 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include "countmetricvalues.h" +#include <sstream> + +namespace metrics { + +template <typename T> +std::string +CountMetricValues<T>::toString() const { + std::ostringstream ost; + ost << _value; + return ost.str(); +} +template <typename T> +double +CountMetricValues<T>::getDoubleValue(const stringref &) const { + return static_cast<double>(_value); +} +template <typename T> +uint64_t +CountMetricValues<T>::getLongValue(const stringref &) const { + return static_cast<uint64_t>(_value); +} +template <typename T> +void +CountMetricValues<T>::output(const std::string&, std::ostream& out) const { + out << _value; +} +template <typename T> +void +CountMetricValues<T>::output(const std::string&, vespalib::JsonStream& stream) const { + stream << _value; +} + +} // metrics diff --git a/metrics/src/vespa/metrics/loadmetric.cpp b/metrics/src/vespa/metrics/loadmetric.cpp new file mode 100644 index 00000000000..861b1256bff --- /dev/null +++ b/metrics/src/vespa/metrics/loadmetric.cpp @@ -0,0 +1,20 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include "loadmetric.hpp" +#include "valuemetric.h" +#include "countmetric.h" +#include <vespa/vespalib/util/stringfmt.h> + +namespace metrics { + +vespalib::string +LoadType::toString() const { + return vespalib::make_string("%s(%u)", _name.c_str(), _id); +} + +template class LoadMetric<ValueMetric<int64_t, int64_t, false>>; +template class LoadMetric<ValueMetric<int64_t, int64_t, true>>; +template class LoadMetric<ValueMetric<double, double, false>>; +template class LoadMetric<ValueMetric<double, double, true>>; +template class LoadMetric<CountMetric<uint64_t, true>>; + +} diff --git a/metrics/src/vespa/metrics/loadmetric.h b/metrics/src/vespa/metrics/loadmetric.h index dc0762dd161..c141c2d9e61 100644 --- a/metrics/src/vespa/metrics/loadmetric.h +++ b/metrics/src/vespa/metrics/loadmetric.h @@ -18,10 +18,8 @@ #pragma once -#include <vespa/metrics/metricset.h> -#include <vespa/metrics/summetric.h> -#include <vespa/vespalib/util/linkedptr.h> -#include <vespa/vespalib/util/stringfmt.h> +#include "metricset.h" +#include "summetric.h" #include <vespa/vespalib/stllike/hash_map.h> namespace metrics { @@ -30,15 +28,13 @@ class MetricSet; class LoadType { public: - typedef vespalib::string string; + using string = vespalib::string; LoadType(uint32_t id, const string& name) : _id(id), _name(name) {} uint32_t getId() const { return _id; } const string& getName() const { return _name; } - string toString() const { - return vespalib::make_string("%s(%u)", _name.c_str(), _id); - } + string toString() const; private: uint32_t _id; string _name; @@ -47,43 +43,18 @@ private: typedef std::vector<LoadType> LoadTypeSet; template<typename MetricType> -class LoadMetric : public metrics::MetricSet { - std::vector<metrics::Metric::LP> _ownerList; +class LoadMetric : public MetricSet { + std::vector<Metric::LP> _ownerList; typedef vespalib::LinkedPtr<MetricType> MetricTypeLP; vespalib::hash_map<uint32_t, MetricTypeLP> _metrics; - metrics::SumMetric<MetricType> _sum; + SumMetric<MetricType> _sum; public: /** * Create a load metric using the given metric as a template to how they * shuold look. They will get prefix names based on load types existing. */ - LoadMetric(const LoadTypeSet& loadTypes, const MetricType& metric, - metrics::MetricSet* owner = 0) - : MetricSet(metric.getName(), "", metric.getDescription(), owner), - _metrics(), - _sum("sum", "loadsum sum", "Sum of all load metrics", this) - { - _metrics.resize(loadTypes.size()); - // Currently, we only set tags and description on the metric set - // itself, to cut down on size of output when downloading metrics, - // and since matching tags of parent is just as good as matching - // them specifically. - setTags(metric.getTags()); - Tags noTags; - for (uint32_t i=0; i<loadTypes.size(); ++i) { - MetricTypeLP copy( - dynamic_cast<MetricType*>( - metric.clone(_ownerList, CLONE, 0, false))); - assert(copy.get()); - copy->setName(loadTypes[i].getName()); - copy->setTags(noTags); - _metrics[loadTypes[i].getId()] = copy; - registerMetric(*copy); - _sum.addMetricToSum(*copy); - } - metrics::trim(_ownerList); - } + LoadMetric(const LoadTypeSet& loadTypes, const MetricType& metric, MetricSet* owner = 0); /** * A load metric implements a copy constructor and a clone functions that @@ -92,38 +63,10 @@ public: * then assign the values to the new set. (Without screwing up copying as * the load metric alters this data in supplied metric) */ - LoadMetric(const LoadMetric<MetricType>& other, metrics::MetricSet* owner) - : MetricSet(other.getName(), "", other.getDescription(), owner), - _metrics(), - _sum("sum", "loadsum sum", "Sum of all load metrics", this) - { - _metrics.resize(2 * other._metrics.size()); - setTags(other.getTags()); - Tags noTags; - for (typename vespalib::hash_map<uint32_t, MetricTypeLP>::const_iterator - it = other._metrics.begin(); it != other._metrics.end(); ++it) - { - MetricTypeLP copy(dynamic_cast<MetricType*>( - it->second->clone(_ownerList, CLONE, 0, false))); - assert(copy.get()); - copy->setName(it->second->getName()); - copy->setTags(noTags); - _metrics[it->first] = copy; - registerMetric(*copy); - _sum.addMetricToSum(*copy); - } - metrics::trim(_ownerList); - } - - virtual Metric* clone(std::vector<Metric::LP>& ownerList, - CopyType copyType, MetricSet* owner, - bool includeUnused = false) const - { - if (copyType == INACTIVE) { - return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused); - } - return new LoadMetric<MetricType>(*this, owner); - } + LoadMetric(const LoadMetric<MetricType>& other, MetricSet* owner); + Metric* clone(std::vector<Metric::LP>& ownerList, + CopyType copyType, MetricSet* owner, + bool includeUnused = false) const override; MetricType& operator[](const LoadType& type) { return getMetric(type); } const MetricType& operator[](const LoadType& type) const @@ -136,31 +79,14 @@ public: if (it == _metrics.end()) { it = _metrics.find(0); assert(it != _metrics.end()); // Default should always exist - metric = it->second.get(); - assert(metric); - } else { - metric = it->second.get(); - assert(metric); } + metric = it->second.get(); + assert(metric); return *metric; } - virtual void addMemoryUsage(metrics::MemoryConsumption& mc) const { - ++mc._loadMetricCount; - mc._loadMetricMeta += sizeof(metrics::Metric::LP) * _ownerList.size(); - for (typename vespalib::hash_map<uint32_t, MetricTypeLP>::const_iterator - it = _metrics.begin(); it != _metrics.end(); ++it) - { - mc._loadMetricMeta += sizeof(uint32_t) + sizeof(MetricTypeLP); - } - _sum.addMemoryUsage(mc); - mc._loadMetricMeta += sizeof(LoadMetric<MetricType>) - - sizeof(metrics::MetricSet) - - sizeof(metrics::SumMetric<MetricType>); - metrics::MetricSet::addMemoryUsage(mc); - } - + void addMemoryUsage(MemoryConsumption& mc) const override; }; } // documentapi diff --git a/metrics/src/vespa/metrics/loadmetric.hpp b/metrics/src/vespa/metrics/loadmetric.hpp new file mode 100644 index 00000000000..68a0a4981a7 --- /dev/null +++ b/metrics/src/vespa/metrics/loadmetric.hpp @@ -0,0 +1,84 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include "loadmetric.h" +#include "memoryconsumption.h" + +namespace metrics { + +template<typename MetricType> +LoadMetric<MetricType>::LoadMetric(const LoadTypeSet& loadTypes, const MetricType& metric, MetricSet* owner) + : MetricSet(metric.getName(), "", metric.getDescription(), owner), + _metrics(), + _sum("sum", "loadsum sum", "Sum of all load metrics", this) +{ + _metrics.resize(loadTypes.size()); + // Currently, we only set tags and description on the metric set + // itself, to cut down on size of output when downloading metrics, + // and since matching tags of parent is just as good as matching + // them specifically. + setTags(metric.getTags()); + Tags noTags; + for (uint32_t i=0; i<loadTypes.size(); ++i) { + MetricTypeLP copy(dynamic_cast<MetricType*>(metric.clone(_ownerList, CLONE, 0, false))); + assert(copy.get()); + copy->setName(loadTypes[i].getName()); + copy->setTags(noTags); + _metrics[loadTypes[i].getId()] = copy; + registerMetric(*copy); + _sum.addMetricToSum(*copy); + } + trim(_ownerList); +} + +template<typename MetricType> +LoadMetric<MetricType>::LoadMetric(const LoadMetric<MetricType>& other, MetricSet* owner) + : MetricSet(other.getName(), "", other.getDescription(), owner), + _metrics(), + _sum("sum", "loadsum sum", "Sum of all load metrics", this) +{ + _metrics.resize(2 * other._metrics.size()); + setTags(other.getTags()); + Tags noTags; + for (const auto & metric : _metrics) { + MetricTypeLP copy(dynamic_cast<MetricType*>(metric.second->clone(_ownerList, CLONE, 0, false))); + assert(copy.get()); + copy->setName(metric.second->getName()); + copy->setTags(noTags); + _metrics[metric.first] = copy; + registerMetric(*copy); + _sum.addMetricToSum(*copy); + } + trim(_ownerList); +} + +template<typename MetricType> +Metric* +LoadMetric<MetricType>::clone(std::vector<Metric::LP>& ownerList, + CopyType copyType, MetricSet* owner, + bool includeUnused) const +{ + if (copyType == INACTIVE) { + return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused); + } + return new LoadMetric<MetricType>(*this, owner); +} + +template<typename MetricType> +void +LoadMetric<MetricType>::addMemoryUsage(MemoryConsumption& mc) const { + ++mc._loadMetricCount; + mc._loadMetricMeta += sizeof(Metric::LP) * _ownerList.size(); + for (const auto & unused : _metrics) { + (void) unused; + mc._loadMetricMeta += sizeof(uint32_t) + sizeof(MetricTypeLP); + } + _sum.addMemoryUsage(mc); + mc._loadMetricMeta += sizeof(LoadMetric<MetricType>) + - sizeof(MetricSet) + - sizeof(SumMetric<MetricType>); + MetricSet::addMemoryUsage(mc); +} + +} + diff --git a/metrics/src/vespa/metrics/memoryconsumption.cpp b/metrics/src/vespa/metrics/memoryconsumption.cpp new file mode 100644 index 00000000000..883a20e3f78 --- /dev/null +++ b/metrics/src/vespa/metrics/memoryconsumption.cpp @@ -0,0 +1,113 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include "memoryconsumption.h" +#include <sstream> + +namespace metrics { + +MemoryConsumption::MemoryConsumption() { + memset(&_consumerCount, 0, reinterpret_cast<size_t>(&_seenStrings) - reinterpret_cast<size_t>(&_consumerCount)); + _seenStrings.resize(1000); +} + +uint32_t +MemoryConsumption::getStringMemoryUsage(const std::string& s, uint32_t& uniqueCount) { + ++_totalStringCount; + const char* internalString = s.c_str(); + if (_seenStrings.find(internalString) != _seenStrings.end()) { + return 0; + } + ++uniqueCount; + _seenStrings.insert(internalString); + return s.capacity(); +} + +void +MemoryConsumption::addSnapShotUsage(const std::string& name, uint32_t usage) { + _snapShotUsage.push_back(std::pair<std::string, uint32_t>(name, usage)); +} + +uint32_t +MemoryConsumption::getTotalMemoryUsage() const { + return _consumerId + _consumerMetricIds + _consumerMeta + + _snapshotSetMeta + _snapshotName + _snapshotMeta + _metricMeta + + _metricName + _metricPath + _metricDescription + + _metricTags + _metricSetMeta + _nameHash + _nameHashStrings + + _metricSetOrder + _countMetricValues + _countMetricMeta + + _valueMetricValues + _valueMetricMeta + _sumMetricMeta + + _sumMetricParentPath + _loadMetricMeta; +} + +void +MemoryConsumption::print(std::ostream& out, bool verbose, + const std::string& indent) const +{ + (void) verbose; + std::string newl = "\n" + indent + " "; + out << "MemoryConsumption(" + << newl << "Total memory used: " << bval(getTotalMemoryUsage()) + << newl << "Consumer count: " << _consumerCount + << newl << "Consumer ids: " << bval(_consumerId) + << newl << "Consumer metric count: " << _consumerMetricsInTotal + << newl << "Consumer metric ids: " << bval(_consumerMetricIds) + << newl << "Consumer meta: " << bval(_consumerMeta) + << newl << "Name hash: " << bval(_nameHash) + << newl << "Name hash strings: " << bval(_nameHashStrings) + << newl << "Snapshot set count: " << _snapshotSetCount + << newl << "Snapshot set meta: " << bval(_snapshotSetMeta) + << newl << "Snapshot count: " << _snapshotCount + << newl << "Snapshot name: " << bval(_snapshotName) + << newl << "Snapshot meta: " << bval(_snapshotMeta) + << newl << "Metric count: " << _metricCount + << newl << "Metric meta: " << bval(_metricMeta) + << newl << "Metric names: " << bval(_metricName) + << newl << "Metric paths: " << bval(_metricPath) + << newl << "Metric descriptions: " << bval(_metricDescription) + << newl << "Metric tag count: " << _metricTagCount + << newl << "Metric tags: " << bval(_metricTags) + << newl << "Metric set count: " << _metricSetCount + << newl << "Metric set meta: " << bval(_metricSetMeta) + << newl << "Metric set order list: " << bval(_metricSetOrder) + << newl << "Count metric count: " << _countMetricCount + << newl << "Count metric values: " << bval(_countMetricValues) + << newl << "Count metric meta: " << bval(_countMetricMeta) + << newl << "Value metric count: " << _valueMetricCount + << newl << "Value metric values: " << bval(_valueMetricValues) + << newl << "Value metric meta: " << bval(_valueMetricMeta) + << newl << "Sum metric count: " << _sumMetricCount + << newl << "Sum metric meta: " << bval(_sumMetricMeta) + << newl << "Sum metric parent path: " << bval(_sumMetricParentPath) + << newl << "Load metric count: " << _loadMetricCount + << newl << "Load metric meta: " << bval(_loadMetricMeta) + << newl << "Unique string count: " << _seenStrings.size() + << newl << "Strings stored: " << _totalStringCount + << newl << "Unique consumer ids: " << _consumerIdUnique + << newl << "Unique cons metric ids: " << _consumerMetricIdsUnique + << newl << "Unique snapshot names: " << _snapshotNameUnique + << newl << "Unique metric names: " << _metricNameUnique + << newl << "Unique metric paths: " << _metricPathUnique + << newl << "Unique metric descs: " << _metricDescriptionUnique + << newl << "Unique metric tags: " << _metricTagsUnique + << newl << "Unique sum metric paths: " << _sumMetricParentPathUnique + << newl << "Unique name hash strings: " << _nameHashUnique; + for (uint32_t i=0; i<_snapShotUsage.size(); ++i) { + out << newl << "Snapshot " << _snapShotUsage[i].first << ": " + << bval(_snapShotUsage[i].second); + } + out << "\n" << indent << ")"; +} + +std::string +MemoryConsumption::bval(uint32_t bytes) { + std::ostringstream ost; + if (bytes < 10 * 1024) { + ost << bytes << " B"; + } else if (bytes < 10 * 1024 * 1024) { + ost << (bytes / 1024) << " kB"; + } else { + ost << (bytes / (1024 * 1024)) << " MB"; + } + return ost.str(); +} + +} // metrics + diff --git a/metrics/src/vespa/metrics/memoryconsumption.h b/metrics/src/vespa/metrics/memoryconsumption.h index a96ad4d3e43..3cbd8ef1e36 100644 --- a/metrics/src/vespa/metrics/memoryconsumption.h +++ b/metrics/src/vespa/metrics/memoryconsumption.h @@ -83,109 +83,16 @@ struct MemoryConsumption : public vespalib::Printable { vespalib::hash_set<const void*> _seenStrings; std::vector<std::pair<std::string, uint32_t> > _snapShotUsage; - MemoryConsumption() { - memset(&_consumerCount, 0, - reinterpret_cast<size_t>(&_seenStrings) - - reinterpret_cast<size_t>(&_consumerCount)); - _seenStrings.resize(1000); - } + MemoryConsumption(); /** Get memory usage of a string that is not included when doing sizeof */ - uint32_t getStringMemoryUsage(const std::string& s, uint32_t& uniqueCount) { - ++_totalStringCount; - const char* internalString = s.c_str(); - if (_seenStrings.find(internalString) != _seenStrings.end()) { - return 0; - } - ++uniqueCount; - _seenStrings.insert(internalString); - return s.capacity(); - } - - void addSnapShotUsage(const std::string& name, uint32_t usage) { - _snapShotUsage.push_back(std::pair<std::string, uint32_t>(name, usage)); - } - - uint32_t getTotalMemoryUsage() const { - return _consumerId + _consumerMetricIds + _consumerMeta - + _snapshotSetMeta + _snapshotName + _snapshotMeta + _metricMeta - + _metricName + _metricPath + _metricDescription - + _metricTags + _metricSetMeta + _nameHash + _nameHashStrings - + _metricSetOrder + _countMetricValues + _countMetricMeta - + _valueMetricValues + _valueMetricMeta + _sumMetricMeta - + _sumMetricParentPath + _loadMetricMeta; - } - - virtual void print(std::ostream& out, bool verbose, - const std::string& indent) const - { - (void) verbose; - std::string newl = "\n" + indent + " "; - out << "MemoryConsumption(" - << newl << "Total memory used: " << bval(getTotalMemoryUsage()) - << newl << "Consumer count: " << _consumerCount - << newl << "Consumer ids: " << bval(_consumerId) - << newl << "Consumer metric count: " << _consumerMetricsInTotal - << newl << "Consumer metric ids: " << bval(_consumerMetricIds) - << newl << "Consumer meta: " << bval(_consumerMeta) - << newl << "Name hash: " << bval(_nameHash) - << newl << "Name hash strings: " << bval(_nameHashStrings) - << newl << "Snapshot set count: " << _snapshotSetCount - << newl << "Snapshot set meta: " << bval(_snapshotSetMeta) - << newl << "Snapshot count: " << _snapshotCount - << newl << "Snapshot name: " << bval(_snapshotName) - << newl << "Snapshot meta: " << bval(_snapshotMeta) - << newl << "Metric count: " << _metricCount - << newl << "Metric meta: " << bval(_metricMeta) - << newl << "Metric names: " << bval(_metricName) - << newl << "Metric paths: " << bval(_metricPath) - << newl << "Metric descriptions: " << bval(_metricDescription) - << newl << "Metric tag count: " << _metricTagCount - << newl << "Metric tags: " << bval(_metricTags) - << newl << "Metric set count: " << _metricSetCount - << newl << "Metric set meta: " << bval(_metricSetMeta) - << newl << "Metric set order list: " << bval(_metricSetOrder) - << newl << "Count metric count: " << _countMetricCount - << newl << "Count metric values: " << bval(_countMetricValues) - << newl << "Count metric meta: " << bval(_countMetricMeta) - << newl << "Value metric count: " << _valueMetricCount - << newl << "Value metric values: " << bval(_valueMetricValues) - << newl << "Value metric meta: " << bval(_valueMetricMeta) - << newl << "Sum metric count: " << _sumMetricCount - << newl << "Sum metric meta: " << bval(_sumMetricMeta) - << newl << "Sum metric parent path: " << bval(_sumMetricParentPath) - << newl << "Load metric count: " << _loadMetricCount - << newl << "Load metric meta: " << bval(_loadMetricMeta) - << newl << "Unique string count: " << _seenStrings.size() - << newl << "Strings stored: " << _totalStringCount - << newl << "Unique consumer ids: " << _consumerIdUnique - << newl << "Unique cons metric ids: " << _consumerMetricIdsUnique - << newl << "Unique snapshot names: " << _snapshotNameUnique - << newl << "Unique metric names: " << _metricNameUnique - << newl << "Unique metric paths: " << _metricPathUnique - << newl << "Unique metric descs: " << _metricDescriptionUnique - << newl << "Unique metric tags: " << _metricTagsUnique - << newl << "Unique sum metric paths: " << _sumMetricParentPathUnique - << newl << "Unique name hash strings: " << _nameHashUnique; - for (uint32_t i=0; i<_snapShotUsage.size(); ++i) { - out << newl << "Snapshot " << _snapShotUsage[i].first << ": " - << bval(_snapShotUsage[i].second); - } - out << "\n" << indent << ")"; - } - - static std::string bval(uint32_t bytes) { - std::ostringstream ost; - if (bytes < 10 * 1024) { - ost << bytes << " B"; - } else if (bytes < 10 * 1024 * 1024) { - ost << (bytes / 1024) << " kB"; - } else { - ost << (bytes / (1024 * 1024)) << " MB"; - } - return ost.str(); - } + uint32_t getStringMemoryUsage(const std::string& s, uint32_t& uniqueCount); + void addSnapShotUsage(const std::string& name, uint32_t usage); + uint32_t getTotalMemoryUsage() const; + virtual void print(std::ostream& out, bool verbose, const std::string& indent) const; + + static std::string bval(uint32_t bytes); }; } // metrics diff --git a/metrics/src/vespa/metrics/metric.cpp b/metrics/src/vespa/metrics/metric.cpp index f4ad47871e6..01ab46d1093 100644 --- a/metrics/src/vespa/metrics/metric.cpp +++ b/metrics/src/vespa/metrics/metric.cpp @@ -1,12 +1,13 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/fastos/fastos.h> -#include <vespa/metrics/metric.h> +#include "metric.h" -#include <vespa/metrics/countmetric.h> -#include <vespa/metrics/valuemetric.h> +#include "countmetric.h" +#include "valuemetric.h" +#include "metricset.h" +#include "namehash.h" #include <vespa/vespalib/text/stringtokenizer.h> #include <vespa/vespalib/util/exceptions.h> -#include <vespa/metrics/metricset.h> #include <algorithm> #include <iterator> diff --git a/metrics/src/vespa/metrics/metric.h b/metrics/src/vespa/metrics/metric.h index 7c42d187f30..40b267652fc 100644 --- a/metrics/src/vespa/metrics/metric.h +++ b/metrics/src/vespa/metrics/metric.h @@ -2,17 +2,9 @@ #pragma once #include <vespa/vespalib/util/printable.h> -#include <map> -#include <vespa/metrics/config-metricsmanager.h> -#include <vespa/metrics/memoryconsumption.h> -#include <vespa/metrics/namehash.h> -#include <string> -#include <vespa/vespalib/objects/cloneable.h> #include <vespa/vespalib/stllike/string.h> #include <vespa/vespalib/util/linkedptr.h> -#include <vespa/vespalib/util/jsonwriter.h> #include <vespa/vespalib/util/regexp.h> -#include <vespa/vespalib/text/stringtokenizer.h> namespace metrics { @@ -22,6 +14,8 @@ class Metric; class MetricSet; class MetricSnapshot; class XmlWriterMetricVisitor; +class MemoryConsumption; +class NameHash; template<typename T> void trim(std::vector<T>& v) { @@ -104,8 +98,7 @@ struct Tag Tag(vespalib::stringref k, vespalib::stringref v) : key(k), value(v) - { - } + { } }; class Metric : public vespalib::Printable diff --git a/metrics/src/vespa/metrics/metricset.cpp b/metrics/src/vespa/metrics/metricset.cpp index cbddccfa787..4fc925f0218 100644 --- a/metrics/src/vespa/metrics/metricset.cpp +++ b/metrics/src/vespa/metrics/metricset.cpp @@ -1,7 +1,8 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/fastos/fastos.h> -#include <vespa/metrics/metricset.h> +#include "metricset.h" +#include "memoryconsumption.h" #include <vespa/vespalib/stllike/hash_map.h> #include <vespa/vespalib/util/exceptions.h> #include <list> diff --git a/metrics/src/vespa/metrics/metricvalueset.cpp b/metrics/src/vespa/metrics/metricvalueset.cpp new file mode 100644 index 00000000000..bb243dfff36 --- /dev/null +++ b/metrics/src/vespa/metrics/metricvalueset.cpp @@ -0,0 +1,21 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "metricvalueset.hpp" +#include "valuemetricvalues.h" +#include "countmetricvalues.h" + +namespace metrics { + +std::string +MetricValueClass::toString(const std::string& id) { + std::ostringstream ost; + output(id, ost); + return ost.str(); +} + +template class MetricValueSet<CountMetricValues<uint64_t>>; +template class MetricValueSet<ValueMetricValues<int64_t, int64_t>>; +template class MetricValueSet<ValueMetricValues<double, double>>; + +} // metrics + diff --git a/metrics/src/vespa/metrics/metricvalueset.h b/metrics/src/vespa/metrics/metricvalueset.h index 98bc6160833..e934a5cdb6f 100644 --- a/metrics/src/vespa/metrics/metricvalueset.h +++ b/metrics/src/vespa/metrics/metricvalueset.h @@ -27,7 +27,6 @@ */ #pragma once -#include <sstream> #include <vector> #include <atomic> #include <vespa/vespalib/stllike/string.h> @@ -36,18 +35,15 @@ namespace metrics { struct MetricValueClass { - typedef std::unique_ptr<MetricValueClass> UP; + using UP = std::unique_ptr<MetricValueClass>; + using stringref = vespalib::stringref; virtual ~MetricValueClass() {} - virtual double getDoubleValue(const vespalib::stringref & id) const = 0; - virtual uint64_t getLongValue(const vespalib::stringref & id) const = 0; + virtual double getDoubleValue(const stringref & id) const = 0; + virtual uint64_t getLongValue(const stringref & id) const = 0; virtual void output(const std::string& id, std::ostream&) const = 0; virtual void output(const std::string& id, vespalib::JsonStream&) const = 0; - std::string toString(const std::string& id) { - std::ostringstream ost; - output(id, ost); - return ost.str(); - } + std::string toString(const std::string& id); }; template<typename ValueClass> @@ -63,42 +59,13 @@ class MetricValueSet { void validateCorrectValueSuperClass(const MetricValueClass&) {} public: - MetricValueSet(uint32_t copyCount = 3) - : _values(copyCount), - _activeValueIndex(0), - _flags(0) - { - } - - MetricValueSet(const MetricValueSet& other, uint32_t copyCount = 3) - : _values(copyCount), - _activeValueIndex(0), - _flags(other._flags.load(std::memory_order_relaxed)) - { - setValues(other.getValues()); - } - - MetricValueSet& operator=(const MetricValueSet& other) - { - setValues(other.getValues()); - return *this; - } + MetricValueSet(uint32_t copyCount = 3); + MetricValueSet(const MetricValueSet& other, uint32_t copyCount = 3); + MetricValueSet& operator=(const MetricValueSet& other); /** Get the current values. */ - ValueClass getValues() const { - ValueClass v{}; - if (!isReset()) { - // Must load with acquire to match release store in setValues. - // Note that despite being atomic on _individual fields_, this - // does not guarantee reading a consistent snapshot _across_ - // fields for any given metric. - const size_t readIndex( - _activeValueIndex.load(std::memory_order_acquire)); - v.relaxedLoadFrom(_values[readIndex]); - } - return v; - } + ValueClass getValues() const; /** * Get the current values from the metric. This function should not be @@ -107,54 +74,19 @@ public: * In which case, redo getValues(), apply the update again, and call * setValues() again. */ - bool setValues(const ValueClass& values) { - validateCorrectValueSuperClass(values); - // Only setter-thread can write _activeValueIndex, so relaxed - // load suffices. - uint32_t nextIndex = (_activeValueIndex.load(std::memory_order_relaxed) - + 1) % _values.size(); - // Reset flag is loaded/stored with relaxed semantics since it does not - // carry data dependencies. _activeValueIndex has a dependency on - // _values, however, so we must ensure that stores are published - // and loads acquired. - if (isReset()) { - removeFlag(RESET); - ValueClass resetValues{}; - resetValues.relaxedStoreInto(_values[nextIndex]); - _activeValueIndex.store(nextIndex, std::memory_order_release); - return false; - } else { - values.relaxedStoreInto(_values[nextIndex]); - _activeValueIndex.store(nextIndex, std::memory_order_release); - return true; - } - } + bool setValues(const ValueClass& values); /** * Retrieve and reset in a single operation, to minimize chance of * alteration in the process. */ - ValueClass getValuesAndReset() { - ValueClass result(getValues()); - setFlag(RESET); - return result; - } + ValueClass getValuesAndReset(); void reset() { setFlag(RESET); } - std::string toString() { - std::ostringstream ost; - ost << "MetricValueSet(reset=" << (isReset() ? "true" : "false") - << ", active " << _activeValueIndex; - ost << "\n empty: " << ValueClass().toString(); - for (uint32_t i=0; i<_values.size(); ++i) { - ost << "\n " << _values[i].toString(); - } - ost << "\n)"; - return ost.str(); - } + std::string toString(); uint32_t getMemoryUsageAllocatedInternally() const { return _values.capacity() * sizeof(ValueClass); diff --git a/metrics/src/vespa/metrics/metricvalueset.hpp b/metrics/src/vespa/metrics/metricvalueset.hpp new file mode 100644 index 00000000000..be62bab3f1e --- /dev/null +++ b/metrics/src/vespa/metrics/metricvalueset.hpp @@ -0,0 +1,97 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include "metricvalueset.h" +#include <sstream> + +namespace metrics { + +template<typename ValueClass> +MetricValueSet<ValueClass>::MetricValueSet(uint32_t copyCount) + : _values(copyCount), + _activeValueIndex(0), + _flags(0) +{ } + +template<typename ValueClass> +MetricValueSet<ValueClass>::MetricValueSet(const MetricValueSet& other, uint32_t copyCount) + : _values(copyCount), + _activeValueIndex(0), + _flags(other._flags.load(std::memory_order_relaxed)) +{ + setValues(other.getValues()); +} + +template<typename ValueClass> +MetricValueSet<ValueClass> & MetricValueSet<ValueClass>::operator=(const MetricValueSet& other) +{ + setValues(other.getValues()); + return *this; +} + +template<typename ValueClass> +ValueClass +MetricValueSet<ValueClass>::getValues() const { + ValueClass v{}; + if (!isReset()) { + // Must load with acquire to match release store in setValues. + // Note that despite being atomic on _individual fields_, this + // does not guarantee reading a consistent snapshot _across_ + // fields for any given metric. + const size_t readIndex(_activeValueIndex.load(std::memory_order_acquire)); + v.relaxedLoadFrom(_values[readIndex]); + } + return v; +} + +template<typename ValueClass> +bool +MetricValueSet<ValueClass>::setValues(const ValueClass& values) { + validateCorrectValueSuperClass(values); + // Only setter-thread can write _activeValueIndex, so relaxed + // load suffices. + uint32_t nextIndex = (_activeValueIndex.load(std::memory_order_relaxed) + + 1) % _values.size(); + // Reset flag is loaded/stored with relaxed semantics since it does not + // carry data dependencies. _activeValueIndex has a dependency on + // _values, however, so we must ensure that stores are published + // and loads acquired. + if (isReset()) { + removeFlag(RESET); + ValueClass resetValues{}; + resetValues.relaxedStoreInto(_values[nextIndex]); + _activeValueIndex.store(nextIndex, std::memory_order_release); + return false; + } else { + values.relaxedStoreInto(_values[nextIndex]); + _activeValueIndex.store(nextIndex, std::memory_order_release); + return true; + } +} + +template<typename ValueClass> +ValueClass +MetricValueSet<ValueClass>::getValuesAndReset() { + ValueClass result(getValues()); + setFlag(RESET); + return result; +} + +template<typename ValueClass> +std::string +MetricValueSet<ValueClass>::toString() { + std::ostringstream ost; + ost << "MetricValueSet(reset=" << (isReset() ? "true" : "false") + << ", active " << _activeValueIndex; +#if 0 + ost << "\n empty: " << ValueClass().toString("unknown"); + for (uint32_t i=0; i<_values.size(); ++i) { + ost << "\n " << _values[i].toString("unknown"); + } +#endif + ost << "\n)"; + return ost.str(); +} + +} // metrics + diff --git a/metrics/src/vespa/metrics/summetric.cpp b/metrics/src/vespa/metrics/summetric.cpp new file mode 100644 index 00000000000..a1658bf0781 --- /dev/null +++ b/metrics/src/vespa/metrics/summetric.cpp @@ -0,0 +1,16 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "summetric.hpp" +#include "valuemetric.h" +#include "countmetric.h" + +namespace metrics { + +template class SumMetric<ValueMetric<int64_t, int64_t, false>>; +template class SumMetric<ValueMetric<int64_t, int64_t, true>>; +template class SumMetric<ValueMetric<double, double, false>>; +template class SumMetric<ValueMetric<double, double, true>>; +template class SumMetric<CountMetric<uint64_t, true>>; + +} // metrics + diff --git a/metrics/src/vespa/metrics/summetric.h b/metrics/src/vespa/metrics/summetric.h index 6f1c92e5b4f..f40495a0334 100644 --- a/metrics/src/vespa/metrics/summetric.h +++ b/metrics/src/vespa/metrics/summetric.h @@ -13,13 +13,12 @@ #pragma once -#include <vespa/vespalib/util/sync.h> -#include <vespa/vespalib/util/exceptions.h> -#include <vespa/metrics/metric.h> -#include <vespa/metrics/metricset.h> +#include "metric.h" namespace metrics { +class MetricSet; + template<typename AddendMetric> class SumMetric : public Metric { @@ -43,14 +42,10 @@ private: std::vector<const AddendMetric*> _metricsToSum; public: - SumMetric(const String& name, const String& tags, - const String& description, MetricSet* owner = 0); - SumMetric(const SumMetric<AddendMetric>& other, - std::vector<Metric::LP>& ownerList, MetricSet* owner = 0); + SumMetric(const String& name, const String& tags, const String& description, MetricSet* owner = 0); + SumMetric(const SumMetric<AddendMetric>& other, std::vector<Metric::LP>& ownerList, MetricSet* owner = 0); - virtual Metric* clone( - std::vector<Metric::LP>&, CopyType, MetricSet* owner, - bool includeUnused = false) const; + virtual Metric* clone( std::vector<Metric::LP>&, CopyType, MetricSet* owner, bool includeUnused = false) const; /** * If you want to support sums of collections of metrics that may @@ -65,16 +60,14 @@ public: virtual bool logEvent(const String& fullName) const; - virtual void print(std::ostream&, bool verbose, - const std::string& indent, uint64_t secondsPassed) const; + virtual void print(std::ostream&, bool verbose, const std::string& indent, uint64_t secondsPassed) const; virtual int64_t getLongValue(const stringref & id) const; virtual double getDoubleValue(const stringref & id) const; virtual void reset() {} - virtual bool visit(MetricVisitor& visitor, - bool tagAsAutoGenerated = false) const; + virtual bool visit(MetricVisitor& visitor, bool tagAsAutoGenerated = false) const; virtual bool used() const; @@ -82,341 +75,16 @@ public: virtual void printDebug(std::ostream&, const std::string& indent="") const; - virtual void addToPart(metrics::Metric&) const; - virtual void addToSnapshot(metrics::Metric&, - std::vector<Metric::LP>&) const; + virtual void addToPart(Metric&) const; + virtual void addToSnapshot(Metric&, std::vector<Metric::LP>&) const; private: friend class MetricManagerTest; std::pair<std::vector<Metric::LP>, Metric::LP> generateSum() const; - virtual void addTo(metrics::Metric&, - std::vector<Metric::LP>* ownerList) const; + virtual void addTo(Metric&, std::vector<Metric::LP>* ownerList) const; bool isAddendType(const Metric* m) const; }; -template<typename AddendMetric> -bool -SumMetric<AddendMetric>::visit(MetricVisitor& visitor, - bool tagAsAutoGenerated) const -{ - (void) tagAsAutoGenerated; - if (_metricsToSum.empty()) return true; - std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); - return sum.second->visit(visitor, true); -} - -template<typename AddendMetric> -SumMetric<AddendMetric>::SumMetric(const String& name, const String& tags, - const String& description, MetricSet* owner) - : Metric(name, tags, description, owner), - _startValue(), - _metricsToSum() -{ -} - -template<typename AddendMetric> -SumMetric<AddendMetric>::SumMetric(const SumMetric<AddendMetric>& other, - std::vector<Metric::LP>& ownerList, - MetricSet* owner) - : Metric(other, owner), - _startValue(other._startValue), - _metricsToSum() -{ - (void) ownerList; - if (other._owner == 0) { - throw vespalib::IllegalStateException( - "Cannot copy a sum metric not registered in a metric set, as " - "we need to use parent to detect new metrics to point to.", - VESPA_STRLOC); - } - if (owner == 0) { - throw vespalib::IllegalStateException( - "Cannot copy a sum metric directly. One needs to at least " - "include metric set above it in order to include metrics " - "summed.", VESPA_STRLOC); - } - std::vector<String> parentPath(other._owner->getPathVector()); - _metricsToSum.reserve(other._metricsToSum.size()); - for (typename std::vector<const AddendMetric*>::const_iterator it - = other._metricsToSum.begin(); - it != other._metricsToSum.end(); ++it) - { - std::vector<String> addendPath((**it).getPathVector()); - MetricSet* newAddendParent = owner; - for (uint32_t i=parentPath.size(), n=addendPath.size() - 1; i<n; ++i) { - Metric* child = newAddendParent->getMetric(addendPath[i]); - if (child == 0) { - throw vespalib::IllegalStateException( - "Metric " + addendPath[i] + " in metric set " - + newAddendParent->getPath() + " was expected to " - "exist. This sounds like a bug.", VESPA_STRLOC); - } - if (!child->isMetricSet()) { - throw vespalib::IllegalStateException( - "Metric " + addendPath[i] + " in metric set " - + newAddendParent->getPath() + " was expected to be a " - "metric set. This sounds like a bug.", VESPA_STRLOC); - } - newAddendParent = static_cast<MetricSet*>(child); - } - Metric* child = newAddendParent->getMetric( - addendPath[addendPath.size() - 1]); - if (child == 0) { - throw vespalib::IllegalStateException( - "Metric " + addendPath[addendPath.size() - 1] + " in " - "metric set " + newAddendParent->getPath() + " was " - "expected to exist. This sounds like a bug.", VESPA_STRLOC); - } - AddendMetric* am(dynamic_cast<AddendMetric*>(child)); - if (am == 0) { - throw vespalib::IllegalStateException( - "Metric " + child->getPath() + " is of wrong type for sum " - + other.getPath() + ". This sounds like a bug.", - VESPA_STRLOC); - } - _metricsToSum.push_back(am); - } -} - -template<typename AddendMetric> -Metric* -SumMetric<AddendMetric>::clone(std::vector<Metric::LP>& ownerList, - CopyType copyType, MetricSet* owner, - bool includeUnused) const -{ - (void) includeUnused; - if (_metricsToSum.empty() && _startValue.get() == 0) { - throw vespalib::IllegalStateException( - "Attempted to clone sum metric without any children or start value. " - "This is currently illegal, to avoid needing to be able to " - "construct a metric of appropriate type without having a " - "template. (Hard to know how to construct any metric.", - VESPA_STRLOC); - } - if (copyType == CLONE) { - return new SumMetric<AddendMetric>(*this, ownerList, owner); - } - // Else we're generating an inactive copy by evaluating sum - typename std::vector<const AddendMetric*>::const_iterator it( - _metricsToSum.begin()); - // Clone start value or first child and use as accumulator - // As the metric cloned will have wrong info, we have to wait to - // register it in parent until we have fixed that. - Metric *m = 0; - if (_startValue.get() != 0) { - m = _startValue->getStartValue().clone(ownerList, INACTIVE, 0, true); - } else { - m = (**it).clone(ownerList, INACTIVE, 0, true); - ++it; - } - m->setName(getName()); - m->setDescription(getDescription()); - m->setTags(getTags()); - if (owner != 0) owner->registerMetric(*m); - // Add the others to the metric cloned. - for (; it != _metricsToSum.end(); ++it) { - (**it).addToPart(*m); - } - return m; -} - -template<typename AddendMetric> -void -SumMetric<AddendMetric>::addToPart(metrics::Metric& m) const -{ - std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); - sum.second->addToPart(m); -} - -template<typename AddendMetric> -void -SumMetric<AddendMetric>::addToSnapshot( - metrics::Metric& m, std::vector<Metric::LP>& ownerList) const -{ - if (isAddendType(&m)) { - // If the type to add to is an addend metric, it is part of an inactive - // copy we need to add data to. - std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); - sum.second->addToSnapshot(m, ownerList); - } -} - -template<typename AddendMetric> -void -SumMetric<AddendMetric>::addTo(metrics::Metric& m, - std::vector<Metric::LP>* ownerList) const -{ - if (ownerList == 0) { - std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); - sum.second->addToPart(m); - } else { - if (isAddendType(&m)) { - // If the type to add to is an addend metric, it is part of an - // inactive copy we need to add data to. - std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); - sum.second->addToSnapshot(m, *ownerList); - } - } -} - -template<typename AddendMetric> -void -SumMetric<AddendMetric>::addMetricToSum(const AddendMetric& metric) -{ - if (_owner == 0) { - throw vespalib::IllegalStateException( - "Sum metric needs to be registered in a parent metric set " - "prior to adding metrics to sum.", VESPA_STRLOC); - } - std::vector<String> sumParentPath(_owner->getPathVector()); - std::vector<String> addedPath(metric.getPathVector()); - bool error = false; - if (addedPath.size() <= sumParentPath.size()) { - error = true; - } else for (uint32_t i=0; i<sumParentPath.size(); ++i) { - if (sumParentPath[i] != addedPath[i]) { - error = true; - break; - } - } - if (error) { - throw vespalib::IllegalStateException( - "Metric added to sum is required to be a child of the sum's " - "direct parent metric set. (Need not be a direct child) " - "Metric set " + metric.getPath() + " is not a child of " - + _owner->getPath(), VESPA_STRLOC); - } - std::vector<const AddendMetric*> metrics(_metricsToSum.size() + 1); - for (uint32_t i=0; i<_metricsToSum.size(); ++i) { - metrics[i] = _metricsToSum[i]; - } - metrics[metrics.size() - 1] = &metric; - _metricsToSum.swap(metrics); - // Ensure we don't use extra memory - assert(_metricsToSum.capacity() == _metricsToSum.size()); -} - -template<typename AddendMetric> -void -SumMetric<AddendMetric>::removeMetricFromSum(const AddendMetric &metric) -{ - _metricsToSum.erase(remove(_metricsToSum.begin(), _metricsToSum.end(), - &metric)); -} - -template<typename AddendMetric> -std::pair<std::vector<Metric::LP>, Metric::LP> -SumMetric<AddendMetric>::generateSum() const -{ - std::pair<std::vector<Metric::LP>, Metric::LP> retVal; - Metric* m = clone(retVal.first, INACTIVE, 0, true); - m->setRegistered(_owner); - retVal.second.reset(m); - return retVal; -} - -template<typename AddendMetric> -int64_t -SumMetric<AddendMetric>::getLongValue(const stringref & id) const -{ - std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); - if (sum.second.get() == 0) return 0; - return sum.second->getLongValue(id); -} - -template<typename AddendMetric> -double -SumMetric<AddendMetric>::getDoubleValue(const stringref & id) const -{ - std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); - if (sum.second.get() == 0) return 0.0; - return sum.second->getDoubleValue(id); -} - -template<typename AddendMetric> -bool -SumMetric<AddendMetric>::logEvent(const String & fullName) const -{ - std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); - if (sum.second.get() == 0) return false; - return sum.second->logEvent(fullName); -} - -template<typename AddendMetric> -void -SumMetric<AddendMetric>::print(std::ostream& out, bool verbose, - const std::string& indent, - uint64_t secondsPassed) const -{ - std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); - if (sum.second.get() == 0) return; - sum.second->print(out, verbose, indent, secondsPassed); -} - -template<typename AddendMetric> -bool -SumMetric<AddendMetric>::used() const -{ - for(typename std::vector<const AddendMetric*>::const_iterator it( - _metricsToSum.begin()); it != _metricsToSum.end(); ++it) - { - if ((**it).used()) return true; - } - return false; -} - -template<typename AddendMetric> -void -SumMetric<AddendMetric>::addMemoryUsage(MemoryConsumption& mc) const -{ - ++mc._sumMetricCount; - mc._sumMetricMeta += sizeof(SumMetric<AddendMetric>) - sizeof(Metric) - + _metricsToSum.capacity() * sizeof(Metric*); - Metric::addMemoryUsage(mc); -} - -template<typename AddendMetric> -void -SumMetric<AddendMetric>::printDebug(std::ostream& out, - const std::string& indent) const -{ - out << "sum "; - Metric::printDebug(out, indent); - out << " {"; - for(typename std::vector<const AddendMetric*>::const_iterator it( - _metricsToSum.begin()); it != _metricsToSum.end(); ++it) - { - out << "\n" << indent << " "; - (**it).printDebug(out, indent + " "); - } - out << "}"; -} - -template<typename AddendMetric> -bool -SumMetric<AddendMetric>::isAddendType(const Metric* m) const -{ - // If metric to addend it a metric set, we can only check if target also is - // a metric set, as other type will be lost when going inactive. Is there a - // way to do this without using an actual instance? - if (_metricsToSum.empty() && _startValue.get() == 0) { - throw vespalib::IllegalStateException( - "Attempted to verify addend type for sum metric without any " - "children or start value.", VESPA_STRLOC); - } - const Metric* wantedType; - if (!_metricsToSum.empty()) { - wantedType = _metricsToSum[0]; - } else { - wantedType = &_startValue->getStartValue(); - } - if (wantedType->isMetricSet()) { - return (m->isMetricSet()); - } else { - return (dynamic_cast<const AddendMetric*>(m) != 0); - } -} - } // metrics diff --git a/metrics/src/vespa/metrics/summetric.hpp b/metrics/src/vespa/metrics/summetric.hpp new file mode 100644 index 00000000000..59739779901 --- /dev/null +++ b/metrics/src/vespa/metrics/summetric.hpp @@ -0,0 +1,335 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include "summetric.h" +#include "metricset.h" +#include "memoryconsumption.h" +#include <vespa/vespalib/util/exceptions.h> + +namespace metrics { + +template<typename AddendMetric> +bool +SumMetric<AddendMetric>::visit(MetricVisitor& visitor, + bool tagAsAutoGenerated) const +{ + (void) tagAsAutoGenerated; + if (_metricsToSum.empty()) return true; + std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); + return sum.second->visit(visitor, true); +} + +template<typename AddendMetric> +SumMetric<AddendMetric>::SumMetric(const String& name, const String& tags, + const String& description, MetricSet* owner) + : Metric(name, tags, description, owner), + _startValue(), + _metricsToSum() +{ +} + +template<typename AddendMetric> +SumMetric<AddendMetric>::SumMetric(const SumMetric<AddendMetric>& other, + std::vector<Metric::LP>& ownerList, + MetricSet* owner) + : Metric(other, owner), + _startValue(other._startValue), + _metricsToSum() +{ + (void) ownerList; + if (other._owner == 0) { + throw vespalib::IllegalStateException( + "Cannot copy a sum metric not registered in a metric set, as " + "we need to use parent to detect new metrics to point to.", + VESPA_STRLOC); + } + if (owner == 0) { + throw vespalib::IllegalStateException( + "Cannot copy a sum metric directly. One needs to at least " + "include metric set above it in order to include metrics " + "summed.", VESPA_STRLOC); + } + std::vector<String> parentPath(other._owner->getPathVector()); + _metricsToSum.reserve(other._metricsToSum.size()); + for (typename std::vector<const AddendMetric*>::const_iterator it + = other._metricsToSum.begin(); + it != other._metricsToSum.end(); ++it) + { + std::vector<String> addendPath((**it).getPathVector()); + MetricSet* newAddendParent = owner; + for (uint32_t i=parentPath.size(), n=addendPath.size() - 1; i<n; ++i) { + Metric* child = newAddendParent->getMetric(addendPath[i]); + if (child == 0) { + throw vespalib::IllegalStateException( + "Metric " + addendPath[i] + " in metric set " + + newAddendParent->getPath() + " was expected to " + "exist. This sounds like a bug.", VESPA_STRLOC); + } + if (!child->isMetricSet()) { + throw vespalib::IllegalStateException( + "Metric " + addendPath[i] + " in metric set " + + newAddendParent->getPath() + " was expected to be a " + "metric set. This sounds like a bug.", VESPA_STRLOC); + } + newAddendParent = static_cast<MetricSet*>(child); + } + Metric* child = newAddendParent->getMetric( + addendPath[addendPath.size() - 1]); + if (child == 0) { + throw vespalib::IllegalStateException( + "Metric " + addendPath[addendPath.size() - 1] + " in " + "metric set " + newAddendParent->getPath() + " was " + "expected to exist. This sounds like a bug.", VESPA_STRLOC); + } + AddendMetric* am(dynamic_cast<AddendMetric*>(child)); + if (am == 0) { + throw vespalib::IllegalStateException( + "Metric " + child->getPath() + " is of wrong type for sum " + + other.getPath() + ". This sounds like a bug.", + VESPA_STRLOC); + } + _metricsToSum.push_back(am); + } +} + +template<typename AddendMetric> +Metric* +SumMetric<AddendMetric>::clone(std::vector<Metric::LP>& ownerList, + CopyType copyType, MetricSet* owner, + bool includeUnused) const +{ + (void) includeUnused; + if (_metricsToSum.empty() && _startValue.get() == 0) { + throw vespalib::IllegalStateException( + "Attempted to clone sum metric without any children or start value. " + "This is currently illegal, to avoid needing to be able to " + "construct a metric of appropriate type without having a " + "template. (Hard to know how to construct any metric.", + VESPA_STRLOC); + } + if (copyType == CLONE) { + return new SumMetric<AddendMetric>(*this, ownerList, owner); + } + // Else we're generating an inactive copy by evaluating sum + typename std::vector<const AddendMetric*>::const_iterator it( + _metricsToSum.begin()); + // Clone start value or first child and use as accumulator + // As the metric cloned will have wrong info, we have to wait to + // register it in parent until we have fixed that. + Metric *m = 0; + if (_startValue.get() != 0) { + m = _startValue->getStartValue().clone(ownerList, INACTIVE, 0, true); + } else { + m = (**it).clone(ownerList, INACTIVE, 0, true); + ++it; + } + m->setName(getName()); + m->setDescription(getDescription()); + m->setTags(getTags()); + if (owner != 0) owner->registerMetric(*m); + // Add the others to the metric cloned. + for (; it != _metricsToSum.end(); ++it) { + (**it).addToPart(*m); + } + return m; +} + +template<typename AddendMetric> +void +SumMetric<AddendMetric>::addToPart(Metric& m) const +{ + std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); + sum.second->addToPart(m); +} + +template<typename AddendMetric> +void +SumMetric<AddendMetric>::addToSnapshot( + Metric& m, std::vector<Metric::LP>& ownerList) const +{ + if (isAddendType(&m)) { + // If the type to add to is an addend metric, it is part of an inactive + // copy we need to add data to. + std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); + sum.second->addToSnapshot(m, ownerList); + } +} + +template<typename AddendMetric> +void +SumMetric<AddendMetric>::addTo(Metric& m, + std::vector<Metric::LP>* ownerList) const +{ + if (ownerList == 0) { + std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); + sum.second->addToPart(m); + } else { + if (isAddendType(&m)) { + // If the type to add to is an addend metric, it is part of an + // inactive copy we need to add data to. + std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); + sum.second->addToSnapshot(m, *ownerList); + } + } +} + +template<typename AddendMetric> +void +SumMetric<AddendMetric>::addMetricToSum(const AddendMetric& metric) +{ + if (_owner == 0) { + throw vespalib::IllegalStateException( + "Sum metric needs to be registered in a parent metric set " + "prior to adding metrics to sum.", VESPA_STRLOC); + } + std::vector<String> sumParentPath(_owner->getPathVector()); + std::vector<String> addedPath(metric.getPathVector()); + bool error = false; + if (addedPath.size() <= sumParentPath.size()) { + error = true; + } else for (uint32_t i=0; i<sumParentPath.size(); ++i) { + if (sumParentPath[i] != addedPath[i]) { + error = true; + break; + } + } + if (error) { + throw vespalib::IllegalStateException( + "Metric added to sum is required to be a child of the sum's " + "direct parent metric set. (Need not be a direct child) " + "Metric set " + metric.getPath() + " is not a child of " + + _owner->getPath(), VESPA_STRLOC); + } + std::vector<const AddendMetric*> metrics(_metricsToSum.size() + 1); + for (uint32_t i=0; i<_metricsToSum.size(); ++i) { + metrics[i] = _metricsToSum[i]; + } + metrics[metrics.size() - 1] = &metric; + _metricsToSum.swap(metrics); + // Ensure we don't use extra memory + assert(_metricsToSum.capacity() == _metricsToSum.size()); +} + +template<typename AddendMetric> +void +SumMetric<AddendMetric>::removeMetricFromSum(const AddendMetric &metric) +{ + _metricsToSum.erase(remove(_metricsToSum.begin(), _metricsToSum.end(), + &metric)); +} + +template<typename AddendMetric> +std::pair<std::vector<Metric::LP>, Metric::LP> +SumMetric<AddendMetric>::generateSum() const +{ + std::pair<std::vector<Metric::LP>, Metric::LP> retVal; + Metric* m = clone(retVal.first, INACTIVE, 0, true); + m->setRegistered(_owner); + retVal.second.reset(m); + return retVal; +} + +template<typename AddendMetric> +int64_t +SumMetric<AddendMetric>::getLongValue(const stringref & id) const +{ + std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); + if (sum.second.get() == 0) return 0; + return sum.second->getLongValue(id); +} + +template<typename AddendMetric> +double +SumMetric<AddendMetric>::getDoubleValue(const stringref & id) const +{ + std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); + if (sum.second.get() == 0) return 0.0; + return sum.second->getDoubleValue(id); +} + +template<typename AddendMetric> +bool +SumMetric<AddendMetric>::logEvent(const String & fullName) const +{ + std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); + if (sum.second.get() == 0) return false; + return sum.second->logEvent(fullName); +} + +template<typename AddendMetric> +void +SumMetric<AddendMetric>::print(std::ostream& out, bool verbose, + const std::string& indent, + uint64_t secondsPassed) const +{ + std::pair<std::vector<Metric::LP>, Metric::LP> sum(generateSum()); + if (sum.second.get() == 0) return; + sum.second->print(out, verbose, indent, secondsPassed); +} + +template<typename AddendMetric> +bool +SumMetric<AddendMetric>::used() const +{ + for(typename std::vector<const AddendMetric*>::const_iterator it( + _metricsToSum.begin()); it != _metricsToSum.end(); ++it) + { + if ((**it).used()) return true; + } + return false; +} + +template<typename AddendMetric> +void +SumMetric<AddendMetric>::addMemoryUsage(MemoryConsumption& mc) const +{ + ++mc._sumMetricCount; + mc._sumMetricMeta += sizeof(SumMetric<AddendMetric>) - sizeof(Metric) + + _metricsToSum.capacity() * sizeof(Metric*); + Metric::addMemoryUsage(mc); +} + +template<typename AddendMetric> +void +SumMetric<AddendMetric>::printDebug(std::ostream& out, + const std::string& indent) const +{ + out << "sum "; + Metric::printDebug(out, indent); + out << " {"; + for(typename std::vector<const AddendMetric*>::const_iterator it( + _metricsToSum.begin()); it != _metricsToSum.end(); ++it) + { + out << "\n" << indent << " "; + (**it).printDebug(out, indent + " "); + } + out << "}"; +} + +template<typename AddendMetric> +bool +SumMetric<AddendMetric>::isAddendType(const Metric* m) const +{ + // If metric to addend it a metric set, we can only check if target also is + // a metric set, as other type will be lost when going inactive. Is there a + // way to do this without using an actual instance? + if (_metricsToSum.empty() && _startValue.get() == 0) { + throw vespalib::IllegalStateException( + "Attempted to verify addend type for sum metric without any " + "children or start value.", VESPA_STRLOC); + } + const Metric* wantedType; + if (!_metricsToSum.empty()) { + wantedType = _metricsToSum[0]; + } else { + wantedType = &_startValue->getStartValue(); + } + if (wantedType->isMetricSet()) { + return (m->isMetricSet()); + } else { + return (dynamic_cast<const AddendMetric*>(m) != 0); + } +} + +} // metrics + diff --git a/metrics/src/vespa/metrics/textwriter.cpp b/metrics/src/vespa/metrics/textwriter.cpp index 0b5cb9a287b..259b8157d57 100644 --- a/metrics/src/vespa/metrics/textwriter.cpp +++ b/metrics/src/vespa/metrics/textwriter.cpp @@ -1,11 +1,11 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/metrics/textwriter.h> - -#include <vespa/metrics/countmetric.h> -#include <vespa/metrics/metricset.h> -#include <vespa/metrics/metricsnapshot.h> -#include <vespa/metrics/valuemetric.h> +#include "textwriter.h" +#include "countmetric.h" +#include "metricset.h" +#include "metricsnapshot.h" +#include "valuemetric.h" +#include <sstream> namespace metrics { diff --git a/metrics/src/vespa/metrics/textwriter.h b/metrics/src/vespa/metrics/textwriter.h index dadeb70a7fc..88fc807fd2f 100644 --- a/metrics/src/vespa/metrics/textwriter.h +++ b/metrics/src/vespa/metrics/textwriter.h @@ -2,8 +2,8 @@ #pragma once +#include "metric.h" #include <vespa/vespalib/util/regexp.h> -#include <vespa/metrics/metric.h> namespace metrics { diff --git a/metrics/src/vespa/metrics/valuemetric.cpp b/metrics/src/vespa/metrics/valuemetric.cpp index 3da48885054..1b00bf8cbeb 100644 --- a/metrics/src/vespa/metrics/valuemetric.cpp +++ b/metrics/src/vespa/metrics/valuemetric.cpp @@ -1,9 +1,7 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/fastos/fastos.h> -#include <vespa/metrics/valuemetric.h> -#include <atomic> - +#include "valuemetric.hpp" #include <vespa/log/log.h> LOG_SETUP(".metrics.metric.value"); @@ -46,4 +44,9 @@ AbstractValueMetric::logNonFiniteValueWarning() const } } +template class ValueMetric<double, double, true>; +template class ValueMetric<double, double, false>; +template class ValueMetric<int64_t, int64_t, true>; +template class ValueMetric<int64_t, int64_t, false>; + } // metrics diff --git a/metrics/src/vespa/metrics/valuemetric.h b/metrics/src/vespa/metrics/valuemetric.h index 9c9d750a1a8..1766c92e680 100644 --- a/metrics/src/vespa/metrics/valuemetric.h +++ b/metrics/src/vespa/metrics/valuemetric.h @@ -13,16 +13,8 @@ #pragma once -#include <vespa/vespalib/util/sync.h> -#include <vespa/vespalib/util/exceptions.h> -#include <sstream> -#include <type_traits> -#include <cmath> -#include <vespa/metrics/metric.h> -#include <vespa/metrics/metricvalueset.h> -#include <vespa/vespalib/util/jsonstream.h> -#include <vespa/vespalib/util/stringfmt.h> -#include <atomic> +#include "valuemetricvalues.h" +#include "metric.h" namespace metrics { @@ -54,98 +46,10 @@ protected: }; template<typename AvgVal, typename TotVal, bool SumOnAdd> -class ValueMetric - : public AbstractValueMetric +class ValueMetric : public AbstractValueMetric { - typedef Metric::String String; // Redefine so template finds it easy - - struct Values : MetricValueClass { - uint32_t _count; - AvgVal _min, _max, _last; - TotVal _total; - - struct AtomicImpl { - std::atomic<uint32_t> _count {0}; - std::atomic<AvgVal> _min {std::numeric_limits<AvgVal>::max()}; - std::atomic<AvgVal> _max {std::numeric_limits<AvgVal>::min()}; - std::atomic<AvgVal> _last {0}; - std::atomic<TotVal> _total {0}; - }; - - Values() - : _count(0), - _min(std::numeric_limits<AvgVal>::max()), - _max(std::numeric_limits<AvgVal>::min()), - _last(0), - _total(0) - { - // numeric_limits min() returns smallest positive number - // for signed floating point types. Haven't found a way to - // get minimum negative value, so using -1 * positive for those. - if (std::numeric_limits<AvgVal>::is_signed - && !std::numeric_limits<AvgVal>::is_integer) - { - _max = -1 * std::numeric_limits<AvgVal>::max(); - } - } - - void relaxedStoreInto(AtomicImpl& target) const noexcept { - target._count.store(_count, std::memory_order_relaxed); - target._min.store(_min, std::memory_order_relaxed); - target._max.store(_max, std::memory_order_relaxed); - target._last.store(_last, std::memory_order_relaxed); - target._total.store(_total, std::memory_order_relaxed); - } - - void relaxedLoadFrom(const AtomicImpl& source) noexcept { - _count = source._count.load(std::memory_order_relaxed); - _min = source._min.load(std::memory_order_relaxed); - _max = source._max.load(std::memory_order_relaxed); - _last = source._last.load(std::memory_order_relaxed); - _total = source._total.load(std::memory_order_relaxed); - } - - template<typename T> - T getValue(const stringref & id) const { - if (id == "last") return static_cast<T>(_last); - if (id == "count") return static_cast<T>(_count); - if (id == "total") return static_cast<T>(_total); - if (id == "min") return static_cast<T>(_count > 0 ? _min : 0); - if (id == "max") return static_cast<T>( _count > 0 ? _max : 0); - throw vespalib::IllegalArgumentException( - "No value " + vespalib::string(id) + " in value metric.", VESPA_STRLOC); - } - - double getDoubleValue(const stringref & id) const { - return getValue<double>(id); - } - uint64_t getLongValue(const stringref & id) const { - return getValue<uint64_t>(id); - } - void output(const std::string& id, std::ostream& out) const { - if (id == "last") { out << _last; return; } - if (id == "count") { out << _count; return; } - if (id == "total") { out << _total; return; } - if (id == "min") { out << (_count > 0 ? _min : 0); return; } - if (id == "max") { out << (_count > 0 ? _max : 0); return; } - throw vespalib::IllegalArgumentException( - "No value " + id + " in value metric.", VESPA_STRLOC); - } - void output(const std::string& id, vespalib::JsonStream& stream) const { - if (id == "last") { stream << _last; return; } - if (id == "count") { stream << _count; return; } - if (id == "total") { stream << _total; return; } - if (id == "min") { stream << (_count > 0 ? _min : 0); return; } - if (id == "max") { stream << (_count > 0 ? _max : 0); return; } - throw vespalib::IllegalArgumentException( - "No value " + id + " in value metric.", VESPA_STRLOC); - } - friend std::ostream & operator << (std::ostream & os, const Values & v) { - os << "count=" << v._count; - os << ", total=" << v._total; - return os; - } - }; + using String = Metric::String; // Redefine so template finds it easy + using Values = ValueMetricValues<AvgVal, TotVal>; MetricValueSet<Values> _values; @@ -251,282 +155,5 @@ typedef ValueMetric<double, double, false> DoubleAverageMetric; typedef ValueMetric<int64_t, int64_t, true> LongValueMetric; typedef ValueMetric<int64_t, int64_t, false> LongAverageMetric; -template<typename AvgVal, typename TotVal, bool SumOnAdd> -ValueMetric<AvgVal, TotVal, SumOnAdd>::ValueMetric( - const String& name, const String& tags, - const String& description, MetricSet* owner) - : AbstractValueMetric(name, tags, description, owner), - _values() -{ - _values.setFlag(LOG_IF_UNSET); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -ValueMetric<AvgVal, TotVal, SumOnAdd>::ValueMetric( - const String& name, const Tags dimensions, - const String& description, MetricSet* owner) - : AbstractValueMetric(name, std::move(dimensions), description, owner), - _values() -{ - _values.setFlag(LOG_IF_UNSET); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -ValueMetric<AvgVal, TotVal, SumOnAdd>::ValueMetric( - const ValueMetric<AvgVal, TotVal, SumOnAdd>& other, - CopyType copyType, MetricSet* owner) - : AbstractValueMetric(other, owner), - _values(other._values, copyType == CLONE ? other._values.size() : 1) -{ -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void ValueMetric<AvgVal, TotVal, SumOnAdd>::inc(AvgVal incVal) -{ - if (!checkFinite(incVal, std::is_floating_point<AvgVal>())) { - return; - } - Values values; - do { - values = _values.getValues(); - AvgVal val = values._last + incVal; - ++values._count; - values._total += val; - if (val < values._min) values._min = val; - if (val > values._max) values._max = val; - values._last = val; - } while (!_values.setValues(values)); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void ValueMetric<AvgVal, TotVal, SumOnAdd>::dec(AvgVal decVal) -{ - if (!checkFinite(decVal, std::is_floating_point<AvgVal>())) { - return; - } - Values values; - do { - values = _values.getValues(); - AvgVal val = values._last - decVal; - ++values._count; - values._total += val; - if (val < values._min) values._min = val; - if (val > values._max) values._max = val; - values._last = val; - } while (!_values.setValues(values)); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void -ValueMetric<AvgVal, TotVal, SumOnAdd>::addToSnapshot( - Metric& other, std::vector<Metric::LP>&) const -{ - ValueMetric<AvgVal, TotVal, SumOnAdd>& o( - reinterpret_cast<ValueMetric<AvgVal, TotVal, SumOnAdd>&>(other)); - if (_values.getValues()._count == 0) return; // Don't add if not set - o.add(_values.getValues(), false); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void -ValueMetric<AvgVal, TotVal, SumOnAdd>::addToPart(Metric& other) const -{ - ValueMetric<AvgVal, TotVal, SumOnAdd>& o( - reinterpret_cast<ValueMetric<AvgVal, TotVal, SumOnAdd>&>( - other)); - o.add(_values.getValues(), SumOnAdd); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void -ValueMetric<AvgVal, TotVal, SumOnAdd>::add(const Values& values2, bool sumOnAdd) -{ - bool overflow; - Values values; - do { - values = _values.getValues(); - overflow = values._count > values._count + values2._count - || (values2._total >= 0 - ? values._total > values._total + values2._total - : values._total < values._total + values2._total); - if (values._count == 0) { - values = values2; - } else if (values2._count == 0) { - // Do nothing - } else if (sumOnAdd) { - double totalAverage - = static_cast<double>(values._total) / values._count - + static_cast<double>(values2._total) / values2._count; - values._count += values2._count; - values._total = static_cast<TotVal>(totalAverage * values._count); - values._last += values2._last; - _values.setFlag(SUMMED_AVERAGE); - } else { - values._count += values2._count; - values._total += values2._total; - values._last = values2._last; - } - if (values._min > values2._min) values._min = values2._min; - if (values._max < values2._max) values._max = values2._max; - } while (!_values.setValues(values)); - if (overflow) { - std::ostringstream ost; - ost << "ValueMetric " << getPath() << " overflowed. Resetting it. Old value = ("; - ost << values << "), Incoming = (" << values2 << ")"; - logWarning(ost.str().c_str()); - _values.reset(); - } -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void -ValueMetric<AvgVal, TotVal, SumOnAdd>::dec(const Values& values2) -{ - bool underflow; - Values values; - do { - values = _values.getValues(); - underflow = values._count < values._count - values2._count - || values._total < values._total - values2._total; - values._count -= values2._count; - values._total -= values2._total; - } while (!_values.setValues(values)); - if (underflow) { - _values.reset(); - std::ostringstream ost; - ost << "ValueMetric " << getPath() << " underflowed. Resetting it."; - logWarning(ost.str().c_str()); - } -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void ValueMetric<AvgVal, TotVal, SumOnAdd>::addValueWithCount( - AvgVal avg, TotVal tot, uint32_t count) -{ - if (!checkFinite(avg, std::is_floating_point<AvgVal>())) { - return; - } - Values values; - do { - values = _values.getValues(); - values._count += count; - values._total += tot; - if (avg < values._min) values._min = avg; - if (avg > values._max) values._max = avg; - values._last = avg; - } while (!_values.setValues(values)); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -ValueMetric<AvgVal, TotVal, SumOnAdd>& -ValueMetric<AvgVal, TotVal, SumOnAdd>::operator+=( - const ValueMetric<AvgVal, TotVal, SumOnAdd>& other) -{ - add(other._values.getValues(), SumOnAdd); - return *this; -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -double -ValueMetric<AvgVal, TotVal, SumOnAdd>::getAverage() const -{ - Values values(_values.getValues()); - if (values._count == 0) return 0; - return static_cast<double>(values._total) / values._count; -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -bool -ValueMetric<AvgVal, TotVal, SumOnAdd>::logEvent(const String& fullName) const -{ - Values values(_values.getValues()); - if (!logIfUnset() && !inUse(values)) return false; - sendLogEvent(fullName, SumOnAdd - ? static_cast<double>(values._last) - : static_cast<double>(values._total) / values._count); - return true; -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void -ValueMetric<AvgVal, TotVal, SumOnAdd>::print( - std::ostream& out, bool verbose, const std::string& indent, - uint64_t secondsPassed) const -{ - (void) indent; - (void) secondsPassed; - Values values(_values.getValues()); - if (!inUse(values) && !verbose) return; - out << this->_name << " average=" << (values._count == 0 - ? 0 : static_cast<double>(values._total) / values._count) - << " last=" << values._last; - if (!summedAverage()) { - if (values._count > 0) { - out << " min=" << values._min << " max=" << values._max; - } - out << " count=" << values._count << " total=" << values._total; - } -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -int64_t -ValueMetric<AvgVal, TotVal, SumOnAdd>::getLongValue(const stringref & id) const -{ - Values values(_values.getValues()); - if (id == "last" || (SumOnAdd && id == "value")) - return static_cast<int64_t>(values._last); - if (id == "average" || (!SumOnAdd && id == "value")) - return static_cast<int64_t>(getAverage()); - if (id == "count") return static_cast<int64_t>(values._count); - if (id == "total") return static_cast<int64_t>(values._total); - if (id == "min") return static_cast<int64_t>( - values._count > 0 ? values._min : 0); - if (id == "max") return static_cast<int64_t>( - values._count > 0 ? values._max : 0); - throw vespalib::IllegalArgumentException( - "No value " + id + " in average metric.", VESPA_STRLOC); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -double -ValueMetric<AvgVal, TotVal, SumOnAdd>::getDoubleValue(const stringref & id) const -{ - Values values(_values.getValues()); - if (id == "last" || (SumOnAdd && id == "value")) - return static_cast<double>(values._last); - if (id == "average" || (!SumOnAdd && id == "value")) - return getAverage(); - if (id == "count") return static_cast<double>(values._count); - if (id == "total") return static_cast<double>(values._total); - if (id == "min") return static_cast<double>( - values._count > 0 ? values._min : 0); - if (id == "max") return static_cast<double>( - values._count > 0 ? values._max : 0); - throw vespalib::IllegalArgumentException( - "No value " + vespalib::string(id) + " in average metric.", VESPA_STRLOC); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void -ValueMetric<AvgVal, TotVal, SumOnAdd>::addMemoryUsage( - MemoryConsumption& mc) const -{ - ++mc._valueMetricCount; - mc._valueMetricValues += _values.getMemoryUsageAllocatedInternally(); - mc._valueMetricMeta += sizeof(ValueMetric<AvgVal, TotVal, SumOnAdd>) - - sizeof(Metric); - Metric::addMemoryUsage(mc); -} - -template<typename AvgVal, typename TotVal, bool SumOnAdd> -void -ValueMetric<AvgVal, TotVal, SumOnAdd>::printDebug( - std::ostream& out, const std::string& indent) const -{ - Values values(_values.getValues()); - out << "value=" << values._last << " "; - Metric::printDebug(out, indent); -} - } // metrics diff --git a/metrics/src/vespa/metrics/valuemetric.hpp b/metrics/src/vespa/metrics/valuemetric.hpp new file mode 100644 index 00000000000..0a196ec420f --- /dev/null +++ b/metrics/src/vespa/metrics/valuemetric.hpp @@ -0,0 +1,289 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include "valuemetric.h" +#include "memoryconsumption.h" +#include <vespa/vespalib/util/exceptions.h> +#include <sstream> + +namespace metrics { + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +ValueMetric<AvgVal, TotVal, SumOnAdd>::ValueMetric( + const String& name, const String& tags, + const String& description, MetricSet* owner) + : AbstractValueMetric(name, tags, description, owner), + _values() +{ + _values.setFlag(LOG_IF_UNSET); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +ValueMetric<AvgVal, TotVal, SumOnAdd>::ValueMetric( + const String& name, const Tags dimensions, + const String& description, MetricSet* owner) + : AbstractValueMetric(name, std::move(dimensions), description, owner), + _values() +{ + _values.setFlag(LOG_IF_UNSET); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +ValueMetric<AvgVal, TotVal, SumOnAdd>::ValueMetric( + const ValueMetric<AvgVal, TotVal, SumOnAdd>& other, + CopyType copyType, MetricSet* owner) + : AbstractValueMetric(other, owner), + _values(other._values, copyType == CLONE ? other._values.size() : 1) +{ +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void ValueMetric<AvgVal, TotVal, SumOnAdd>::inc(AvgVal incVal) +{ + if (!checkFinite(incVal, std::is_floating_point<AvgVal>())) { + return; + } + Values values; + do { + values = _values.getValues(); + AvgVal val = values._last + incVal; + ++values._count; + values._total += val; + if (val < values._min) values._min = val; + if (val > values._max) values._max = val; + values._last = val; + } while (!_values.setValues(values)); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void ValueMetric<AvgVal, TotVal, SumOnAdd>::dec(AvgVal decVal) +{ + if (!checkFinite(decVal, std::is_floating_point<AvgVal>())) { + return; + } + Values values; + do { + values = _values.getValues(); + AvgVal val = values._last - decVal; + ++values._count; + values._total += val; + if (val < values._min) values._min = val; + if (val > values._max) values._max = val; + values._last = val; + } while (!_values.setValues(values)); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void +ValueMetric<AvgVal, TotVal, SumOnAdd>::addToSnapshot( + Metric& other, std::vector<Metric::LP>&) const +{ + ValueMetric<AvgVal, TotVal, SumOnAdd>& o( + reinterpret_cast<ValueMetric<AvgVal, TotVal, SumOnAdd>&>(other)); + if (_values.getValues()._count == 0) return; // Don't add if not set + o.add(_values.getValues(), false); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void +ValueMetric<AvgVal, TotVal, SumOnAdd>::addToPart(Metric& other) const +{ + ValueMetric<AvgVal, TotVal, SumOnAdd>& o( + reinterpret_cast<ValueMetric<AvgVal, TotVal, SumOnAdd>&>( + other)); + o.add(_values.getValues(), SumOnAdd); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void +ValueMetric<AvgVal, TotVal, SumOnAdd>::add(const Values& values2, bool sumOnAdd) +{ + bool overflow; + Values values; + do { + values = _values.getValues(); + overflow = values._count > values._count + values2._count + || (values2._total >= 0 + ? values._total > values._total + values2._total + : values._total < values._total + values2._total); + if (values._count == 0) { + values = values2; + } else if (values2._count == 0) { + // Do nothing + } else if (sumOnAdd) { + double totalAverage + = static_cast<double>(values._total) / values._count + + static_cast<double>(values2._total) / values2._count; + values._count += values2._count; + values._total = static_cast<TotVal>(totalAverage * values._count); + values._last += values2._last; + _values.setFlag(SUMMED_AVERAGE); + } else { + values._count += values2._count; + values._total += values2._total; + values._last = values2._last; + } + if (values._min > values2._min) values._min = values2._min; + if (values._max < values2._max) values._max = values2._max; + } while (!_values.setValues(values)); + if (overflow) { + std::ostringstream ost; + ost << "ValueMetric " << getPath() << " overflowed. Resetting it. Old value = ("; + ost << values << "), Incoming = (" << values2 << ")"; + logWarning(ost.str().c_str()); + _values.reset(); + } +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void +ValueMetric<AvgVal, TotVal, SumOnAdd>::dec(const Values& values2) +{ + bool underflow; + Values values; + do { + values = _values.getValues(); + underflow = values._count < values._count - values2._count + || values._total < values._total - values2._total; + values._count -= values2._count; + values._total -= values2._total; + } while (!_values.setValues(values)); + if (underflow) { + _values.reset(); + std::ostringstream ost; + ost << "ValueMetric " << getPath() << " underflowed. Resetting it."; + logWarning(ost.str().c_str()); + } +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void ValueMetric<AvgVal, TotVal, SumOnAdd>::addValueWithCount( + AvgVal avg, TotVal tot, uint32_t count) +{ + if (!checkFinite(avg, std::is_floating_point<AvgVal>())) { + return; + } + Values values; + do { + values = _values.getValues(); + values._count += count; + values._total += tot; + if (avg < values._min) values._min = avg; + if (avg > values._max) values._max = avg; + values._last = avg; + } while (!_values.setValues(values)); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +ValueMetric<AvgVal, TotVal, SumOnAdd>& +ValueMetric<AvgVal, TotVal, SumOnAdd>::operator+=( + const ValueMetric<AvgVal, TotVal, SumOnAdd>& other) +{ + add(other._values.getValues(), SumOnAdd); + return *this; +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +double +ValueMetric<AvgVal, TotVal, SumOnAdd>::getAverage() const +{ + Values values(_values.getValues()); + if (values._count == 0) return 0; + return static_cast<double>(values._total) / values._count; +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +bool +ValueMetric<AvgVal, TotVal, SumOnAdd>::logEvent(const String& fullName) const +{ + Values values(_values.getValues()); + if (!logIfUnset() && !inUse(values)) return false; + sendLogEvent(fullName, SumOnAdd + ? static_cast<double>(values._last) + : static_cast<double>(values._total) / values._count); + return true; +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void +ValueMetric<AvgVal, TotVal, SumOnAdd>::print( + std::ostream& out, bool verbose, const std::string& indent, + uint64_t secondsPassed) const +{ + (void) indent; + (void) secondsPassed; + Values values(_values.getValues()); + if (!inUse(values) && !verbose) return; + out << this->_name << " average=" << (values._count == 0 + ? 0 : static_cast<double>(values._total) / values._count) + << " last=" << values._last; + if (!summedAverage()) { + if (values._count > 0) { + out << " min=" << values._min << " max=" << values._max; + } + out << " count=" << values._count << " total=" << values._total; + } +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +int64_t +ValueMetric<AvgVal, TotVal, SumOnAdd>::getLongValue(const stringref & id) const +{ + Values values(_values.getValues()); + if (id == "last" || (SumOnAdd && id == "value")) + return static_cast<int64_t>(values._last); + if (id == "average" || (!SumOnAdd && id == "value")) + return static_cast<int64_t>(getAverage()); + if (id == "count") return static_cast<int64_t>(values._count); + if (id == "total") return static_cast<int64_t>(values._total); + if (id == "min") return static_cast<int64_t>( + values._count > 0 ? values._min : 0); + if (id == "max") return static_cast<int64_t>( + values._count > 0 ? values._max : 0); + throw vespalib::IllegalArgumentException( + "No value " + id + " in average metric.", VESPA_STRLOC); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +double +ValueMetric<AvgVal, TotVal, SumOnAdd>::getDoubleValue(const stringref & id) const +{ + Values values(_values.getValues()); + if (id == "last" || (SumOnAdd && id == "value")) + return static_cast<double>(values._last); + if (id == "average" || (!SumOnAdd && id == "value")) + return getAverage(); + if (id == "count") return static_cast<double>(values._count); + if (id == "total") return static_cast<double>(values._total); + if (id == "min") return static_cast<double>( + values._count > 0 ? values._min : 0); + if (id == "max") return static_cast<double>( + values._count > 0 ? values._max : 0); + throw vespalib::IllegalArgumentException( + "No value " + vespalib::string(id) + " in average metric.", VESPA_STRLOC); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void +ValueMetric<AvgVal, TotVal, SumOnAdd>::addMemoryUsage( + MemoryConsumption& mc) const +{ + ++mc._valueMetricCount; + mc._valueMetricValues += _values.getMemoryUsageAllocatedInternally(); + mc._valueMetricMeta += sizeof(ValueMetric<AvgVal, TotVal, SumOnAdd>) + - sizeof(Metric); + Metric::addMemoryUsage(mc); +} + +template<typename AvgVal, typename TotVal, bool SumOnAdd> +void +ValueMetric<AvgVal, TotVal, SumOnAdd>::printDebug( + std::ostream& out, const std::string& indent) const +{ + Values values(_values.getValues()); + out << "value=" << values._last << " "; + Metric::printDebug(out, indent); +} + +} // metrics + diff --git a/metrics/src/vespa/metrics/valuemetricvalues.cpp b/metrics/src/vespa/metrics/valuemetricvalues.cpp new file mode 100644 index 00000000000..bab66bfa346 --- /dev/null +++ b/metrics/src/vespa/metrics/valuemetricvalues.cpp @@ -0,0 +1,13 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include "valuemetricvalues.hpp" + +namespace metrics { + +template class ValueMetricValues<int64_t, int64_t>; +template std::ostream & operator << (std::ostream & os, const ValueMetricValues<int64_t, int64_t> & v); + +template class ValueMetricValues<double, double>; +template std::ostream & operator << (std::ostream & os, const ValueMetricValues<double, double> & v); + +} // metrics + diff --git a/metrics/src/vespa/metrics/valuemetricvalues.h b/metrics/src/vespa/metrics/valuemetricvalues.h new file mode 100644 index 00000000000..347205ebe75 --- /dev/null +++ b/metrics/src/vespa/metrics/valuemetricvalues.h @@ -0,0 +1,49 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * @class metrics::ValueMetric + * @ingroup metrics + * + * @brief Creates a metric measuring any value. + * + * A value metric have the following properties: + * - Logs the average as a value event. (It is not strictly increasing) + * - When summing average metrics together, the sum becomes the average of + * all values added to both. + */ + +#pragma once + +#include "metricvalueset.h" + +namespace metrics { + +template<typename AvgVal, typename TotVal> +struct ValueMetricValues : MetricValueClass { + uint32_t _count; + AvgVal _min, _max, _last; + TotVal _total; + + struct AtomicImpl { + std::atomic<uint32_t> _count {0}; + std::atomic<AvgVal> _min {std::numeric_limits<AvgVal>::max()}; + std::atomic<AvgVal> _max {std::numeric_limits<AvgVal>::min()}; + std::atomic<AvgVal> _last {0}; + std::atomic<TotVal> _total {0}; + }; + + ValueMetricValues(); + void relaxedStoreInto(AtomicImpl& target) const noexcept; + void relaxedLoadFrom(const AtomicImpl& source) noexcept; + + template<typename T> + T getValue(const stringref & id) const; + double getDoubleValue(const stringref & id) const override; + uint64_t getLongValue(const stringref & id) const override; + void output(const std::string& id, std::ostream& out) const override; + void output(const std::string& id, vespalib::JsonStream& stream) const override; + template<typename A, typename T> + friend std::ostream & operator << (std::ostream & os, const ValueMetricValues<A, T> & v); +}; + +} // metrics + diff --git a/metrics/src/vespa/metrics/valuemetricvalues.hpp b/metrics/src/vespa/metrics/valuemetricvalues.hpp new file mode 100644 index 00000000000..fe58a1f53c6 --- /dev/null +++ b/metrics/src/vespa/metrics/valuemetricvalues.hpp @@ -0,0 +1,93 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include "valuemetricvalues.h" +#include <ostream> + +namespace metrics { + +using vespalib::IllegalArgumentException; + +template<typename AvgVal, typename TotVal> +ValueMetricValues<AvgVal, TotVal>::ValueMetricValues() + : _count(0), + _min(std::numeric_limits<AvgVal>::max()), + _max(std::numeric_limits<AvgVal>::min()), + _last(0), + _total(0) +{ + // numeric_limits min() returns smallest positive number + // for signed floating point types. Haven't found a way to + // get minimum negative value, so using -1 * positive for those. + if (std::numeric_limits<AvgVal>::is_signed + && !std::numeric_limits<AvgVal>::is_integer) + { + _max = -1 * std::numeric_limits<AvgVal>::max(); + } +} + +template<typename AvgVal, typename TotVal> +void ValueMetricValues<AvgVal, TotVal>::relaxedStoreInto(AtomicImpl& target) const noexcept { + target._count.store(_count, std::memory_order_relaxed); + target._min.store(_min, std::memory_order_relaxed); + target._max.store(_max, std::memory_order_relaxed); + target._last.store(_last, std::memory_order_relaxed); + target._total.store(_total, std::memory_order_relaxed); +} + +template<typename AvgVal, typename TotVal> +void ValueMetricValues<AvgVal, TotVal>::relaxedLoadFrom(const AtomicImpl& source) noexcept { + _count = source._count.load(std::memory_order_relaxed); + _min = source._min.load(std::memory_order_relaxed); + _max = source._max.load(std::memory_order_relaxed); + _last = source._last.load(std::memory_order_relaxed); + _total = source._total.load(std::memory_order_relaxed); +} + +template<typename AvgVal, typename TotVal> +template<typename T> +T ValueMetricValues<AvgVal, TotVal>::getValue(const stringref & id) const { + if (id == "last") return static_cast<T>(_last); + if (id == "count") return static_cast<T>(_count); + if (id == "total") return static_cast<T>(_total); + if (id == "min") return static_cast<T>(_count > 0 ? _min : 0); + if (id == "max") return static_cast<T>( _count > 0 ? _max : 0); + throw IllegalArgumentException("No value " + vespalib::string(id) + " in value metric.", VESPA_STRLOC); +} + +template<typename AvgVal, typename TotVal> +double ValueMetricValues<AvgVal, TotVal>::getDoubleValue(const stringref & id) const { + return getValue<double>(id); +} +template<typename AvgVal, typename TotVal> +uint64_t ValueMetricValues<AvgVal, TotVal>::getLongValue(const stringref & id) const { + return getValue<uint64_t>(id); +} +template<typename AvgVal, typename TotVal> +void ValueMetricValues<AvgVal, TotVal>::output(const std::string& id, std::ostream& out) const { + if (id == "last") { out << _last; return; } + if (id == "count") { out << _count; return; } + if (id == "total") { out << _total; return; } + if (id == "min") { out << (_count > 0 ? _min : 0); return; } + if (id == "max") { out << (_count > 0 ? _max : 0); return; } + throw IllegalArgumentException("No value " + id + " in value metric.", VESPA_STRLOC); +} +template<typename AvgVal, typename TotVal> +void ValueMetricValues<AvgVal, TotVal>::output(const std::string& id, vespalib::JsonStream& stream) const { + if (id == "last") { stream << _last; return; } + if (id == "count") { stream << _count; return; } + if (id == "total") { stream << _total; return; } + if (id == "min") { stream << (_count > 0 ? _min : 0); return; } + if (id == "max") { stream << (_count > 0 ? _max : 0); return; } + throw IllegalArgumentException("No value " + id + " in value metric.", VESPA_STRLOC); +} + +template<typename AvgVal, typename TotVal> +std::ostream & operator << (std::ostream & os, const ValueMetricValues<AvgVal, TotVal> & v) { + os << "count=" << v._count; + os << ", total=" << v._total; + return os; +} + +} // metrics + |