From b0ec43934ac614d0fcc2a22d6a10e9117744bec5 Mon Sep 17 00:00:00 2001 From: Daniel-Constantin Mierla Date: Fri, 22 Jan 2016 00:39:24 +0100 Subject: [PATCH] statsc: new modules - statistics collector - record the values of various statistics for a period of time, based on a timer interval and provide reports about them via rpc - reports implemented so far: - list: the recorded values - diff: the diff between values (val[T] - val[T-1]) --- modules/statsc/Makefile | 14 + modules/statsc/README | 134 +++++++++ modules/statsc/doc/Makefile | 4 + modules/statsc/doc/statsc.xml | 37 +++ modules/statsc/doc/statsc_admin.xml | 139 +++++++++ modules/statsc/statsc_mod.c | 431 ++++++++++++++++++++++++++++ 6 files changed, 759 insertions(+) create mode 100644 modules/statsc/Makefile create mode 100644 modules/statsc/README create mode 100644 modules/statsc/doc/Makefile create mode 100644 modules/statsc/doc/statsc.xml create mode 100644 modules/statsc/doc/statsc_admin.xml create mode 100644 modules/statsc/statsc_mod.c diff --git a/modules/statsc/Makefile b/modules/statsc/Makefile new file mode 100644 index 00000000000..8d7003e29cb --- /dev/null +++ b/modules/statsc/Makefile @@ -0,0 +1,14 @@ +# +# WARNING: do not run this directly, it should be run by the master Makefile + +include ../../Makefile.defs +auto_gen= +NAME=statsc.so +LIBS= + +DEFS+=-DKAMAILIO_MOD_INTERFACE + +SERLIBPATH=../../lib +SER_LIBS+=$(SERLIBPATH)/kcore/kcore + +include ../../Makefile.modules diff --git a/modules/statsc/README b/modules/statsc/README new file mode 100644 index 00000000000..d87677b1d39 --- /dev/null +++ b/modules/statsc/README @@ -0,0 +1,134 @@ +STATSC Module + +Daniel-Constantin Mierla + + + +Edited by + +Daniel-Constantin Mierla + + + + Copyright © 2016 asipto.com + __________________________________________________________________ + + Table of Contents + + 1. Admin Guide + + 1. Overview + 2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + + 3. Parameters + + 3.1. interval (int) + 3.2. items (int) + + 4. RPC Commands + + 4.1. statsc.exec + + List of Examples + + 1.1. Set interval parameter + 1.2. Set items parameter + +Chapter 1. Admin Guide + + Table of Contents + + 1. Overview + 2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + + 3. Parameters + + 3.1. interval (int) + 3.2. items (int) + + 4. RPC Commands + + 4.1. statsc.exec + +1. Overview + + This module provides a statistics collector engine. It can track the + values of various internal Kamailio statistics for a specific period of + time, allowing to retrieve them or a report over them via RPC commands. + +2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + +2.1. Kamailio Modules + + The following modules must be loaded along this module: + * various - for getting access to the statistics exported by these + modules. + +2.2. External Libraries or Applications + + The following libraries or applications must be installed before + running Kamailio with this module loaded: + * none + +3. Parameters + + 3.1. interval (int) + 3.2. items (int) + +3.1. interval (int) + + Timer interval when to record the value for statistics. + + Default value is 900 (15min). + + Example 1.1. Set interval parameter +... +modparam("statsc", "interval", 300) +... + +3.2. items (int) + + How many items to store for each statistic. + + Default value is 100. + + Example 1.2. Set items parameter +... +modparam("statsc", "items", 200) +... + +4. RPC Commands + + 4.1. statsc.exec + +4.1. statsc.exec + + Get the report of recorded statistics. + + Name: statsc.exec + + Parameters: + * _report_ : + + "list": list recorded values + + "diff": show diff between recorded values + * _name_: (optional) name of the statistic to show the report for. If + missing or set to 'all', then the reports for all recorded + statistics are done. + * _limit_: (optional) how many items to be included in the report + + Example: +... +# prototype: kamcmd statsc.exec _report_ _name_ _limit_ +kamcmd statsc list +kamcmd statsc list all 10 +kamcmd statsc diff +... diff --git a/modules/statsc/doc/Makefile b/modules/statsc/doc/Makefile new file mode 100644 index 00000000000..3c945bf602f --- /dev/null +++ b/modules/statsc/doc/Makefile @@ -0,0 +1,4 @@ +docs = statsc.xml + +docbook_dir = ../../../docbook +include $(docbook_dir)/Makefile.module diff --git a/modules/statsc/doc/statsc.xml b/modules/statsc/doc/statsc.xml new file mode 100644 index 00000000000..c8ec7863ee1 --- /dev/null +++ b/modules/statsc/doc/statsc.xml @@ -0,0 +1,37 @@ + + + +%docentities; + +]> + + + + STATSC Module + kamailio.org + + + Daniel-Constantin + Mierla + miconda@gmail.com + + + Daniel-Constantin + Mierla + miconda@gmail.com + + + + 2016 + asipto.com + + + + + + + + diff --git a/modules/statsc/doc/statsc_admin.xml b/modules/statsc/doc/statsc_admin.xml new file mode 100644 index 00000000000..7b785a8ce97 --- /dev/null +++ b/modules/statsc/doc/statsc_admin.xml @@ -0,0 +1,139 @@ + + + +%docentities; + +]> + + + + + &adminguide; + +
+ Overview + + This module provides a statistics collector engine. It can + track the values of various internal &kamailio; statistics + for a specific period of time, allowing to retrieve them + or a report over them via RPC commands. + +
+ +
+ Dependencies +
+ &kamailio; Modules + + The following modules must be loaded along this module: + + + + various - for getting access + to the statistics exported by these modules. + + + + +
+
+ External Libraries or Applications + + The following libraries or applications must be installed before running + &kamailio; with this module loaded: + + + + none + + + + +
+
+ +
+ Parameters +
+ <varname>interval</varname> (int) + + Timer interval when to record the value for statistics. + + + + Default value is 900 (15min). + + + + Set <varname>interval</varname> parameter + +... +modparam("statsc", "interval", 300) +... + + +
+
+ <varname>items</varname> (int) + + How many items to store for each statistic. + + + + Default value is 100. + + + + Set <varname>items</varname> parameter + +... +modparam("statsc", "items", 200) +... + + +
+
+ +
+ RPC Commands +
+ + <function moreinfo="none">statsc.exec</function> + + + Get the report of recorded statistics. + + + Name: statsc.exec + + Parameters: + + _report_ : + + list: list recorded values + diff: show diff between recorded values + + + + _name_: (optional) name of the statistic to show the report for. + If missing or set to 'all', then the reports for all recorded statistics are done. + + _limit_: (optional) how many items to be included in the report + + + Example: + + +... +# prototype: &kamcmd; statsc.exec _report_ _name_ _limit_ +&kamcmd; statsc list +&kamcmd; statsc list all 10 +&kamcmd; statsc diff +... + +
+
+
diff --git a/modules/statsc/statsc_mod.c b/modules/statsc/statsc_mod.c new file mode 100644 index 00000000000..8e15499056d --- /dev/null +++ b/modules/statsc/statsc_mod.c @@ -0,0 +1,431 @@ +/** + * Copyright (C) 2016 Daniel-Constantin Mierla (asipto.com) + * + * This file is part of Kamailio, a free SIP server. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include + +#include "../../sr_module.h" +#include "../../dprint.h" +#include "../../timer_proc.h" +#include "../../rpc.h" +#include "../../rpc_lookup.h" + +#include "../../lib/kcore/statistics.h" + +MODULE_VERSION + +int statsc_init(void); +void statsc_timer(unsigned int ticks, void *param); +int statsc_init_rpc(void); + +static int statsc_interval = 540; /* 15 min */ +static int statsc_items = 100; /* history items */ + +static int mod_init(void); +static int child_init(int); +static void mod_destroy(void); + +static int w_statsc_reset(sip_msg_t* msg, char* p1, char* p2); + +static cmd_export_t cmds[]={ + {"statsc_reset", (cmd_function)w_statsc_reset, 0, + 0, 0, ANY_ROUTE}, + {0, 0, 0, 0, 0, 0} +}; + +static param_export_t params[]={ + {"interval", INT_PARAM, &statsc_interval}, + {"items", INT_PARAM, &statsc_items}, + {0, 0, 0} +}; + +struct module_exports exports = { + "statsc", + DEFAULT_DLFLAGS, /* dlopen flags */ + cmds, + params, + 0, + 0, /* exported MI functions */ + 0, /* exported pseudo-variables */ + 0, /* extra processes */ + mod_init, /* module initialization function */ + 0, /* response function */ + mod_destroy, /* destroy function */ + child_init /* per child init function */ +}; + + +/** + * @brief Initialize statsc module function + */ +static int mod_init(void) +{ + if(statsc_init_rpc()<0) { + LM_ERR("failed to register rpc commands\n"); + return -1; + } + if(sr_wtimer_add(statsc_timer, 0, statsc_interval)<0) { + LM_ERR("failed to register timer routine\n"); + return -1; + } + if(statsc_init()<0) { + LM_ERR("failed to initialize the stats collector structure\n"); + return -1; + } + return 0; +} + +/** + * @brief Initialize statsc module children + */ +static int child_init(int rank) +{ + return 0; +} + +/** + * destroy module function + */ +static void mod_destroy(void) +{ + return; +} + +/** + * + */ +static int w_statsc_reset(sip_msg_t* msg, char* p1, char* p2) +{ + return 1; +} + +typedef int (*statsc_func_t)(void *p, int64_t *res); + +typedef struct statsc_nmap { + str sname; + int sindex; + statsc_func_t f; + void *p; +} statsc_nmap_t; + + +int statsc_timestamp(void *p, int64_t *res) +{ + return 0; +} + +int statsc_svalue(void *p, int64_t *res) +{ + stat_var *stat; + str name; + + name.s = (char*)p; + name.len = strlen(name.s); + + stat = get_stat(&name); + if(stat==NULL) { + LM_ERR("statistic %.*s not found\n", name.len, name.s); + return -1; + } + + *res = (int64_t)get_stat_val(stat); + + return 0; +} + +static statsc_nmap_t _statsc_nmap[] = { + { {"timestamp", 9}, 0, statsc_timestamp, (void*)0}, + { {"shm.free", 8}, 1, statsc_svalue, (void*)"free_size"}, /* shmem:free_size */ + { {"shm.used", 8}, 2, statsc_svalue, (void*)"used_size"}, + { {"shm.real_used", 13}, 3, statsc_svalue, (void*)"real_used_size"}, + { {0, 0}, 0, 0} +}; + + +int statsc_nmap_index(str *sn) +{ + int i; + + for(i=0; _statsc_nmap[i].sname.s!=0; i++) { + if(sn->len==_statsc_nmap[i].sname.len + && strncmp(sn->s, _statsc_nmap[i].sname.s, sn->len)==0) { + return i; + } + } + return -1; +} + +typedef struct _statsc_info { + uint64_t steps; + uint32_t slots; + int64_t **stable; +} statsc_info_t; + + +static statsc_info_t *_statsc_info = NULL; + +int statsc_init(void) +{ + int i; + + _statsc_info = shm_malloc(sizeof(statsc_info_t)); + if(_statsc_info==NULL) { + LM_ERR("no more shared memory\n"); + return -1; + } + memset(_statsc_info, 0, sizeof(statsc_info_t)); + + for(i=0; _statsc_nmap[i].sname.s!=0; i++); + _statsc_info->slots = i; + + _statsc_info->stable = shm_malloc(_statsc_info->slots * sizeof(int64_t*)); + if(_statsc_info->stable==NULL) { + LM_ERR("no more shared memory\n"); + shm_free(_statsc_info); + _statsc_info=NULL; + return -1; + } + memset(_statsc_info->stable, 0, _statsc_info->slots * sizeof(int64_t*)); + for(i=0; i<_statsc_info->slots; i++) { + _statsc_info->stable[i] = shm_malloc(statsc_items * sizeof(int64_t)); + if(_statsc_info->stable[i]==NULL) { + LM_ERR("no more shared memory\n"); + i--; + while(i>=0) { + shm_free(_statsc_info->stable[i]); + i--; + } + shm_free(_statsc_info->stable); + shm_free(_statsc_info); + return -1; + } + memset(_statsc_info->stable[i], 0, statsc_items * sizeof(int64_t)); + } + + return 0; +} + + +void statsc_timer(unsigned int ticks, void *param) +{ + time_t tn; + int i; + int n; + + if(_statsc_info==NULL) { + LM_ERR("statsc not initialized\n"); + return; + } + + tn = time(NULL); + n = _statsc_info->steps % statsc_items; + _statsc_info->stable[0][n] = (int64_t)tn; + + LM_DBG("statsc timer - time: %lu - ticks: %u - index: %d - steps: %llu\n", + (unsigned long)tn, ticks, n, _statsc_info->steps); + + for(i=1; i<_statsc_info->slots; i++) { + _statsc_nmap[i].f(_statsc_nmap[i].p, _statsc_info->stable[i] + n); + } + _statsc_info->steps++; +} + + +/** + * + */ +static const char* statsc_rpc_exec_doc[2] = { + "Statistics collector control command", + 0 +}; + +/** + * + */ +static void statsc_rpc_exec(rpc_t* rpc, void* ctx) +{ + str cname; + int cmode; + str sname; + int range; + int sidx; + int i, k, n, r, m, v; + time_t tn; + void* th; + void* ts; + void* ti; + void* ta; + void* td; + + if(_statsc_info==NULL) { + rpc->fault(ctx, 500, "Statistics collector not initialized"); + return; + } + if(_statsc_info->steps==0) { + rpc->fault(ctx, 500, "Nothing collected yet - try later"); + return; + } + n = (_statsc_info->steps - 1) % statsc_items; + + cmode = 0; + if(rpc->scan(ctx, "S", &cname) != 1) { + rpc->fault(ctx, 500, "Missing command parameter"); + return; + } + + if(cname.len==4 && strncmp(cname.s, "list", 4)==0) { + cmode = 1; + } else if(cname.len==4 && strncmp(cname.s, "diff", 4)==0) { + cmode = 2; + } else { + rpc->fault(ctx, 500, "Invalid command"); + return; + } + + range = 0; + sidx = -1; + if(rpc->scan(ctx, "*S", &sname) != 1) { + sname.len = 0; + sname.s = NULL; + } else { + if(sname.len!=3 || strncmp(sname.s, "all", 3)!=0) { + if((sidx = statsc_nmap_index(&sname))<0) { + rpc->fault(ctx, 500, "Invalid statistic name"); + return; + } + } + rpc->scan(ctx, "*d", &range); + if(range<0 || range>statsc_items) + range = 0; + } + + tn = time(NULL); + if(rpc->add(ctx, "{", &th) < 0) { + rpc->fault(ctx, 500, "Error creating rpc (1)"); + return; + } + if(rpc->struct_add(th, "u[", + "timestamp", (unsigned int)tn, + "stats", &ts )<0) { + rpc->fault(ctx, 500, "Error creating rpc (2)"); + return; + } + for(i=1; i<_statsc_info->slots; i++) { + if(sidx==-1 || sidx==i) { + if(rpc->array_add(ts, "{", &ta)<0) { + rpc->fault(ctx, 500, "Error creating rpc (3)"); + return; + } + if(rpc->struct_add(ta, "S[", + "name", &_statsc_nmap[i].sname, + "data", &td )<0) { + rpc->fault(ctx, 500, "Error creating rpc (4)"); + return; + } + m = 0; + r = range; + for(k=n; k>=0; k--) { + if(rpc->array_add(td, "{", &ti)<0) { + rpc->fault(ctx, 500, "Error creating rpc (5)"); + return; + } + v = (int)_statsc_info->stable[i][k]; + switch(cmode) { + case 1: + break; + case 2: + if((n==statsc_items-1) && k==0) { + continue; + } + if(k==0) { + v -= (int)_statsc_info->stable[i][statsc_items-1]; + } else { + v -= (int)_statsc_info->stable[i][k-1]; + } + break; + } + if(rpc->struct_add(ti, "udd", + "timestamp", (unsigned int)_statsc_info->stable[0][k], + "value", v, + "index", m++)<0) { + rpc->fault(ctx, 500, "Error creating rpc (6)"); + return; + } + if(range>0 && m>=range) { + break; + } + } + for(k=statsc_items-1; k>n; k--) { + if(rpc->array_add(td, "{", &ti)<0) { + rpc->fault(ctx, 500, "Error creating rpc (7)"); + return; + } + v = (int)_statsc_info->stable[i][k]; + switch(cmode) { + case 1: + break; + case 2: + if(n==k-1) { + continue; + } + v -= (int)_statsc_info->stable[i][k-1]; + break; + } + if(rpc->struct_add(ti, "udd", + "timestamp", (unsigned int)_statsc_info->stable[0][k], + "value", v, + "index", m++)<0) { + rpc->fault(ctx, 500, "Error creating rpc (8)"); + return; + } + if(range>0 && m>=range) { + break; + } + } + } + } +} + +/** + * + */ +rpc_export_t statsc_rpc[] = { + {"statsc.exec", statsc_rpc_exec, statsc_rpc_exec_doc, 0}, + {0, 0, 0, 0} +}; + +/** + * + */ +int statsc_init_rpc(void) +{ + if (rpc_register_array(statsc_rpc)!=0) + { + LM_ERR("failed to register RPC commands\n"); + return -1; + } + return 0; +} +