/** * 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 "../../parser/parse_param.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 */ int statsc_track_param(modparam_t type, void* val); 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[]={ {"track", PARAM_STRING|USE_FUNC_PARAM, (void*)statsc_track_param}, {"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; str rname; int64_t *vals; struct statsc_nmap *next; } statsc_nmap_t; int statsc_svalue(str *name, int64_t *res) { stat_var *stat; 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_default[] = { { str_init("shm.free"), str_init("free_size"), 0, 0}, /* shmem:free_size */ { str_init("shm.used"), str_init("used_size"), 0, 0}, { str_init("shm.real_used"), str_init("real_used_size"), 0, 0}, { {0, 0}, {0, 0}, 0, 0} }; typedef struct _statsc_info { uint64_t steps; uint32_t slots; statsc_nmap_t *slist; } statsc_info_t; static statsc_info_t *_statsc_info = NULL; int statsc_nmap_add(str *sname, str *rname) { int sz; statsc_nmap_t *sm = NULL; statsc_nmap_t *sl = NULL; if(_statsc_info==NULL) { return -1; } sz = sizeof(statsc_nmap_t) + statsc_items * sizeof(int64_t) + sname->len + rname->len + 4; sm = shm_malloc(sz); if(sm==NULL) { LM_ERR("no more shared memory\n"); return -1; } memset(sm, 0, sz); sm->sname.s = (char*)((char*)sm + sizeof(statsc_nmap_t)); sm->sname.len = sname->len; sm->rname.s = (char*)((char*)sm->sname.s + sm->sname.len + 1); sm->rname.len = rname->len; sm->vals = (int64_t*)((char*)sm->rname.s + sm->rname.len + 1); memcpy(sm->sname.s, sname->s, sname->len); memcpy(sm->rname.s, rname->s, rname->len); if(_statsc_info->slist==NULL) { _statsc_info->slist = sm; _statsc_info->slots = 1; return 0; } sl = _statsc_info->slist; while(sl->next!=NULL) sl = sl->next; sl->next = sm; _statsc_info->slots++; return 0; } int statsc_init(void) { int i; int sz; statsc_nmap_t *sm = NULL; if(_statsc_info!=NULL) { return 0; } _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)); /* first slot with timestamps */ sz = sizeof(statsc_nmap_t) + statsc_items * sizeof(int64_t); sm = shm_malloc(sz); if(sm==NULL) { LM_ERR("no more shared memory\n"); return -1; } memset(sm, 0, sz); sm->vals = (int64_t*)((char*)sm + sizeof(statsc_nmap_t)); _statsc_info->slist = sm; _statsc_info->slots = 1; for(i=0; _statsc_nmap_default[i].sname.s!=0; i++) { if(statsc_nmap_add(&_statsc_nmap_default[i].sname, &_statsc_nmap_default[i].rname)<0) { LM_ERR("cannot enable tracking default statistics\n"); return -1; } } return 0; } void statsc_timer(unsigned int ticks, void *param) { statsc_nmap_t *sm = NULL; time_t tn; int n; if(_statsc_info==NULL || _statsc_info->slist==NULL) { LM_ERR("statsc not initialized\n"); return; } tn = time(NULL); n = _statsc_info->steps % statsc_items; _statsc_info->slist->vals[n] = (int64_t)tn; LM_DBG("statsc timer - time: %lu - ticks: %u - index: %d - steps: %llu\n", (unsigned long)tn, ticks, n, (unsigned long long)_statsc_info->steps); for(sm=_statsc_info->slist->next; sm!=NULL; sm=sm->next) { statsc_svalue(&sm->rname, sm->vals + n); } _statsc_info->steps++; } /** * */ int statsc_track_param(modparam_t type, void* val) { param_t* params_list = NULL; param_hooks_t phooks; param_t *pit=NULL; str s; if(val==NULL) return -1; if(statsc_init()<0) return -1; s.s = (char*)val; s.len = strlen(s.s); if(s.s[s.len-1]==';') s.len--; if (parse_params(&s, CLASS_ANY, &phooks, ¶ms_list)<0) return -1; for (pit = params_list; pit; pit=pit->next) { if(statsc_nmap_add(&pit->name, &pit->body)<0) { LM_ERR("cannot enable tracking statistics\n"); return -1; } } free_params(params_list); return 0; } /** * */ static const char* statsc_rpc_report_doc[2] = { "Statistics collector control command", 0 }; /** * */ static void statsc_rpc_report(rpc_t* rpc, void* ctx) { statsc_nmap_t *sm = NULL; str cname; int cmode; str sname; int range; int k, m, n, v; time_t tn; void* th; void* ts; void* ti; void* ta; void* td; if(_statsc_info==NULL || _statsc_info->slist==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; if(rpc->scan(ctx, "*S", &sname) != 1) { sname.len = 0; sname.s = NULL; } else { if(sname.len==3 && strncmp(sname.s, "all", 3)==0) { sname.len = 0; sname.s = NULL; } 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(sm=_statsc_info->slist->next; sm!=NULL; sm=sm->next) { if(sname.s==NULL || (sname.len == sm->sname.len && strncmp(sname.s, sm->sname.s, sname.len)==0)) { if(rpc->array_add(ts, "{", &ta)<0) { rpc->fault(ctx, 500, "Error creating rpc (3)"); return; } if(rpc->struct_add(ta, "S[", "name", &sm->sname, "data", &td )<0) { rpc->fault(ctx, 500, "Error creating rpc (4)"); return; } m = 0; for(k=n; k>=0; k--) { if(rpc->array_add(td, "{", &ti)<0) { rpc->fault(ctx, 500, "Error creating rpc (5)"); return; } v = (int)sm->vals[k]; switch(cmode) { case 1: break; case 2: if((n==statsc_items-1) && k==0) { continue; } if(k==0) { v -= (int)sm->vals[statsc_items-1]; } else { v -= (int)sm->vals[k-1]; } break; } if(rpc->struct_add(ti, "udd", "timestamp", (unsigned int)_statsc_info->slist->vals[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)sm->vals[k]; switch(cmode) { case 1: break; case 2: if(n==k-1) { continue; } v -= (int)sm->vals[k-1]; break; } if(rpc->struct_add(ti, "udd", "timestamp", (unsigned int)_statsc_info->slist->vals[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.report", statsc_rpc_report, statsc_rpc_report_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; }