stats_mgr.h 41.2 KB
 Marcin Siodelski committed Jul 10, 2012 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 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  Marcin Siodelski committed Jul 11, 2012 18 #include  Marcin Siodelski committed Jul 10, 2012 19 20 21 22 23 #include #include #include #include  Marcin Siodelski committed Jul 12, 2012 24 #include  Marcin Siodelski committed Jul 10, 2012 25 #include  Marcin Siodelski committed Jul 12, 2012 26 #include  Marcin Siodelski committed Jul 25, 2012 27 #include  Marcin Siodelski committed Jul 10, 2012 28 #include  Marcin Siodelski committed Jul 10, 2012 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54  #include 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 StatsMgr : public boost::noncopyable { public:  Marcin Siodelski committed Jul 19, 2012 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90  /// \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.  Marcin Siodelski committed Jul 25, 2012 91  unsigned long long getValue() const {  Marcin Siodelski committed Jul 19, 2012 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110  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() { };  Marcin Siodelski committed Jul 25, 2012 111 112  unsigned long long counter_; ///< Counter's value. std::string name_; ///< Counter's name.  Marcin Siodelski committed Jul 19, 2012 113 114 115 116  }; typedef typename boost::shared_ptr CustomCounterPtr;  Marcin Siodelski committed Jul 10, 2012 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134  /// 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:  Marcin Siodelski committed Jul 23, 2012 135 136 137 138 139 140 141 142 143 144  /// \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.  Marcin Siodelski committed Jul 30, 2012 145  static uint32_t hashTransid(const boost::shared_ptr& packet) {  Marcin Siodelski committed Jul 23, 2012 146 147 148  if (!packet) { isc_throw(BadValue, "Packet is null"); }  Marcin Siodelski committed Jul 12, 2012 149 150 151  return packet->getTransid() & 1023; }  Marcin Siodelski committed Jul 10, 2012 152 153 154 155 156 157  /// \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 packet transaction id. typedef boost::multi_index_container<  Marcin Siodelski committed Jul 30, 2012 158  boost::shared_ptr,  Marcin Siodelski committed Jul 10, 2012 159 160  boost::multi_index::indexed_by< boost::multi_index::sequenced<>,  Marcin Siodelski committed Jul 12, 2012 161  boost::multi_index::hashed_non_unique<  Marcin Siodelski committed Jul 12, 2012 162  boost::multi_index::global_fun<  Marcin Siodelski committed Jul 30, 2012 163  const boost::shared_ptr&,  164  uint32_t,  Marcin Siodelski committed Jul 23, 2012 165  &ExchangeStats::hashTransid  Marcin Siodelski committed Jul 12, 2012 166  >  Marcin Siodelski committed Jul 25, 2012 167 168 169 170 171 172 173  >, boost::multi_index::hashed_non_unique< boost::multi_index::const_mem_fun< T, uint32_t, &T::getTransid >  Marcin Siodelski committed Jul 10, 2012 174 175 176 177 178  > > > PktList; /// Packet list iterator for sequencial access to elements.  Marcin Siodelski committed Jul 20, 2012 179  typedef typename PktList::const_iterator PktListIterator;  Marcin Siodelski committed Jul 25, 2012 180  /// Packet list index to search packets using transaction id hash.  Marcin Siodelski committed Jul 10, 2012 181  typedef typename PktList::template nth_index<1>::type  Marcin Siodelski committed Jul 25, 2012 182 183 184 185 186 187  PktListTransidHashIndex; /// Packet list iterator to access packets using transaction id hash. typedef typename PktListTransidHashIndex::const_iterator PktListTransidHashIterator; /// Packet list index to search packets using transaction id. typedef typename PktList::template nth_index<2>::type  Marcin Siodelski committed Jul 10, 2012 188 189  PktListTransidIndex; /// Packet list iterator to access packets using transaction id.  Marcin Siodelski committed Jul 25, 2012 190 191  typedef typename PktListTransidIndex::const_iterator PktListTransidIterator;  Marcin Siodelski committed Jul 10, 2012 192 193 194 195 196  /// \brief Constructor /// /// \param xchg_type exchange type ExchangeStats(const ExchangeType xchg_type)  Marcin Siodelski committed Jul 11, 2012 197 198 199 200 201  : xchg_type_(xchg_type), min_delay_(std::numeric_limits::max()), max_delay_(0.), sum_delay_(0.), orphans_(0),  Marcin Siodelski committed Jul 12, 2012 202 203 204  square_sum_delay_(0.), ordered_lookups_(0), unordered_lookup_size_sum_(0),  205 206 207  unordered_lookups_(0), sent_packets_num_(0), rcvd_packets_num_(0) {  Marcin Siodelski committed Jul 10, 2012 208 209 210 211 212 213 214  sent_packets_cache_ = sent_packets_.begin(); } /// \brief Add new packet to list of sent packets. /// /// Method adds new packet to list of sent packets. ///  Marcin Siodelski committed Jul 12, 2012 215  /// \param packet packet object to be added.  Marcin Siodelski committed Jul 23, 2012 216  /// \throw isc::BadValue if packet is null.  Marcin Siodelski committed Jul 30, 2012 217  void appendSent(const boost::shared_ptr& packet) {  Marcin Siodelski committed Jul 23, 2012 218 219 220  if (!packet) { isc_throw(BadValue, "Packet is null"); }  221  ++sent_packets_num_;  Marcin Siodelski committed Jul 10, 2012 222 223 224  sent_packets_.template get<0>().push_back(packet); }  Marcin Siodelski committed Jul 12, 2012 225 226 227 228 229  /// \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.  Marcin Siodelski committed Jul 23, 2012 230  /// \throw isc::BadValue if packet is null.  Marcin Siodelski committed Jul 30, 2012 231  void appendRcvd(const boost::shared_ptr& packet) {  Marcin Siodelski committed Jul 23, 2012 232 233 234  if (!packet) { isc_throw(BadValue, "Packet is null"); }  235  ++rcvd_packets_num_;  Marcin Siodelski committed Jul 25, 2012 236  rcvd_packets_.push_back(packet);  Marcin Siodelski committed Jul 12, 2012 237 238  }  239 240 241 242 243 244 245  /// \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  Marcin Siodelski committed Jul 23, 2012 246  /// \throw isc::BadValue if sent or received packet is null.  247  /// \throw isc::Unexpected if failed to calculate timestamps  Marcin Siodelski committed Jul 30, 2012 248 249  void updateDelays(const boost::shared_ptr& sent_packet, const boost::shared_ptr& rcvd_packet) {  Marcin Siodelski committed Jul 23, 2012 250 251 252 253 254 255 256  if (!sent_packet) { isc_throw(BadValue, "Sent packet is null"); } if (!rcvd_packet) { isc_throw(BadValue, "Received packet is null"); }  257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291  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(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; square_sum_delay_ += delta * delta; }  Marcin Siodelski committed Jul 10, 2012 292 293 294 295 296 297 298 299 300 301 302  /// \brief Find packet on the list of sent packets. /// /// 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. ///  Marcin Siodelski committed Jul 23, 2012 303  /// \param rcvd_packet received packet to be matched with sent packet.  Marcin Siodelski committed Jul 23, 2012 304  /// \throw isc::BadValue if received packet is null.  Marcin Siodelski committed Jul 11, 2012 305 306  /// \return packet having specified transaction or NULL if packet /// not found  Marcin Siodelski committed Jul 30, 2012 307  boost::shared_ptr findSent(const boost::shared_ptr& rcvd_packet) {  Marcin Siodelski committed Jul 23, 2012 308 309 310 311  if (!rcvd_packet) { isc_throw(BadValue, "Received packet is null"); }  Marcin Siodelski committed Jul 10, 2012 312  if (sent_packets_.size() == 0) {  313 314 315 316  // 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.  Marcin Siodelski committed Jul 11, 2012 317  ++orphans_;  Marcin Siodelski committed Jul 30, 2012 318  return boost::shared_ptr();  Marcin Siodelski committed Jul 10, 2012 319  } else if (sent_packets_cache_ == sent_packets_.end()) {  320 321 322  // 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 cache.  Marcin Siodelski committed Jul 10, 2012 323 324 325  sent_packets_cache_ = sent_packets_.begin(); }  326 327  // With this variable we will be signalling success or failure // to find the packet.  Marcin Siodelski committed Jul 10, 2012 328  bool packet_found = false;  329 330  // Most likely responses are sent from the server in the same // order as client's requests to the server. We are caching  Marcin Siodelski committed Jul 19, 2012 331  // next sent packet and first try to match it with the next  332 333 334  // 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.  Marcin Siodelski committed Jul 23, 2012 335  if ((*sent_packets_cache_)->getTransid() == rcvd_packet->getTransid()) {  Marcin Siodelski committed Jul 12, 2012 336  ++ordered_lookups_;  Marcin Siodelski committed Jul 10, 2012 337 338  packet_found = true; } else {  339 340 341 342  // 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).  Marcin Siodelski committed Jul 25, 2012 343  PktListTransidHashIndex& idx = sent_packets_.template get<1>();  Marcin Siodelski committed Jul 19, 2012 344  // Packets are grouped using trasaction id masked with value  345 346 347  // 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  Marcin Siodelski committed Jul 19, 2012 348  // bucket of packets and we need to iterate through the bucket  349  // to find the one that has desired transaction id.  Marcin Siodelski committed Jul 25, 2012 350  std::pair p =  Marcin Siodelski committed Jul 23, 2012 351  idx.equal_range(hashTransid(rcvd_packet));  352  // We want to keep statistics of unordered lookups to make  Marcin Siodelski committed Jul 19, 2012 353  // sure that there is a right balance between number of  354 355 356  // 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.  Marcin Siodelski committed Jul 12, 2012 357  ++unordered_lookups_;  358 359 360  // 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.  Marcin Siodelski committed Jul 12, 2012 361  unordered_lookup_size_sum_ += std::distance(p.first, p.second);  Marcin Siodelski committed Jul 25, 2012 362  for (PktListTransidHashIterator it = p.first; it != p.second;  Marcin Siodelski committed Jul 12, 2012 363  ++it) {  Marcin Siodelski committed Jul 23, 2012 364  if ((*it)->getTransid() == rcvd_packet->getTransid()) {  Marcin Siodelski committed Jul 12, 2012 365 366 367 368 369  packet_found = true; sent_packets_cache_ = sent_packets_.template project<0>(it); break; }  Marcin Siodelski committed Jul 10, 2012 370 371 372 373  } } if (!packet_found) {  374 375  // If we are here, it means that both ordered lookup and // unordered lookup failed. Searched packet is not on the list.  Marcin Siodelski committed Jul 11, 2012 376  ++orphans_;  Marcin Siodelski committed Jul 30, 2012 377  return boost::shared_ptr();  Marcin Siodelski committed Jul 10, 2012 378 379  }  Marcin Siodelski committed Jul 30, 2012 380  boost::shared_ptr sent_packet(*sent_packets_cache_);  381 382 383 384  // 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. sent_packets_cache_ = eraseSent(sent_packets_cache_);  Marcin Siodelski committed Jul 10, 2012 385 386 387  return sent_packet; }  Marcin Siodelski committed Jul 10, 2012 388 389 390 391 392  /// \brief Return minumum delay between sent and received packet. /// /// Method returns minimum delay between sent and received packet. /// /// \return minimum delay between packets.  Marcin Siodelski committed Jul 10, 2012 393  double getMinDelay() const { return min_delay_; }  Marcin Siodelski committed Jul 10, 2012 394 395 396 397 398 399  /// \brief Return maxmimum delay between sent and received packet. /// /// Method returns maximum delay between sent and received packet. /// /// \return maximum delay between packets.  Marcin Siodelski committed Jul 10, 2012 400  double getMaxDelay() const { return max_delay_; }  Marcin Siodelski committed Jul 10, 2012 401   Marcin Siodelski committed Jul 25, 2012 402  /// \brief Return avarage packet delay.  Marcin Siodelski committed Jul 10, 2012 403  ///  Marcin Siodelski committed Jul 25, 2012 404 405 406  /// 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.  Marcin Siodelski committed Jul 10, 2012 407  ///  Marcin Siodelski committed Jul 25, 2012 408 409 410 411 412 413 414 415 416  /// \throw isc::InvalidOperation if no packets for this exchange /// have been received yet. /// \return average packet delay. double getAvgDelay() const { if (sum_delay_ == 0) { isc_throw(InvalidOperation, "no packets received"); } return sum_delay_ / rcvd_packets_num_; }  Marcin Siodelski committed Jul 10, 2012 417   Marcin Siodelski committed Jul 25, 2012 418  /// \brief Return standard deviation of packet delay.  Marcin Siodelski committed Jul 10, 2012 419  ///  Marcin Siodelski committed Jul 25, 2012 420 421 422 423  /// 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.  Marcin Siodelski committed Jul 10, 2012 424  ///  Marcin Siodelski committed Jul 25, 2012 425 426 427 428 429 430 431 432 433 434  /// \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(square_sum_delay_ / rcvd_packets_num_ - getAvgDelay() * getAvgDelay()); }  Marcin Siodelski committed Jul 11, 2012 435 436 437 438 439 440 441 442  /// \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.  Marcin Siodelski committed Jul 25, 2012 443  unsigned long long getOrphans() const { return orphans_; }  Marcin Siodelski committed Jul 12, 2012 444 445 446 447  /// \brief Return average unordered lookup set size. /// /// Method returns average unordered lookup set size.  Marcin Siodelski committed Jul 23, 2012 448 449  /// This value changes every time \ref ExchangeStats::findSent /// function performs unordered packet lookup.  Marcin Siodelski committed Jul 12, 2012 450  ///  Marcin Siodelski committed Jul 25, 2012 451 452  /// \throw isc::InvalidOperation if there have been no unordered /// lookups yet.  Marcin Siodelski committed Jul 12, 2012 453 454  /// \return average unordered lookup set size. double getAvgUnorderedLookupSetSize() const {  455  if (unordered_lookups_ == 0) {  Marcin Siodelski committed Jul 25, 2012 456  isc_throw(InvalidOperation, "no unordered lookups");  457  }  Marcin Siodelski committed Jul 12, 2012 458 459 460 461 462 463 464 465 466 467 468 469  return static_cast(unordered_lookup_size_sum_) / static_cast(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.  Marcin Siodelski committed Jul 25, 2012 470  unsigned long long getUnorderedLookups() const { return unordered_lookups_; }  Marcin Siodelski committed Jul 12, 2012 471 472 473 474 475 476 477 478 479 480  /// \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.  Marcin Siodelski committed Jul 25, 2012 481  unsigned long long getOrderedLookups() const { return ordered_lookups_; }  482 483 484 485 486 487  /// \brief Return total number of sent packets /// /// Method returns total number of sent packets. /// /// \return number of sent packets.  Marcin Siodelski committed Jul 25, 2012 488  unsigned long long getSentPacketsNum() const {  489 490 491 492 493 494 495 496  return sent_packets_num_; } /// \brief Return total number of received packets /// /// Method returns total number of received packets. /// /// \return number of received packets.  Marcin Siodelski committed Jul 25, 2012 497  unsigned long long getRcvdPacketsNum() const {  498 499 500  return rcvd_packets_num_; }  Marcin Siodelski committed Jul 25, 2012 501 502 503 504 505 506 507 508 509 510 511  //// \brief Print timestamps for sent and received packets. /// /// Method prints timestamps for all sent and received packets for /// packet exchange. /// /// \throw isc::InvalidOperation if found packet with no timestamp set. void printTimestamps() { // Iterate through all received packets. for (PktListIterator it = rcvd_packets_.begin(); it != rcvd_packets_.end(); ++it) {  Marcin Siodelski committed Jul 30, 2012 512  boost::shared_ptr rcvd_packet = *it;  Marcin Siodelski committed Jul 25, 2012 513 514 515 516 517 518 519 520  // Search for corresponding sent packet using transaction id // of received packet. PktListTransidIndex& idx = archived_packets_.template get<2>(); PktListTransidIterator it_archived = idx.find(rcvd_packet->getTransid()); // This should not happen that there is no corresponding // sent packet. If it does however, we just drop the packet. if (it_archived != idx.end()) {  Marcin Siodelski committed Jul 30, 2012 521  boost::shared_ptr sent_packet = *it_archived;  Marcin Siodelski committed Jul 25, 2012 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548  // Get sent and received packet times. boost::posix_time::ptime sent_time = sent_packet->getTimestamp(); boost::posix_time::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. boost::posix_time::ptime epoch_time(boost::posix_time::min_date_time); boost::posix_time::time_period sent_period(epoch_time, sent_time); boost::posix_time::time_period rcvd_period(epoch_time, rcvd_time); // Print timestamps for sent and received packet. printf("sent/received times: %s / %s\n", boost::posix_time::to_iso_string(sent_period.length()).c_str(), boost::posix_time::to_iso_string(rcvd_period.length()).c_str()); } } }  Marcin Siodelski committed Jul 10, 2012 549 550 551 552  private: /// \brief Private default constructor. ///  Marcin Siodelski committed Jul 10, 2012 553  /// Default constructor is private because we want the client  Marcin Siodelski committed Jul 10, 2012 554 555 556  /// class to specify exchange type explicitely. ExchangeStats();  557 558 559 560 561 562 563 564  /// \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) {  Marcin Siodelski committed Jul 25, 2012 565 566 567 568 569 570 571 572  // 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); return sent_packets_.template get<0>().erase(it);  573 574  }  Marcin Siodelski committed Jul 10, 2012 575 576 577 578 579 580 581 582 583 584  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 sent_packets_cache_; PktList rcvd_packets_; ///< List of received packets.  Marcin Siodelski committed Jul 25, 2012 585 586 587 588 589  /// 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_;  Marcin Siodelski committed Jul 10, 2012 590 591 592 593 594 595 596 597  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 square_sum_delay_; ///< Square sum of delays between ///< sent and recived packets.  Marcin Siodelski committed Jul 11, 2012 598   Marcin Siodelski committed Jul 25, 2012 599  unsigned long long orphans_; ///< Number of orphant received packets.  Marcin Siodelski committed Jul 12, 2012 600 601 602 603 604 605  /// 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.  Marcin Siodelski committed Jul 25, 2012 606  unsigned long long unordered_lookup_size_sum_;  Marcin Siodelski committed Jul 12, 2012 607   Marcin Siodelski committed Jul 25, 2012 608  unsigned long long unordered_lookups_; ///< Number of unordered sent packets  Marcin Siodelski committed Jul 12, 2012 609  ///< lookups.  Marcin Siodelski committed Jul 25, 2012 610  unsigned long long ordered_lookups_; ///< Number of ordered sent packets  Marcin Siodelski committed Jul 12, 2012 611 612  ///< lookups.  Marcin Siodelski committed Jul 25, 2012 613 614  unsigned long long sent_packets_num_; ///< Total number of sent packets. unsigned long long rcvd_packets_num_; ///< Total number of received packets.  Marcin Siodelski committed Jul 10, 2012 615 616 617 618 619 620 621  }; /// Pointer to ExchangeStats. typedef boost::shared_ptr ExchangeStatsPtr; /// Map containing all specified exchange types. typedef typename std::map ExchangesMap; /// Iterator poiting to \ref ExchangesMap  Marcin Siodelski committed Jul 11, 2012 622  typedef typename ExchangesMap::const_iterator ExchangesMapIterator;  Marcin Siodelski committed Jul 19, 2012 623 624 625  /// Map containing custom counters. typedef typename std::map CustomCountersMap; /// Iterator for \ref CustomCountersMap.  Marcin Siodelski committed Jul 20, 2012 626  typedef typename CustomCountersMap::const_iterator CustomCountersMapIterator;  Marcin Siodelski committed Jul 19, 2012 627 628 629 630 631 632  /// \brief Constructor. StatsMgr() : exchanges_(), custom_counters_() { }  Marcin Siodelski committed Jul 10, 2012 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648  /// \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)); }  Marcin Siodelski committed Jul 19, 2012 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691  /// \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. /// \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); }  Marcin Siodelski committed Jul 10, 2012 692 693 694 695 696 697 698 699  /// \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  Marcin Siodelski committed Jul 23, 2012 700 701  /// \throw isc::BadValue if invalid exchange type specified or /// packet is null.  Marcin Siodelski committed Jul 10, 2012 702  void passSentPacket(const ExchangeType xchg_type,  Marcin Siodelski committed Jul 30, 2012 703  const boost::shared_ptr& packet) {  Marcin Siodelski committed Jul 11, 2012 704 705  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); xchg_stats->appendSent(packet);  Marcin Siodelski committed Jul 10, 2012 706 707 708 709 710 711 712 713 714 715 716  } /// \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  Marcin Siodelski committed Jul 23, 2012 717 718  /// \throw isc::BadValue if invalid exchange type specified /// or packet is null.  Marcin Siodelski committed Jul 10, 2012 719 720 721  /// \throw isc::Unexpected if corresponding packet was not /// found on the list of sent packets. void passRcvdPacket(const ExchangeType xchg_type,  Marcin Siodelski committed Jul 30, 2012 722  const boost::shared_ptr& packet) {  Marcin Siodelski committed Jul 11, 2012 723  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);  Marcin Siodelski committed Jul 30, 2012 724  boost::shared_ptr sent_packet  Marcin Siodelski committed Jul 23, 2012 725  = xchg_stats->findSent(packet);  Marcin Siodelski committed Jul 11, 2012 726 727 728  if (sent_packet) { xchg_stats->updateDelays(sent_packet, packet);  Marcin Siodelski committed Jul 12, 2012 729  xchg_stats->appendRcvd(packet);  Marcin Siodelski committed Jul 11, 2012 730 731 732  } }  733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758  /// \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(); }  Marcin Siodelski committed Jul 25, 2012 759  /// \brief Return avarage packet delay.  760  ///  Marcin Siodelski committed Jul 25, 2012 761 762  /// Method returns average packet delay for specified /// exchange type.  763  ///  Marcin Siodelski committed Jul 25, 2012 764 765  /// \return average packet delay. double getAvgDelay(const ExchangeType xchg_type) const {  766  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);  Marcin Siodelski committed Jul 25, 2012 767  return xchg_stats->getAvgDelay();  768 769  }  Marcin Siodelski committed Jul 25, 2012 770  /// \brief Return standard deviation of packet delay.  771  ///  Marcin Siodelski committed Jul 25, 2012 772 773  /// Method returns standard deviation of packet delay /// for specified exchange type.  774  ///  Marcin Siodelski committed Jul 25, 2012 775 776  /// \return standard deviation of packet delay. double getStdDevDelay(const ExchangeType xchg_type) const {  777  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);  Marcin Siodelski committed Jul 25, 2012 778  return xchg_stats->getStdDevDelay();  779 780  }  Marcin Siodelski committed Jul 11, 2012 781 782 783 784 785 786 787 788  /// \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.  Marcin Siodelski committed Jul 25, 2012 789  unsigned long long getOrphans(const ExchangeType xchg_type) const {  Marcin Siodelski committed Jul 11, 2012 790 791 792  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); return xchg_stats->getOrphans(); }  Marcin Siodelski committed Jul 12, 2012 793 794 795 796  /// \brief Return average unordered lookup set size. /// /// Method returns average unordered lookup set size.  Marcin Siodelski committed Jul 23, 2012 797 798  /// This value changes every time \ref ExchangeStats::findSent /// function performs unordered packet lookup.  Marcin Siodelski committed Jul 12, 2012 799 800  /// /// \param xchg_type exchange type.  801  /// \throw isc::BadValue if invalid exchange type specified.  Marcin Siodelski committed Jul 12, 2012 802 803 804 805 806 807 808 809 810 811 812 813 814  /// \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. ///  815 816  /// \param xchg_type exchange type. /// \throw isc::BadValue if invalid exchange type specified.  Marcin Siodelski committed Jul 12, 2012 817  /// \return number of unordered lookups.  Marcin Siodelski committed Jul 25, 2012 818  unsigned long long getUnorderedLookups(const ExchangeType xchg_type) const {  Marcin Siodelski committed Jul 12, 2012 819 820 821 822 823 824 825 826 827 828 829 830  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). ///  831 832  /// \param xchg_type exchange type. /// \throw isc::BadValue if invalid exchange type specified.  Marcin Siodelski committed Jul 12, 2012 833  /// \return number of ordered lookups.  Marcin Siodelski committed Jul 25, 2012 834  unsigned long long getOrderedLookups(const ExchangeType xchg_type) const {  Marcin Siodelski committed Jul 12, 2012 835 836 837 838  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); return xchg_stats->getOrderedLookups(); }  839 840 841 842 843 844 845 846  /// \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.  Marcin Siodelski committed Jul 25, 2012 847  unsigned long long getSentPacketsNum(const ExchangeType xchg_type) const {  848 849 850  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); return xchg_stats->getSentPacketsNum(); }  Marcin Siodelski committed Jul 11, 2012 851   852 853 854 855 856 857 858 859  /// \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.  Marcin Siodelski committed Jul 25, 2012 860  unsigned long long getRcvdPacketsNum(const ExchangeType xchg_type) const {  861 862 863  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); return xchg_stats->getRcvdPacketsNum(); }  Marcin Siodelski committed Jul 19, 2012 864   Marcin Siodelski committed Jul 25, 2012 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968  /// \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 main statistics for all exchange types. /// /// Method prints main statistics for all exchange types. /// Statistics includes: number of sent and received packets, /// number of dropped packets and number of orphans. void printMainStats() const { for (ExchangesMapIterator it = exchanges_.begin(); it != exchanges_.end(); ++it) { ExchangeStatsPtr xchg_stats = it->second; printf("***Statistics for packet exchange %s***\n" "sent: %llu, received: %llu\n" "drops: %lld, orphans: %llu\n\n", exchangeToString(it->first).c_str(), xchg_stats->getSentPacketsNum(), xchg_stats->getRcvdPacketsNum(), xchg_stats->getRcvdPacketsNum() - xchg_stats->getSentPacketsNum(), xchg_stats->getOrphans()); } } /// \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 { for (ExchangesMapIterator it = exchanges_.begin(); it != exchanges_.end(); ++it) { ExchangeStatsPtr xchg_stats = it->second; printf("***Round trip time Statistics for packet exchange %s***\n" "min delay: %.3f\n" "avg delay: %.3f\n" "max delay: %.3f\n" "std deviation: %.3f\n", exchangeToString(it->first).c_str(), xchg_stats->getMinDelay(), xchg_stats->getAvgDelay(), xchg_stats->getMaxDelay(), xchg_stats->getStdDevDelay()); } } /// \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. void printTimestamps() const { for (ExchangesMapIterator it = exchanges_.begin(); it != exchanges_.end(); ++it) { ExchangeStatsPtr xchg_stats = it->second; printf("***Timestamps for packets in exchange %s***\n", exchangeToString(it->first).c_str()); xchg_stats->printTimestamps(); } } /// \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. void printCustomCounters() const { if (custom_counters_.size() > 0) { printf("***Various statistics counters***\n"); } for (CustomCountersMapIterator it = custom_counters_.begin(); it != custom_counters_.end(); ++it) { CustomCounterPtr counter = it->second; printf("%s: %llu\n", counter->getName().c_str(), counter->getValue()); } }  969 private:  Marcin Siodelski committed Jul 11, 2012 970 971 972 973 974 975 976 977  /// \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 {  Marcin Siodelski committed Jul 10, 2012 978 979 980 981  ExchangesMapIterator it = exchanges_.find(xchg_type); if (it == exchanges_.end()) { isc_throw(BadValue, "Packets exchange not specified"); }  Marcin Siodelski committed Jul 10, 2012 982  ExchangeStatsPtr xchg_stats = it->second;  Marcin Siodelski committed Jul 11, 2012 983  return xchg_stats;  Marcin Siodelski committed Jul 10, 2012 984 985  }  Marcin Siodelski committed Jul 19, 2012 986 987  ExchangesMap exchanges_; ///< Map of exchange types. CustomCountersMap custom_counters_; ///< Map with custom counters.  Marcin Siodelski committed Jul 10, 2012 988 989 990 991 992 993 }; } // namespace perfdhcp } // namespace isc #endif // __STATS_MGR_H