stats_mgr.h 19.3 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 26 #include #include  Marcin Siodelski committed Jul 12, 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 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  #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: /// 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 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< boost::shared_ptr, boost::multi_index::indexed_by< boost::multi_index::sequenced<>,  Marcin Siodelski committed Jul 12, 2012 82 83 84 85  boost::multi_index::hashed_non_unique< boost::multi_index::const_mem_fun< T, uint32_t, &T::getTransid >  Marcin Siodelski committed Jul 10, 2012 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101  > > > PktList; /// Packet list iterator for sequencial access to elements. typedef typename PktList::iterator PktListIterator; /// Packet list index to search packets using transaction id. typedef typename PktList::template nth_index<1>::type PktListTransidIndex; /// Packet list iterator to access packets using transaction id. typedef typename PktListTransidIndex::iterator PktListTransidIterator; /// \brief Constructor /// /// \param xchg_type exchange type ExchangeStats(const ExchangeType xchg_type)  Marcin Siodelski committed Jul 11, 2012 102 103 104 105 106  : xchg_type_(xchg_type), min_delay_(std::numeric_limits::max()), max_delay_(0.), sum_delay_(0.), orphans_(0),  Marcin Siodelski committed Jul 12, 2012 107 108 109 110  square_sum_delay_(0.), ordered_lookups_(0), unordered_lookup_size_sum_(0), unordered_lookups_(0) {  Marcin Siodelski committed Jul 10, 2012 111 112 113 114 115 116 117  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 118  /// \param packet packet object to be added.  Marcin Siodelski committed Jul 10, 2012 119 120 121 122  void appendSent(const boost::shared_ptr packet) { sent_packets_.template get<0>().push_back(packet); }  Marcin Siodelski committed Jul 12, 2012 123 124 125 126 127 128 129 130 131  /// \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. void appendRcvd(const boost::shared_ptr packet) { rcvd_packets_.template get<0>().push_back(packet); }  Marcin Siodelski committed Jul 10, 2012 132 133 134 135 136 137 138 139 140 141 142 143  /// \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. /// /// \param transid transaction id of the packet to search  Marcin Siodelski committed Jul 11, 2012 144 145  /// \return packet having specified transaction or NULL if packet /// not found  Marcin Siodelski committed Jul 10, 2012 146 147  boost::shared_ptr findSent(const uint32_t transid) { if (sent_packets_.size() == 0) {  Marcin Siodelski committed Jul 11, 2012 148 149  ++orphans_; return boost::shared_ptr();  Marcin Siodelski committed Jul 10, 2012 150 151 152 153 154 155  } else if (sent_packets_cache_ == sent_packets_.end()) { sent_packets_cache_ = sent_packets_.begin(); } bool packet_found = false; if ((*sent_packets_cache_)->getTransid() == transid) {  Marcin Siodelski committed Jul 12, 2012 156  ++ordered_lookups_;  Marcin Siodelski committed Jul 10, 2012 157 158 159  packet_found = true; } else { PktListTransidIndex& idx = sent_packets_.template get<1>();  Marcin Siodelski committed Jul 12, 2012 160 161 162 163 164 165 166 167 168 169 170 171  std::pair p = idx.equal_range(transid); ++unordered_lookups_; unordered_lookup_size_sum_ += std::distance(p.first, p.second); for (PktListTransidIterator it = p.first; it != p.second; ++it) { if ((*it)->getTransid() == transid) { packet_found = true; sent_packets_cache_ = sent_packets_.template project<0>(it); break; }  Marcin Siodelski committed Jul 10, 2012 172 173 174 175  } } if (!packet_found) {  Marcin Siodelski committed Jul 11, 2012 176 177  ++orphans_; return boost::shared_ptr();  Marcin Siodelski committed Jul 10, 2012 178 179 180 181 182 183 184  } boost::shared_ptr sent_packet(*sent_packets_cache_); ++sent_packets_cache_; return sent_packet; }  Marcin Siodelski committed Jul 10, 2012 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227  /// \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::Unexpected if failed to calculate timestamps void updateDelays(const boost::shared_ptr sent_packet, const boost::shared_ptr rcvd_packet) { 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); 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"); } if (delta < min_delay_) { min_delay_ = delta; } if (delta > max_delay_) { max_delay_ = delta; } sum_delay_ += delta; square_sum_delay_ += delta * delta; } /// \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 228  double getMinDelay() const { return min_delay_; }  Marcin Siodelski committed Jul 10, 2012 229 230 231 232 233 234  /// \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 235  double getMaxDelay() const { return max_delay_; }  Marcin Siodelski committed Jul 10, 2012 236 237 238 239 240 241  /// \brief Return sum of delays between sent and received packets. /// /// Method returns sum of delays between sent and received packets. /// /// \return sum of delays between sent and received packets.  Marcin Siodelski committed Jul 10, 2012 242  double getSumDelay() const { return sum_delay_; }  Marcin Siodelski committed Jul 10, 2012 243 244 245 246 247 248 249 250  /// \brief Return square sum of delays between sent and received /// packets. /// /// Method returns square sum of delays between sent and received /// packets. /// /// \return square sum of delays between sent and received packets.  Marcin Siodelski committed Jul 10, 2012 251  double getSquareSumDelay() const { return square_sum_delay_; }  Marcin Siodelski committed Jul 11, 2012 252 253 254 255 256 257 258 259 260  /// \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_; }  Marcin Siodelski committed Jul 12, 2012 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 292 293 294  /// \brief Return average unordered lookup set size. /// /// Method returns average unordered lookup set size. /// This value is changes every time \findSet function uses /// unordered packet lookup using transaction id. /// /// \return average unordered lookup set size. double getAvgUnorderedLookupSetSize() const { 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. 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_; }  Marcin Siodelski committed Jul 10, 2012 295 296 297 298  private: /// \brief Private default constructor. ///  Marcin Siodelski committed Jul 10, 2012 299  /// Default constructor is private because we want the client  Marcin Siodelski committed Jul 10, 2012 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320  /// class to specify exchange type explicitely. ExchangeStats(); 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. 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 321 322  uint64_t orphans_; ///< Number of orphant received packets.  Marcin Siodelski committed Jul 12, 2012 323 324 325 326 327 328 329 330 331 332 333 334 335  /// 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.  Marcin Siodelski committed Jul 10, 2012 336 337 338 339 340 341 342  }; /// 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 343  typedef typename ExchangesMap::const_iterator ExchangesMapIterator;  Marcin Siodelski committed Jul 10, 2012 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370  /// \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)); } /// \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. void passSentPacket(const ExchangeType xchg_type, const boost::shared_ptr packet) {  Marcin Siodelski committed Jul 11, 2012 371 372  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); xchg_stats->appendSent(packet);  Marcin Siodelski committed Jul 10, 2012 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388  } /// \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. /// \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 packet) {  Marcin Siodelski committed Jul 11, 2012 389 390 391 392 393 394  ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); boost::shared_ptr sent_packet = xchg_stats->findSent(packet->getTransid()); if (sent_packet) { xchg_stats->updateDelays(sent_packet, packet);  Marcin Siodelski committed Jul 12, 2012 395  xchg_stats->appendRcvd(packet);  Marcin Siodelski committed Jul 11, 2012 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410  } } /// \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(); }  Marcin Siodelski committed Jul 12, 2012 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451  /// \brief Return average unordered lookup set size. /// /// Method returns average unordered lookup set size. /// This value is changes every time \findSet function uses /// unordered packet lookup using transaction id. /// /// \param xchg_type exchange type. /// \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. /// /// \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). /// /// \return number of ordered lookups. uint64_t getOrderedLookups(const ExchangeType xchg_type) const { ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type); return xchg_stats->getOrderedLookups(); }  Marcin Siodelski committed Jul 11, 2012 452 453 454 455 456 457 458 459 460 461 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 {  Marcin Siodelski committed Jul 10, 2012 462 463 464 465  ExchangesMapIterator it = exchanges_.find(xchg_type); if (it == exchanges_.end()) { isc_throw(BadValue, "Packets exchange not specified"); }  Marcin Siodelski committed Jul 10, 2012 466  ExchangeStatsPtr xchg_stats = it->second;  Marcin Siodelski committed Jul 11, 2012 467  return xchg_stats;  Marcin Siodelski committed Jul 10, 2012 468 469 470 471 472 473 474 475 476  } ExchangesMap exchanges_; ///< Map of exchange types. }; } // namespace perfdhcp } // namespace isc #endif // __STATS_MGR_H