#ifndef __ThreadMonitor_h_ #define __ThreadMonitor_h_ ///////////////////////////////////////////////////////////////////// // ThreadMonitor // // This class keeps performance information, such as how many times // a particular task is executed, and how much time is spent on // each task. // // Previous attempts at this were customized for each thread. This // was more efficient, but took more time to write and maintain. // This is easier because we want every thread to use one of these. // If nothing else, the thread should report how much time it is // asleep, vs. how much CPU time it is using. // // All reports show the amount of "wall clock" time. You can get // a good estimate of the CPU time based on which task you are // looking at. (Waiting on a lock or sleeping uses almost no // CPU. Calling mysql uses some, but not much. "Working" is // almost entirely CPU time.) The times system call gives you // CPU time, but that only applies to the program as a whole, not // to the current thread. // ///////////////////////////////////////////////////////////////////// #include #include #include #include "MiscSupport.h" class ThreadMonitor : NoCopy, NoAssign { public: class Extra { public: // This is called each time we print a message to the log file. If this // returns "", nothing happens. If this returns anything else, that is // appended to the line we send to the log file. // // It is not specified when this is called relative to our other work. // This could be called before or after we read the timers and counters. // It is best not to call increment() or setState() from here. virtual std::string getInfoForThreadMonitor() =0; virtual ~Extra() { } }; private: ThreadMonitor(); std::string _name; TimeVal::Microseconds _lastOutput; TimeVal::Microseconds _stateStart; std::string _currentState; std::map< std::string, TimeVal::Microseconds > _timeInState; std::map< std::string, unsigned int > _counters; std::set< Extra * > _extras; public: static ThreadMonitor &find(); void setName(std::string const &name) { _name = name; pthread_setname_np(pthread_self(), name.c_str()); } std::string const &getName() const { return _name; } void setState(std::string const &state); std::string const &getState() { return _currentState; } void increment(std::string const &counter, int by=1); /* This is a convenient way to save the current state and set a new state. * The state will be restored automatically when this object goes out of * scope. I.e. create an object of this type at the top of a function * with the name of the function. Whenever you exit the function (even * if there are 10 different return statements) the old state will be * restored. */ class SetState : NoCopy, NoAssign { private: ThreadMonitor &_tm; const std::string _oldState; public: SetState(std::string const &newState) : _tm(ThreadMonitor::find()), _oldState(_tm.getState()) { _tm.setState(newState); } ~SetState() { _tm.setState(_oldState); } void setState(std::string const &state) { _tm.setState(state); } void increment(std::string const &counter) { _tm.increment(counter); } void increment(std::string const &counter, int by) { _tm.increment(counter, by); } }; // Extend the reporting mechanism. One nice feature of ThreadMonitor is that // it reports periodically. This function lets you reuse that functionality. // // Your extra information will be merged with the default report. Typically // you want to compare statistics for different things all for the same // time period. E.g. there were 500 alert requests reported by increment(), // and there were 50 users on line, reported by an Extra object. // // ThreadMonitor is good at what it does, the counters and states. But // ThreadMonitor is unable to handle other types of information, like how // many people are currently connected. That type of snapshot would be a // very simple thing to implement with an Extra object. // // We store a std::set of Extra objects. Typically a ThreadClass or a // ContainerThreadUser will call add() as part of it's initialization. // Multiple ContainerThreadUser objects might share a ThreadMonitor without // knowing about each other. void add(Extra *extra) { _extras.insert(extra); } // This tracks the time of a given event. If a lot of events happen in our // normal reporting period, we only report the longest time for each name. // // ThreadMonitor::setState() lets you track the total time used for a // specific purpose. Combine that with ThreadMonitor::increment() to get // the average time. LongestTime, on the other hand, is focused on the // exceptions. If the worst MySQL query is taking a lot longer than the // average, then we need to find what's special about that one. If they are // all about the same, we need a different approach to make things faster. // // We compute the average in this class because the average you could get // from setState() would be different. It's measured in a different way. // It's easy to do here. And if you want the longest you almost certainly // want the average, too. class LongestTime { private: class Helper : private Extra { private: struct Info { TimeVal::Microseconds longest; TimeVal::Microseconds total; int count; Info() : longest(0), total(0), count(0) { } }; std::map< std::string, Info > _longest; virtual std::string getInfoForThreadMonitor(); Helper(); public: static Helper &find(); void report(std::string const &name, TimeVal::Microseconds time); }; const std::string _name; const TimeVal _start; public: // Create one of these to start monitoring. Use different names to monitor // different things. We make exactly one report per name for each name. LongestTime(std::string const &name); // Stop the current session. Record the current value. Note that we // record the entire time between the constructor and destructor. This // is different from SetState and setState(). Each thread will be in // exactly one state at a time. If you have multiple LongestTime objects // running at the same time, they all continue to accrue time. ~LongestTime(); }; }; #endif