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 
00028 /*! \file
00029  * \brief Benchmark :: Module Core
00030  *
00031  * \ingroup benchmark
00032  * - Module: benchmark
00033  */
00034 
00035 /*! \defgroup benchmark Benchmark :: Developer benchmarking module
00036  *
00037  * This module is for Kamailio developers, as well as admins. It gives
00038  * a possibility to clock certain critical paths in module code or 
00039  * configuration sections.
00040  * 
00041  */
00042 
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 "../../mi/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    bm_mycfg = (bm_cfg_t*)shm_malloc(sizeof(bm_cfg_t));
00186    memset(bm_mycfg, 0, sizeof(bm_cfg_t));
00187    bm_mycfg->enable_global = bm_enable_global;
00188    bm_mycfg->granularity   = bm_granularity;
00189    bm_mycfg->loglevel      = bm_loglevel;
00190 
00191    return 0;
00192 }
00193 
00194 
00195 /*
00196  * destroy
00197  * called by openser at exit time
00198  */
00199 static void destroy(void)
00200 {
00201    benchmark_timer_t *bmt = 0;
00202    benchmark_timer_t *bmp = 0;
00203 
00204    if(bm_mycfg!=NULL) 
00205    {
00206       /* free timers list */
00207       bmt = bm_mycfg->timers;
00208       while(bmt)
00209       {
00210          bmp = bmt;
00211          bmt = bmt->next;
00212          shm_free(bmp);
00213       }
00214       if(bm_mycfg->tindex) shm_free(bm_mycfg->tindex);
00215       shm_free(bm_mycfg);
00216    }
00217 }
00218 
00219 void bm_reset_timer(int i)
00220 {
00221    if(bm_mycfg==NULL || bm_mycfg->tindex[i]==NULL)
00222       return;
00223    bm_mycfg->tindex[i]->calls = 0;
00224    bm_mycfg->tindex[i]->sum = 0;
00225    bm_mycfg->tindex[i]->last_max = 0;
00226    bm_mycfg->tindex[i]->last_min = 0xffffffff;
00227    bm_mycfg->tindex[i]->last_sum = 0;
00228    bm_mycfg->tindex[i]->global_max = 0;
00229    bm_mycfg->tindex[i]->global_min = 0xffffffff;
00230 }
00231 
00232 void reset_timers(void)
00233 {
00234    int i;
00235    if(bm_mycfg==NULL)
00236       return;
00237 
00238    for (i = 0; i < bm_mycfg->nrtimers; i++)
00239       bm_reset_timer(i);
00240 }
00241 
00242 /*! \brief
00243  * timer_active().
00244  *
00245  * Global enable mode can be:
00246  * -1 - All timing disabled
00247  *  0 - Timing enabled, watch for single timers enabled (default: off)
00248  *  1 - Timing enabled for all timers
00249  */
00250 
00251 inline int timer_active(unsigned int id)
00252 {
00253    if (bm_mycfg->enable_global > 0 || bm_mycfg->timers[id].enabled > 0)
00254       return 1;
00255    else
00256       return 0;
00257 }
00258 
00259 
00260 /*! \brief
00261  * start_timer()
00262  */
00263 
00264 int _bm_start_timer(unsigned int id)
00265 {
00266    if (timer_active(id))
00267    {
00268       if(bm_get_time(bm_mycfg->tindex[id]->start)!=0)
00269       {
00270          LM_ERR("error getting current time\n");
00271          return -1;
00272       }
00273    }
00274 
00275    return 1;
00276 }
00277 
00278 int bm_start_timer(struct sip_msg* _msg, char* timer, char *foobar)
00279 {
00280    return _bm_start_timer((unsigned int)(unsigned long)timer);
00281 }
00282 
00283 
00284 /*! \brief
00285  * log_timer()
00286  */
00287 
00288 int _bm_log_timer(unsigned int id)
00289 {
00290    /* BM_CLOCK_REALTIME */
00291    bm_timeval_t now;
00292    unsigned long long tdiff;
00293 
00294    if (!timer_active(id))
00295       return 1;
00296 
00297    if(bm_get_time(&now)<0)
00298    {
00299       LM_ERR("error getting current time\n");
00300       return -1;
00301    }
00302    
00303    tdiff = bm_diff_time(bm_mycfg->tindex[id]->start, &now);
00304    _bm_last_time_diff = (int)tdiff;
00305 
00306    /* What to do
00307     * - update min, max, sum
00308     * - if granularity hit: Log, reset min/max
00309     */
00310 
00311    bm_mycfg->tindex[id]->sum += tdiff;
00312    bm_mycfg->tindex[id]->last_sum += tdiff;
00313    bm_mycfg->tindex[id]->calls++;
00314    
00315    if (tdiff < bm_mycfg->tindex[id]->last_min)
00316       bm_mycfg->tindex[id]->last_min = tdiff;
00317 
00318    if (tdiff > bm_mycfg->tindex[id]->last_max)
00319       bm_mycfg->tindex[id]->last_max = tdiff;
00320 
00321    if (tdiff < bm_mycfg->tindex[id]->global_min)
00322       bm_mycfg->tindex[id]->global_min = tdiff;
00323 
00324    if (tdiff > bm_mycfg->tindex[id]->global_max)
00325       bm_mycfg->tindex[id]->global_max = tdiff;
00326 
00327 
00328    if ((bm_mycfg->tindex[id]->calls % bm_mycfg->granularity) == 0)
00329    {
00330       LM_GEN1(bm_mycfg->loglevel, "benchmark (timer %s [%d]): %llu ["
00331          " msgs/total/min/max/avg - LR:"
00332          " %i/%lld/%lld/%lld/%f | GB: %lld/%lld/%lld/%lld/%f]\n",
00333          bm_mycfg->tindex[id]->name,
00334          id,
00335          tdiff,
00336          bm_mycfg->granularity,
00337          bm_mycfg->tindex[id]->last_sum,
00338          bm_mycfg->tindex[id]->last_min,
00339          bm_mycfg->tindex[id]->last_max,
00340          ((double)bm_mycfg->tindex[id]->last_sum)/bm_mycfg->granularity,
00341          bm_mycfg->tindex[id]->calls,
00342          bm_mycfg->tindex[id]->sum,
00343          bm_mycfg->tindex[id]->global_min,
00344          bm_mycfg->tindex[id]->global_max,
00345          ((double)bm_mycfg->tindex[id]->sum)/bm_mycfg->tindex[id]->calls);
00346 
00347       bm_mycfg->tindex[id]->last_sum = 0;
00348       bm_mycfg->tindex[id]->last_max = 0;
00349       bm_mycfg->tindex[id]->last_min = 0xffffffff;
00350    }
00351 
00352    return 1;
00353 }  
00354 
00355 int bm_log_timer(struct sip_msg* _msg, char* timer, char* mystr)
00356 {
00357    return _bm_log_timer((unsigned int)(unsigned long)timer);
00358 }
00359 
00360 int _bm_register_timer(char *tname, int mode, unsigned int *id)
00361 {
00362    benchmark_timer_t *bmt = 0;
00363    benchmark_timer_t **tidx = 0;
00364 
00365    if(tname==NULL || id==NULL || bm_mycfg==NULL || strlen(tname)==0
00366          || strlen(tname)>BM_NAME_LEN-1)
00367       return -1;
00368 
00369    bmt = bm_mycfg->timers;
00370    while(bmt)
00371    {
00372       if(strcmp(bmt->name, tname)==0)
00373       {
00374          *id = bmt->id;
00375          return 0;
00376       }
00377       bmt = bmt->next;
00378    }
00379    if(mode==0)
00380       return -1;
00381 
00382    bmt = (benchmark_timer_t*)shm_malloc(sizeof(benchmark_timer_t));
00383 
00384    if(bmt==0)
00385    {
00386       LM_ERR("no more shm\n");
00387       return -1;
00388    }
00389    memset(bmt, 0, sizeof(benchmark_timer_t));
00390 
00391    /* private memory, otherwise we have races */
00392    bmt->start = (bm_timeval_t*)pkg_malloc(sizeof(bm_timeval_t)); 
00393    if(bmt->start == NULL)
00394    {
00395       shm_free(bmt);
00396       LM_ERR("no more pkg\n");
00397       return -1;
00398    }
00399    memset(bmt->start, 0, sizeof(bm_timeval_t)); 
00400 
00401    strcpy(bmt->name, tname);
00402    if(bm_mycfg->timers==0)
00403    {
00404       bmt->id = 0;
00405       bm_mycfg->timers = bmt;
00406    } else {
00407       bmt->id = bm_mycfg->timers->id+1;
00408       bmt->next = bm_mycfg->timers;
00409       bm_mycfg->timers = bmt;
00410    }
00411 
00412    /* do the indexing */
00413    if(bmt->id%10==0)
00414    {
00415       if(bm_mycfg->tindex!=NULL)
00416          tidx = bm_mycfg->tindex;
00417       bm_mycfg->tindex = (benchmark_timer_t**)shm_malloc((10+bmt->id)*
00418                         sizeof(benchmark_timer_t*));
00419       if(bm_mycfg->tindex==0)
00420       {
00421          LM_ERR("no more share memory\n");
00422          if(tidx!=0)
00423             shm_free(tidx);
00424          return -1;
00425       }
00426       memset(bm_mycfg->tindex, 0, (10+bmt->id)*sizeof(benchmark_timer_t*));
00427       if(tidx!=0)
00428       {
00429          memcpy(bm_mycfg->tindex, tidx, bmt->id*sizeof(benchmark_timer_t*));
00430          shm_free(tidx);
00431       }
00432    }
00433    bm_mycfg->tindex[bmt->id] = bmt;
00434    bm_mycfg->nrtimers = bmt->id + 1;
00435    bm_reset_timer(bmt->id);
00436    *id = bmt->id;
00437    LM_DBG("timer [%s] added with index <%u>\n", bmt->name, bmt->id);
00438 
00439    return 0;
00440 }
00441 
00442 /*! \brief API Binding */
00443 int load_bm( struct bm_binds *bmb)
00444 {
00445    if(bmb==NULL)
00446       return -1;
00447 
00448    bmb->bm_register = _bm_register_timer;
00449    bmb->bm_start    = _bm_start_timer;
00450    bmb->bm_log      = _bm_log_timer;
00451 
00452    return 1;
00453 }
00454 
00455 
00456 static inline char * pkg_strndup( char* _p, int _len)
00457 {
00458    char *s;
00459 
00460    s = (char*)pkg_malloc(_len+1);
00461    if (s==NULL)
00462       return NULL;
00463    memcpy(s,_p,_len);
00464    s[_len] = 0;
00465    return s;
00466 }
00467 
00468 
00469 /*! \name TimerMIfunctions MI functions */
00470 /*@{ */
00471 
00472 /*! \brief
00473  * Expects 1 node: 0 for disable, 1 for enable
00474  */
00475 struct mi_root* mi_bm_enable_global(struct mi_root *cmd, void *param)
00476 {
00477    struct mi_node *node;
00478 
00479    char *p1, *e1;
00480    long int v1;
00481 
00482    node = cmd->node.kids;
00483 
00484    if ((node == NULL) || (node->next != NULL))
00485       return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00486 
00487    p1 = pkg_strndup(node->value.s, node->value.len);
00488 
00489    v1 = strtol(p1, &e1, 0);
00490 
00491    if ((*e1 != '\0') || (*p1 == '\0')) {
00492       pkg_free(p1);
00493       return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00494    }
00495 
00496    if ((v1 < -1) || (v1 > 1)) {
00497       pkg_free(p1);
00498       return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00499    }
00500 
00501    bm_mycfg->enable_global = v1;
00502 
00503    pkg_free(p1);
00504    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00505 }
00506 
00507 struct mi_root* mi_bm_enable_timer(struct mi_root *cmd, void *param)
00508 {
00509    struct mi_node *node;
00510 
00511    char *p1, *p2, *e2;
00512    long int v2;
00513    unsigned int id;
00514 
00515    node = cmd->node.kids;
00516 
00517    if ((node == NULL) || (node->next == NULL) || (node->next->next != NULL))
00518       return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00519 
00520    p1 = pkg_strndup(node->value.s, node->value.len);
00521 
00522    if(_bm_register_timer(p1, 0, &id)!=0)
00523    {
00524       pkg_free(p1);
00525       return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00526    }
00527    p2 = pkg_strndup(node->next->value.s, node->next->value.len);
00528    v2 = strtol(p2, &e2, 0);
00529    
00530    pkg_free(p1);
00531    pkg_free(p2);
00532 
00533    if (*e2 != '\0' || *p2 == '\0')
00534       return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00535 
00536    if ((v2 < 0) || (v2 > 1))
00537       return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00538 
00539    bm_mycfg->timers[id].enabled = v2;
00540 
00541    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00542 }
00543 
00544 struct mi_root* mi_bm_granularity(struct mi_root *cmd, void *param)
00545 {
00546    struct mi_node *node;
00547 
00548    char *p1, *e1;
00549    long int v1;
00550 
00551    node = cmd->node.kids;
00552 
00553    if ((node == NULL) || (node->next != NULL))
00554       return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00555 
00556    p1 = pkg_strndup(node->value.s, node->value.len);
00557 
00558    v1 = strtol(p1, &e1, 0);
00559 
00560    pkg_free(p1);
00561 
00562    if ((*e1 != '\0') || (*p1 == '\0'))
00563       return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00564 
00565    if (v1 < 1)
00566       return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00567 
00568    bm_mycfg->granularity = v1;
00569 
00570    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00571 }
00572 
00573 struct mi_root* mi_bm_loglevel(struct mi_root *cmd, void *param)
00574 {
00575    struct mi_node *node;
00576 
00577    char *p1, *e1;
00578    long int v1;
00579 
00580    node = cmd->node.kids;
00581 
00582    if ((node == NULL) || (node->next != NULL))
00583       return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00584 
00585    p1 = pkg_strndup(node->value.s, node->value.len);
00586 
00587    v1 = strtol(p1, &e1, 0);
00588    
00589    pkg_free(p1);
00590 
00591    if ((*e1 != '\0') || (*p1 == '\0'))
00592       return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00593 
00594    if ((v1 < -3) || (v1 > 4)) /* Maximum log levels */
00595       return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
00596 
00597    bm_mycfg->enable_global = v1;
00598 
00599    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00600 }
00601 /*@} */
00602 
00603 /*! \brief PV get function for time diff */
00604 static int bm_get_time_diff(struct sip_msg *msg, pv_param_t *param,
00605       pv_value_t *res)
00606 {
00607    if(msg==NULL)
00608       return -1;
00609    return pv_get_sintval(msg, param, res, _bm_last_time_diff);
00610 }
00611 
00612 
00613 static inline int fixup_bm_timer(void** param, int param_no)
00614 {
00615    unsigned int tid = 0;
00616    if (param_no == 1)
00617    {
00618       if((_bm_register_timer((char*)(*param), 1, &tid))!=0)
00619       {
00620          LM_ERR("cannot register timer [%s]\n", (char*)(*param));
00621          return E_UNSPEC;
00622       }
00623       pkg_free(*param);
00624       *param = (void*)(unsigned long)tid;
00625    }
00626    return 0;
00627 }
00628 
00629 /* End of file */

Generated on Thu May 17 12:00:25 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6