Commit d2382755 authored by Yoshitaka Aharen's avatar Yoshitaka Aharen
Browse files

[trac510] added a new library for statistics counters

  - added a library for statistics counters to hold counter items
  - added a library for statistics counters to hold a set of counters
    for per zone or per process counters
parent 902ad260
......@@ -873,6 +873,8 @@ AC_CONFIG_FILES([Makefile
src/lib/util/tests/Makefile
src/lib/acl/Makefile
src/lib/acl/tests/Makefile
src/lib/statistics/Makefile
src/lib/statistics/tests/Makefile
tests/Makefile
tests/system/Makefile
tests/tools/Makefile
......
......@@ -64,6 +64,7 @@ b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_auth_LDADD += $(top_builddir)/src/lib/log/liblog.la
b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
b10_auth_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
b10_auth_LDADD += $(SQLITE_LIBS)
# TODO: config.h.in is wrong because doesn't honor pkgdatadir
......
......@@ -661,9 +661,9 @@ void
AuthSrvImpl::incCounter(const int protocol) {
// Increment query counter.
if (protocol == IPPROTO_UDP) {
counters_.inc(AuthCounters::COUNTER_UDP_QUERY);
counters_.inc(AuthCounters::SERVER_UDP_QUERY);
} else if (protocol == IPPROTO_TCP) {
counters_.inc(AuthCounters::COUNTER_TCP_QUERY);
counters_.inc(AuthCounters::SERVER_TCP_QUERY);
} else {
// unknown protocol
isc_throw(Unexpected, "Unknown protocol: " << protocol);
......@@ -740,7 +740,7 @@ bool AuthSrv::submitStatistics() const {
}
uint64_t
AuthSrv::getCounter(const AuthCounters::CounterType type) const {
AuthSrv::getCounter(const AuthCounters::ServerCounterType type) const {
return (impl_->counters_.getCounter(type));
}
......
......@@ -343,7 +343,7 @@ public:
/// \param type Type of a counter to get the value of
///
/// \return the value of the counter.
uint64_t getCounter(const AuthCounters::CounterType type) const;
uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
/**
* \brief Set and get the addresses we listen on.
......
......@@ -28,5 +28,6 @@ query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
query_bench_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
query_bench_LDADD += $(SQLITE_LIBS)
......@@ -18,45 +18,64 @@
#include <cc/data.h>
#include <cc/session.h>
#include <statistics/counter.h>
#include <statistics/counter_dict.h>
#include <sstream>
#include <iostream>
#include <boost/noncopyable.hpp>
using namespace isc::auth;
using namespace isc::statistics;
// TODO: We need a namespace ("auth_server"?) to hold
// AuthSrv and AuthCounters.
// AuthSrv and AuthCounters.
class AuthCountersImpl {
private:
// prohibit copy
AuthCountersImpl(const AuthCountersImpl& source);
AuthCountersImpl& operator=(const AuthCountersImpl& source);
// TODO: Make use of wrappers like isc::dns::Opcode
// for counter item type.
class AuthCountersImpl : boost::noncopyable {
public:
AuthCountersImpl();
~AuthCountersImpl();
void inc(const AuthCounters::CounterType type);
void inc(const AuthCounters::ServerCounterType type);
void inc(const std::string& zone,
const AuthCounters::PerZoneCounterType type);
bool submitStatistics() const;
void setStatisticsSession(isc::cc::AbstractSession* statistics_session);
// Currently for testing purpose only
uint64_t getCounter(const AuthCounters::CounterType type) const;
uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
private:
std::vector<uint64_t> counters_;
Counter server_counter_;
CounterDictionary per_zone_counter_;
isc::cc::AbstractSession* statistics_session_;
};
AuthCountersImpl::AuthCountersImpl() :
// initialize counter
// size: AuthCounters::COUNTER_TYPES, initial value: 0
counters_(AuthCounters::COUNTER_TYPES, 0),
// size of server_counter_: AuthCounters::SERVER_COUNTER_TYPES
// size of per_zone_counter_: AuthCounters::PER_ZONE_COUNTER_TYPES
server_counter_(AuthCounters::SERVER_COUNTER_TYPES),
per_zone_counter_(AuthCounters::PER_ZONE_COUNTER_TYPES),
statistics_session_(NULL)
{}
{
per_zone_counter_.addElement("_SERVER_");
}
AuthCountersImpl::~AuthCountersImpl()
{}
void
AuthCountersImpl::inc(const AuthCounters::CounterType type) {
++counters_.at(type);
AuthCountersImpl::inc(const AuthCounters::ServerCounterType type) {
server_counter_.inc(type);
}
void
AuthCountersImpl::inc(const std::string& zone,
const AuthCounters::PerZoneCounterType type)
{
per_zone_counter_[zone].inc(type);
}
bool
......@@ -69,9 +88,9 @@ AuthCountersImpl::submitStatistics() const {
statistics_string << "{\"command\": [\"set\","
<< "{ \"stats_data\": "
<< "{ \"auth.queries.udp\": "
<< counters_.at(AuthCounters::COUNTER_UDP_QUERY)
<< server_counter_.get(AuthCounters::SERVER_UDP_QUERY)
<< ", \"auth.queries.tcp\": "
<< counters_.at(AuthCounters::COUNTER_TCP_QUERY)
<< server_counter_.get(AuthCounters::SERVER_TCP_QUERY)
<< " }"
<< "}"
<< "]}";
......@@ -107,19 +126,17 @@ AuthCountersImpl::setStatisticsSession
// Currently for testing purpose only
uint64_t
AuthCountersImpl::getCounter(const AuthCounters::CounterType type) const {
return (counters_.at(type));
AuthCountersImpl::getCounter(const AuthCounters::ServerCounterType type) const {
return (server_counter_.get(type));
}
AuthCounters::AuthCounters() : impl_(new AuthCountersImpl())
{}
AuthCounters::~AuthCounters() {
delete impl_;
}
AuthCounters::~AuthCounters() {}
void
AuthCounters::inc(const AuthCounters::CounterType type) {
AuthCounters::inc(const AuthCounters::ServerCounterType type) {
impl_->inc(type);
}
......@@ -136,6 +153,6 @@ AuthCounters::setStatisticsSession
}
uint64_t
AuthCounters::getCounter(const AuthCounters::CounterType type) const {
AuthCounters::getCounter(const AuthCounters::ServerCounterType type) const {
return (impl_->getCounter(type));
}
......@@ -17,6 +17,7 @@
#include <cc/session.h>
#include <stdint.h>
#include <boost/scoped_ptr.hpp>
class AuthCountersImpl;
......@@ -51,13 +52,18 @@ class AuthCountersImpl;
/// \todo Consider overhead of \c AuthCounters::inc()
class AuthCounters {
private:
AuthCountersImpl* impl_;
boost::scoped_ptr<AuthCountersImpl> impl_;
public:
// Enum for the type of counter
enum CounterType {
COUNTER_UDP_QUERY = 0, ///< COUNTER_UDP_QUERY: counter for UDP queries
COUNTER_TCP_QUERY = 1, ///< COUNTER_TCP_QUERY: counter for TCP queries
COUNTER_TYPES = 2 ///< The number of defined counters
enum ServerCounterType {
SERVER_UDP_QUERY = 0, ///< SERVER_UDP_QUERY: counter for UDP queries
SERVER_TCP_QUERY = 1, ///< SERVER_TCP_QUERY: counter for TCP queries
SERVER_COUNTER_TYPES = 2 ///< The number of defined counters
};
enum PerZoneCounterType {
ZONE_UDP_QUERY = 0, ///< ZONE_UDP_QUERY: counter for UDP queries
ZONE_TCP_QUERY = 1, ///< ZONE_TCP_QUERY: counter for TCP queries
PER_ZONE_COUNTER_TYPES = 2 ///< The number of defined counters
};
/// The constructor.
///
......@@ -79,7 +85,7 @@ public:
///
/// usage: counter.inc(CounterType::COUNTER_UDP_QUERY);
///
void inc(const CounterType type);
void inc(const ServerCounterType type);
/// \brief Submit statistics counters to statistics module.
///
......@@ -130,7 +136,7 @@ public:
///
/// \return the value of the counter specified by \a type.
///
uint64_t getCounter(const AuthCounters::CounterType type) const;
uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
};
#endif // __STATISTICS_H
......
......@@ -56,6 +56,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
......
......@@ -702,7 +702,7 @@ TEST_F(AuthSrvTest, cacheSlots) {
// Submit UDP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
// Create UDP message and process.
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("example.com"),
......@@ -711,13 +711,13 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
server.processMessage(*io_message, parse_message, response_obuffer,
&dnsserv);
// After processing UDP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
}
// Submit TCP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
// Create TCP message and process.
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("example.com"),
......@@ -726,13 +726,13 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
server.processMessage(*io_message, parse_message, response_obuffer,
&dnsserv);
// After processing TCP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
// Submit TCP AXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
......@@ -740,7 +740,7 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
// so we shouldn't have to respond.
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
// After processing TCP AXFR query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
// class for queryCounterUnexpected test
......
......@@ -141,25 +141,23 @@ AuthCountersTest::MockSession::setThrowSessionTimeout(bool flag) {
TEST_F(AuthCountersTest, incrementUDPCounter) {
// The counter should be initialized to 0.
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_NO_THROW(counters.inc(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_UDP_QUERY));
// After increment, the counter should be 1.
EXPECT_EQ(1, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
}
TEST_F(AuthCountersTest, incrementTCPCounter) {
// The counter should be initialized to 0.
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_NO_THROW(counters.inc(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_TCP_QUERY));
// After increment, the counter should be 1.
EXPECT_EQ(1, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
}
TEST_F(AuthCountersTest, incrementInvalidCounter) {
// Expect to throw isc::InvalidParameter if the type of the counter is
// invalid.
EXPECT_THROW(counters.inc(AuthCounters::COUNTER_TYPES),
std::out_of_range);
// Expect to die.
EXPECT_DEATH(counters.inc(AuthCounters::SERVER_COUNTER_TYPES), "Assertion.+failed");
}
TEST_F(AuthCountersTest, submitStatisticsWithoutSession) {
......@@ -186,14 +184,14 @@ TEST_F(AuthCountersTest, submitStatistics) {
// Validate if it submits correct data.
// Counters should be initialized to 0.
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_UDP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::COUNTER_TCP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
// UDP query counter is set to 2.
counters.inc(AuthCounters::COUNTER_UDP_QUERY);
counters.inc(AuthCounters::COUNTER_UDP_QUERY);
counters.inc(AuthCounters::SERVER_UDP_QUERY);
counters.inc(AuthCounters::SERVER_UDP_QUERY);
// TCP query counter is set to 1.
counters.inc(AuthCounters::COUNTER_TCP_QUERY);
counters.inc(AuthCounters::SERVER_TCP_QUERY);
counters.submitStatistics();
// Destination is "Stats".
......
SUBDIRS = exceptions util log cryptolink dns cc config acl python xfr \
bench asiolink asiodns nsas cache resolve testutils datasrc \
server_common
server_common statistics
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/statistics -I$(top_builddir)/src/lib/statistics
AM_CXXFLAGS = $(B10_CXXFLAGS)
# Some versions of GCC warn about some versions of Boost regarding
# missing initializer for members in its posix_time.
# https://svn.boost.org/trac/boost/ticket/3477
# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
if USE_CLANGPP
# clang++ complains about unused function parameters in some boost header
# files.
AM_CXXFLAGS += -Wno-unused-parameter
endif
lib_LTLIBRARIES = libstatistics.la
libstatistics_la_SOURCES = counter.h counter.cc
libstatistics_la_SOURCES += counter_dict.h counter_dict.cc
CLEANFILES = *.gcno *.gcda
#include <vector>
#include <cassert>
#include <boost/unordered_map.hpp>
#include <statistics/counter.h>
namespace {
const unsigned int InitialValue = 0;
} // namespace
namespace isc {
namespace statistics {
class CounterImpl {
private:
CounterImpl(const CounterImpl& source);
CounterImpl& operator=(const CounterImpl& source);
private:
std::vector<Counter::Value> counters_;
public:
CounterImpl(const size_t nelements);
~CounterImpl();
void inc(const Counter::Type&);
const Counter::Value& get(const Counter::Type&) const;
};
CounterImpl::CounterImpl(const size_t items) :
counters_(items, InitialValue)
{
assert(items != 0);
}
CounterImpl::~CounterImpl() {}
void
CounterImpl::inc(const Counter::Type& type) {
assert(type < counters_.size());
++counters_.at(type);
return;
}
const Counter::Value&
CounterImpl::get(const Counter::Type& type) const {
assert(type < counters_.size());
return (counters_.at(type));
}
Counter::Counter(const size_t items) : impl_(new CounterImpl(items))
{}
Counter::~Counter() {}
void
Counter::inc(const Type& type) {
impl_->inc(type);
return;
}
const Counter::Value&
Counter::get(const Type& type) const {
return (impl_->get(type));
}
} // namespace statistics
} // namespace isc
#ifndef __COUNTER_H
#define __COUNTER_H 1
#include <boost/scoped_ptr.hpp>
namespace isc {
namespace statistics {
// forward declaration for pImpl idiom
class CounterImpl;
class Counter {
private:
Counter(const Counter& source);
Counter& operator=(const Counter& source);
boost::scoped_ptr<CounterImpl> impl_;
public:
typedef unsigned int Type;
typedef unsigned int Value;
/// The constructor.
///
/// This constructor is mostly exception free. But it may still throw
/// a standard exception if memory allocation fails inside the method.
///
/// Note: \a items must be greater than 0. Otherwise this constructor
/// causes an assertion failure.
///
/// \param items A number of counter items to hold (greater than 0)
Counter(const size_t items);
/// The destructor.
///
/// This method never throws an exception.
~Counter();
/// \brief Increment a counter item specified with \a type.
///
/// Note: \a type must be valid counter type. Otherwise this method
/// causes an assertion failure.
///
/// \param type %Counter item to increment
void inc(const Type& type);
/// \brief Get the value of a counter item specified with \a type.
///
/// \param type %Counter item to get the value of
const Value& get(const Type& type) const;
};
} // namespace statistics
} // namespace isc
#endif
#include <cassert>
#include <stdexcept>
#include <iterator>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
#include <statistics/counter_dict.h>
namespace {
typedef boost::shared_ptr<isc::statistics::Counter> CounterPtr;
typedef boost::unordered_map<std::string, CounterPtr> DictionaryMap;
}
namespace isc {
namespace statistics {
// Implementation detail class for CounterDictionary::ConstIterator
class CounterDictionaryConstIteratorImpl;
class CounterDictionaryImpl : boost::noncopyable {
private:
DictionaryMap dictionary_;
std::vector<std::string> elements_;
const size_t items_;
// Default constructor is forbidden; number of counter items must be
// specified at the construction of this class.
CounterDictionaryImpl();
public:
CounterDictionaryImpl(const size_t items);
~CounterDictionaryImpl();
void addElement(const std::string& element);
void deleteElement(const std::string& elemet);
Counter& getElement(const std::string& element);
public:
CounterDictionaryConstIteratorImpl begin() const;
CounterDictionaryConstIteratorImpl end() const;
};
// Constructor with number of items
CounterDictionaryImpl::CounterDictionaryImpl(const size_t items) :
items_(items)
{
// The number of the items must not be 0.
assert(items != 0);
}
// Destructor
CounterDictionaryImpl::~CounterDictionaryImpl() {}
void
CounterDictionaryImpl::addElement(const std::string& element) {
// throw if the element already exists
if (dictionary_.count(element) != 0) {
isc_throw(isc::InvalidParameter,
"Element " << element << " already exists");
}
// Create a new Counter and add to the map
dictionary_.insert(
DictionaryMap::value_type(element, CounterPtr(new Counter(items_))));
}
void
CounterDictionaryImpl::deleteElement(const std::string& element) {
size_t result = dictionary_.erase(element);
if (result != 1) {
// If an element with specified name does not exist, throw
// isc::OutOfRange.
isc_throw(isc::OutOfRange, "Element " << element << " does not exist");
}
}
Counter&
CounterDictionaryImpl::getElement(const std::string& element) {
try {
return (*(dictionary_.at(element)));
} catch (const std::out_of_range &e) {
// If an element with specified name does not exist, throw
// isc::OutOfRange.
isc_throw(isc::OutOfRange, "Element " << element << " does not exist");
}
}
// Constructor
// Initialize impl_
CounterDictionary::CounterDictionary(const size_t items) :
impl_(new CounterDictionaryImpl(items))
{}
// Destructor
// impl_ will be freed automatically with scoped_ptr
CounterDictionary::~CounterDictionary() {}
void
CounterDictionary::addElement(const std::string& element) {
impl_->addElement(element);
}
void
CounterDictionary::deleteElement(const std::string& element) {
impl_->deleteElement(element);
}
Counter&
CounterDictionary::getElement(const std::string& element) const {
return (impl_->getElement(element));
}
Counter&
CounterDictionary::operator[](const std::string& element) const {
return (impl_->getElement(element));
}
// Implementation detail class for CounterDictionary::ConstIterator
class CounterDictionaryConstIteratorImpl {
public:
CounterDictionaryConstIteratorImpl();
~CounterDictionaryConstIteratorImpl();