From 21642b8c9a39c63c96dd37a42fa8cb5329e217c3 Mon Sep 17 00:00:00 2001 From: Stefan Sayer Date: Wed, 23 Feb 2011 23:11:33 +0100 Subject: [PATCH] statistics counters: callsmax/avg, cpsmax/avg max/avg calls and calls/s values, can be accessed via get_callsmax/get_callsavg/get_cpsmax/get_cpsavg from - stats server - XMLRPC through xmlrpc2di based on a patch by Robert Szokovacs rsokovacs gammatelecom hu --- apps/xmlrpc2di/XMLRPC2DI.cpp | 25 +++- apps/xmlrpc2di/XMLRPC2DI.h | 12 ++ core/AmSession.cpp | 168 +++++++++++++++++++++++--- core/AmSession.h | 24 ++++ core/plug-in/stats/StatsUDPServer.cpp | 13 ++ doc/Readme.xmlrpc2di.txt | 5 +- 6 files changed, 228 insertions(+), 19 deletions(-) diff --git a/apps/xmlrpc2di/XMLRPC2DI.cpp b/apps/xmlrpc2di/XMLRPC2DI.cpp index cc743eff..133eb2f0 100644 --- a/apps/xmlrpc2di/XMLRPC2DI.cpp +++ b/apps/xmlrpc2di/XMLRPC2DI.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 iptego GmbH + * Copyright (C) 2010-2011 Stefan Sayer * * This file is part of SEMS, a free SIP media server. * @@ -336,7 +337,12 @@ XMLRPC2DIServer::XMLRPC2DIServer(unsigned int port, // register method 'set_shutdownmode' setshutdownmode_method(s), // register method 'get_shutdownmode' - getshutdownmode_method(s) + getshutdownmode_method(s), + getcallsavg_method(s), + getcallsmax_method(s), + getcpsavg_method(s), + getcpsmax_method(s) + { DBG("XMLRPC Server: enabled builtin method 'calls'\n"); @@ -344,6 +350,10 @@ XMLRPC2DIServer::XMLRPC2DIServer(unsigned int port, DBG("XMLRPC Server: enabled builtin method 'set_loglevel'\n"); DBG("XMLRPC Server: enabled builtin method 'get_shutdownmode'\n"); DBG("XMLRPC Server: enabled builtin method 'set_shutdownmode'\n"); + DBG("XMLRPC Server: enabled builtin method 'get_callsavg'\n"); + DBG("XMLRPC Server: enabled builtin method 'get_callsmax'\n"); + DBG("XMLRPC Server: enabled builtin method 'get_cpsavg'\n"); + DBG("XMLRPC Server: enabled builtin method 'get_cpsmax'\n"); // export all methods via 'di' function? if (di_export) { @@ -460,6 +470,19 @@ void XMLRPC2DIServerSetShutdownmodeMethod::execute(XmlRpcValue& params, XmlRpcVa result = "200 OK"; } +#define XMLMETH_EXEC(_meth, _sess_func, _descr) \ + void _meth::execute(XmlRpcValue& params, XmlRpcValue& result) { \ + unsigned int res = AmSession::_sess_func(); \ + result = (int)res; \ + DBG("XMLRPC2DI: " _descr "(): %u\n", res); \ +} + +XMLMETH_EXEC(XMLRPC2DIServerGetCallsavgMethod, getAvgSessionNum, "get_callsavg"); +XMLMETH_EXEC(XMLRPC2DIServerGetCallsmaxMethod, getMaxSessionNum, "get_callsmax"); +XMLMETH_EXEC(XMLRPC2DIServerGetCpsavgMethod, getAvgCPS, "get_cpsavg"); +XMLMETH_EXEC(XMLRPC2DIServerGetCpsmaxMethod, getMaxCPS, "get_cpsmax"); +#undef XMLMETH_EXEC + void XMLRPC2DIServerDIMethod::execute(XmlRpcValue& params, XmlRpcValue& result) { try { if (params.size() < 2) { diff --git a/apps/xmlrpc2di/XMLRPC2DI.h b/apps/xmlrpc2di/XMLRPC2DI.h index 13cc43bf..fe6d3387 100644 --- a/apps/xmlrpc2di/XMLRPC2DI.h +++ b/apps/xmlrpc2di/XMLRPC2DI.h @@ -60,6 +60,12 @@ DEF_XMLRPCSERVERMETHOD(XMLRPC2DIServerGetLoglevelMethod, "get_loglevel"); DEF_XMLRPCSERVERMETHOD(XMLRPC2DIServerSetShutdownmodeMethod, "set_shutdownmode"); DEF_XMLRPCSERVERMETHOD(XMLRPC2DIServerGetShutdownmodeMethod, "get_shutdownmode"); +DEF_XMLRPCSERVERMETHOD(XMLRPC2DIServerGetCallsavgMethod, "get_callsavg"); +DEF_XMLRPCSERVERMETHOD(XMLRPC2DIServerGetCallsmaxMethod, "get_callsmax"); +DEF_XMLRPCSERVERMETHOD(XMLRPC2DIServerGetCpsavgMethod, "get_cpsavg"); +DEF_XMLRPCSERVERMETHOD(XMLRPC2DIServerGetCpsmaxMethod, "get_cpsmax"); + + class XMLRPC2DIServerDIMethod : public XmlRpcServerMethod { @@ -95,6 +101,12 @@ class XMLRPC2DIServer : public AmThread { XMLRPC2DIServerGetLoglevelMethod getloglevel_method; XMLRPC2DIServerSetShutdownmodeMethod setshutdownmode_method; XMLRPC2DIServerGetShutdownmodeMethod getshutdownmode_method; + + XMLRPC2DIServerGetCallsavgMethod getcallsavg_method; + XMLRPC2DIServerGetCallsmaxMethod getcallsmax_method; + XMLRPC2DIServerGetCpsavgMethod getcpsavg_method; + XMLRPC2DIServerGetCpsmaxMethod getcpsmax_method; + XMLRPC2DIServerDIMethod* di_method; void registerMethods(const std::string& iface); diff --git a/core/AmSession.cpp b/core/AmSession.cpp index e093757e..7963453c 100644 --- a/core/AmSession.cpp +++ b/core/AmSession.cpp @@ -52,7 +52,21 @@ volatile unsigned int AmSession::session_num = 0; AmMutex AmSession::session_num_mut; - +volatile unsigned int AmSession::max_session_num = 0; +volatile unsigned long long AmSession::avg_session_num = 0; +volatile unsigned long AmSession::max_cps = 0; +volatile unsigned long AmSession::max_cps_counter = 0; +volatile unsigned long AmSession::avg_cps = 0; + +struct timeval get_now() { + struct timeval res; + gettimeofday(&res, NULL); + return res; +} +struct timeval avg_last_timestamp = get_now(); +struct timeval avg_first_timestamp = avg_last_timestamp; +struct timeval cps_first_timestamp = avg_last_timestamp; +struct timeval cps_max_timestamp = avg_last_timestamp; // AmSession methods @@ -370,9 +384,7 @@ bool AmSession::startup() { } #endif - session_num_mut.lock(); - session_num++; - session_num_mut.unlock(); + session_started(); try { try { @@ -394,9 +406,7 @@ bool AmSession::startup() { onBeforeDestroy(); destroy(); - session_num_mut.lock(); - session_num--; - session_num_mut.unlock(); + session_stopped(); return false; } @@ -514,9 +524,7 @@ void AmSession::finalize() { onBeforeDestroy(); destroy(); - session_num_mut.lock(); - session_num--; - session_num_mut.unlock(); + session_stopped(); DBG("session is stopped.\n"); } @@ -541,14 +549,12 @@ void AmSession::setStopped(bool wakeup) { new AmEvent(0)); } -void AmSession::destroy() -{ +void AmSession::destroy() { DBG("AmSession::destroy()\n"); AmSessionContainer::instance()->destroySession(this); } -string AmSession::getNewId() -{ +string AmSession::getNewId() { struct timeval t; gettimeofday(&t,NULL); @@ -560,9 +566,57 @@ string AmSession::getNewId() return id; } +/* bookkeeping functions - TODO: move to monitoring */ +void AmSession::session_started() { + struct timeval now, delta; -unsigned int AmSession::getSessionNum() -{ + session_num_mut.lock(); + //avg session number + gettimeofday(&now, NULL); + timersub(&now, &avg_last_timestamp, &delta); + avg_session_num += session_num * (delta.tv_sec * 1000000ULL + delta.tv_usec); + avg_last_timestamp = now; + + //current session number + session_num++; + + //maximum session number + if(session_num > max_session_num) max_session_num = session_num; + + //cps average + ++avg_cps; + + //cps maximum + ++max_cps_counter; + timersub(&now, &cps_max_timestamp, &delta); + unsigned long long d_usec = delta.tv_sec * 1000000ULL + delta.tv_usec; + if (delta.tv_sec > 0) { + //more than 1 sec has passed + unsigned long long secavg = ((max_cps_counter * 1000000ULL) + d_usec - 1) / d_usec; + if (max_cps < secavg) { + max_cps = secavg; + } + cps_max_timestamp = now; + max_cps_counter = 0; + } + + session_num_mut.unlock(); +} + +void AmSession::session_stopped() { + struct timeval now, delta; + session_num_mut.lock(); + //avg session number + gettimeofday(&now, NULL); + timersub(&now, &avg_last_timestamp, &delta); + avg_session_num += session_num * (delta.tv_sec * 1000000ULL + delta.tv_usec); + avg_last_timestamp = now; + //current session number + session_num--; + session_num_mut.unlock(); +} + +unsigned int AmSession::getSessionNum() { unsigned int res = 0; session_num_mut.lock(); res = session_num; @@ -570,6 +624,88 @@ unsigned int AmSession::getSessionNum() return res; } +unsigned int AmSession::getMaxSessionNum() { + unsigned int res = 0; + session_num_mut.lock(); + res = max_session_num; + max_session_num = session_num; + session_num_mut.unlock(); + return res; +} + +unsigned int AmSession::getAvgSessionNum() { + unsigned int res = 0; + struct timeval now, delta; + session_num_mut.lock(); + gettimeofday(&now, NULL); + timersub(&now, &avg_last_timestamp, &delta); + avg_session_num += session_num * (delta.tv_sec * 1000000ULL + delta.tv_usec); + timersub(&now, &avg_first_timestamp, &delta); + unsigned long long d_usec = delta.tv_sec * 1000000ULL + delta.tv_usec; + if (!d_usec) { + res = 0; + WARN("zero delta!\n"); + } else { + //Round up + res = (unsigned int)((avg_session_num + d_usec - 1) / d_usec); + } + avg_session_num = 0; + avg_last_timestamp = now; + avg_first_timestamp = now; + session_num_mut.unlock(); + return res; +} + +unsigned int AmSession::getMaxCPS() +{ + unsigned int res = 0; + struct timeval now, delta; + session_num_mut.lock(); + gettimeofday(&now, NULL); + timersub(&now, &cps_max_timestamp, &delta); + unsigned long long d_usec = delta.tv_sec * 1000000ULL + delta.tv_usec; + if(delta.tv_sec > 0) { + //more than 1 sec has passed + //Round up + unsigned long long secavg = ((max_cps_counter * 1000000ULL) + d_usec - 1) / d_usec; + if (max_cps < secavg) { + max_cps = secavg; + } + cps_max_timestamp = now; + max_cps_counter = 0; + } + + res = max_cps; + max_cps = 0; + session_num_mut.unlock(); + return res; +} + +unsigned int AmSession::getAvgCPS() +{ + unsigned int res = 0; + struct timeval now, delta; + unsigned long n_avg_cps; + + session_num_mut.lock(); + gettimeofday(&now, NULL); + timersub(&now, &cps_first_timestamp, &delta); + cps_first_timestamp = now; + n_avg_cps = avg_cps; + avg_cps = 0; + session_num_mut.unlock(); + + unsigned long long d_usec = delta.tv_sec * 1000000ULL + delta.tv_usec; + if(!d_usec) { + res = 0; + WARN("zero delta!\n"); + } else { + //Round up + res = (unsigned int)(((n_avg_cps * 1000000ULL) + d_usec - 1) / d_usec); + } + + return res; +} void AmSession::setInbandDetector(Dtmf::InbandDetectorType t) { diff --git a/core/AmSession.h b/core/AmSession.h index d5b69b77..9ab0d209 100644 --- a/core/AmSession.h +++ b/core/AmSession.h @@ -138,7 +138,15 @@ private: AmCondition sess_stopped; AmCondition detached; + static void session_started(); + static void session_stopped(); + static volatile unsigned int session_num; + static volatile unsigned int max_session_num; + static volatile unsigned long long avg_session_num; + static volatile unsigned long max_cps; + static volatile unsigned long max_cps_counter; + static volatile unsigned long avg_cps; static AmMutex session_num_mut; friend class AmMediaProcessor; @@ -402,6 +410,22 @@ public: * Gets the number of running sessions */ static unsigned int getSessionNum(); + /** + * Gets the maximum of running sessions since last query + */ + static unsigned int getMaxSessionNum(); + /** + * Gets the average of running sessions since last query + */ + static unsigned int getAvgSessionNum(); + /** + * Gets the maximum of calls per second since last query + */ + static unsigned int getMaxCPS(); + /** + * Gets the timeaverage of calls per second since last query + */ + static unsigned int getAvgCPS(); /** * Entry point for DTMF events diff --git a/core/plug-in/stats/StatsUDPServer.cpp b/core/plug-in/stats/StatsUDPServer.cpp index 03e51d11..36aa0847 100644 --- a/core/plug-in/stats/StatsUDPServer.cpp +++ b/core/plug-in/stats/StatsUDPServer.cpp @@ -294,6 +294,10 @@ int StatsUDPServer::execute(char* msg_buf, string& reply, "get_loglevel - get log level\n" "set_shutdownmode <1 or 0> - turns on and off shutdown mode\n" "get_shutdownmode - returns the shutdown mode's current state\n" + "get_callsavg - get number of active calls (average since the last query)\n" + "get_callsmax - get maximum of active calls since the last query\n" + "get_cpsavg - get calls per second (average since the last query)\n" + "get_cpsmax - get maximum of CPS since the last query\n" "DI ()* - invoke DI command\n" "\n" @@ -337,6 +341,15 @@ int StatsUDPServer::execute(char* msg_buf, string& reply, reply= "loglevel is "+int2str(log_level)+".\n"; } + else if(cmd_str.substr(4, 8) == "callsavg") + reply = "Average active calls: " + int2str(AmSession::getAvgSessionNum()) + "\n"; + else if(cmd_str.substr(4, 8) == "callsmax") + reply = "Maximum active calls: " + int2str(AmSession::getMaxSessionNum()) + "\n"; + else if(cmd_str.substr(4, 8) == "cpsavg") + reply = "Average calls per second: " + int2str(AmSession::getAvgCPS()) + "\n"; + else if(cmd_str.substr(4, 8) == "cpsmax") + reply = "Maximum calls per second: " + int2str(AmSession::getMaxCPS()) + "\n"; + else if (cmd_str.substr(4, 12) == "shutdownmode") { if(AmConfig::ShutdownMode) { diff --git a/doc/Readme.xmlrpc2di.txt b/doc/Readme.xmlrpc2di.txt index 21ce90f6..7e839a00 100644 --- a/doc/Readme.xmlrpc2di.txt +++ b/doc/Readme.xmlrpc2di.txt @@ -2,8 +2,9 @@ xmlrpc2di: DI call via XMLRPC This module makes the Dynamic Invocation (DI) Interfaces exported by component modules accessible from XMLRPC. Additionaly the builtin -methods "calls", "get_loglevel"/"set_loglevel" and -"get_shutdownmode"/"set_shutdownmode" are implemented. +methods "calls", "get_loglevel"/"set_loglevel", +"get_shutdownmode"/"set_shutdownmode", and access to the statistics counters +(get_callsavg/get_callsmax/get_cpsmax/get_cpsavg) are implemented. Additionally, it can be used as client to access XMLRPC servers. Applications can use the DI function newConnection to add a new server entry, and sendRequest