mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
477 lines
10 KiB
477 lines
10 KiB
/**
|
|
* 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 <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#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;
|
|
}
|
|
|