From: tho Date: Mon, 30 Mar 2009 12:02:14 +0000 (+0000) Subject: moved statistics classes from NetEmu to SENF X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=f2f5d59e83863f3b513950173baee1b6da2aee3c;p=senf.git moved statistics classes from NetEmu to SENF git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1177 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/PPI/RateAnalyzer.cc b/PPI/RateAnalyzer.cc new file mode 100644 index 0000000..3b84d7a --- /dev/null +++ b/PPI/RateAnalyzer.cc @@ -0,0 +1,80 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief RateAnalyzer non-inline non-template implementation */ + +#include "RateAnalyzer.hh" +//#include "RateAnalyzer.ih" + +// Custom includes + +//#include "RateAnalyzer.mpp" +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +prefix_ senf::ppi::module::RateAnalyzer::RateAnalyzer() + : packets_ (0u), bytes_ (0u), minSize_ (0u), maxSize_ (0u), factor_ (0.0f) +{ + registerEvent(timer_, &RateAnalyzer::tick); +} + +prefix_ void senf::ppi::module::RateAnalyzer::startStatistics(senf::ClockService::clock_type interval) +{ + timer_.interval(interval); + factor_ = double(senf::ClockService::in_nanoseconds(interval)) / + double(senf::ClockService::in_nanoseconds( + senf::ClockService::seconds(1))); +} + +prefix_ void senf::ppi::module::RateAnalyzer::v_handlePacket(Packet const & p) +{ + ++packets_; + unsigned sz (p.data().size()); + bytes_ += sz; + if (sz < minSize_ || minSize_ == 0u) minSize_ = sz; + if (sz > maxSize_) maxSize_ = sz; +} + +prefix_ void senf::ppi::module::RateAnalyzer::tick() +{ + signals.packetsPerSecond(packets_/factor_); + signals.bytesPerSecond(bytes_/factor_); + signals.bytesPerPacket(minSize_, float(bytes_)/float(packets_), maxSize_); + + packets_ = bytes_ = minSize_ = maxSize_ = 0; +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ +//#include "RateAnalyzer.mpp" + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/PPI/RateAnalyzer.hh b/PPI/RateAnalyzer.hh new file mode 100644 index 0000000..ed85b33 --- /dev/null +++ b/PPI/RateAnalyzer.hh @@ -0,0 +1,121 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief RateAnalyzer public header */ + +#ifndef HH_SENF_PPI_RateAnalyzer_ +#define HH_SENF_PPI_RateAnalyzer_ 1 + +// Custom includes +#include +#include +#include "MonitorModule.hh" +#include "IntervalTimer.hh" + +//#include "RateAnalyzer.mpp" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { +namespace ppi { +namespace module { + + /** \brief Generate periodic packet statistics + + This module will periodically generate statistics concerning the traversing packets. The + statistics are emitted as Boost.Signals signals: + + \li \c signals.packetsPerSecond: number of packets in the last interval scaled to 1 second + \li \c signals.bytesPerSecond; number of bytes in the last interval scaled to 1 second + \li \c signals.bytesPerPacket: minimal, average and maximal packet size in the last interval + + These signals are normally connected as needed to senf::Statistics instances. + + \code + senf::RateAnalyzer analyzer; + senf::Statistics packets; + senf::Statistics packetSize; + + analyzer.signals.packetsPerSecond.connect(packets); + analyzer.signals.bytesPerPacket.connect(packetSize); + + analyzer.startStatistics(senf::ClockService::milliseconds(100u)); + \endcode + + Statistics output is only generated after a call to startStatistics() + + \ingroup routing_modules + */ + class RateAnalyzer + : public MonitorModule<> + { + SENF_PPI_MODULE(RateAnalyzer); + public: + /////////////////////////////////////////////////////////////////////////// + ///\name Structors and default members + ///\{ + + RateAnalyzer(); + + ///\} + /////////////////////////////////////////////////////////////////////////// + // Statistics signals + + struct Statistics { + boost::signal packetsPerSecond; + boost::signal bytesPerSecond; + boost::signal bytesPerPacket; + } signals; + + void startStatistics(senf::ClockService::clock_type interval); + ///< Start generating statistics at given interval + + private: + void v_handlePacket(Packet const & p); + void tick(); + + senf::ppi::IntervalTimer timer_; + unsigned packets_; + unsigned bytes_; + unsigned minSize_; + unsigned maxSize_; + double factor_; + }; + +}}} + +///////////////////////////////hh.e//////////////////////////////////////// +//#include "RateAnalyzer.cci" +//#include "RateAnalyzer.ct" +//#include "RateAnalyzer.cti" +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/PPI/RateAnalyzer.test.cc b/PPI/RateAnalyzer.test.cc new file mode 100644 index 0000000..110c929 --- /dev/null +++ b/PPI/RateAnalyzer.test.cc @@ -0,0 +1,112 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief RateAnalyzer unit tests */ + +//#include "RateAnalyzer.test.hh" +//#include "RateAnalyzer.test.ih" + +// Custom includes +#include "RateAnalyzer.hh" +#include "PPI.hh" + +#include "../Utils/auto_unit_test.hh" +#include +#include + +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +namespace { + + unsigned calls (0u); + float ppss[] = { 13.333333f, 20.f, 13.333333f, 20.f, 13.333333f, 20.f }; + + float pps; + float bps; + + void collectPPS(float packetsPerSecond) + { + // 2 + 3 + 2 + 3 + 2 + 3 packets / interval + // -> 13.333333 + 20. + 13.333333 + 20. + 13.333333 + 20. / 6 pps + // -> 16.666666 pps + // -> 216.66666 bps + // This test sometime fails when system load is to high ... + // BOOST_CHECK_CLOSE( packetsPerSecond, ppss[calls], 0.1f ); + pps += packetsPerSecond; + ++ calls; + if (calls >= sizeof(ppss) / sizeof(ppss[0])) + senf::scheduler::terminate(); + } + + void collectBPS(float bytesPerSecond) + { + bps += bytesPerSecond; + } + + void collectSize(float min, float avg, float max) + { + BOOST_CHECK_CLOSE(min, 13.f, 0.01f); + BOOST_CHECK_CLOSE(avg, 13.f, 0.01f); + BOOST_CHECK_CLOSE(max, 13.f, 0.01f); + } +} + +BOOST_AUTO_UNIT_TEST(rateAnalyzer) +{ + senf::DataPacket p (senf::DataPacket::create(13u)); + senf::ppi::module::CloneSource source (p); + senf::ppi::module::RateFilter filter (senf::ClockService::milliseconds(58u)); + senf::ppi::module::RateAnalyzer analyzer; + analyzer.startStatistics(senf::ClockService::milliseconds(150u)); + analyzer.signals.packetsPerSecond.connect(&collectPPS); + analyzer.signals.bytesPerSecond.connect(&collectBPS); + analyzer.signals.bytesPerPacket.connect(&collectSize); + + senf::ppi::connect(source, filter); + senf::ppi::connect(filter, analyzer); + + senf::ppi::run(); + + BOOST_CHECK_EQUAL( calls, 6u ); + + pps /= calls; + bps /= calls; + + BOOST_CHECK_CLOSE( pps, 16.67f, .1f ); + BOOST_CHECK_CLOSE( bps, 216.67f, .1f ); +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/Mainpage.dox b/Utils/Mainpage.dox index 1adfc8e..6e2c7f8 100644 --- a/Utils/Mainpage.dox +++ b/Utils/Mainpage.dox @@ -94,6 +94,8 @@ namespace senf { + + diff --git a/Utils/Statistics.cc b/Utils/Statistics.cc new file mode 100644 index 0000000..0b6ab8f --- /dev/null +++ b/Utils/Statistics.cc @@ -0,0 +1,293 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Statistics non-inline non-template implementation */ + +#include "Statistics.hh" +//#include "Statistics.ih" + +// Custom includes +#include +#include "Console/Console.hh" +#include "StatisticsTargets.hh" + +//#include "Statistics.mpp" +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::StatisticsBase + +prefix_ void senf::StatisticsBase::enter(float min, float avg, float max) +{ + min_ = min; + avg_ = avg; + max_ = max; + generateOutput(); + signalChildren(); +} + +prefix_ senf::Collector & senf::StatisticsBase::operator[](unsigned rank) +{ + Children::iterator i (children_.find(rank)); + if (i == children_.end()) + throw InvalidRankException(); + return i->second; +} + +prefix_ senf::Collector & senf::StatisticsBase::collect(unsigned rank) +{ + std::pair state ( + children_.insert(std::make_pair(rank, Collector(this, rank))) ); + if (! state.second) + throw DuplicateRankException(); + return state.first->second; +} + +prefix_ senf::StatisticsBase::OutputProxy +senf::StatisticsBase::output(unsigned n) +{ + OutputMap::iterator i (outputs_.find(n)); + if (i == outputs_.end()) { + i = outputs_.insert(std::make_pair(n, OutputEntry(n))).first; + std::stringstream nm; + nm << "output" << path() << ":" << n; + base().dir.node().add(nm.str(), i->second.dir); + detail::StatisticsLoggerRegistry::instance().apply(*this, n, i->second.dir); + } + if (n > maxQueueLen_) + maxQueueLen_ = n; + return OutputProxy(this, &(i->second)); +} + +prefix_ void senf::StatisticsBase::consoleList(unsigned level, std::ostream & os) + const +{ + os << boost::format("%s%-5d%|15t| %12g %12g %12g\n") + % std::string(2*level,' ') % rank() % min() % avg() % max(); + { + OutputMap::const_iterator i (outputs_.begin()); + OutputMap::const_iterator i_end (outputs_.end()); + for (; i != i_end; ++i) + os << boost::format(" %3d %12g %12g %12g\n") + % i->second.n + % (i->second.min/i->second.n) + % (i->second.avg/i->second.n) + % (i->second.max/i->second.n); + } + { + Children::const_iterator i (children_.begin()); + Children::const_iterator const i_end (children_.end()); + for (; i != i_end; ++i) + i->second.consoleList(level+1, os); + } +} + +prefix_ void senf::StatisticsBase::generateOutput() +{ + queue_.push_front(QueueEntry(min_, avg_, max_)); + while (queue_.size() > maxQueueLen_) + queue_.pop_back(); + + OutputMap::iterator i (outputs_.begin()); + OutputMap::iterator const i_end (outputs_.end()); + for (; i != i_end; ++i) { + i->second.min = i->second.avg = i->second.max = 0.0f; + Queue::const_iterator j (queue_.begin()); + Queue::const_iterator const j_end (queue_.end()); + unsigned n (0); + for (; n < i->second.n && j != j_end; ++n, ++j) { + i->second.min += j->min; + i->second.avg += j->avg; + i->second.max += j->max; + } + i->second.signal(i->second.min/n, i->second.avg/n, i->second.max/n); + } +} + +prefix_ void senf::StatisticsBase::signalChildren() +{ + Children::iterator i (children_.begin()); + Children::iterator const i_end (children_.end()); + for (; i != i_end; ++i) + i->second.enter(min_, avg_, max_); +} + +/////////////////////////////////////////////////////////////////////////// +// senf::Statistics + +prefix_ senf::Statistics::Statistics() +#ifndef SENF_DISABLE_CONSOLE + : dir (this) +#endif +{ +#ifndef SENF_DISABLE_CONSOLE + dir.add("list", &Statistics::consoleList) + .doc("List statistics collection intervals and current values.\n" + "\n" + "Columns:\n" + " RANK Number of values collected. Since the statistics collectors form\n" + " a tree, the value is indented according to it's tree location.\n" + " WIN Size of output average window.\n" + " MIN Last entered minimum value.\n" + " AVG Last entered average value.\n" + " MAX Last entered maximum value."); + dir.add("collect", &Statistics::consoleCollect) + .doc("Add statistics collection groups. The argument gives a sequence of collector\n" + "ranks each building on the preceding collector:\n" + "\n" + " $ collect (10 60 60)\n" + "\n" + "Will start by collecting every 10 values together to a new value. 60 of such\n" + "combined values will be collected together in the next step again followed by\n" + "a collection of 60 values. If the statistics is entered with a frequency of\n" + "10 values per second, this will provide combined statistics over the second,\n" + "minutes and hours ranges.\n" + "\n" + "You may call collect multiple times. Any missing collection ranks will be\n" + "added.") + .arg("ranks","chain of collector ranks"); + dir.add("output", &Statistics::consoleOutput) + .doc("Generate statistics output. This statement will add an additional output\n" + "generator. This generator will be attached to the collector specified by\n" + "the {rank} parameter. This parameter is a chain of successive rank values\n" + "which specifies the exact collector to use. If the collector does not\n" + "exist, it will be created (this is like automatically calling 'collect'\n" + "with {rank} as argument).\n" + "\n" + "If the output is to be sent somewhere it must be connected to a statistics\n" + "target.\n" + "\n" + "The output may optionally be built using a sliding average over the last\n" + "{window} values.\n" + "\n" + " $ output ()\n" + "\n" + "will output the basic statistics value each time a new value is entered.\n" + "\n" + " $ output (10 60) 5\n" + "\n" + "Assuming that new data values are entered 10 times per second, this command\n" + "will generate output once every minute. The value will be the average over\n" + "the last 5 minutes.") + .arg("rank","Rank chain selecting the value to generate output for") + .arg("window","Optional size of sliding average window", + senf::console::kw::default_value = 1u); +#endif +} + +prefix_ void senf::Statistics::consoleList(std::ostream & os) +{ + os << "RANK WIN MIN AVG MAX\n"; + StatisticsBase::consoleList(0, os); +} + +prefix_ void senf::Statistics::consoleCollect(std::vector & ranks) +{ + StatisticsBase * stats (this); + std::vector::const_iterator i (ranks.begin()); + std::vector::const_iterator const i_end (ranks.end()); + + try { + for (; i != i_end; ++i) + stats = &(*stats)[*i]; + } + catch (InvalidRankException &) {} + + for (; i != i_end; ++i) + stats = & (stats->collect(*i)); + +} + +prefix_ boost::shared_ptr +senf::Statistics::consoleOutput(std::vector & ranks, unsigned window) +{ + StatisticsBase * stats (this); + std::vector::const_iterator i (ranks.begin()); + std::vector::const_iterator const i_end (ranks.end()); + + try { + for (; i != i_end; ++i) + stats = &(*stats)[*i]; + } + catch (InvalidRankException &) {} + + for (; i != i_end; ++i) + stats = & (stats->collect(*i)); + + return stats->output(window).dir().node().thisptr(); +} + +prefix_ senf::Statistics & senf::Statistics::v_base() +{ + return *this; +} + +prefix_ std::string senf::Statistics::v_path() + const +{ + return ""; +} + +/////////////////////////////////////////////////////////////////////////// +// senf::Collector + +prefix_ void senf::Collector::enter(float min, float avg, float max) +{ + accAvg_ += avg; + if (min < accMin_) accMin_ = min; + if (max > accMax_) accMax_ = max; + if (++i_ >= rank_) { + StatisticsBase::enter(accMin_, accAvg_ / rank_, accMax_); + i_ = 0; + accMin_ = FLT_MAX; + accAvg_ = 0.0f; + accMax_ = -FLT_MAX; + } +} + +prefix_ senf::Statistics & senf::Collector::v_base() +{ + return owner_->base(); +} + +prefix_ std::string senf::Collector::v_path() + const +{ + return owner_->path() + "-" + senf::str(rank_); +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ +//#include "Statistics.mpp" + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/Statistics.cci b/Utils/Statistics.cci new file mode 100644 index 0000000..1f109db --- /dev/null +++ b/Utils/Statistics.cci @@ -0,0 +1,192 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Statistics inline non-template implementation */ + +//#include "Statistics.ih" + +// Custom includes +#include +#include "Range.hh" + +#define prefix_ inline +///////////////////////////////cci.p/////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::StatisticsBase::Transform + +prefix_ senf::StatisticsBase::Transform::result_type +senf::StatisticsBase::Transform::operator()(first_argument_type i) + const +{ + return i.second; +} + +/////////////////////////////////////////////////////////////////////////// +// senf::StatisticsBase::OutputEntry + +prefix_ senf::StatisticsBase::OutputEntry::OutputEntry() + : n(), min(), avg(), max() +{ + initDir(); +} + +prefix_ senf::StatisticsBase::OutputEntry::OutputEntry(unsigned n_) + : n(n_), min(), avg(), max() +{ + initDir(); +} + +prefix_ senf::StatisticsBase::OutputEntry::OutputEntry(const OutputEntry& other) + : n(other.n), min(other.min), avg(other.avg), max(other.max) +{ + initDir(); +} + +prefix_ void senf::StatisticsBase::OutputEntry::initDir() +{ + dir.add("list", senf::membind(&OutputEntry::consoleList, this)) + .doc("List all known connected targets. This list might not be complete."); +} + +prefix_ senf::StatisticsBase::OutputEntry & +senf::StatisticsBase::OutputEntry::operator=(const OutputEntry& other) +{ + n = other.n; + min = other.min; + avg = other.avg; + max = other.max; + return *this; +} + +prefix_ void senf::StatisticsBase::OutputEntry::consoleList(std::ostream & os) +{ + for (boost::ptr_vector::iterator i (targets_.begin()); + i != targets_.end(); ++i) + if (! i->label.empty()) + os << i->label << "\n"; +} + +///////////////////////////////////////////////////////////////////////// +// senf::StatisticsBase + +prefix_ senf::StatisticsBase::StatisticsBase() + : min_ (0.0f), avg_ (0.0f), max_ (0.0f), maxQueueLen_ (0u) +{} + +prefix_ senf::StatisticsBase::~StatisticsBase() +{} + +prefix_ senf::StatisticsBase::CollectorRange senf::StatisticsBase::collectors() +{ + return senf::make_transform_range(children_, Transform()); +} + +prefix_ float senf::StatisticsBase::min() + const +{ + return min_; +} + +prefix_ float senf::StatisticsBase::avg() + const +{ + return avg_; +} + +prefix_ float senf::StatisticsBase::max() + const +{ + return max_; +} + +prefix_ unsigned senf::StatisticsBase::rank() + const +{ + return 1; +} + +prefix_ senf::Statistics & senf::StatisticsBase::base() +{ + return v_base(); +} + +prefix_ std::string senf::StatisticsBase::path() + const +{ + return v_path(); +} + +/////////////////////////////////////////////////////////////////////////// +// senf::Collector + +prefix_ senf::Collector::Collector(StatisticsBase * owner, unsigned rank) + : rank_ (rank), i_ (0u), accMin_ (FLT_MAX), accAvg_ (0.0f), accMax_ (-FLT_MAX), + owner_ (owner) +{} + +prefix_ unsigned senf::Collector::rank() + const +{ + return rank_; +} + +prefix_ senf::StatisticsBase::OutputProxy +senf::Collector::output(unsigned n) +{ + + return StatisticsBase::OutputProxy(this, StatisticsBase::output(n)); +} + +/////////////////////////////////////////////////////////////////////////// +// senf::Statistics + +prefix_ void senf::Statistics::operator()(float min, float avg, float max) +{ + enter(min, avg, max); +} + +prefix_ void senf::Statistics::operator()(float value) +{ + enter(value, value, value); +} + +prefix_ senf::StatisticsBase::OutputProxy +senf::Statistics::output(unsigned n) +{ + return StatisticsBase::OutputProxy(this, StatisticsBase::output(n)); +} + +///////////////////////////////cci.e/////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/Statistics.cti b/Utils/Statistics.cti new file mode 100644 index 0000000..d34b9ba --- /dev/null +++ b/Utils/Statistics.cti @@ -0,0 +1,100 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Statistics inline template implementation */ + +//#include "Statistics.ih" + +// Custom includes + +#define prefix_ inline +///////////////////////////////cti.p/////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::StatisticsBase::OutputProxy + +template +prefix_ senf::StatisticsBase::OutputProxy::OutputProxy(Owner * owner, OutputEntry * entry) + : owner_ (owner), entry_ (entry) +{} + +template +template +prefix_ senf::StatisticsBase::OutputProxy:: +OutputProxy(Owner * owner, OutputProxy const & other) + : owner_ (owner), entry_ (other.entry_) +{} + +template +template +prefix_ Owner & senf::StatisticsBase::OutputProxy::connect(Target & target, std::string label) + const +{ + if (label.empty()) + label = prettyName(typeid(Target)); + entry_->signal.connect(boost::ref(target)); + entry_->targets_.push_back(new OutputEntry::Target(label)); + return * owner_; +} + +template +template +prefix_ Owner & +senf::StatisticsBase::OutputProxy::connect(std::auto_ptr target, std::string label) + const +{ + if (label.empty()) + label = prettyName(typeid(PTarget)); + PTarget * targetp (target.get()); + entry_->targets_.push_back(new OutputEntry::Target(target,label)); + entry_->signal.connect(boost::ref(*targetp)); + return * owner_; +} + +template +prefix_ Owner & senf::StatisticsBase::OutputProxy::noconnect() + const +{ + return * owner_; +} + +template +prefix_ senf::console::ScopedDirectory<> & senf::StatisticsBase::OutputProxy::dir() + const +{ + return entry_->dir; +} + +///////////////////////////////cti.e/////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/Statistics.hh b/Utils/Statistics.hh new file mode 100644 index 0000000..6409ab1 --- /dev/null +++ b/Utils/Statistics.hh @@ -0,0 +1,497 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Statistics public header */ + +#ifndef HH_SENF_Utils_Statistics_ +#define HH_SENF_Utils_Statistics_ 1 + +// Custom includes +#include +#include +#include +#include +#include +#include +#include +#include +#include "Exception.hh" +#include "Logger/Logger.hh" +#include "Console/Console.hh" + +//#include "Statistics.mpp" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { + + /** \defgroup senf_statistics Statistics + + The statistics functionality has two parts: + + \li the senf::Statistics class + \li statistics sources + + Each senf::Statistics instance collects information about a single parameter. Which + parameter is set up by connecting the Statistics instance with an arbitrary statistics + source. + + %Statistics sources are + Boost Signals which are emitted periodically to provide new data. + */ + + class Collector; + class Statistics; + + /** \brief Internal: Generic Statistics collection */ + class StatisticsBase + { + typedef std::map Children; + + struct Transform { + typedef Children::value_type & first_argument_type; + typedef Collector & result_type; + result_type operator()(first_argument_type i) const; + }; + + typedef boost::transform_iterator ValueIterator; + + struct OutputEntry; + + public: + /////////////////////////////////////////////////////////////////////////// + // Types + + typedef boost::iterator_range CollectorRange; + + /** \brief Output connection interface + + This class is returned from senf::StatisticsBase::output() and the derived + output() implementations to allow connecting an output with an arbitrary + target. + + There are two types of targets: + \li Externally managed targets. These targets live outside of the statistics + module, just a reference to those targets is saved. The target should derive from + boost::signals::trackable to ensure they are automatically disconnected + when destroyed. + \li Internally managed targets. Those targets are owned by the statistics + module and will be destroyed when the statistics class is destroyed. + + Externally managed targets are passed by non-const reference to connect(), internally + managed targets are passed using std::auto_ptr. + + A target is any callable object which takes three float values as argument: The current + minimum, average and maximum value. + + \code + // Simple function as statistics target + void collect(float min, float avg, float max) + { ... } + + // Function object + struct Collector + { + void operator()(float min, float avg, float max) + { ... } + }; + \endcode + + \ingroup senf_statistics + */ + template + class OutputProxy + { + public: + template Owner & connect(Target & target, + std::string label="") const; + ///< Connect externally managed target + template Owner & connect(std::auto_ptr target, + std::string label="") const; + ///< Connect internally managed target + Owner & noconnect() const; ///< Don't connect the output + senf::console::ScopedDirectory<> & dir() const; + ///< Get target's console directory + +#ifdef DOXYGEN + private: +#endif + OutputProxy(Owner * owner, OutputEntry * entry); + template + OutputProxy(Owner * owner, OutputProxy const & other); + + private: + Owner * owner_; + OutputEntry * entry_; + + template friend class OutputProxy; + }; + + /////////////////////////////////////////////////////////////////////////// + ///\name Accessing the current value + ///\{ + + float min() const; ///< Last min value entered + float avg() const; ///< Last avg value entered + float max() const; ///< Last max value entered + + virtual unsigned rank() const; ///< Return collectors rank value + /**< \returns number of basic values collected into each new + value by this collector. */ + + ///\} + /////////////////////////////////////////////////////////////////////////// + ///\name Child collectors + ///\{ + + Collector & operator[](unsigned rank); + ///< Get child collector + /**< This member will return a reference to the collector + collecting \a rank values. + \param[in] rank Number of values the requested + collector collects into each combined value. + \throws InvalidRankException if \a rank is not a valid + registered rank value. */ + CollectorRange collectors(); ///< List all child collectors + /**< \returns iterator range of child collector + references */ + + Collector & collect(unsigned rank); ///< Register a new collector + /**< Adds a collector collecting \a rank values into each + combined value. + \param[in] rank number of values to collect + \returns Reference to new collector + \throws DuplicateRankException if a collector + collecting \a rank values already exists. */ + + Statistics & base(); ///< Get base statistics object + /**< Returns the base statistics object. If this is + a child collector, this will return the outermost + statistics object, otherwise it will return + \c *this. */ + + std::string path() const; ///< Get the path to this collector + /**< Returns the '-'-separated list of collectors up to + here. If this is the basic statistics object, the value + is empty, otherwise it is built by joining the + collector ranks. */ + + ///\} + /////////////////////////////////////////////////////////////////////////// + ///\name Result generation + + OutputProxy output(unsigned n = 1u); + ///< Register output + /**< This call will request the collector to output + statistics build by averaging the last \a n + values. This output is generated for every new value in + the collector. The output signal can be connected to an + arbitrary target using the returned proxy. Example: + \code + stats.output(4u).connect( + senf::StatisticsLogger()); + \endcode + \param[in] n size of sliding average window */ + + ///\} + /////////////////////////////////////////////////////////////////////////// + // Exceptions + + struct InvalidRankException : public senf::Exception + { InvalidRankException() : senf::Exception("Invalid rank value") {} }; + + struct DuplicateRankException : public senf::Exception + { DuplicateRankException() : senf::Exception("Duplicate rank value") {} }; + + void consoleList(unsigned level, std::ostream & os) const; + + protected: + StatisticsBase(); + virtual ~StatisticsBase(); + void enter(float min, float avg, float max); + + private: + virtual Statistics & v_base() = 0; + virtual std::string v_path() const = 0; + + void generateOutput(); + void signalChildren(); + + float min_; + float avg_; + float max_; + Children children_; + + struct QueueEntry { + float min; + float avg; + float max; + QueueEntry() : min(), avg(), max() {} + QueueEntry(float min_, float avg_, float max_) + : min(min_), avg(avg_), max(max_) {} + }; + typedef std::deque Queue; + Queue queue_; + + struct OutputEntry { + struct TargetBase + { + explicit TargetBase(std::string const & label_) : label (label_) {} + virtual ~TargetBase() {}; + std::string label; + }; + + template + struct Target : public TargetBase + { + boost::scoped_ptr target_; + Target(std::auto_ptr target, std::string const & label) + : TargetBase (label), target_ (target.release()) {} + explicit Target(std::string const & label) + : TargetBase (label), target_ (0) {} + }; + + OutputEntry(); + explicit OutputEntry(unsigned n_); + OutputEntry(const OutputEntry& other); + OutputEntry& operator=(const OutputEntry& other); + + void initDir(); + void consoleList(std::ostream & os); + + unsigned n; + float min; + float avg; + float max; + + boost::signal signal; + boost::ptr_vector targets_; + + senf::console::ScopedDirectory<> dir; + }; + typedef std::map OutputMap; + OutputMap outputs_; + unsigned maxQueueLen_; + }; + + /** \brief Collect statistics and generate %log messages + + The Statistics class collects information about a single value. The assumption is, that + Statistics::operator() is called \e periodically to supply new data. + + Each data entry is comprised of a minimum, average and maximum value. These values should + describe the value of whatever parameter is analyzed by a Statistics instance over the last + period. The length of the period is defined by the interval, at which data is entered. + + The Statistics class combines successive data values into ever larger groups. These groups + form a tree where each node combines a fixed number of data values of it's parent node. An + example: + + Let us assume, that Statistics::operator() is called every 100ms. We can than build a chain + of collectors: + \li The basic statistics module provides information with a resolution of 1/10th of a second + \li A collector collecting 10 values provides information with 1 second resolution + \li The next collector collects 60 values and provides a resolution of 1 minute + \li Again the next collector collects 60 values and provides a resolution of 1 hour + \li ... and so on + + This way, we create a hierarchy of values. Each collector manages a minimum, average and + maximum value always created over it's the last complete interval. + + It is possible to have more than one collector based on the same basic interval. In above + scenario, we might want to add another collector which for example collects information + with a 100 second scale. This is possible and changes the list of collectors into a tree. + + Now to turn all this into code: + + \code + senf::ppi::module::RateAnalyzer rateAnalyzer; + senf::Statistics packetStats; + + rateAnalyzer.signals.packetsPerSecond.connect(boost::ref(packetStats)); + + packetStats // called 10 times / second + .collect(10u) // seconds + .collect(60u) // minutes + .collect(60u); // hours + + packetStats[10u].collect(100u); // 100 seconds + + rateAnalyzer.startStatistics(senf::ClockService::milliseconds(100u)); + \endcode + + This code will collect the statistics as described in the Example above. However, no output + will be generated. + + For every collector, any number of outputs may be defined. Each output consists of the + number of values to calculate a sliding average over and an identifying label. + + Lets say, we want to produce the following outputs: + \li A sliding average of 5 values based on the raw 1/10th second data. + \li Three different outputs from the seconds staistics: current value without average, + sliding average over 10 seconds and sliding average over 60 seconds. + \li Output the minutes and hourly value without averaging. + + To achieve this, we can augment above code in the following way: + + \code + packetStats + .output( 5u).connect(senf::StatisicsLogger("pps 100ms 5")) + .collect(10u) + .output( ).noconnect() + .output(10u).connect(senf::StatisicsLogger("pps 1s 10")) + .output(60u).connect(senf::StatisicsLogger("pps 1s 60")) + .collect(60u) + .output( ).connect(senf::StatisicsLogger("pps 1min 1")) + .collect(60u) + .output( ).connect(senf::StatisicsLogger("pps 1h 1")); + + packetStats.output(5u).connect( + senf::StatisticsLogger("pps")); + + senf::log::FileTarget statslog ("stats.log"); + + statslog.showArea(false); + statslog.showLevel(false); + statslog.tag(""); + statslog.timeFormat(""); + + statslog.route(); + \endcode + + We use a StatisticsLogger to send the log messages to the senf::StatisticsStream log + stream. The stream, area an level to send the statistics log messages to may be configured + using template arguments to StatisticsLogger. + + It is also possible to skip sending the output to any target or send one output to several + targets. + + Here we have opted to use a label which explicitly describes the name of the variable, the + basic interval and the size of the sliding average window. However, the label is completely + arbitrary. + + All output is generated using the Senf Logger on the senf::StatisticsStream %log stream. In + the example above, we have configured a special logfile \c stats.log which contains the + statistics values each prefixed with a timestamp in nanoseconds (but no further information + like %log level or tag): +
+        0000000000.000000000 pps 100ms 5 43.3 43.3 43.3
+        0000000000.010311928 pps 100ms 5 42.5 42.5 42.5
+        ...
+        0000000001.002413391 pps 100ms 5 62.0 62.0 62.0
+        0000000001.003920018 pps 1s 1 42.1 53.4 62.0
+        ...
+        
+ (the nanosecond values will of course be somewhat different ...) + + \see senf::StatisticsBase::OutputProxy for the output proxy (connect) interface + \ingroup senf_statistics + */ + class Statistics + : public StatisticsBase, boost::noncopyable + { + public: +#ifndef SENF_DISABLE_CONSOLE + console::ScopedDirectory dir; +#endif + + Statistics(); + + void operator()(float min, float avg, float max); + ///< Enter new data + /**< This member must be called whenever a new data value is + available. It is important to call this member \e + periodically. The frequency at which this member is + called defines the basic statistics time scale. + + If \a min and \a max values are not available, this + member should be called with \a min, \a avg and \a max + set to the same value. + + \param[in] min minimal data value since last call + \param[in] avg average data value since last call + \param[in] max maximal data values since last call */ + + void operator()(float value); ///< Same as enter() with \a min == \a avg == \a max + /**< Provided so a Statistics instance can be directly used + as a signal target. */ + + StatisticsBase::OutputProxy output(unsigned n = 1u); + + void consoleList(std::ostream & os); + void consoleCollect(std::vector & ranks); + boost::shared_ptr consoleOutput( + std::vector & ranks, unsigned window); + + private: + Statistics & v_base(); + std::string v_path() const; + }; + + /** \brief Accumulated statistics collector + + This class collects accumulated statistics. It is automatically created by + senf::Statistics::collect() + + \see senf::Statistics + */ + class Collector : public StatisticsBase + { + public: + virtual unsigned rank() const; + + StatisticsBase::OutputProxy output(unsigned n = 1u); + + private: + Collector(StatisticsBase * owner, unsigned rank); + void enter(float min, float avg, float max); + Statistics & v_base(); + std::string v_path() const; + + unsigned rank_; + unsigned i_; + float accMin_; + float accAvg_; + float accMax_; + StatisticsBase * owner_; + + friend class StatisticsBase; + }; + +} + +///////////////////////////////hh.e//////////////////////////////////////// +#include "Statistics.cci" +//#include "Statistics.ct" +#include "Statistics.cti" +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/Statistics.test.cc b/Utils/Statistics.test.cc new file mode 100644 index 0000000..99c31e3 --- /dev/null +++ b/Utils/Statistics.test.cc @@ -0,0 +1,181 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Statistics unit tests */ + +//#include "Statistics.test.hh" +//#include "Statistics.test.ih" + +// Custom includes +#include "Statistics.hh" +#include "StatisticsTargets.hh" + +#include "auto_unit_test.hh" +#include +#include + +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +namespace { + + struct GetRange + { + typedef senf::Collector const & first_argument_type; + typedef unsigned result_type; + result_type operator()(first_argument_type arg) const + { return arg.rank(); } + }; + +} + +BOOST_AUTO_UNIT_TEST(statistics) +{ + senf::Statistics stats; + senf::log::StringTarget statslog; + + statslog.tag(""); + statslog.showTime(false); + statslog.showArea(false); + statslog.showLevel(false); + statslog.route(); + + stats + .output(2u).connect( + senf::StatisticsLogger("level0")) + .collect(4u) + .output().connect( + senf::StatisticsLogger("level1")) + .output(3u).connect( + senf::StatisticsLogger("averaged1")) + .collect(3u) + .collect(2u) + .output().connect( + senf::StatisticsLogger("level3")); + + unsigned children1[] = { 4u }; + BOOST_CHECK_EQUAL_COLLECTIONS( + boost::make_transform_iterator(stats.collectors().begin(), GetRange()), + boost::make_transform_iterator(stats.collectors().end(), GetRange()), + children1, children1 + sizeof(children1)/sizeof(children1[0]) ); + + unsigned children2[] = { 3u }; + BOOST_CHECK_EQUAL_COLLECTIONS( + boost::make_transform_iterator(stats[4].collectors().begin(), GetRange()), + boost::make_transform_iterator(stats[4].collectors().end(), GetRange()), + children2, children2 + sizeof(children2)/sizeof(children2[0]) ); + + unsigned children3[] = { 2u }; + BOOST_CHECK_EQUAL_COLLECTIONS( + boost::make_transform_iterator(stats[4][3].collectors().begin(), GetRange()), + boost::make_transform_iterator(stats[4][3].collectors().end(), GetRange()), + children3, children3 + sizeof(children3)/sizeof(children3[0]) ); + + float values[][3] = { + { -1.0f, 2.3f, 2.5f }, { 0.3f, 2.4f, 3.8f }, { -1.1f, -0.3f, 0.0f }, + { -0.3f, 3.2f, 3.3f }, { 1.0f, 1.1f, 1.1f }, { 0.5f, 0.5f, 0.5f }, + { 0.0f, 0.0f, 0.0f }, { -2.0f, -1.8f, -1.0f }, { 0.0f, 2.3f, 2.4f }, + { 0.4f, 1.2f, 2.0f }, { -1.0f, 2.3f, 2.5f }, { 0.3f, 2.4f, 3.8f }, + { -1.1f, -0.3f, 0.0f }, { -0.3f, 3.2f, 3.3f }, { 1.0f, 1.1f, 1.1f }, + { 0.5f, 0.5f, 0.5f }, { 0.0f, 0.0f, 0.0f }, { -2.0f, -1.8f, -1.0f }, + { 0.0f, 2.3f, 2.4f }, { 0.4f, 1.2f, 2.0f }, { -1.0f, 2.3f, 2.5f }, + { 0.3f, 2.4f, 3.8f }, { -1.1f, -0.3f, 0.0f }, { -0.3f, 3.2f, 3.3f }, + { 1.0f, 1.1f, 1.1f }, { 0.5f, 0.5f, 0.5f }, { 0.0f, 0.0f, 0.0f }, + { -2.0f, -1.8f, -1.0f }, { 0.0f, 2.3f, 2.4f }, { 0.4f, 1.2f, 4.0f } }; + + for (unsigned i (0); i < sizeof(values)/sizeof(values[0]); ++i) + stats(values[i][0], values[i][1], values[i][2]); + + BOOST_CHECK_CLOSE( stats.min(), 0.4f, .1f ); + BOOST_CHECK_CLOSE( stats.avg(), 1.2f, .1f ); + BOOST_CHECK_CLOSE( stats.max(), 4.0f, .1f ); + + BOOST_CHECK_CLOSE( stats[4].min(), -2.0f, .1f ); + BOOST_CHECK_CLOSE( stats[4].avg(), -0.05f, .1f ); + BOOST_CHECK_CLOSE( stats[4].max(), 1.1f, .1f ); + + BOOST_CHECK_CLOSE( stats[4][3].min(), -2.0f, .1f ); + BOOST_CHECK_CLOSE( stats[4][3].avg(), 1.15f, .1f ); + BOOST_CHECK_CLOSE( stats[4][3].max(), 3.8f, .1f ); + + BOOST_CHECK_EQUAL( statslog.str(), + "level0 -1 2.3 2.5\n" + "level0 -0.35 2.35 3.15\n" + "level0 -0.4 1.05 1.9\n" + "level0 -0.7 1.45 1.65\n" + "level1 -1.1 1.9 3.8\n" + "averaged1 -1.1 1.9 3.8\n" + "level0 0.35 2.15 2.2\n" + "level0 0.75 0.8 0.8\n" + "level0 0.25 0.25 0.25\n" + "level0 -1 -0.9 -0.5\n" + "level1 -2 -0.05 1.1\n" + "averaged1 -1.55 0.925 2.45\n" + "level0 -1 0.25 0.7\n" + "level0 0.2 1.75 2.2\n" + "level0 -0.3 1.75 2.25\n" + "level0 -0.35 2.35 3.15\n" + "level1 -1 2.05 3.8\n" + "averaged1 -1.36667 1.3 2.9\n" + "level0 -0.4 1.05 1.9\n" + "level0 -0.7 1.45 1.65\n" + "level0 0.35 2.15 2.2\n" + "level0 0.75 0.8 0.8\n" + "level1 -1.1 1.125 3.3\n" + "averaged1 -1.36667 1.04167 2.73333\n" + "level0 0.25 0.25 0.25\n" + "level0 -1 -0.9 -0.5\n" + "level0 -1 0.25 0.7\n" + "level0 0.2 1.75 2.2\n" + "level1 -2 0.425 2.4\n" + "averaged1 -1.36667 1.2 3.16667\n" + "level0 -0.3 1.75 2.25\n" + "level0 -0.35 2.35 3.15\n" + "level0 -0.4 1.05 1.9\n" + "level0 -0.7 1.45 1.65\n" + "level1 -1.1 1.9 3.8\n" + "averaged1 -1.4 1.15 3.16667\n" + "level3 -2 1.225 3.8\n" + "level0 0.35 2.15 2.2\n" + "level0 0.75 0.8 0.8\n" + "level0 0.25 0.25 0.25\n" + "level0 -1 -0.9 -0.5\n" + "level1 -2 -0.05 1.1\n" + "averaged1 -1.7 0.758333 2.43333\n" + "level0 -1 0.25 0.7\n" + "level0 0.2 1.75 3.2\n" ); +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/StatisticsTargets.cc b/Utils/StatisticsTargets.cc new file mode 100644 index 0000000..a131e36 --- /dev/null +++ b/Utils/StatisticsTargets.cc @@ -0,0 +1,111 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief StatisticsTargets non-inline non-template implementation */ + +#include "StatisticsTargets.hh" +#include "StatisticsTargets.ih" + +// Custom includes +#include +#include "Console/Console.hh" +#include "Statistics.hh" + +//#include "StatisticsTargets.mpp" +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::detail::StatisticsLoggerRegistry + +prefix_ void +senf::detail::StatisticsLoggerRegistry::apply(senf::StatisticsBase & stats, + unsigned rank, + senf::console::DirectoryNode & dir) +{ + Adders::const_iterator i (adders_.begin()); + Adders::const_iterator const i_end (adders_.end()); + for (; i != i_end; ++i) + (*i)(stats, rank, dir); +} + +/////////////////////////////////////////////////////////////////////////// + +namespace { + + struct RegisterStatisticsLogger + { + RegisterStatisticsLogger(); + + static void adder(senf::StatisticsBase & stats, + unsigned rank, + senf::console::DirectoryNode & dir); + + static void consoleCreate(senf::StatisticsBase & stats, + unsigned rank, + std::string const & prefix); + }; + + RegisterStatisticsLogger registerStatisticsLogger; +} + +prefix_ RegisterStatisticsLogger::RegisterStatisticsLogger() +{ + senf::detail::StatisticsLoggerRegistry::instance().add(&adder); +} + +prefix_ void RegisterStatisticsLogger::adder(senf::StatisticsBase & stats, + unsigned rank, + senf::console::DirectoryNode & dir) +{ + namespace kw = senf::console::kw; + + dir.add("logger", boost::function( + boost::bind(&consoleCreate, boost::ref(stats), rank, _1))) + .arg("prefix","Optional prefix string to add to each log message", + kw::default_value = "") + .doc("Send log messages to statistics log stream"); +} + +prefix_ void RegisterStatisticsLogger::consoleCreate(senf::StatisticsBase & stats, + unsigned rank, + std::string const & prefix) +{ + stats.output(rank).connect(senf::StatisticsLogger(prefix), + "senf::StatisticsLogger(\"" + prefix + "\")"); +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ +//#include "StatisticsTargets.mpp" + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/StatisticsTargets.cci b/Utils/StatisticsTargets.cci new file mode 100644 index 0000000..b42e9e8 --- /dev/null +++ b/Utils/StatisticsTargets.cci @@ -0,0 +1,62 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief StatisticsTargets inline non-template implementation */ + +#include "StatisticsTargets.ih" + +// Custom includes + +#define prefix_ inline +///////////////////////////////cci.p/////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::detail::StatisticsLoggerRegistry + +prefix_ void senf::detail::StatisticsLoggerRegistry::add(AddFn fn) +{ + adders_.push_back(fn); +} + +/////////////////////////////////////////////////////////////////////////// + +prefix_ std::auto_ptr< senf::detail::StatisticsLogger > +senf::StatisticsLogger(std::string const & label) +{ + return std::auto_ptr< detail::StatisticsLogger >( + new detail::StatisticsLogger(label)); +} + +///////////////////////////////cci.e/////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/StatisticsTargets.ct b/Utils/StatisticsTargets.ct new file mode 100644 index 0000000..9bb386a --- /dev/null +++ b/Utils/StatisticsTargets.ct @@ -0,0 +1,56 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief StatisticsTargets non-inline template implementation */ + +#include "StatisticsTargets.ih" + +// Custom includes + +#define prefix_ +///////////////////////////////ct.p//////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::detail::StatisticsLogger + +template +prefix_ void senf::detail::StatisticsLogger::operator()(float min, + float avg, + float max) +{ + SENF_LOG_TPL((StatisticsStream)(Stream)(Area)(Level)(label << min << " " << avg << " " << max)); +} + +///////////////////////////////ct.e//////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/StatisticsTargets.cti b/Utils/StatisticsTargets.cti new file mode 100644 index 0000000..922578e --- /dev/null +++ b/Utils/StatisticsTargets.cti @@ -0,0 +1,80 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief StatisticsTargets inline template implementation */ + +#include "StatisticsTargets.ih" + +// Custom includes + +#define prefix_ inline +///////////////////////////////cti.p/////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::detail::StatisticsLogger + +template +prefix_ senf::detail::StatisticsLogger:: +StatisticsLogger(std::string const & label_) + : label (label_.empty() ? "" : label_ + " ") +{} + +/////////////////////////////////////////////////////////////////////////// + +template +prefix_ std::auto_ptr< senf::detail::StatisticsLogger > +senf::StatisticsLogger(std::string const & label) +{ + return std::auto_ptr< detail::StatisticsLogger >( + new detail::StatisticsLogger(label)); +} + +template +prefix_ std::auto_ptr< senf::detail::StatisticsLogger > +senf::StatisticsLogger(std::string const & label) +{ + return std::auto_ptr< detail::StatisticsLogger >( + new detail::StatisticsLogger(label)); +} + +template +prefix_ std::auto_ptr< senf::detail::StatisticsLogger > +senf::StatisticsLogger(std::string const & label) +{ + return std::auto_ptr< detail::StatisticsLogger >( + new detail::StatisticsLogger(label)); +} + +///////////////////////////////cti.e/////////////////////////////////////// +#undef prefix_ + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/StatisticsTargets.hh b/Utils/StatisticsTargets.hh new file mode 100644 index 0000000..93a2180 --- /dev/null +++ b/Utils/StatisticsTargets.hh @@ -0,0 +1,111 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief StatisticsTargets public header */ + +#ifndef HH_SENF_PPI_Utils_StatisticsTargets_ +#define HH_SENF_PPI_Utils_StatisticsTargets_ 1 + +// Custom includes +#include +#include "Logger/Logger.hh" + +//#include "StatisticsTargets.mpp" +#include "StatisticsTargets.ih" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { + +#ifdef DOXYGEN + /** \brief Logging stream for statistics messages + \ingroup senf_statistics + */ + typedef unspecified StatisticsStream; +#else + SENF_LOG_DEFINE_STREAM(StatisticsStream, + senf::log::MESSAGE, senf::log::MESSAGE, senf::log::MESSAGE); +#endif + +#ifdef DOXYGEN + + /** \brief Send statistics to SENF log + + This statistics target will send all statistics values to the SENF log. The template + parameters optionally specify the stream, area and/or level to send the messages to. An + optional \a label string may be specified which will be added as prefix to all log entries. + + The log stream defaults to senf::StatisticsStream, the level defaults to + senf::log::MESSAGE and the are defaults to senf::log::DefaultArea. + + \code + // Connect with default parameters + stats.output(2u).connect() + + // Send statistics messages to the debug stream at default level (MESSAGE) + // Place them in the MyStatistics area. Add 'stat1' prefix to each log message + SENF_LOG_DEF_AREA(MyStatistics); + stats.output(2u).connect("stat1"); + \endcode + + \ingroup senf_statistics + */ + template + std::auto_ptr< unspecified > StatisticsLogger(std::string const & label="") + +#else + + std::auto_ptr< detail::StatisticsLogger > StatisticsLogger( + std::string const & label=""); + + template + std::auto_ptr< detail::StatisticsLogger > StatisticsLogger( + std::string const & label=""); + + template + std::auto_ptr< detail::StatisticsLogger > StatisticsLogger( + std::string const & label=""); + + template + std::auto_ptr< detail::StatisticsLogger > StatisticsLogger( + std::string const & label=""); + +#endif + +} + +///////////////////////////////hh.e//////////////////////////////////////// +#include "StatisticsTargets.cci" +#include "StatisticsTargets.ct" +#include "StatisticsTargets.cti" +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/StatisticsTargets.ih b/Utils/StatisticsTargets.ih new file mode 100644 index 0000000..54c5d00 --- /dev/null +++ b/Utils/StatisticsTargets.ih @@ -0,0 +1,87 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief StatisticsTargets internal header */ + +#ifndef IH_SENF_Utils_StatisticsTargets_ +#define IH_SENF_Utils_StatisticsTargets_ 1 + +// Custom includes +#include +#include +#include + +///////////////////////////////ih.p//////////////////////////////////////// + +namespace senf { + + namespace console { class DirectoryNode; } + + class StatisticsBase; + +namespace detail { + + class StatisticsLoggerRegistry + : public senf::singleton + { + public: + typedef void (*AddFn)(senf::StatisticsBase &, unsigned, + senf::console::DirectoryNode &); + + using senf::singleton::instance; + using senf::singleton::alive; + + void add(AddFn fn); + void apply(senf::StatisticsBase & stats, unsigned rank, + senf::console::DirectoryNode & dir); + + private: + typedef std::vector Adders; + Adders adders_; + }; + + template + struct StatisticsLogger + : boost::noncopyable + { + StatisticsLogger(std::string const & label_); + void operator()(float min, float avg, float max); + + std::string label; + }; + +}} + +///////////////////////////////ih.e//////////////////////////////////////// +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End:
\ref exceptionstandard exception for system errors (errno)
\ref senf_statisticsstatistics functionality
\ref hexdumpa simple but usefull function to write binary data in in hexadecimal format.