| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- /* Copyright The kNet Project.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License. */
- #pragma once
- /** @file StatsEventHierarchy.h
- @brief Stores a hierarchy of network events for profiling purposes. */
- #include <map>
- #include <string>
- #include "kNet/WaitFreeQueue.h"
- #include "kNet/Clock.h"
- // This macro is used inside MessageConnection and NetworkServer objects, which have the 'owner' member.
- static const int cEventOldAgeMSecs = 30 * 1000;
- #ifdef KNET_NETWORK_PROFILING
- #define ADDEVENT(name, value, valueType) (owner ? owner->Statistics()->AddEventToHierarchy((name), (value), (valueType), cEventOldAgeMSecs) : ((void)0))
- #else
- #define ADDEVENT(name, value, valueType) ((void)0)
- #endif
- namespace kNet
- {
- struct StatsEvent
- {
- float value;
- tick_t time;
- };
- inline std::string FirstToken(const char *str, char delimiter, int &nextTokenStart)
- {
- int i = 0;
- while(str[i] != '\0' && str[i] != delimiter)
- ++i;
- if (str[i] == '\0' || str[i+1] == delimiter)
- nextTokenStart = -1;
- else
- nextTokenStart = i+1;
- return std::string(str, str + i);
- }
- class StatsEventHierarchyNode
- {
- public:
- ///\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.
- typedef std::map<std::string, StatsEventHierarchyNode> NodeMap;
- NodeMap children;
- WaitFreeQueue<StatsEvent> events;
- /// Specifies the unit of the numeric data in this node.
- std::string valueType;
- StatsEventHierarchyNode()
- :events(4) // The default size for the queue must be at least four elements (pow2, >2).
- {
- }
- void PruneOldEventsThisLevel(int ageMSecs)
- {
- assert(ageMSecs >= 0);
- tick_t tooOldMessageTime = Clock::Tick() - (tick_t)ageMSecs * Clock::TicksPerSec() / 1000;
- while(events.Size() > 0)
- {
- StatsEvent *front = events.Front();
- if (Clock::IsNewer(tooOldMessageTime, front->time))
- events.PopFront();
- else
- 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.
- }
- }
- void PruneOldEventsHierarchy(int ageMSecs)
- {
- PruneOldEventsThisLevel(ageMSecs);
- for(NodeMap::iterator iter = children.begin(); iter != children.end(); ++iter)
- iter->second.PruneOldEventsHierarchy(ageMSecs);
- }
- void AddEventToThisLevel(float value, int oldAgeMSecs)
- {
- StatsEvent e;
- e.value = value;
- e.time = Clock::Tick();
- PruneOldEventsThisLevel(oldAgeMSecs);
- if (events.Size() < 16384)
- events.InsertWithResize(e);
- }
- ///\ @param name The event track in the profiler hierachy to add the event to, e.g. "connection.messageIn.myMessageName". This
- /// string may not contain two consecutive periods, e.g. "a..b".
- void AddEventToHierarchy(const char *name, float value, const char *valueType, int oldAgeMSecs)
- {
- int nextTokenStart = 0;
- std::string childName = FirstToken(name, '.', nextTokenStart);
- if (childName.empty())
- AddEventToThisLevel(value, oldAgeMSecs);
- else
- {
- NodeMap::iterator iter = children.find(childName);
- if (iter == children.end())
- children[childName].valueType = valueType; // To optimize, only copy this field in the first time the node is created.
- if (nextTokenStart == -1)
- children[childName].AddEventToThisLevel(value, oldAgeMSecs);
- else
- children[childName].AddEventToHierarchy(name + nextTokenStart, value, valueType, oldAgeMSecs);
- }
- }
- StatsEventHierarchyNode *FindChild(const char *name)
- {
- int nextTokenStart = 0;
- std::string childName = FirstToken(name, '.', nextTokenStart);
- if (childName.empty())
- return this;
- else
- {
- NodeMap::iterator iter = children.find(childName);
- if (iter == children.end())
- return 0;
- if (nextTokenStart == -1)
- return &children[childName];
- else
- return children[childName].FindChild(name + nextTokenStart);
- }
- }
- int AccumulateTotalCountThisLevel() const
- {
- return events.Size();
- }
- int AccumulateTotalCountHierarchy() const
- {
- int count = AccumulateTotalCountThisLevel();
- for(NodeMap::const_iterator iter = children.begin(); iter != children.end(); ++iter)
- count += iter->second.AccumulateTotalCountHierarchy();
- return count;
- }
- float AccumulateTotalValueThisLevel() const
- {
- float value = 0.f;
- for(int i = 0; i < events.Size(); ++i)
- value += events.ItemAt(i)->value;
- return value;
- }
- float AccumulateTotalValueHierarchy() const
- {
- float value = AccumulateTotalValueThisLevel();
- for(NodeMap::const_iterator iter = children.begin(); iter != children.end(); ++iter)
- value += iter->second.AccumulateTotalValueHierarchy();
- return value;
- }
- float LatestValue()
- {
- if (events.Size() == 0)
- return 0.f;
- else
- return events.Back()->value;
- }
- };
- } // ~kNet
|