123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137 |
- // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #ifndef __STATS_MGR_H
- #define __STATS_MGR_H
- #include <iostream>
- #include <map>
- #include <boost/noncopyable.hpp>
- #include <boost/shared_ptr.hpp>
- #include <boost/multi_index_container.hpp>
- #include <boost/multi_index/hashed_index.hpp>
- #include <boost/multi_index/sequenced_index.hpp>
- #include <boost/multi_index/global_fun.hpp>
- #include <boost/multi_index/mem_fun.hpp>
- #include <boost/date_time/posix_time/posix_time.hpp>
- #include <exceptions/exceptions.h>
- namespace isc {
- namespace perfdhcp {
- /// \brief Statistics Manager
- ///
- /// This class template is a storage for various performance statistics
- /// collected during performance tests execution with perfdhcp tool.
- ///
- /// Statistics Manager holds lists of sent and received packets and
- /// groups them into exchanges. For example: DHCPDISCOVER message and
- /// corresponding DHCPOFFER messages belong to one exchange, DHCPREQUEST
- /// and corresponding DHCPACK message belong to another exchange etc.
- /// In order to update statistics for a particular exchange type, client
- /// class passes sent and received packets. Internally, Statistics Manager
- /// tries to match transaction id of received packet with sent packet
- /// stored on the list of sent packets. When packets are matched the
- /// round trip time can be calculated.
- ///
- /// \tparam T class representing DHCPv4 or DHCPv6 packet.
- template <class T>
- class StatsMgr : public boost::noncopyable {
- public:
- /// \brief Custom Counter
- ///
- /// This class represents custom statistics counters. Client class
- /// may create unlimited number of counters. Such counters are
- /// being stored in map in Statistics Manager and access using
- /// unique string key.
- class CustomCounter {
- public:
- /// \brief Constructor.
- ///
- /// This constructor sets counter name. This name is used in
- /// log file to report value of each counter.
- ///
- /// \param name name of the counter used in log file.
- CustomCounter(const std::string& name) :
- counter_(0),
- name_(name) { };
- /// \brief Increment operator.
- const CustomCounter& operator++() {
- ++counter_;
- return(*this);
- }
- /// \brief Increment operator.
- const CustomCounter& operator++(int) {
- CustomCounter& this_counter(*this);
- operator++();
- return(this_counter);
- }
- /// \brief Return counter value.
- ///
- /// Method returns counter value.
- ///
- /// \return counter value.
- uint64_t getValue() const { return(counter_); }
- /// \brief Return counter name.
- ///
- /// Method returns counter name.
- ///
- /// \return counter name.
- const std::string& getName() const { return(name_); }
- private:
- /// \brief Default constructor.
- ///
- /// Default constrcutor is private because we don't want client
- /// class to call it because we want client class to specify
- /// counter's name.
- CustomCounter() { };
- uint64_t counter_; ///< Counter's value.
- std::string name_; ///< Counter's name.
- };
- typedef typename boost::shared_ptr<CustomCounter> CustomCounterPtr;
- /// DHCP packet exchange types.
- enum ExchangeType {
- XCHG_DO, ///< DHCPv4 DISCOVER-OFFER
- XCHG_RA, ///< DHCPv4 REQUEST-ACK
- XCHG_SA, ///< DHCPv6 SOLICIT-ADVERTISE
- XCHG_RR ///< DHCPv6 REQUEST-REPLY
- };
- /// \brief Exchange Statistics.
- ///
- /// This class collects statistics for exchanges. Parent class
- /// may define number of different packet exchanges like:
- /// DHCPv4 DISCOVER-OFFER, DHCPv6 SOLICIT-ADVERTISE etc. Performance
- /// statistics will be collected for each of those separately in
- /// corresponding instance of ExchangeStats.
- class ExchangeStats {
- public:
- /// \brief Hash transaction id of the packet.
- ///
- /// Function hashes transaction id of the packet. Hashing is
- /// non-unique. Many packets may have the same hash value and thus
- /// they belong to the same packet buckets. Packet buckets are
- /// used for unordered packets search with multi index container.
- ///
- /// \param packet packet which transaction id is to be hashed.
- /// \throw isc::BadValue if packet is null.
- /// \return transaction id hash.
- static uint32_t hashTransid(const boost::shared_ptr<const T>& packet) {
- if (!packet) {
- isc_throw(BadValue, "Packet is null");
- }
- return(packet->getTransid() & 1023);
- }
- /// \brief List of packets (sent or received).
- ///
- /// List of packets based on multi index container allows efficient
- /// search of packets based on their sequence (order in which they
- /// were inserted) as well as based on their hashed transaction id.
- /// The first index (sequenced) provides the way to use container
- /// as a regular list (including iterators, removal of elements from
- /// the middle of the collection etc.). This index is meant to be used
- /// more frequently than the latter one and it is based on the
- /// assumption that responses from the DHCP server are received in
- /// order. In this case, when next packet is received it can be
- /// matched with next packet on the list of sent packets. This
- /// prevents intensive searches on the list of sent packets every
- /// time new packet arrives. In many cases however packets can be
- /// dropped by the server or may be sent out of order and we still
- /// want to have ability to search packets using transaction id.
- /// The second index can be used for this purpose. This index is
- /// hashing transaction ids using custom function \ref hashTransid.
- /// Note that other possibility would be to simply specify index
- /// that uses transaction id directly (instead of hashing with
- /// \ref hashTransid). In this case however we have chosen to use
- /// hashing function because it shortens the index size to just
- /// 1023 values maximum. Search operation on this index generally
- /// returns the range of packets that have the same transaction id
- /// hash assigned but most often these ranges will be short so further
- /// search within a range to find a packet with pacrticular transaction
- /// id will not be intensive.
- ///
- /// Example 1: Add elements to the list
- /// \code
- /// PktList packets_collection();
- /// boost::shared_ptr<Pkt4> pkt1(new Pkt4(...));
- /// boost::shared_ptr<Pkt4> pkt2(new Pkt4(...));
- /// // Add new packet to the container, it will be available through
- /// // both indexes
- /// packets_collection.push_back(pkt1);
- /// // Here is another way to add packet to the container. The result
- /// // is exactly the same as previously.
- /// packets_collection.template get<0>().push_back(pkt2);
- /// \endcode
- ///
- /// Example 2: Access elements through sequencial index
- /// \code
- /// PktList packets_collection();
- /// ... # Add elements to the container
- /// for (PktListIterator it = packets_collection.begin();
- /// it != packets_collection.end();
- /// ++it) {
- /// boost::shared_ptr<Pkt4> pkt = *it;
- /// # Do something with packet;
- /// }
- /// \endcode
- ///
- /// Example 3: Access elements through hashed index
- /// \code
- /// // Get the instance of the second search index.
- /// PktListTransidHashIndex& idx = sent_packets_.template get<1>();
- /// // Get the range (bucket) of packets sharing the same transaction
- /// // id hash.
- /// std::pair<PktListTransidHashIterator,PktListTransidHashIterator> p =
- /// idx.equal_range(hashTransid(rcvd_packet));
- /// // Iterate through the returned bucket.
- /// for (PktListTransidHashIterator it = p.first; it != p.second;
- /// ++it) {
- /// boost::shared_ptr pkt = *it;
- /// ... # Do something with the packet (e.g. check transaction id)
- /// }
- /// \endcode
- typedef boost::multi_index_container<
- boost::shared_ptr<const T>,
- boost::multi_index::indexed_by<
- boost::multi_index::sequenced<>,
- boost::multi_index::hashed_non_unique<
- boost::multi_index::global_fun<
- const boost::shared_ptr<const T>&,
- uint32_t,
- &ExchangeStats::hashTransid
- >
- >
- >
- > PktList;
- /// Packet list iterator for sequencial access to elements.
- typedef typename PktList::const_iterator PktListIterator;
- /// Packet list index to search packets using transaction id hash.
- typedef typename PktList::template nth_index<1>::type
- PktListTransidHashIndex;
- /// Packet list iterator to access packets using transaction id hash.
- typedef typename PktListTransidHashIndex::const_iterator
- PktListTransidHashIterator;
- /// \brief Constructor
- ///
- /// \param xchg_type exchange type
- /// \param archive_enabled if true packets archive mode is enabled.
- /// In this mode all packets are stored throughout the test execution.
- ExchangeStats(const ExchangeType xchg_type, const bool archive_enabled)
- : xchg_type_(xchg_type),
- min_delay_(std::numeric_limits<double>::max()),
- max_delay_(0.),
- sum_delay_(0.),
- orphans_(0),
- sum_delay_squared_(0.),
- ordered_lookups_(0),
- unordered_lookup_size_sum_(0),
- unordered_lookups_(0),
- sent_packets_num_(0),
- rcvd_packets_num_(0),
- sent_packets_(),
- rcvd_packets_(),
- archived_packets_(),
- archive_enabled_(archive_enabled) {
- next_sent_ = sent_packets_.begin();
- }
- /// \brief Add new packet to list of sent packets.
- ///
- /// Method adds new packet to list of sent packets.
- ///
- /// \param packet packet object to be added.
- /// \throw isc::BadValue if packet is null.
- void appendSent(const boost::shared_ptr<const T>& packet) {
- if (!packet) {
- isc_throw(BadValue, "Packet is null");
- }
- ++sent_packets_num_;
- sent_packets_.template get<0>().push_back(packet);
- }
- /// \brief Add new packet to list of received packets.
- ///
- /// Method adds new packet to list of received packets.
- ///
- /// \param packet packet object to be added.
- /// \throw isc::BadValue if packet is null.
- void appendRcvd(const boost::shared_ptr<const T>& packet) {
- if (!packet) {
- isc_throw(BadValue, "Packet is null");
- }
- rcvd_packets_.push_back(packet);
- }
- /// \brief Update delay counters.
- ///
- /// Method updates delay counters based on timestamps of
- /// sent and received packets.
- ///
- /// \param sent_packet sent packet
- /// \param rcvd_packet received packet
- /// \throw isc::BadValue if sent or received packet is null.
- /// \throw isc::Unexpected if failed to calculate timestamps
- void updateDelays(const boost::shared_ptr<const T>& sent_packet,
- const boost::shared_ptr<const T>& rcvd_packet) {
- if (!sent_packet) {
- isc_throw(BadValue, "Sent packet is null");
- }
- if (!rcvd_packet) {
- isc_throw(BadValue, "Received packet is null");
- }
- boost::posix_time::ptime sent_time = sent_packet->getTimestamp();
- boost::posix_time::ptime rcvd_time = rcvd_packet->getTimestamp();
- if (sent_time.is_not_a_date_time() ||
- rcvd_time.is_not_a_date_time()) {
- isc_throw(Unexpected,
- "Timestamp must be set for sent and "
- "received packet to measure RTT");
- }
- boost::posix_time::time_period period(sent_time, rcvd_time);
- // We don't bother calculating deltas in nanoseconds. It is much
- // more convenient to use seconds instead because we are going to
- // sum them up.
- double delta =
- static_cast<double>(period.length().total_nanoseconds()) / 1e9;
- if (delta < 0) {
- isc_throw(Unexpected, "Sent packet's timestamp must not be "
- "greater than received packet's timestamp");
- }
- // Record the minimum delay between sent and received packets.
- if (delta < min_delay_) {
- min_delay_ = delta;
- }
- // Record the maximum delay between sent and received packets.
- if (delta > max_delay_) {
- max_delay_ = delta;
- }
- // Update delay sum and square sum. That will be used to calculate
- // mean delays.
- sum_delay_ += delta;
- sum_delay_squared_ += delta * delta;
- }
- /// \brief Match received packet with the corresponding sent packet.
- ///
- /// Method finds packet with specified transaction id on the list
- /// of sent packets. It is used to match received packet with
- /// corresponding sent packet.
- /// Since packets from the server most often come in the same order
- /// as they were sent by client, this method will first check if
- /// next sent packet matches. If it doesn't, function will search
- /// the packet using indexing by transaction id. This reduces
- /// packet search time significantly.
- ///
- /// \param rcvd_packet received packet to be matched with sent packet.
- /// \throw isc::BadValue if received packet is null.
- /// \return packet having specified transaction or NULL if packet
- /// not found
- boost::shared_ptr<const T> matchPackets(const boost::shared_ptr<const T>& rcvd_packet) {
- if (!rcvd_packet) {
- isc_throw(BadValue, "Received packet is null");
- }
- if (sent_packets_.size() == 0) {
- // List of sent packets is empty so there is no sense
- // to continue looking fo the packet. It also means
- // that the received packet we got has no corresponding
- // sent packet so orphans counter has to be updated.
- ++orphans_;
- return(boost::shared_ptr<const T>());
- } else if (next_sent_ == sent_packets_.end()) {
- // Even if there are still many unmatched packets on the
- // list we might hit the end of it because of unordered
- // lookups. The next logical step is to reset iterator.
- next_sent_ = sent_packets_.begin();
- }
- // With this variable we will be signalling success or failure
- // to find the packet.
- bool packet_found = false;
- // Most likely responses are sent from the server in the same
- // order as client's requests to the server. We are caching
- // next sent packet and first try to match it with the next
- // incoming packet. We are successful if there is no
- // packet drop or out of order packets sent. This is actually
- // the fastest way to look for packets.
- if ((*next_sent_)->getTransid() == rcvd_packet->getTransid()) {
- ++ordered_lookups_;
- packet_found = true;
- } else {
- // If we are here, it means that we were unable to match the
- // next incoming packet with next sent packet so we need to
- // take a little more expensive approach to look packets using
- // alternative index (transaction id & 1023).
- PktListTransidHashIndex& idx = sent_packets_.template get<1>();
- // Packets are grouped using trasaction id masked with value
- // of 1023. For instance, packets with transaction id equal to
- // 1, 1024 ... will belong to the same group (a.k.a. bucket).
- // When using alternative index we don't find the packet but
- // bucket of packets and we need to iterate through the bucket
- // to find the one that has desired transaction id.
- std::pair<PktListTransidHashIterator,PktListTransidHashIterator> p =
- idx.equal_range(hashTransid(rcvd_packet));
- // We want to keep statistics of unordered lookups to make
- // sure that there is a right balance between number of
- // unordered lookups and ordered lookups. If number of unordered
- // lookups is high it may mean that many packets are lost or
- // sent out of order.
- ++unordered_lookups_;
- // We also want to keep the mean value of the bucket. The lower
- // bucket size the better. If bucket sizes appear to big we
- // might want to increase number of buckets.
- unordered_lookup_size_sum_ += std::distance(p.first, p.second);
- for (PktListTransidHashIterator it = p.first; it != p.second;
- ++it) {
- if ((*it)->getTransid() == rcvd_packet->getTransid()) {
- packet_found = true;
- next_sent_ =
- sent_packets_.template project<0>(it);
- break;
- }
- }
- }
- if (!packet_found) {
- // If we are here, it means that both ordered lookup and
- // unordered lookup failed. Searched packet is not on the list.
- ++orphans_;
- return(boost::shared_ptr<const T>());
- }
- // Packet is matched so we count it. We don't count unmatched packets
- // as they are counted as orphans with a separate counter.
- ++rcvd_packets_num_;
- boost::shared_ptr<const T> sent_packet(*next_sent_);
- // If packet was found, we assume it will be never searched
- // again. We want to delete this packet from the list to
- // improve performance of future searches.
- next_sent_ = eraseSent(next_sent_);
- return(sent_packet);
- }
- /// \brief Return minumum delay between sent and received packet.
- ///
- /// Method returns minimum delay between sent and received packet.
- ///
- /// \return minimum delay between packets.
- double getMinDelay() const { return(min_delay_); }
- /// \brief Return maxmimum delay between sent and received packet.
- ///
- /// Method returns maximum delay between sent and received packet.
- ///
- /// \return maximum delay between packets.
- double getMaxDelay() const { return(max_delay_); }
- /// \brief Return avarage packet delay.
- ///
- /// Method returns average packet delay. If no packets have been
- /// received for this exchange avg delay can't be calculated and
- /// thus method throws exception.
- ///
- /// \throw isc::InvalidOperation if no packets for this exchange
- /// have been received yet.
- /// \return average packet delay.
- double getAvgDelay() const {
- if (rcvd_packets_num_ == 0) {
- isc_throw(InvalidOperation, "no packets received");
- }
- return(sum_delay_ / rcvd_packets_num_);
- }
- /// \brief Return standard deviation of packet delay.
- ///
- /// Method returns standard deviation of packet delay. If no
- /// packets have been received for this exchange, the standard
- /// deviation can't be calculated and thus method throws
- /// exception.
- ///
- /// \throw isc::InvalidOperation if number of received packets
- /// for the exchange is equal to zero.
- /// \return standard deviation of packet delay.
- double getStdDevDelay() const {
- if (rcvd_packets_num_ == 0) {
- isc_throw(InvalidOperation, "no packets received");
- }
- return(sqrt(sum_delay_squared_ / rcvd_packets_num_ -
- getAvgDelay() * getAvgDelay()));
- }
- /// \brief Return number of orphant packets.
- ///
- /// Method returns number of received packets that had no matching
- /// sent packet. It is possible that such packet was late or not
- /// for us.
- ///
- /// \return number of orphant received packets.
- uint64_t getOrphans() const { return(orphans_); }
- /// \brief Return average unordered lookup set size.
- ///
- /// Method returns average unordered lookup set size.
- /// This value changes every time \ref ExchangeStats::matchPackets
- /// function performs unordered packet lookup.
- ///
- /// \throw isc::InvalidOperation if there have been no unordered
- /// lookups yet.
- /// \return average unordered lookup set size.
- double getAvgUnorderedLookupSetSize() const {
- if (unordered_lookups_ == 0) {
- isc_throw(InvalidOperation, "no unordered lookups");
- }
- return(static_cast<double>(unordered_lookup_size_sum_) /
- static_cast<double>(unordered_lookups_));
- }
- /// \brief Return number of unordered sent packets lookups
- ///
- /// Method returns number of unordered sent packet lookups.
- /// Unordered lookup is used when received packet was sent
- /// out of order by server - transaction id of received
- /// packet does not match transaction id of next sent packet.
- ///
- /// \return number of unordered lookups.
- uint64_t getUnorderedLookups() const { return(unordered_lookups_); }
- /// \brief Return number of ordered sent packets lookups
- ///
- /// Method returns number of ordered sent packet lookups.
- /// Ordered lookup is used when packets are received in the
- /// same order as they were sent to the server.
- /// If packets are skipped or received out of order, lookup
- /// function will use unordered lookup (with hash table).
- ///
- /// \return number of ordered lookups.
- uint64_t getOrderedLookups() const { return(ordered_lookups_); }
- /// \brief Return total number of sent packets
- ///
- /// Method returns total number of sent packets.
- ///
- /// \return number of sent packets.
- uint64_t getSentPacketsNum() const { return(sent_packets_num_); }
- /// \brief Return total number of received packets
- ///
- /// Method returns total number of received packets.
- ///
- /// \return number of received packets.
- uint64_t getRcvdPacketsNum() const { return(rcvd_packets_num_); }
- /// \brief Print main statistics for packet exchange.
- ///
- /// Method prints main statistics for particular exchange.
- /// Statistics includes: number of sent and received packets,
- /// number of dropped packets and number of orphans.
- void printMainStats() const {
- using namespace std;
- uint64_t drops = getRcvdPacketsNum() - getSentPacketsNum();
- cout << "sent packets: " << getSentPacketsNum() << endl
- << "received packets: " << getRcvdPacketsNum() << endl
- << "drops: " << drops << endl
- << "orphans: " << getOrphans() << endl;
- }
- /// \brief Print round trip time packets statistics.
- ///
- /// Method prints round trip time packets statistics. Statistics
- /// includes minimum packet delay, maximum packet delay, average
- /// packet delay and standard deviation of delays. Packet delay
- /// is a duration between sending a packet to server and receiving
- /// response from server.
- void printRTTStats() const {
- using namespace std;
- try {
- cout << fixed << setprecision(3)
- << "min delay: " << getMinDelay() * 1e3 << " ms" << endl
- << "avg delay: " << getAvgDelay() * 1e3 << " ms" << endl
- << "max delay: " << getMaxDelay() * 1e3 << " ms" << endl
- << "std deviation: " << getStdDevDelay() * 1e3 << " ms"
- << endl;
- } catch (const Exception& e) {
- cout << "Unavailable! No packets received." << endl;
- }
- }
- //// \brief Print timestamps for sent and received packets.
- ///
- /// Method prints timestamps for all sent and received packets for
- /// packet exchange. In order to run this method the packets
- /// archiving mode has to be enabled during object constructions.
- /// Otherwise sent packets are not stored during tests execution
- /// and this method has no ability to get and print their timestamps.
- ///
- /// \throw isc::InvalidOperation if found packet with no timestamp or
- /// if packets archive mode is disabled.
- void printTimestamps() {
- // If archive mode is disabled there is no sense to proceed
- // because we don't have packets and their timestamps.
- if (!archive_enabled_) {
- isc_throw(isc::InvalidOperation,
- "packets archive mode is disabled");
- }
- if (rcvd_packets_num_ == 0) {
- std::cout << "Unavailable! No packets received." << std::endl;
- }
- // We will be using boost::posix_time extensivelly here
- using namespace boost::posix_time;
- // Iterate through all received packets.
- for (PktListIterator it = rcvd_packets_.begin();
- it != rcvd_packets_.end();
- ++it) {
- boost::shared_ptr<const T> rcvd_packet = *it;
- PktListTransidHashIndex& idx =
- archived_packets_.template get<1>();
- std::pair<PktListTransidHashIterator,
- PktListTransidHashIterator> p =
- idx.equal_range(hashTransid(rcvd_packet));
- for (PktListTransidHashIterator it_archived = p.first;
- it_archived != p.second;
- ++it) {
- if ((*it_archived)->getTransid() ==
- rcvd_packet->getTransid()) {
- boost::shared_ptr<const T> sent_packet = *it_archived;
- // Get sent and received packet times.
- ptime sent_time = sent_packet->getTimestamp();
- ptime rcvd_time = rcvd_packet->getTimestamp();
- // All sent and received packets should have timestamps
- // set but if there is a bug somewhere and packet does
- // not have timestamp we want to catch this here.
- if (sent_time.is_not_a_date_time() ||
- rcvd_time.is_not_a_date_time()) {
- isc_throw(InvalidOperation,
- "packet time is not set");
- }
- // Calculate durations of packets from beginning of epoch.
- ptime epoch_time(min_date_time);
- time_period sent_period(epoch_time, sent_time);
- time_period rcvd_period(epoch_time, rcvd_time);
- // Print timestamps for sent and received packet.
- std::cout << "sent / received: "
- << to_iso_string(sent_period.length())
- << " / "
- << to_iso_string(rcvd_period.length())
- << std::endl;
- break;
- }
- }
- }
- }
- private:
- /// \brief Private default constructor.
- ///
- /// Default constructor is private because we want the client
- /// class to specify exchange type explicitely.
- ExchangeStats();
- /// \brief Erase packet from the list of sent packets.
- ///
- /// Method erases packet from the list of sent packets.
- ///
- /// \param it iterator pointing to packet to be erased.
- /// \return iterator pointing to packet following erased
- /// packet or sent_packets_.end() if packet not found.
- PktListIterator eraseSent(const PktListIterator it) {
- if (archive_enabled_) {
- // We don't want to keep list of all sent packets
- // because it will affect packet lookup performance.
- // If packet is matched with received packet we
- // move it to list of archived packets. List of
- // archived packets may be used for diagnostics
- // when test is completed.
- archived_packets_.push_back(*it);
- }
- // get<0>() template returns sequencial index to
- // container.
- return(sent_packets_.template get<0>().erase(it));
- }
- ExchangeType xchg_type_; ///< Packet exchange type.
- PktList sent_packets_; ///< List of sent packets.
- /// Iterator pointing to the packet on sent list which will most
- /// likely match next received packet. This is based on the
- /// assumption that server responds in order to incoming packets.
- PktListIterator next_sent_;
- PktList rcvd_packets_; ///< List of received packets.
- /// List of archived packets. All sent packets that have
- /// been matched with received packet are moved to this
- /// list for diagnostics purposes.
- PktList archived_packets_;
- /// Indicates all packets have to be preserved after matching.
- /// By default this is disabled which means that when received
- /// packet is matched with sent packet both are deleted. This
- /// is important when test is executed for extended period of
- /// time and high memory usage might be the issue.
- /// When timestamps listing is specified from the command line
- /// (using diagnostics selector), all packets have to be preserved
- /// so as the printing method may read their timestamps and
- /// print it to user. In such usage model it will be rare to
- /// run test for extended period of time so it should be fine
- /// to keep all packets archived throughout the test.
- bool archive_enabled_;
- double min_delay_; ///< Minimum delay between sent
- ///< and received packets.
- double max_delay_; ///< Maximum delay between sent
- ///< and received packets.
- double sum_delay_; ///< Sum of delays between sent
- ///< and received packets.
- double sum_delay_squared_; ///< Squared sum of delays between
- ///< sent and recived packets.
- uint64_t orphans_; ///< Number of orphant received packets.
- /// Sum of unordered lookup sets. Needed to calculate mean size of
- /// lookup set. It is desired that number of unordered lookups is
- /// minimal for performance reasons. Tracking number of lookups and
- /// mean size of the lookup set should give idea of packets serach
- /// complexity.
- uint64_t unordered_lookup_size_sum_;
- uint64_t unordered_lookups_; ///< Number of unordered sent packets
- ///< lookups.
- uint64_t ordered_lookups_; ///< Number of ordered sent packets
- ///< lookups.
- uint64_t sent_packets_num_; ///< Total number of sent packets.
- uint64_t rcvd_packets_num_; ///< Total number of received packets.
- };
- /// Pointer to ExchangeStats.
- typedef boost::shared_ptr<ExchangeStats> ExchangeStatsPtr;
- /// Map containing all specified exchange types.
- typedef typename std::map<ExchangeType, ExchangeStatsPtr> ExchangesMap;
- /// Iterator poiting to \ref ExchangesMap
- typedef typename ExchangesMap::const_iterator ExchangesMapIterator;
- /// Map containing custom counters.
- typedef typename std::map<std::string, CustomCounterPtr> CustomCountersMap;
- /// Iterator for \ref CustomCountersMap.
- typedef typename CustomCountersMap::const_iterator CustomCountersMapIterator;
- /// \brief Constructor.
- ///
- /// This constructor by default disables packets archiving mode.
- /// In this mode all packets from the list of sent packets are
- /// moved to list of archived packets once they have been matched
- /// with received packets. This is required if it has been selected
- /// from the command line to print timestamps for all packets after
- /// the test. If this is not selected archiving should be disabled
- /// for performance reasons and to avoid waste of memory for storing
- /// large list of archived packets.
- ///
- /// \param archive_enabled true indicates that packets
- /// archive mode is enabled.
- StatsMgr(const bool archive_enabled = false) :
- exchanges_(),
- custom_counters_(),
- archive_enabled_(archive_enabled) {
- }
- /// \brief Specify new exchange type.
- ///
- /// This method creates new \ref ExchangeStats object that will
- /// collect statistics data from packets exchange of the specified
- /// type.
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if exchange of specified type exists.
- void addExchangeStats(const ExchangeType xchg_type) {
- if (exchanges_.find(xchg_type) != exchanges_.end()) {
- isc_throw(BadValue, "Exchange of specified type already added.");
- }
- exchanges_[xchg_type] =
- ExchangeStatsPtr(new ExchangeStats(xchg_type, archive_enabled_));
- }
- /// \brief Add named custom uint64 counter.
- ///
- /// Method creates new named counter and stores in counter's map under
- /// key specified here as short_name.
- ///
- /// \param short_name key to use to access counter in the map.
- /// \param long_name name of the counter presented in the log file.
- void addCustomCounter(const std::string& short_name,
- const std::string& long_name) {
- if (custom_counters_.find(short_name) != custom_counters_.end()) {
- isc_throw(BadValue,
- "Custom counter " << short_name << " already added.");
- }
- custom_counters_[short_name] =
- CustomCounterPtr(new CustomCounter(long_name));
- }
- /// \brief Return specified counter.
- ///
- /// Method returns specified counter.
- ///
- /// \param counter_key key poiting to the counter in the counters map.
- /// The short counter name has to be used to access counter.
- /// \return pointer to specified counter object.
- CustomCounterPtr getCounter(const std::string& counter_key) {
- CustomCountersMapIterator it = custom_counters_.find(counter_key);
- if (it == custom_counters_.end()) {
- isc_throw(BadValue,
- "Custom counter " << counter_key << "does not exist");
- }
- return(it->second);
- }
- /// \brief Increment specified counter.
- ///
- /// Increement counter value by one.
- ///
- /// \param counter_key key poitinh to the counter in the counters map.
- /// \return pointer to specified counter after incrementation.
- const CustomCounter& IncrementCounter(const std::string& counter_key) {
- CustomCounterPtr counter = getCounter(counter_key);
- return(++(*counter));
- }
- /// \brief Adds new packet to the sent packets list.
- ///
- /// Method adds new packet to the sent packets list.
- /// Packets are added to the list sequentially and
- /// most often read sequentially.
- ///
- /// \param xchg_type exchange type.
- /// \param packet packet to be added to the list
- /// \throw isc::BadValue if invalid exchange type specified or
- /// packet is null.
- void passSentPacket(const ExchangeType xchg_type,
- const boost::shared_ptr<const T>& packet) {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- xchg_stats->appendSent(packet);
- }
- /// \brief Add new received packet and match with sent packet.
- ///
- /// Method adds new packet to the list of received packets. It
- /// also searches for corresponding packet on the list of sent
- /// packets. When packets are matched the statistics counters
- /// are updated accordingly for the particular exchange type.
- ///
- /// \param xchg_type exchange type.
- /// \param packet received packet
- /// \throw isc::BadValue if invalid exchange type specified
- /// or packet is null.
- /// \throw isc::Unexpected if corresponding packet was not
- /// found on the list of sent packets.
- void passRcvdPacket(const ExchangeType xchg_type,
- const boost::shared_ptr<const T>& packet) {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- boost::shared_ptr<const T> sent_packet
- = xchg_stats->matchPackets(packet);
- if (sent_packet) {
- xchg_stats->updateDelays(sent_packet, packet);
- if (archive_enabled_) {
- xchg_stats->appendRcvd(packet);
- }
- }
- }
- /// \brief Return minumum delay between sent and received packet.
- ///
- /// Method returns minimum delay between sent and received packet
- /// for specified exchange type.
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if invalid exchange type specified.
- /// \return minimum delay between packets.
- double getMinDelay(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getMinDelay());
- }
- /// \brief Return maxmimum delay between sent and received packet.
- ///
- /// Method returns maximum delay between sent and received packet
- /// for specified exchange type.
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if invalid exchange type specified.
- /// \return maximum delay between packets.
- double getMaxDelay(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getMaxDelay());
- }
- /// \brief Return avarage packet delay.
- ///
- /// Method returns average packet delay for specified
- /// exchange type.
- ///
- /// \return average packet delay.
- double getAvgDelay(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getAvgDelay());
- }
- /// \brief Return standard deviation of packet delay.
- ///
- /// Method returns standard deviation of packet delay
- /// for specified exchange type.
- ///
- /// \return standard deviation of packet delay.
- double getStdDevDelay(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getStdDevDelay());
- }
- /// \brief Return number of orphant packets.
- ///
- /// Method returns number of orphant packets for specified
- /// exchange type.
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if invalid exchange type specified.
- /// \return number of orphant packets so far.
- uint64_t getOrphans(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getOrphans());
- }
- /// \brief Return average unordered lookup set size.
- ///
- /// Method returns average unordered lookup set size.
- /// This value changes every time \ref ExchangeStats::matchPackets
- /// function performs unordered packet lookup.
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if invalid exchange type specified.
- /// \return average unordered lookup set size.
- double getAvgUnorderedLookupSetSize(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getAvgUnorderedLookupSetSize());
- }
- /// \brief Return number of unordered sent packets lookups
- ///
- /// Method returns number of unordered sent packet lookups.
- /// Unordered lookup is used when received packet was sent
- /// out of order by server - transaction id of received
- /// packet does not match transaction id of next sent packet.
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if invalid exchange type specified.
- /// \return number of unordered lookups.
- uint64_t getUnorderedLookups(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getUnorderedLookups());
- }
- /// \brief Return number of ordered sent packets lookups
- ///
- /// Method returns number of ordered sent packet lookups.
- /// Ordered lookup is used when packets are received in the
- /// same order as they were sent to the server.
- /// If packets are skipped or received out of order, lookup
- /// function will use unordered lookup (with hash table).
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if invalid exchange type specified.
- /// \return number of ordered lookups.
- uint64_t getOrderedLookups(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getOrderedLookups());
- }
- /// \brief Return total number of sent packets
- ///
- /// Method returns total number of sent packets for specified
- /// exchange type.
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if invalid exchange type specified.
- /// \return number of sent packets.
- uint64_t getSentPacketsNum(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getSentPacketsNum());
- }
- /// \brief Return total number of received packets
- ///
- /// Method returns total number of received packets for specified
- /// exchange type.
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if invalid exchange type specified.
- /// \return number of received packets.
- uint64_t getRcvdPacketsNum(const ExchangeType xchg_type) const {
- ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- return(xchg_stats->getRcvdPacketsNum());
- }
- /// \brief Return name of the exchange.
- ///
- /// Method returns name of the specified exchange type.
- /// This function is mainly for logging purposes.
- ///
- /// \param xchg_type exchange type.
- /// \return string representing name of the exchange.
- std::string exchangeToString(ExchangeType xchg_type) const {
- switch(xchg_type) {
- case XCHG_DO:
- return("DISCOVER-OFFER");
- case XCHG_RA:
- return("REQUEST-ACK");
- case XCHG_SA:
- return("SOLICIT-ADVERTISE");
- case XCHG_RR:
- return("REQUEST-REPLY");
- default:
- return("Unknown exchange type");
- }
- }
- /// \brief Print statistics counters for all exchange types.
- ///
- /// Method prints statistics for all exchange types.
- /// Statistics includes:
- /// - number of sent and received packets
- /// - number of dropped packets and number of orphans
- /// - minimum packets delay,
- /// - average packets delay,
- /// - maximum packets delay,
- /// - standard deviation of packets delay.
- ///
- /// \throw isc::InvalidOperation if no exchange type added to
- /// track statistics.
- void printStats() const {
- if (exchanges_.size() == 0) {
- isc_throw(isc::InvalidOperation,
- "no exchange type added for tracking");
- }
- for (ExchangesMapIterator it = exchanges_.begin();
- it != exchanges_.end();
- ++it) {
- ExchangeStatsPtr xchg_stats = it->second;
- std::cout << "***Statistics for: " << exchangeToString(it->first)
- << "***" << std::endl;
- xchg_stats->printMainStats();
- std::cout << std::endl;
- xchg_stats->printRTTStats();
- std::cout << std::endl;
- }
- }
- /// \brief Print timestamps of all packets.
- ///
- /// Method prints timestamps of all sent and received
- /// packets for all defined exchange types.
- ///
- /// \throw isc::InvalidOperation if one of the packets has
- /// no timestamp value set or if packets archive mode is
- /// disabled.
- ///
- /// \throw isc::InvalidOperation if no exchange type added to
- /// track statistics or packets archive mode is disabled.
- void printTimestamps() const {
- if (exchanges_.size() == 0) {
- isc_throw(isc::InvalidOperation,
- "no exchange type added for tracking");
- }
- for (ExchangesMapIterator it = exchanges_.begin();
- it != exchanges_.end();
- ++it) {
- ExchangeStatsPtr xchg_stats = it->second;
- std::cout << "***Timestamps for packets: "
- << exchangeToString(it->first)
- << "***" << std::endl;
- xchg_stats->printTimestamps();
- std::cout << std::endl;
- }
- }
- /// \brief Print names and values of custom counters.
- ///
- /// Method prints names and values of custom counters. Custom counters
- /// are defined by client class for tracking different statistics.
- ///
- /// \throw isc::InvalidOperation if no custom counters added for tracking.
- void printCustomCounters() const {
- if (custom_counters_.size() == 0) {
- isc_throw(isc::InvalidOperation, "no custom counters specified");
- }
- for (CustomCountersMapIterator it = custom_counters_.begin();
- it != custom_counters_.end();
- ++it) {
- CustomCounterPtr counter = it->second;
- std::cout << counter->getName() << ": " << counter->getValue()
- << std::endl;
- }
- }
- private:
- /// \brief Return exchange stats object for given exchange type
- ///
- /// Method returns exchange stats object for given exchange type.
- ///
- /// \param xchg_type exchange type.
- /// \throw isc::BadValue if invalid exchange type specified.
- /// \return exchange stats object.
- ExchangeStatsPtr getExchangeStats(const ExchangeType xchg_type) const {
- ExchangesMapIterator it = exchanges_.find(xchg_type);
- if (it == exchanges_.end()) {
- isc_throw(BadValue, "Packets exchange not specified");
- }
- ExchangeStatsPtr xchg_stats = it->second;
- return(xchg_stats);
- }
- ExchangesMap exchanges_; ///< Map of exchange types.
- CustomCountersMap custom_counters_; ///< Map with custom counters.
- /// Indicates that packets from list of sent packets should be
- /// archived (moved to list of archived packets) once they are
- /// matched with received packets. This is required when it has
- /// been selected from the command line to print packets'
- /// timestamps after test. This may affect performance and
- /// consume large amount of memory when the test is running
- /// for extended period of time and many packets have to be
- /// archived.
- bool archive_enabled_;
- };
- } // namespace perfdhcp
- } // namespace isc
- #endif // __STATS_MGR_H
|