StatsEventHierarchy.h 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /* Copyright The kNet Project.
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License. */
  11. #pragma once
  12. /** @file StatsEventHierarchy.h
  13. @brief Stores a hierarchy of network events for profiling purposes. */
  14. #include <map>
  15. #include <string>
  16. #include "kNet/WaitFreeQueue.h"
  17. #include "kNet/Clock.h"
  18. // This macro is used inside MessageConnection and NetworkServer objects, which have the 'owner' member.
  19. static const int cEventOldAgeMSecs = 30 * 1000;
  20. #ifdef KNET_NETWORK_PROFILING
  21. #define ADDEVENT(name, value, valueType) (owner ? owner->Statistics()->AddEventToHierarchy((name), (value), (valueType), cEventOldAgeMSecs) : ((void)0))
  22. #else
  23. #define ADDEVENT(name, value, valueType) ((void)0)
  24. #endif
  25. namespace kNet
  26. {
  27. struct StatsEvent
  28. {
  29. float value;
  30. tick_t time;
  31. };
  32. inline std::string FirstToken(const char *str, char delimiter, int &nextTokenStart)
  33. {
  34. int i = 0;
  35. while(str[i] != '\0' && str[i] != delimiter)
  36. ++i;
  37. if (str[i] == '\0' || str[i+1] == delimiter)
  38. nextTokenStart = -1;
  39. else
  40. nextTokenStart = i+1;
  41. return std::string(str, str + i);
  42. }
  43. class StatsEventHierarchyNode
  44. {
  45. public:
  46. ///\todo To improve performance, don't use a std::string as a key to the map, and replace the map with a more efficient data structure.
  47. typedef std::map<std::string, StatsEventHierarchyNode> NodeMap;
  48. NodeMap children;
  49. WaitFreeQueue<StatsEvent> events;
  50. /// Specifies the unit of the numeric data in this node.
  51. std::string valueType;
  52. StatsEventHierarchyNode()
  53. :events(4) // The default size for the queue must be at least four elements (pow2, >2).
  54. {
  55. }
  56. void PruneOldEventsThisLevel(int ageMSecs)
  57. {
  58. assert(ageMSecs >= 0);
  59. tick_t tooOldMessageTime = Clock::Tick() - (tick_t)ageMSecs * Clock::TicksPerSec() / 1000;
  60. while(events.Size() > 0)
  61. {
  62. StatsEvent *front = events.Front();
  63. if (Clock::IsNewer(tooOldMessageTime, front->time))
  64. events.PopFront();
  65. else
  66. break; // The items are added to the queue in their time order, so if the oldest item is newer than our limit, they all are.
  67. }
  68. }
  69. void PruneOldEventsHierarchy(int ageMSecs)
  70. {
  71. PruneOldEventsThisLevel(ageMSecs);
  72. for(NodeMap::iterator iter = children.begin(); iter != children.end(); ++iter)
  73. iter->second.PruneOldEventsHierarchy(ageMSecs);
  74. }
  75. void AddEventToThisLevel(float value, int oldAgeMSecs)
  76. {
  77. StatsEvent e;
  78. e.value = value;
  79. e.time = Clock::Tick();
  80. PruneOldEventsThisLevel(oldAgeMSecs);
  81. if (events.Size() < 16384)
  82. events.InsertWithResize(e);
  83. }
  84. ///\ @param name The event track in the profiler hierachy to add the event to, e.g. "connection.messageIn.myMessageName". This
  85. /// string may not contain two consecutive periods, e.g. "a..b".
  86. void AddEventToHierarchy(const char *name, float value, const char *valueType, int oldAgeMSecs)
  87. {
  88. int nextTokenStart = 0;
  89. std::string childName = FirstToken(name, '.', nextTokenStart);
  90. if (childName.empty())
  91. AddEventToThisLevel(value, oldAgeMSecs);
  92. else
  93. {
  94. NodeMap::iterator iter = children.find(childName);
  95. if (iter == children.end())
  96. children[childName].valueType = valueType; // To optimize, only copy this field in the first time the node is created.
  97. if (nextTokenStart == -1)
  98. children[childName].AddEventToThisLevel(value, oldAgeMSecs);
  99. else
  100. children[childName].AddEventToHierarchy(name + nextTokenStart, value, valueType, oldAgeMSecs);
  101. }
  102. }
  103. StatsEventHierarchyNode *FindChild(const char *name)
  104. {
  105. int nextTokenStart = 0;
  106. std::string childName = FirstToken(name, '.', nextTokenStart);
  107. if (childName.empty())
  108. return this;
  109. else
  110. {
  111. NodeMap::iterator iter = children.find(childName);
  112. if (iter == children.end())
  113. return 0;
  114. if (nextTokenStart == -1)
  115. return &children[childName];
  116. else
  117. return children[childName].FindChild(name + nextTokenStart);
  118. }
  119. }
  120. int AccumulateTotalCountThisLevel() const
  121. {
  122. return events.Size();
  123. }
  124. int AccumulateTotalCountHierarchy() const
  125. {
  126. int count = AccumulateTotalCountThisLevel();
  127. for(NodeMap::const_iterator iter = children.begin(); iter != children.end(); ++iter)
  128. count += iter->second.AccumulateTotalCountHierarchy();
  129. return count;
  130. }
  131. float AccumulateTotalValueThisLevel() const
  132. {
  133. float value = 0.f;
  134. for(int i = 0; i < events.Size(); ++i)
  135. value += events.ItemAt(i)->value;
  136. return value;
  137. }
  138. float AccumulateTotalValueHierarchy() const
  139. {
  140. float value = AccumulateTotalValueThisLevel();
  141. for(NodeMap::const_iterator iter = children.begin(); iter != children.end(); ++iter)
  142. value += iter->second.AccumulateTotalValueHierarchy();
  143. return value;
  144. }
  145. float LatestValue()
  146. {
  147. if (events.Size() == 0)
  148. return 0.f;
  149. else
  150. return events.Back()->value;
  151. }
  152. };
  153. } // ~kNet