stats_mgr.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef __STATS_MGR_H
  15. #define __STATS_MGR_H
  16. #include <iostream>
  17. #include <map>
  18. #include <boost/noncopyable.hpp>
  19. #include <boost/shared_ptr.hpp>
  20. #include <boost/multi_index_container.hpp>
  21. #include <boost/multi_index/hashed_index.hpp>
  22. #include <boost/multi_index/sequenced_index.hpp>
  23. #include <boost/multi_index/mem_fun.hpp>
  24. #include <boost/multi_index/composite_key.hpp>
  25. #include <boost/date_time/posix_time/posix_time.hpp>
  26. #include <exceptions/exceptions.h>
  27. namespace isc {
  28. namespace perfdhcp {
  29. /// \brief Statistics Manager
  30. ///
  31. /// This class template is a storage for various performance statistics
  32. /// collected during performance tests execution with perfdhcp tool.
  33. ///
  34. /// Statistics Manager holds lists of sent and received packets and
  35. /// groups them into exchanges. For example: DHCPDISCOVER message and
  36. /// corresponding DHCPOFFER messages belong to one exchange, DHCPREQUEST
  37. /// and corresponding DHCPACK message belong to another exchange etc.
  38. /// In order to update statistics for a particular exchange type, client
  39. /// class passes sent and received packets. Internally, Statistics Manager
  40. /// tries to match transaction id of received packet with sent packet
  41. /// stored on the list of sent packets. When packets are matched the
  42. /// round trip time can be calculated.
  43. ///
  44. /// \tparam T class representing DHCPv4 or DHCPv6 packet.
  45. template <class T>
  46. class StatsMgr : public boost::noncopyable {
  47. public:
  48. /// DHCP packet exchange types.
  49. enum ExchangeType {
  50. XCHG_DO, ///< DHCPv4 DISCOVER-OFFER
  51. XCHG_RA, ///< DHCPv4 REQUEST-ACK
  52. XCHG_SA, ///< DHCPv6 SOLICIT-ADVERTISE
  53. XCHG_RR ///< DHCPv6 REQUEST-REPLY
  54. };
  55. /// \brief Exchange Statistics.
  56. ///
  57. /// This class collects statistics for exchanges. Parent class
  58. /// may define number of different packet exchanges like:
  59. /// DHCPv4 DISCOVER-OFFER, DHCPv6 SOLICIT-ADVERTISE etc. Performance
  60. /// statistics will be collected for each of those separately in
  61. /// corresponding instance of ExchangeStats.
  62. class ExchangeStats {
  63. public:
  64. /// \brief List of packets (sent or received).
  65. ///
  66. /// List of packets based on multi index container allows efficient
  67. /// search of packets based on their sequence (order in which they
  68. /// were inserted) as well as based on packet transaction id.
  69. typedef boost::multi_index_container<
  70. boost::shared_ptr<T>,
  71. boost::multi_index::indexed_by<
  72. boost::multi_index::sequenced<>,
  73. boost::multi_index::hashed_non_unique<
  74. boost::multi_index::const_mem_fun<
  75. T, uint32_t, &T::getTransid
  76. >
  77. >
  78. >
  79. > PktList;
  80. /// Packet list iterator for sequencial access to elements.
  81. typedef typename PktList::iterator PktListIterator;
  82. /// Packet list index to search packets using transaction id.
  83. typedef typename PktList::template nth_index<1>::type
  84. PktListTransidIndex;
  85. /// Packet list iterator to access packets using transaction id.
  86. typedef typename PktListTransidIndex::iterator PktListTransidIterator;
  87. /// \brief Constructor
  88. ///
  89. /// \param xchg_type exchange type
  90. ExchangeStats(const ExchangeType xchg_type)
  91. : xchg_type_(xchg_type),
  92. min_delay_(std::numeric_limits<double>::max()),
  93. max_delay_(0.),
  94. sum_delay_(0.),
  95. orphans_(0),
  96. square_sum_delay_(0.),
  97. ordered_lookups_(0),
  98. unordered_lookup_size_sum_(0),
  99. unordered_lookups_(0) {
  100. sent_packets_cache_ = sent_packets_.begin();
  101. }
  102. /// \brief Add new packet to list of sent packets.
  103. ///
  104. /// Method adds new packet to list of sent packets.
  105. ///
  106. /// \param packet packet object to be added.
  107. void appendSent(const boost::shared_ptr<T> packet) {
  108. sent_packets_.template get<0>().push_back(packet);
  109. }
  110. /// \brief Add new packet to list of received packets.
  111. ///
  112. /// Method adds new packet to list of received packets.
  113. ///
  114. /// \param packet packet object to be added.
  115. void appendRcvd(const boost::shared_ptr<T> packet) {
  116. rcvd_packets_.template get<0>().push_back(packet);
  117. }
  118. /// \brief Find packet on the list of sent packets.
  119. ///
  120. /// Method finds packet with specified transaction id on the list
  121. /// of sent packets. It is used to match received packet with
  122. /// corresponding sent packet.
  123. /// Since packets from the server most often come in the same order
  124. /// as they were sent by client, this method will first check if
  125. /// next sent packet matches. If it doesn't, function will search
  126. /// the packet using indexing by transaction id. This reduces
  127. /// packet search time significantly.
  128. ///
  129. /// \param transid transaction id of the packet to search
  130. /// \return packet having specified transaction or NULL if packet
  131. /// not found
  132. boost::shared_ptr<T> findSent(const uint32_t transid) {
  133. if (sent_packets_.size() == 0) {
  134. ++orphans_;
  135. return boost::shared_ptr<T>();
  136. } else if (sent_packets_cache_ == sent_packets_.end()) {
  137. sent_packets_cache_ = sent_packets_.begin();
  138. }
  139. bool packet_found = false;
  140. if ((*sent_packets_cache_)->getTransid() == transid) {
  141. ++ordered_lookups_;
  142. packet_found = true;
  143. } else {
  144. PktListTransidIndex& idx = sent_packets_.template get<1>();
  145. std::pair<PktListTransidIterator,PktListTransidIterator> p =
  146. idx.equal_range(transid);
  147. ++unordered_lookups_;
  148. unordered_lookup_size_sum_ += std::distance(p.first, p.second);
  149. for (PktListTransidIterator it = p.first; it != p.second;
  150. ++it) {
  151. if ((*it)->getTransid() == transid) {
  152. packet_found = true;
  153. sent_packets_cache_ =
  154. sent_packets_.template project<0>(it);
  155. break;
  156. }
  157. }
  158. }
  159. if (!packet_found) {
  160. ++orphans_;
  161. return boost::shared_ptr<T>();
  162. }
  163. boost::shared_ptr<T> sent_packet(*sent_packets_cache_);
  164. ++sent_packets_cache_;
  165. return sent_packet;
  166. }
  167. /// \brief Update delay counters.
  168. ///
  169. /// Method updates delay counters based on timestamps of
  170. /// sent and received packets.
  171. ///
  172. /// \param sent_packet sent packet
  173. /// \param rcvd_packet received packet
  174. /// \throw isc::Unexpected if failed to calculate timestamps
  175. void updateDelays(const boost::shared_ptr<T> sent_packet,
  176. const boost::shared_ptr<T> rcvd_packet) {
  177. boost::posix_time::ptime sent_time = sent_packet->getTimestamp();
  178. boost::posix_time::ptime rcvd_time = rcvd_packet->getTimestamp();
  179. if (sent_time.is_not_a_date_time() ||
  180. rcvd_time.is_not_a_date_time()) {
  181. isc_throw(Unexpected,
  182. "Timestamp must be set for sent and "
  183. "received packet to measure RTT");
  184. }
  185. boost::posix_time::time_period period(sent_time, rcvd_time);
  186. double delta =
  187. static_cast<double>(period.length().total_nanoseconds()) / 1e9;
  188. if (delta < 0) {
  189. isc_throw(Unexpected, "Sent packet's timestamp must not be "
  190. "greater than received packet's timestamp");
  191. }
  192. if (delta < min_delay_) {
  193. min_delay_ = delta;
  194. }
  195. if (delta > max_delay_) {
  196. max_delay_ = delta;
  197. }
  198. sum_delay_ += delta;
  199. square_sum_delay_ += delta * delta;
  200. }
  201. /// \brief Return minumum delay between sent and received packet.
  202. ///
  203. /// Method returns minimum delay between sent and received packet.
  204. ///
  205. /// \return minimum delay between packets.
  206. double getMinDelay() const { return min_delay_; }
  207. /// \brief Return maxmimum delay between sent and received packet.
  208. ///
  209. /// Method returns maximum delay between sent and received packet.
  210. ///
  211. /// \return maximum delay between packets.
  212. double getMaxDelay() const { return max_delay_; }
  213. /// \brief Return sum of delays between sent and received packets.
  214. ///
  215. /// Method returns sum of delays between sent and received packets.
  216. ///
  217. /// \return sum of delays between sent and received packets.
  218. double getSumDelay() const { return sum_delay_; }
  219. /// \brief Return square sum of delays between sent and received
  220. /// packets.
  221. ///
  222. /// Method returns square sum of delays between sent and received
  223. /// packets.
  224. ///
  225. /// \return square sum of delays between sent and received packets.
  226. double getSquareSumDelay() const { return square_sum_delay_; }
  227. /// \brief Return number of orphant packets.
  228. ///
  229. /// Method returns number of received packets that had no matching
  230. /// sent packet. It is possible that such packet was late or not
  231. /// for us.
  232. ///
  233. /// \return number of orphant received packets.
  234. uint64_t getOrphans() const { return orphans_; }
  235. /// \brief Return average unordered lookup set size.
  236. ///
  237. /// Method returns average unordered lookup set size.
  238. /// This value is changes every time \findSet function uses
  239. /// unordered packet lookup using transaction id.
  240. ///
  241. /// \return average unordered lookup set size.
  242. double getAvgUnorderedLookupSetSize() const {
  243. return static_cast<double>(unordered_lookup_size_sum_) /
  244. static_cast<double>(unordered_lookups_);
  245. }
  246. /// \brief Return number of unordered sent packets lookups
  247. ///
  248. /// Method returns number of unordered sent packet lookups.
  249. /// Unordered lookup is used when received packet was sent
  250. /// out of order by server - transaction id of received
  251. /// packet does not match transaction id of next sent packet.
  252. ///
  253. /// \return number of unordered lookups.
  254. uint64_t getUnorderedLookups() const { return unordered_lookups_; }
  255. /// \brief Return number of ordered sent packets lookups
  256. ///
  257. /// Method returns number of ordered sent packet lookups.
  258. /// Ordered lookup is used when packets are received in the
  259. /// same order as they were sent to the server.
  260. /// If packets are skipped or received out of order, lookup
  261. /// function will use unordered lookup (with hash table).
  262. ///
  263. /// \return number of ordered lookups.
  264. uint64_t getOrderedLookups() const { return ordered_lookups_; }
  265. private:
  266. /// \brief Private default constructor.
  267. ///
  268. /// Default constructor is private because we want the client
  269. /// class to specify exchange type explicitely.
  270. ExchangeStats();
  271. ExchangeType xchg_type_; ///< Packet exchange type.
  272. PktList sent_packets_; ///< List of sent packets.
  273. /// Iterator pointing to the packet on sent list which will most
  274. /// likely match next received packet. This is based on the
  275. /// assumption that server responds in order to incoming packets.
  276. PktListIterator sent_packets_cache_;
  277. PktList rcvd_packets_; ///< List of received packets.
  278. double min_delay_; ///< Minimum delay between sent
  279. ///< and received packets.
  280. double max_delay_; ///< Maximum delay between sent
  281. ///< and received packets.
  282. double sum_delay_; ///< Sum of delays between sent
  283. ///< and received packets.
  284. double square_sum_delay_; ///< Square sum of delays between
  285. ///< sent and recived packets.
  286. uint64_t orphans_; ///< Number of orphant received packets.
  287. /// Sum of unordered lookup sets. Needed to calculate mean size of
  288. /// lookup set. It is desired that number of unordered lookups is
  289. /// minimal for performance reasons. Tracking number of lookups and
  290. /// mean size of the lookup set should give idea of packets serach
  291. /// complexity.
  292. uint64_t unordered_lookup_size_sum_;
  293. uint64_t unordered_lookups_; ///< Number of unordered sent packets
  294. ///< lookups.
  295. uint64_t ordered_lookups_; ///< Number of ordered sent packets
  296. ///< lookups.
  297. };
  298. /// Pointer to ExchangeStats.
  299. typedef boost::shared_ptr<ExchangeStats> ExchangeStatsPtr;
  300. /// Map containing all specified exchange types.
  301. typedef typename std::map<ExchangeType, ExchangeStatsPtr> ExchangesMap;
  302. /// Iterator poiting to \ref ExchangesMap
  303. typedef typename ExchangesMap::const_iterator ExchangesMapIterator;
  304. /// \brief Specify new exchange type.
  305. ///
  306. /// This method creates new \ref ExchangeStats object that will
  307. /// collect statistics data from packets exchange of the specified
  308. /// type.
  309. ///
  310. /// \param xchg_type exchange type.
  311. /// \throw isc::BadValue if exchange of specified type exists.
  312. void addExchangeStats(const ExchangeType xchg_type) {
  313. if (exchanges_.find(xchg_type) != exchanges_.end()) {
  314. isc_throw(BadValue, "Exchange of specified type already added.");
  315. }
  316. exchanges_[xchg_type] = ExchangeStatsPtr(new ExchangeStats(xchg_type));
  317. }
  318. /// \brief Adds new packet to the sent packets list.
  319. ///
  320. /// Method adds new packet to the sent packets list.
  321. /// Packets are added to the list sequentially and
  322. /// most often read sequentially.
  323. ///
  324. /// \param xchg_type exchange type.
  325. /// \param packet packet to be added to the list
  326. /// \throw isc::BadValue if invalid exchange type specified.
  327. void passSentPacket(const ExchangeType xchg_type,
  328. const boost::shared_ptr<T> packet) {
  329. ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
  330. xchg_stats->appendSent(packet);
  331. }
  332. /// \brief Add new received packet and match with sent packet.
  333. ///
  334. /// Method adds new packet to the list of received packets. It
  335. /// also searches for corresponding packet on the list of sent
  336. /// packets. When packets are matched the statistics counters
  337. /// are updated accordingly for the particular exchange type.
  338. ///
  339. /// \param xchg_type exchange type.
  340. /// \param packet received packet
  341. /// \throw isc::BadValue if invalid exchange type specified.
  342. /// \throw isc::Unexpected if corresponding packet was not
  343. /// found on the list of sent packets.
  344. void passRcvdPacket(const ExchangeType xchg_type,
  345. const boost::shared_ptr<T> packet) {
  346. ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
  347. boost::shared_ptr<T> sent_packet
  348. = xchg_stats->findSent(packet->getTransid());
  349. if (sent_packet) {
  350. xchg_stats->updateDelays(sent_packet, packet);
  351. xchg_stats->appendRcvd(packet);
  352. }
  353. }
  354. /// \brief Return number of orphant packets.
  355. ///
  356. /// Method returns number of orphant packets for specified
  357. /// exchange type.
  358. ///
  359. /// \param xchg_type exchange type.
  360. /// \throw isc::BadValue if invalid exchange type specified.
  361. /// \return number of orphant packets so far.
  362. uint64_t getOrphans(const ExchangeType xchg_type) const {
  363. ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
  364. return xchg_stats->getOrphans();
  365. }
  366. /// \brief Return average unordered lookup set size.
  367. ///
  368. /// Method returns average unordered lookup set size.
  369. /// This value is changes every time \findSet function uses
  370. /// unordered packet lookup using transaction id.
  371. ///
  372. /// \param xchg_type exchange type.
  373. /// \return average unordered lookup set size.
  374. double getAvgUnorderedLookupSetSize(const ExchangeType xchg_type) const {
  375. ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
  376. return xchg_stats->getAvgUnorderedLookupSetSize();
  377. }
  378. /// \brief Return number of unordered sent packets lookups
  379. ///
  380. /// Method returns number of unordered sent packet lookups.
  381. /// Unordered lookup is used when received packet was sent
  382. /// out of order by server - transaction id of received
  383. /// packet does not match transaction id of next sent packet.
  384. ///
  385. /// \return number of unordered lookups.
  386. uint64_t getUnorderedLookups(const ExchangeType xchg_type) const {
  387. ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
  388. return xchg_stats->getUnorderedLookups();
  389. }
  390. /// \brief Return number of ordered sent packets lookups
  391. ///
  392. /// Method returns number of ordered sent packet lookups.
  393. /// Ordered lookup is used when packets are received in the
  394. /// same order as they were sent to the server.
  395. /// If packets are skipped or received out of order, lookup
  396. /// function will use unordered lookup (with hash table).
  397. ///
  398. /// \return number of ordered lookups.
  399. uint64_t getOrderedLookups(const ExchangeType xchg_type) const {
  400. ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
  401. return xchg_stats->getOrderedLookups();
  402. }
  403. private:
  404. /// \brief Return exchange stats object for given exchange type
  405. ///
  406. /// Method returns exchange stats object for given exchange type.
  407. ///
  408. /// \param xchg_type exchange type.
  409. /// \throw isc::BadValue if invalid exchange type specified.
  410. /// \return exchange stats object.
  411. ExchangeStatsPtr getExchangeStats(const ExchangeType xchg_type) const {
  412. ExchangesMapIterator it = exchanges_.find(xchg_type);
  413. if (it == exchanges_.end()) {
  414. isc_throw(BadValue, "Packets exchange not specified");
  415. }
  416. ExchangeStatsPtr xchg_stats = it->second;
  417. return xchg_stats;
  418. }
  419. ExchangesMap exchanges_; ///< Map of exchange types.
  420. };
  421. } // namespace perfdhcp
  422. } // namespace isc
  423. #endif // __STATS_MGR_H