benchmark.c

Go to the documentation of this file.
00001 /*
00002  * $Id: benchmark.c 941 2007-04-11 12:37:21Z bastian $
00003  *
00004  * Benchmarking module for Kamailio
00005  *
00006  * Copyright (C) 2007 Collax GmbH
00007  *                    (Bastian Friedrich <bastian.friedrich@collax.com>)
00008  * Copyright (C) 2007 Voice Sistem SRL
00009  *
00010  * This file is part of Kamailio, a free SIP server.
00011  *
00012  * Kamailio is free software; you can redistribute it and/or modify
00013  * it under the terms of the GNU General Public License as published by
00014  * the Free Software Foundation; either version 2 of the License, or
00015  * (at your option) any later version
00016  *
00017  * Kamailio is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  * GNU General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU General Public License
00023  * along with this program; if not, write to the Free Software
00024  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025  *
00026  */
00027 
00043 #define _GNU_SOURCE
00044 #include <string.h>
00045 #include <sys/time.h>
00046 #include <stdlib.h>
00047 
00048 #include "../../sr_module.h"
00049 #include "../../lib/kmi/mi.h"
00050 #include "../../mem/mem.h"
00051 #include "../../ut.h"
00052 
00053 #include "benchmark.h"
00054 
00055 #include "../../mem/shm_mem.h"
00056 
00057 
00058 MODULE_VERSION
00059 
00060 /* Exported functions */
00061 int bm_start_timer(struct sip_msg* _msg, char* timer, char *foobar);
00062 int bm_log_timer(struct sip_msg* _msg, char* timer, char* mystr);
00063 
00064 /*
00065  * Module destroy function prototype
00066  */
00067 static void destroy(void);
00068 
00069 
00070 /*
00071  * Module initialization function prototype
00072  */
00073 static int mod_init(void);
00074 
00075 
00076 /*
00077  * Exported parameters
00078  * Copied to mycfg on module initialization
00079  */
00080 static int bm_enable_global = 0;
00081 static int bm_granularity = 1;
00082 static int bm_loglevel = L_INFO;
00083 
00084 static int _bm_last_time_diff = 0;
00085 
00086 /*
00087  * Module setup
00088  */
00089 
00090 typedef struct bm_cfg {
00091         int enable_global;
00092         int granularity;
00093         int loglevel;
00094         /* The internal timers */
00095         int nrtimers;
00096         benchmark_timer_t *timers;
00097         benchmark_timer_t **tindex;
00098 } bm_cfg_t;
00099 
00100 /*
00101  * The setup is located in shared memory so that
00102  * all instances can access this variable
00103  */
00104 
00105 bm_cfg_t *bm_mycfg = 0;
00106 
00107 static inline int fixup_bm_timer(void** param, int param_no);
00108 
00109 /*
00110  * Exported functions
00111  */
00112 static cmd_export_t cmds[] = {
00113         { "bm_start_timer", (cmd_function)bm_start_timer, 1, fixup_bm_timer, 0,
00114                 REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
00115         { "bm_log_timer",   (cmd_function)bm_log_timer, 1, fixup_bm_timer, 0,
00116                 REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
00117         {"load_bm",         (cmd_function)load_bm, 0, 0, 0, 0},
00118         { 0, 0, 0, 0, 0, 0 }
00119 };
00120 
00121 
00122 /*
00123  * Exported parameters
00124  */
00125 static param_export_t params[] = {
00126         {"enable",      INT_PARAM, &bm_enable_global},
00127         {"granularity", INT_PARAM, &bm_granularity},
00128         {"loglevel",    INT_PARAM, &bm_loglevel},
00129         { 0, 0, 0 }
00130 };
00131 
00132 
00133 /*
00134  * Exported MI functions
00135  */
00136 struct mi_root* mi_bm_enable_global(struct mi_root *cmd, void *param);
00137 struct mi_root* mi_bm_enable_timer(struct mi_root *cmd, void *param);
00138 struct mi_root* mi_bm_granularity(struct mi_root *cmd, void *param);
00139 struct mi_root* mi_bm_loglevel(struct mi_root *cmd, void *param);
00140 
00141 static mi_export_t mi_cmds[] = {
00142         { "bm_enable_global", mi_bm_enable_global,  0,  0,  0  },
00143         { "bm_enable_timer",  mi_bm_enable_timer,   0,  0,  0  },
00144         { "bm_granularity",   mi_bm_granularity,    0,  0,  0  },
00145         { "bm_loglevel",      mi_bm_loglevel,       0,  0,  0  },
00146         { 0, 0, 0, 0, 0}
00147 };
00148 
00149 static int bm_get_time_diff(struct sip_msg *msg, pv_param_t *param, 
00150                 pv_value_t *res);
00151 
00152 static pv_export_t mod_items[] = {
00153         { {"BM_time_diff", sizeof("BM_time_diff")-1}, PVT_OTHER, bm_get_time_diff, 0,
00154                 0, 0, 0, 0 },
00155         { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
00156 };
00157 
00158 /*
00159  * Module interface
00160  */
00161 struct module_exports exports = {
00162         "benchmark", 
00163         DEFAULT_DLFLAGS,
00164         cmds,       /* Exported functions */
00165         params,     /* Exported parameters */
00166         0,          /* exported statistics */
00167         mi_cmds,    /* exported MI functions */
00168         mod_items,  /* exported pseudo-variables */
00169         0,          /* extra processes */
00170         mod_init,   /* module initialization function */
00171         0,          /* response function */
00172         destroy,    /* destroy function */
00173         0           /* child initialization function */
00174 };
00175 
00176 
00177 /****************/
00178 
00179 
00180 /*
00181  * mod_init
00182  * Called by openser at init time
00183  */
00184 static int mod_init(void) {
00185         if(register_mi_mod(exports.name, mi_cmds)!=0)
00186         {
00187                 LM_ERR("failed to register MI commands\n");
00188                 return -1;
00189         }
00190 
00191         bm_mycfg = (bm_cfg_t*)shm_malloc(sizeof(bm_cfg_t));
00192         memset(bm_mycfg, 0, sizeof(bm_cfg_t));
00193         bm_mycfg->enable_global = bm_enable_global;
00194         bm_mycfg->granularity   = bm_granularity;
00195         bm_mycfg->loglevel      = bm_loglevel;
00196 
00197         return 0;
00198 }
00199 
00200 
00201 /*
00202  * destroy
00203  * called by openser at exit time
00204  */
00205 static void destroy(void)
00206 {
00207         benchmark_timer_t *bmt = 0;
00208         benchmark_timer_t *bmp = 0;
00209 
00210         if(bm_mycfg!=NULL) 
00211         {
00212                 /* free timers list */
00213                 bmt = bm_mycfg->timers;
00214                 while(bmt)
00215                 {
00216                         bmp = bmt;
00217                         bmt = bmt->next;
00218                         shm_free(bmp);
00219                 }
00220                 if(bm_mycfg->tindex) shm_free(bm_mycfg->tindex);
00221                 shm_free(bm_mycfg);
00222         }
00223 }
00224 
00225 void bm_reset_timer(int i)
00226 {
00227         if(bm_mycfg==NULL || bm_mycfg->tindex[i]==NULL)
00228                 return;
00229         bm_mycfg->tindex[i]->calls = 0;
00230         bm_mycfg->tindex[i]->sum = 0;
00231         bm_mycfg->tindex[i]->last_max = 0;
00232         bm_mycfg->tindex[i]->last_min = 0xffffffff;
00233         bm_mycfg->tindex[i]->last_sum = 0;
00234         bm_mycfg->tindex[i]->global_max = 0;
00235         bm_mycfg->tindex[i]->global_min = 0xffffffff;
00236 }
00237 
00238 void reset_timers(void)
00239 {
00240         int i;
00241         if(bm_mycfg==NULL)
00242                 return;
00243 
00244         for (i = 0; i < bm_mycfg->nrtimers; i++)
00245                 bm_reset_timer(i);
00246 }
00247 
00257 inline int timer_active(unsigned int id)
00258 {
00259         if (bm_mycfg->enable_global > 0 || bm_mycfg->timers[id].enabled > 0)
00260                 return 1;
00261         else
00262                 return 0;
00263 }
00264 
00265 
00270 int _bm_start_timer(unsigned int id)
00271 {
00272         if (timer_active(id))
00273         {
00274                 if(bm_get_time(bm_mycfg->tindex[id]->start)!=0)
00275                 {
00276                         LM_ERR("error getting current time\n");
00277                         return -1;
00278                 }
00279         }
00280 
00281         return 1;
00282 }
00283 
00284 int bm_start_timer(struct sip_msg* _msg, char* timer, char *foobar)
00285 {
00286         return _bm_start_timer((unsigned int)(unsigned long)timer);
00287 }
00288 
00289 
00294 int _bm_log_timer(unsigned int id)
00295 {
00296         /* BM_CLOCK_REALTIME */
00297         bm_timeval_t now;
00298         unsigned long long tdiff;
00299 
00300         if (!timer_active(id))
00301                 return 1;
00302 
00303         if(bm_get_time(&now)<0)
00304         {
00305                 LM_ERR("error getting current time\n");
00306                 return -1;
00307         }
00308         
00309         tdiff = bm_diff_time(bm_mycfg->tindex[id]->start, &now);
00310         _bm_last_time_diff = (int)tdiff;
00311 
00312         /* What to do
00313          * - update min, max, sum
00314          * - if granularity hit: Log, reset min/max
00315          */
00316 
00317         bm_mycfg->tindex[id]->sum += tdiff;
00318         bm_mycfg->tindex[id]->last_sum += tdiff;
00319         bm_mycfg->tindex[id]->calls++;
00320         
00321         if (tdiff < bm_mycfg->tindex[id]->last_min)
00322                 bm_mycfg->tindex[id]->last_min = tdiff;
00323 
00324         if (tdiff > bm_mycfg->tindex[id]->last_max)
00325                 bm_mycfg->tindex[id]->last_max = tdiff;
00326 
00327         if (tdiff < bm_mycfg->tindex[id]->global_min)
00328                 bm_mycfg->tindex[id]->global_min = tdiff;
00329 
00330         if (tdiff > bm_mycfg->tindex[id]->global_max)
00331                 bm_mycfg->tindex[id]->global_max = tdiff;
00332 
00333 
00334         if ((bm_mycfg->tindex[id]->calls % bm_mycfg->granularity) == 0)
00335         {
00336                 LM_GEN1(bm_mycfg->loglevel, "benchmark (timer %s [%d]): %llu ["
00337                         " msgs/total/min/max/avg - LR:"
00338                         " %i/%llu/%llu/%llu/%f | GB: %llu/%llu/%llu/%llu/%f]\n",
00339                         bm_mycfg->tindex[id]->name,
00340                         id,
00341                         tdiff,
00342                         bm_mycfg->granularity,
00343                         bm_mycfg->tindex[id]->last_sum,
00344                         bm_mycfg->tindex[id]->last_min,
00345                         bm_mycfg->tindex[id]->last_max,
00346                         ((double)bm_mycfg->tindex[id]->last_sum)/bm_mycfg->granularity,
00347                         bm_mycfg->tindex[id]->calls,
00348                         bm_mycfg->tindex[id]->sum,
00349                         bm_mycfg->tindex[id]->global_min,
00350                         bm_mycfg->tindex[id]->global_max,
00351                         ((double)bm_mycfg->tindex[id]->sum)/bm_mycfg->tindex[id]->calls);
00352 
00353                 bm_mycfg->tindex[id]->last_sum = 0;
00354                 bm_mycfg->tindex[id]->last_max = 0;
00355                 bm_mycfg->tindex[id]->last_min = 0xffffffff;
00356         }
00357 
00358         return 1;
00359 }       
00360 
00361 int bm_log_timer(struct sip_msg* _msg, char* timer, char* mystr)
00362 {
00363         return _bm_log_timer((unsigned int)(unsigned long)timer);
00364 }
00365 
00366 int _bm_register_timer(char *tname, int mode, unsigned int *id)
00367 {
00368         benchmark_timer_t *bmt = 0;
00369         benchmark_timer_t **tidx = 0;
00370 
00371         if(tname==NULL || id==NULL || bm_mycfg==NULL || strlen(tname)==0
00372                         || strlen(tname)>BM_NAME_LEN-1)
00373                 return -1;
00374 
00375         bmt = bm_mycfg->timers;
00376         while(bmt)
00377         {
00378                 if(strcmp(bmt->name, tname)==0)
00379                 {
00380                         *id = bmt->id;
00381                         return 0;
00382                 }
00383                 bmt = bmt->next;
00384         }
00385         if(mode==0)
00386                 return -1;
00387 
00388         bmt = (benchmark_timer_t*)shm_malloc(sizeof(benchmark_timer_t));
00389 
00390         if(bmt==0)
00391         {
00392                 LM_ERR("no more shm\n");
00393                 return -1;
00394         }
00395         memset(bmt, 0, sizeof(benchmark_timer_t));
00396 
00397         /* private memory, otherwise we have races */
00398         bmt->start = (bm_timeval_t*)pkg_malloc(sizeof(bm_timeval_t)); 
00399         if(bmt->start == NULL)
00400         {
00401                 shm_free(bmt);
00402                 LM_ERR("no more pkg\n");
00403                 return -1;
00404         }
00405         memset(bmt->start, 0, sizeof(bm_timeval_t)); 
00406 
00407         strcpy(bmt->name, tname);
00408         if(bm_mycfg->timers==0)
00409         {
00410                 bmt->id = 0;
00411                 bm_mycfg->timers = bmt;
00412         } else {
00413                 bmt->id = bm_mycfg->timers->id+1;
00414                 bmt->next = bm_mycfg->timers;
00415                 bm_mycfg->timers = bmt;
00416         }
00417 
00418         /* do the indexing */
00419         if(bmt->id%10==0)
00420         {
00421                 if(bm_mycfg->tindex!=NULL)
00422                         tidx = bm_mycfg->tindex;
00423                 bm_mycfg->tindex = (benchmark_timer_t**)shm_malloc((10+bmt->id)*
00424                                                                 sizeof(benchmark_timer_t*));
00425                 if(bm_mycfg->tindex==0)
00426                 {
00427                         LM_ERR("no more share memory\n");
00428                         if(tidx!=0)
00429                                 shm_free(tidx);
00430                         return -1;
00431                 }
00432                 memset(bm_mycfg->tindex, 0, (10+bmt->id)*sizeof(benchmark_timer_t*));
00433                 if(tidx!=0)
00434                 {
00435                         memcpy(bm_mycfg->tindex, tidx, bmt->id*sizeof(benchmark_timer_t*));
00436                         shm_free(tidx);
00437                 }
00438         }
00439         bm_mycfg->tindex[bmt->id] = bmt;
00440         bm_mycfg->nrtimers = bmt->id + 1;
00441         bm_reset_timer(bmt->id);
00442         *id = bmt->id;
00443         LM_DBG("timer [%s] added with index <%u>\n", bmt->name, bmt->id);
00444 
00445         return 0;
00446 }
00447 
00449 int load_bm( struct bm_binds *bmb)
00450 {
00451         if(bmb==NULL)
00452                 return -1;
00453 
00454         bmb->bm_register = _bm_register_timer;
00455         bmb->bm_start    = _bm_start_timer;
00456         bmb->bm_log          = _bm_log_timer;
00457 
00458         return 1;
00459 }
00460 
00461 
00462 static inline char * pkg_strndup( char* _p, int _len)
00463 {
00464         char *s;
00465 
00466         s = (char*)pkg_malloc(_len+1);
00467         if (s==NULL)
00468                 return NULL;
00469         memcpy(s,_p,_len);
00470         s[_len] = 0;
00471         return s;
00472 }
00473 
00474 
00476 
00481 struct mi_root* mi_bm_enable_global(struct mi_root *cmd, void *param)
00482 {
00483         struct mi_node *node;
00484 
00485         char *p1, *e1;
00486         long int v1;
00487 
00488         node = cmd->node.kids;
00489 
00490         if ((node == NULL) || (node->next != NULL))
00491                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00492 
00493         p1 = pkg_strndup(node->value.s, node->value.len);
00494 
00495         v1 = strtol(p1, &e1, 0);
00496 
00497         if ((*e1 != '\0') || (*p1 == '\0')) {
00498                 pkg_free(p1);
00499                 return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00500         }
00501 
00502         if ((v1 < -1) || (v1 > 1)) {
00503                 pkg_free(p1);
00504                 return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00505         }
00506 
00507         bm_mycfg->enable_global = v1;
00508 
00509         pkg_free(p1);
00510         return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00511 }
00512 
00513 struct mi_root* mi_bm_enable_timer(struct mi_root *cmd, void *param)
00514 {
00515         struct mi_node *node;
00516 
00517         char *p1, *p2, *e2;
00518         long int v2;
00519         unsigned int id;
00520 
00521         node = cmd->node.kids;
00522 
00523         if ((node == NULL) || (node->next == NULL) || (node->next->next != NULL))
00524                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00525 
00526         p1 = pkg_strndup(node->value.s, node->value.len);
00527 
00528         if(_bm_register_timer(p1, 0, &id)!=0)
00529         {
00530                 pkg_free(p1);
00531                 return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00532         }
00533         p2 = pkg_strndup(node->next->value.s, node->next->value.len);
00534         v2 = strtol(p2, &e2, 0);
00535         
00536         pkg_free(p1);
00537         pkg_free(p2);
00538 
00539         if (*e2 != '\0' || *p2 == '\0')
00540                 return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00541 
00542         if ((v2 < 0) || (v2 > 1))
00543                 return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00544 
00545         bm_mycfg->timers[id].enabled = v2;
00546 
00547         return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00548 }
00549 
00550 struct mi_root* mi_bm_granularity(struct mi_root *cmd, void *param)
00551 {
00552         struct mi_node *node;
00553 
00554         char *p1, *e1;
00555         long int v1;
00556 
00557         node = cmd->node.kids;
00558 
00559         if ((node == NULL) || (node->next != NULL))
00560                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00561 
00562         p1 = pkg_strndup(node->value.s, node->value.len);
00563 
00564         v1 = strtol(p1, &e1, 0);
00565 
00566         pkg_free(p1);
00567 
00568         if ((*e1 != '\0') || (*p1 == '\0'))
00569                 return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00570 
00571         if (v1 < 1)
00572                 return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00573 
00574         bm_mycfg->granularity = v1;
00575 
00576         return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00577 }
00578 
00579 struct mi_root* mi_bm_loglevel(struct mi_root *cmd, void *param)
00580 {
00581         struct mi_node *node;
00582 
00583         char *p1, *e1;
00584         long int v1;
00585 
00586         node = cmd->node.kids;
00587 
00588         if ((node == NULL) || (node->next != NULL))
00589                 return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00590 
00591         p1 = pkg_strndup(node->value.s, node->value.len);
00592 
00593         v1 = strtol(p1, &e1, 0);
00594         
00595         pkg_free(p1);
00596 
00597         if ((*e1 != '\0') || (*p1 == '\0'))
00598                 return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00599 
00600         if ((v1 < -3) || (v1 > 4)) /* Maximum log levels */
00601                 return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00602 
00603         bm_mycfg->enable_global = v1;
00604 
00605         return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00606 }
00610 static int bm_get_time_diff(struct sip_msg *msg, pv_param_t *param,
00611                 pv_value_t *res)
00612 {
00613         if(msg==NULL)
00614                 return -1;
00615         return pv_get_sintval(msg, param, res, _bm_last_time_diff);
00616 }
00617 
00618 
00619 static inline int fixup_bm_timer(void** param, int param_no)
00620 {
00621         unsigned int tid = 0;
00622         if (param_no == 1)
00623         {
00624                 if((_bm_register_timer((char*)(*param), 1, &tid))!=0)
00625                 {
00626                         LM_ERR("cannot register timer [%s]\n", (char*)(*param));
00627                         return E_UNSPEC;
00628                 }
00629                 pkg_free(*param);
00630                 *param = (void*)(unsigned long)tid;
00631         }
00632         return 0;
00633 }
00634 
00635 /* End of file */