--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "../core/except.h"
+#include "../time/units.h"
+#include "profiler.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Debug {
+
+Profiler::Profiler():
+ period(0),
+ inner(0)
+{ }
+
+void Profiler::set_period(unsigned p)
+{
+ if(p==period)
+ return;
+
+ period=p;
+ for(map<string, ScopeInfo>::iterator i=scopes.begin(); i!=scopes.end(); ++i)
+ {
+ ScopeInfo &si=i->second;
+ if(p==0)
+ si.history.clear();
+ else
+ si.history.assign(period, Time::zero);
+ si.hist_pos=0;
+ }
+}
+
+void Profiler::add_scope(const std::string &name)
+{
+ if(!scopes.count(name))
+ {
+ map<string, ScopeInfo>::iterator i=scopes.insert(map<string, ScopeInfo>::value_type(name, ScopeInfo())).first;
+ i->second.history.resize(period);
+ }
+}
+
+ProfilingScope *Profiler::enter(ProfilingScope *ps)
+{
+ ProfilingScope *old=inner;
+ inner=ps;
+ return old;
+}
+
+void Profiler::record(const string &scope_name, const string &parent, const Time::TimeDelta &time, const Time::TimeDelta &child_t)
+{
+ map<string, ScopeInfo>::iterator i=scopes.find(scope_name);
+ if(i==scopes.end())
+ {
+ i=scopes.insert(map<string, ScopeInfo>::value_type(scope_name, ScopeInfo())).first;
+ i->second.history.resize(period);
+ }
+
+ ScopeInfo &si=i->second;
+ ++si.calls;
+ ++si.called_from[parent];
+ si.total_time+=time;
+ si.self_time+=time-child_t;
+ if(period)
+ {
+ si.avg_time+=(time-si.history[si.hist_pos])/period;
+ si.history[si.hist_pos++]=time;
+ if(si.hist_pos>=period)
+ si.hist_pos-=period;
+ }
+ else
+ si.avg_time=si.total_time/si.calls;
+}
+
+const Profiler::ScopeInfo &Profiler::scope(const string &sn) const
+{
+ map<string, ScopeInfo>::const_iterator i=scopes.find(sn);
+ if(i==scopes.end())
+ throw KeyError("Unknown scope");
+
+ return i->second;
+}
+
+
+Profiler::ScopeInfo::ScopeInfo():
+ calls(0),
+ hist_pos(0)
+{ }
+
+} // namespace Debug
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_DEBUG_PROFILER_H_
+#define MSP_DEBUG_PROFILER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+#include "../time/timedelta.h"
+
+namespace Msp {
+namespace Debug {
+
+class ProfilingScope;
+
+/**
+A class for collecting timing data from a program. It's not as efficient as
+external profilers, but allows profiling of custom scopes and retrieving the
+profiling data at run time. An example usage could be showing realtime
+performance statistics of a game or a simulation.
+
+See also class ProfilingScope.
+
+Note: This is not thread-safe. To profile multiple threads, create a separate
+Profiler for each thread.
+*/
+class Profiler
+{
+public:
+ struct ScopeInfo
+ {
+ unsigned calls;
+ Time::TimeDelta total_time;
+ Time::TimeDelta self_time;
+ Time::TimeDelta avg_time;
+ std::vector<Time::TimeDelta> history;
+ unsigned hist_pos;
+ std::map<std::string, unsigned> called_from;
+
+ ScopeInfo();
+ };
+
+private:
+ unsigned period;
+ std::map<std::string, ScopeInfo> scopes;
+ ProfilingScope *inner;
+
+public:
+ Profiler();
+
+ /**
+ Sets the averaging period for timing data, measured in calls. Previous
+ average timings are cleared.
+ */
+ void set_period(unsigned p);
+
+ /**
+ Adds a scope without recording any calls to it. Useful if you might need to
+ access a scope before it has finished for the first time.
+ */
+ void add_scope(const std::string &name);
+
+ /**
+ Changes the recorded innermost scope pointer and returns the old one. This
+ is used by ProfilingScope to track child time and should not be called
+ manually.
+ */
+ ProfilingScope *enter(ProfilingScope *ps);
+
+ /**
+ Records a call to a scope. You'll probably want to use a ProfilingScope
+ instead of calling this manually.
+
+ @param sn Scope name
+ @param pn Parent scope name
+ @param t Time spent in the scope
+ @param ct Time spent in child scopes
+ */
+ void record(const std::string &sn, const std::string &pn, const Time::TimeDelta &t, const Time::TimeDelta &ct);
+
+ /**
+ Returns informations about a scope.
+ */
+ const ScopeInfo &scope(const std::string &) const;
+};
+
+} // namespace Debug
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "../time/utils.h"
+#include "profilingscope.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Debug {
+
+ProfilingScope::ProfilingScope(Profiler &p, const string &n):
+ profiler(p),
+ name(n),
+ parent(profiler.enter(this)),
+ start_t(Time::now())
+{ }
+
+ProfilingScope::~ProfilingScope()
+{
+ const Time::TimeDelta dt=start_t-Time::now();
+ if(parent)
+ {
+ parent->child_t+=dt;
+ profiler.record(name, parent->name, dt, child_t);
+ }
+ else
+ profiler.record(name, string(), dt, child_t);
+ profiler.enter(parent);
+}
+
+} // namespace Debug
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspcore
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_DEBUG_PROFILINGSCOPE_H_
+#define MSP_DEBUG_PROFILINGSCOPE_H_
+
+#include "../time/timestamp.h"
+#include "profiler.h"
+
+namespace Msp {
+namespace Debug {
+
+/**
+RAII timing class to accompany Profiler. Timing starts when an object is
+created and ends when it goes out of scope. If there was another object in an
+outer scope, it is notified of the time used in inner scopes.
+*/
+class ProfilingScope
+{
+private:
+ Profiler &profiler;
+ std::string name;
+ ProfilingScope *parent;
+ Time::TimeStamp start_t;
+ Time::TimeDelta child_t;
+
+ ProfilingScope(const ProfilingScope &);
+ ProfilingScope &operator=(const ProfilingScope &);
+public:
+ ProfilingScope(Profiler &p, const std::string &n);
+ ~ProfilingScope();
+};
+
+} // namespace Debug
+} // namespace Msp
+
+#endif