benchmark.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. /*
  2. * $Id: benchmark.c 941 2007-04-11 12:37:21Z bastian $
  3. *
  4. * Benchmarking module for Kamailio
  5. *
  6. * Copyright (C) 2007 Collax GmbH
  7. * (Bastian Friedrich <[email protected]>)
  8. * Copyright (C) 2007 Voice Sistem SRL
  9. *
  10. * This file is part of Kamailio, a free SIP server.
  11. *
  12. * Kamailio is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License as published by
  14. * the Free Software Foundation; either version 2 of the License, or
  15. * (at your option) any later version
  16. *
  17. * Kamailio is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program; if not, write to the Free Software
  24. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  25. *
  26. */
  27. /*! \file
  28. * \brief Benchmark :: Module Core
  29. *
  30. * \ingroup benchmark
  31. * - Module: benchmark
  32. */
  33. /*! \defgroup benchmark Benchmark :: Developer benchmarking module
  34. *
  35. * This module is for Kamailio developers, as well as admins. It gives
  36. * a possibility to clock certain critical paths in module code or
  37. * configuration sections.
  38. *
  39. */
  40. #define _GNU_SOURCE
  41. #include <string.h>
  42. #include <sys/time.h>
  43. #include <stdlib.h>
  44. #include "../../sr_module.h"
  45. #include "../../lib/kmi/mi.h"
  46. #include "../../mem/mem.h"
  47. #include "../../ut.h"
  48. #include "benchmark.h"
  49. #include "../../mem/shm_mem.h"
  50. MODULE_VERSION
  51. /* Exported functions */
  52. int bm_start_timer(struct sip_msg* _msg, char* timer, char *foobar);
  53. int bm_log_timer(struct sip_msg* _msg, char* timer, char* mystr);
  54. /*
  55. * Module destroy function prototype
  56. */
  57. static void destroy(void);
  58. /*
  59. * Module initialization function prototype
  60. */
  61. static int mod_init(void);
  62. /*
  63. * Exported parameters
  64. * Copied to mycfg on module initialization
  65. */
  66. static int bm_enable_global = 0;
  67. static int bm_granularity = 1;
  68. static int bm_loglevel = L_INFO;
  69. static int _bm_last_time_diff = 0;
  70. /*
  71. * Module setup
  72. */
  73. typedef struct bm_cfg {
  74. int enable_global;
  75. int granularity;
  76. int loglevel;
  77. /* The internal timers */
  78. int nrtimers;
  79. benchmark_timer_t *timers;
  80. benchmark_timer_t **tindex;
  81. } bm_cfg_t;
  82. /*
  83. * The setup is located in shared memory so that
  84. * all instances can access this variable
  85. */
  86. bm_cfg_t *bm_mycfg = 0;
  87. static inline int fixup_bm_timer(void** param, int param_no);
  88. /*
  89. * Exported functions
  90. */
  91. static cmd_export_t cmds[] = {
  92. { "bm_start_timer", (cmd_function)bm_start_timer, 1, fixup_bm_timer, 0,
  93. REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
  94. { "bm_log_timer", (cmd_function)bm_log_timer, 1, fixup_bm_timer, 0,
  95. REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
  96. {"load_bm", (cmd_function)load_bm, 0, 0, 0, 0},
  97. { 0, 0, 0, 0, 0, 0 }
  98. };
  99. /*
  100. * Exported parameters
  101. */
  102. static param_export_t params[] = {
  103. {"enable", INT_PARAM, &bm_enable_global},
  104. {"granularity", INT_PARAM, &bm_granularity},
  105. {"loglevel", INT_PARAM, &bm_loglevel},
  106. { 0, 0, 0 }
  107. };
  108. /*
  109. * Exported MI functions
  110. */
  111. struct mi_root* mi_bm_enable_global(struct mi_root *cmd, void *param);
  112. struct mi_root* mi_bm_enable_timer(struct mi_root *cmd, void *param);
  113. struct mi_root* mi_bm_granularity(struct mi_root *cmd, void *param);
  114. struct mi_root* mi_bm_loglevel(struct mi_root *cmd, void *param);
  115. static mi_export_t mi_cmds[] = {
  116. { "bm_enable_global", mi_bm_enable_global, 0, 0, 0 },
  117. { "bm_enable_timer", mi_bm_enable_timer, 0, 0, 0 },
  118. { "bm_granularity", mi_bm_granularity, 0, 0, 0 },
  119. { "bm_loglevel", mi_bm_loglevel, 0, 0, 0 },
  120. { 0, 0, 0, 0, 0}
  121. };
  122. static int bm_get_time_diff(struct sip_msg *msg, pv_param_t *param,
  123. pv_value_t *res);
  124. static pv_export_t mod_items[] = {
  125. { {"BM_time_diff", sizeof("BM_time_diff")-1}, PVT_OTHER, bm_get_time_diff, 0,
  126. 0, 0, 0, 0 },
  127. { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
  128. };
  129. /*
  130. * Module interface
  131. */
  132. struct module_exports exports = {
  133. "benchmark",
  134. DEFAULT_DLFLAGS,
  135. cmds, /* Exported functions */
  136. params, /* Exported parameters */
  137. 0, /* exported statistics */
  138. mi_cmds, /* exported MI functions */
  139. mod_items, /* exported pseudo-variables */
  140. 0, /* extra processes */
  141. mod_init, /* module initialization function */
  142. 0, /* response function */
  143. destroy, /* destroy function */
  144. 0 /* child initialization function */
  145. };
  146. /****************/
  147. /*
  148. * mod_init
  149. * Called by Kamailio at init time
  150. */
  151. static int mod_init(void) {
  152. if(register_mi_mod(exports.name, mi_cmds)!=0)
  153. {
  154. LM_ERR("failed to register MI commands\n");
  155. return -1;
  156. }
  157. bm_mycfg = (bm_cfg_t*)shm_malloc(sizeof(bm_cfg_t));
  158. memset(bm_mycfg, 0, sizeof(bm_cfg_t));
  159. bm_mycfg->enable_global = bm_enable_global;
  160. bm_mycfg->granularity = bm_granularity;
  161. bm_mycfg->loglevel = bm_loglevel;
  162. return 0;
  163. }
  164. /*
  165. * destroy
  166. * called by Kamailio at exit time
  167. */
  168. static void destroy(void)
  169. {
  170. benchmark_timer_t *bmt = 0;
  171. benchmark_timer_t *bmp = 0;
  172. if(bm_mycfg!=NULL)
  173. {
  174. /* free timers list */
  175. bmt = bm_mycfg->timers;
  176. while(bmt)
  177. {
  178. bmp = bmt;
  179. bmt = bmt->next;
  180. shm_free(bmp);
  181. }
  182. if(bm_mycfg->tindex) shm_free(bm_mycfg->tindex);
  183. shm_free(bm_mycfg);
  184. }
  185. }
  186. void bm_reset_timer(int i)
  187. {
  188. if(bm_mycfg==NULL || bm_mycfg->tindex[i]==NULL)
  189. return;
  190. bm_mycfg->tindex[i]->calls = 0;
  191. bm_mycfg->tindex[i]->sum = 0;
  192. bm_mycfg->tindex[i]->last_max = 0;
  193. bm_mycfg->tindex[i]->last_min = 0xffffffff;
  194. bm_mycfg->tindex[i]->last_sum = 0;
  195. bm_mycfg->tindex[i]->global_max = 0;
  196. bm_mycfg->tindex[i]->global_min = 0xffffffff;
  197. }
  198. void reset_timers(void)
  199. {
  200. int i;
  201. if(bm_mycfg==NULL)
  202. return;
  203. for (i = 0; i < bm_mycfg->nrtimers; i++)
  204. bm_reset_timer(i);
  205. }
  206. /*! \brief
  207. * timer_active().
  208. *
  209. * Global enable mode can be:
  210. * -1 - All timing disabled
  211. * 0 - Timing enabled, watch for single timers enabled (default: off)
  212. * 1 - Timing enabled for all timers
  213. */
  214. static inline int timer_active(unsigned int id)
  215. {
  216. if (bm_mycfg->enable_global > 0 || bm_mycfg->timers[id].enabled > 0)
  217. return 1;
  218. else
  219. return 0;
  220. }
  221. /*! \brief
  222. * start_timer()
  223. */
  224. int _bm_start_timer(unsigned int id)
  225. {
  226. if (timer_active(id))
  227. {
  228. if(bm_get_time(bm_mycfg->tindex[id]->start)!=0)
  229. {
  230. LM_ERR("error getting current time\n");
  231. return -1;
  232. }
  233. }
  234. return 1;
  235. }
  236. int bm_start_timer(struct sip_msg* _msg, char* timer, char *foobar)
  237. {
  238. return _bm_start_timer((unsigned int)(unsigned long)timer);
  239. }
  240. /*! \brief
  241. * log_timer()
  242. */
  243. int _bm_log_timer(unsigned int id)
  244. {
  245. /* BM_CLOCK_REALTIME */
  246. bm_timeval_t now;
  247. unsigned long long tdiff;
  248. if (!timer_active(id))
  249. return 1;
  250. if(bm_get_time(&now)<0)
  251. {
  252. LM_ERR("error getting current time\n");
  253. return -1;
  254. }
  255. tdiff = bm_diff_time(bm_mycfg->tindex[id]->start, &now);
  256. _bm_last_time_diff = (int)tdiff;
  257. /* What to do
  258. * - update min, max, sum
  259. * - if granularity hit: Log, reset min/max
  260. */
  261. bm_mycfg->tindex[id]->sum += tdiff;
  262. bm_mycfg->tindex[id]->last_sum += tdiff;
  263. bm_mycfg->tindex[id]->calls++;
  264. if (tdiff < bm_mycfg->tindex[id]->last_min)
  265. bm_mycfg->tindex[id]->last_min = tdiff;
  266. if (tdiff > bm_mycfg->tindex[id]->last_max)
  267. bm_mycfg->tindex[id]->last_max = tdiff;
  268. if (tdiff < bm_mycfg->tindex[id]->global_min)
  269. bm_mycfg->tindex[id]->global_min = tdiff;
  270. if (tdiff > bm_mycfg->tindex[id]->global_max)
  271. bm_mycfg->tindex[id]->global_max = tdiff;
  272. if ((bm_mycfg->tindex[id]->calls % bm_mycfg->granularity) == 0)
  273. {
  274. LM_GEN1(bm_mycfg->loglevel, "benchmark (timer %s [%d]): %llu ["
  275. " msgs/total/min/max/avg - LR:"
  276. " %i/%llu/%llu/%llu/%f | GB: %llu/%llu/%llu/%llu/%f]\n",
  277. bm_mycfg->tindex[id]->name,
  278. id,
  279. tdiff,
  280. bm_mycfg->granularity,
  281. bm_mycfg->tindex[id]->last_sum,
  282. bm_mycfg->tindex[id]->last_min,
  283. bm_mycfg->tindex[id]->last_max,
  284. ((double)bm_mycfg->tindex[id]->last_sum)/bm_mycfg->granularity,
  285. bm_mycfg->tindex[id]->calls,
  286. bm_mycfg->tindex[id]->sum,
  287. bm_mycfg->tindex[id]->global_min,
  288. bm_mycfg->tindex[id]->global_max,
  289. ((double)bm_mycfg->tindex[id]->sum)/bm_mycfg->tindex[id]->calls);
  290. bm_mycfg->tindex[id]->last_sum = 0;
  291. bm_mycfg->tindex[id]->last_max = 0;
  292. bm_mycfg->tindex[id]->last_min = 0xffffffff;
  293. }
  294. return 1;
  295. }
  296. int bm_log_timer(struct sip_msg* _msg, char* timer, char* mystr)
  297. {
  298. return _bm_log_timer((unsigned int)(unsigned long)timer);
  299. }
  300. int _bm_register_timer(char *tname, int mode, unsigned int *id)
  301. {
  302. benchmark_timer_t *bmt = 0;
  303. benchmark_timer_t **tidx = 0;
  304. if(tname==NULL || id==NULL || bm_mycfg==NULL || strlen(tname)==0
  305. || strlen(tname)>BM_NAME_LEN-1)
  306. return -1;
  307. bmt = bm_mycfg->timers;
  308. while(bmt)
  309. {
  310. if(strcmp(bmt->name, tname)==0)
  311. {
  312. *id = bmt->id;
  313. return 0;
  314. }
  315. bmt = bmt->next;
  316. }
  317. if(mode==0)
  318. return -1;
  319. bmt = (benchmark_timer_t*)shm_malloc(sizeof(benchmark_timer_t));
  320. if(bmt==0)
  321. {
  322. LM_ERR("no more shm\n");
  323. return -1;
  324. }
  325. memset(bmt, 0, sizeof(benchmark_timer_t));
  326. /* private memory, otherwise we have races */
  327. bmt->start = (bm_timeval_t*)pkg_malloc(sizeof(bm_timeval_t));
  328. if(bmt->start == NULL)
  329. {
  330. shm_free(bmt);
  331. LM_ERR("no more pkg\n");
  332. return -1;
  333. }
  334. memset(bmt->start, 0, sizeof(bm_timeval_t));
  335. strcpy(bmt->name, tname);
  336. if(bm_mycfg->timers==0)
  337. {
  338. bmt->id = 0;
  339. bm_mycfg->timers = bmt;
  340. } else {
  341. bmt->id = bm_mycfg->timers->id+1;
  342. bmt->next = bm_mycfg->timers;
  343. bm_mycfg->timers = bmt;
  344. }
  345. /* do the indexing */
  346. if(bmt->id%10==0)
  347. {
  348. if(bm_mycfg->tindex!=NULL)
  349. tidx = bm_mycfg->tindex;
  350. bm_mycfg->tindex = (benchmark_timer_t**)shm_malloc((10+bmt->id)*
  351. sizeof(benchmark_timer_t*));
  352. if(bm_mycfg->tindex==0)
  353. {
  354. LM_ERR("no more share memory\n");
  355. if(tidx!=0)
  356. shm_free(tidx);
  357. return -1;
  358. }
  359. memset(bm_mycfg->tindex, 0, (10+bmt->id)*sizeof(benchmark_timer_t*));
  360. if(tidx!=0)
  361. {
  362. memcpy(bm_mycfg->tindex, tidx, bmt->id*sizeof(benchmark_timer_t*));
  363. shm_free(tidx);
  364. }
  365. }
  366. bm_mycfg->tindex[bmt->id] = bmt;
  367. bm_mycfg->nrtimers = bmt->id + 1;
  368. bm_reset_timer(bmt->id);
  369. *id = bmt->id;
  370. LM_DBG("timer [%s] added with index <%u>\n", bmt->name, bmt->id);
  371. return 0;
  372. }
  373. /*! \brief API Binding */
  374. int load_bm( struct bm_binds *bmb)
  375. {
  376. if(bmb==NULL)
  377. return -1;
  378. bmb->bm_register = _bm_register_timer;
  379. bmb->bm_start = _bm_start_timer;
  380. bmb->bm_log = _bm_log_timer;
  381. return 1;
  382. }
  383. static inline char * pkg_strndup( char* _p, int _len)
  384. {
  385. char *s;
  386. s = (char*)pkg_malloc(_len+1);
  387. if (s==NULL)
  388. return NULL;
  389. memcpy(s,_p,_len);
  390. s[_len] = 0;
  391. return s;
  392. }
  393. /*! \name TimerMIfunctions MI functions */
  394. /*@{ */
  395. /*! \brief
  396. * Expects 1 node: 0 for disable, 1 for enable
  397. */
  398. struct mi_root* mi_bm_enable_global(struct mi_root *cmd, void *param)
  399. {
  400. struct mi_node *node;
  401. char *p1, *e1;
  402. long int v1;
  403. node = cmd->node.kids;
  404. if ((node == NULL) || (node->next != NULL))
  405. return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
  406. p1 = pkg_strndup(node->value.s, node->value.len);
  407. v1 = strtol(p1, &e1, 0);
  408. if ((*e1 != '\0') || (*p1 == '\0')) {
  409. pkg_free(p1);
  410. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  411. }
  412. if ((v1 < -1) || (v1 > 1)) {
  413. pkg_free(p1);
  414. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  415. }
  416. bm_mycfg->enable_global = v1;
  417. pkg_free(p1);
  418. return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
  419. }
  420. struct mi_root* mi_bm_enable_timer(struct mi_root *cmd, void *param)
  421. {
  422. struct mi_node *node;
  423. char *p1, *p2, *e2;
  424. long int v2;
  425. unsigned int id;
  426. node = cmd->node.kids;
  427. if ((node == NULL) || (node->next == NULL) || (node->next->next != NULL))
  428. return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
  429. p1 = pkg_strndup(node->value.s, node->value.len);
  430. p2 = pkg_strndup(node->next->value.s, node->next->value.len);
  431. if(!p1 || !p2)
  432. goto error;
  433. if(_bm_register_timer(p1, 0, &id)!=0)
  434. {
  435. pkg_free(p1);
  436. pkg_free(p2);
  437. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  438. }
  439. v2 = strtol(p2, &e2, 0);
  440. pkg_free(p1);
  441. if (*e2 != '\0' || *p2 == '\0') {
  442. pkg_free(p2);
  443. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  444. }
  445. pkg_free(p2);
  446. if ((v2 < 0) || (v2 > 1))
  447. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  448. bm_mycfg->timers[id].enabled = v2;
  449. return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
  450. error:
  451. if(p1) pkg_free(p1);
  452. if(p2) pkg_free(p2);
  453. return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
  454. }
  455. struct mi_root* mi_bm_granularity(struct mi_root *cmd, void *param)
  456. {
  457. struct mi_node *node;
  458. char *p1, *e1;
  459. long int v1;
  460. node = cmd->node.kids;
  461. if ((node == NULL) || (node->next != NULL))
  462. return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
  463. p1 = pkg_strndup(node->value.s, node->value.len);
  464. v1 = strtol(p1, &e1, 0);
  465. if ((*e1 != '\0') || (*p1 == '\0'))
  466. {
  467. pkg_free(p1);
  468. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  469. }
  470. pkg_free(p1);
  471. if (v1 < 1)
  472. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  473. bm_mycfg->granularity = v1;
  474. return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
  475. }
  476. struct mi_root* mi_bm_loglevel(struct mi_root *cmd, void *param)
  477. {
  478. struct mi_node *node;
  479. char *p1, *e1;
  480. long int v1;
  481. node = cmd->node.kids;
  482. if ((node == NULL) || (node->next != NULL))
  483. return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
  484. p1 = pkg_strndup(node->value.s, node->value.len);
  485. v1 = strtol(p1, &e1, 0);
  486. if ((*e1 != '\0') || (*p1 == '\0'))
  487. {
  488. pkg_free(p1);
  489. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  490. }
  491. pkg_free(p1);
  492. if ((v1 < -3) || (v1 > 4)) /* Maximum log levels */
  493. return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
  494. bm_mycfg->enable_global = v1;
  495. return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
  496. }
  497. /*@} */
  498. /*! \brief PV get function for time diff */
  499. static int bm_get_time_diff(struct sip_msg *msg, pv_param_t *param,
  500. pv_value_t *res)
  501. {
  502. if(msg==NULL)
  503. return -1;
  504. return pv_get_sintval(msg, param, res, _bm_last_time_diff);
  505. }
  506. static inline int fixup_bm_timer(void** param, int param_no)
  507. {
  508. unsigned int tid = 0;
  509. if (param_no == 1)
  510. {
  511. if((_bm_register_timer((char*)(*param), 1, &tid))!=0)
  512. {
  513. LM_ERR("cannot register timer [%s]\n", (char*)(*param));
  514. return E_UNSPEC;
  515. }
  516. pkg_free(*param);
  517. *param = (void*)(unsigned long)tid;
  518. }
  519. return 0;
  520. }
  521. /* End of file */