Coverage Report

Created: 2026-03-11 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/statistics.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2006 Voice Sistem SRL
3
 * Copyright (C) 2010-2012 OpenSIPS Solutions
4
 *
5
 * This file is part of opensips, a free SIP server.
6
 *
7
 * opensips is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * opensips is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
 *
21
 *
22
 * History:
23
 * ---------
24
 *  2006-01-16  first version (bogdan)
25
 *  2006-11-28  added get_stat_var_from_num_code() (Jeffrey Magder -
26
 *              SOMA Networks)
27
 *  2009-04-23  function var accepts a context parameter (bogdan)
28
 *  2012-09-21  support for dynamic statistics (created of demand at runtime)
29
 *              (bogdan)
30
 */
31
32
/*!
33
 * \file
34
 * \brief Statistics support
35
 */
36
37
#include <string.h>
38
39
#include "atomic.h"
40
#include "mem/shm_mem.h"
41
#include "mem/rpm_mem.h"
42
#include "mi/mi.h"
43
#include "ut.h"
44
#include "dprint.h"
45
#include "locking.h"
46
#include "core_stats.h"
47
#include "statistics.h"
48
#include "pt.h"
49
#include "globals.h"
50
#include "rw_locking.h"
51
52
#ifdef STATISTICS
53
54
static stats_collector *collector = NULL;
55
static int stats_ready;
56
57
static mi_response_t *mi_get_stats(const mi_params_t *params,
58
                struct mi_handler *async_hdl);
59
static mi_response_t *w_mi_list_stats(const mi_params_t *params,
60
                struct mi_handler *async_hdl);
61
static mi_response_t *w_mi_list_stats_1(const mi_params_t *params,
62
                struct mi_handler *async_hdl);
63
static mi_response_t *mi_reset_stats(const mi_params_t *params,
64
                struct mi_handler *async_hdl);
65
static mi_response_t *mi_reset_all_stats(const mi_params_t *params,
66
                struct mi_handler *async_hdl);
67
static const mi_export_t mi_stat_cmds[] = {
68
  { "get",
69
    "prints the statistics (all, group or one) realtime values.", 0, 0, {
70
    {mi_get_stats, {"statistics", 0}},
71
    {EMPTY_MI_RECIPE}}, {"get_statistics", 0}
72
  },
73
  { "list",
74
    "lists all the registered statistics and their types", 0, 0, {
75
    {w_mi_list_stats, {0}},
76
    {w_mi_list_stats_1, {"statistics", 0}},
77
    {EMPTY_MI_RECIPE}}, {"list_statistics", 0}
78
  },
79
  { "reset", "resets the value of a statistic variable", 0, 0, {
80
    {mi_reset_stats, {"statistics", 0}},
81
    {EMPTY_MI_RECIPE}}, {"reset_statistics", 0}
82
  },
83
  { "reset_all", "resets the value of all resetable variables", 0, 0, {
84
    {mi_reset_all_stats, {0}},
85
    {EMPTY_MI_RECIPE}}, {"reset_all_statistics", 0}
86
  },
87
  {EMPTY_MI_EXPORT}
88
};
89
90
91
#ifdef NO_ATOMIC_OPS
92
#warning STATISTICS: Architecture with no support for atomic operations. \
93
         Using Locks!!
94
gen_lock_t *stat_lock = 0;
95
#endif
96
97
0
#define stat_hash(_s) core_hash( _s, NULL, STATS_HASH_SIZE)
98
99
0
#define stat_is_hidden(_s)  ((_s)->flags&STAT_HIDDEN)
100
101
102
/*! \brief
103
 * Returns the statistic associated with 'numerical_code' and 'out_codes'.
104
 * Specifically:
105
 *
106
 *  - if out_codes is nonzero, then the stat_var for the number of messages
107
 *    _sent out_ with the 'numerical_code' will be returned if it exists.
108
 *  - otherwise, the stat_var for the number of messages _received_ with the
109
 *    'numerical_code' will be returned, if the stat exists.
110
 */
111
stat_var *get_stat_var_from_num_code(unsigned int numerical_code, int out_codes)
112
0
{
113
0
  static char msg_code[INT2STR_MAX_LEN+4];
114
0
  str stat_name;
115
116
0
  stat_name.s = int2bstr( (unsigned long)numerical_code, msg_code,
117
0
    &stat_name.len);
118
0
  stat_name.s[stat_name.len++] = '_';
119
120
0
  if (out_codes) {
121
0
    stat_name.s[stat_name.len++] = 'o';
122
0
    stat_name.s[stat_name.len++] = 'u';
123
0
    stat_name.s[stat_name.len++] = 't';
124
0
  } else {
125
0
    stat_name.s[stat_name.len++] = 'i';
126
0
    stat_name.s[stat_name.len++] = 'n';
127
0
  }
128
129
0
  return get_stat(&stat_name);
130
0
}
131
132
133
char *build_stat_name( str* prefix, char *var_name)
134
0
{
135
0
  int n;
136
0
  char *s;
137
0
  char *p;
138
139
0
  n = prefix->len + 1 + strlen(var_name) + 1;
140
0
  s = (char*)shm_malloc( n );
141
0
  if (s==0) {
142
0
    LM_ERR("no more shm mem\n");
143
0
    return 0;
144
0
  }
145
0
  memcpy( s, prefix->s, prefix->len);
146
0
  p = s + prefix->len;
147
0
  *(p++) = '-';
148
0
  memcpy( p , var_name, strlen(var_name));
149
0
  p += strlen(var_name);
150
0
  *(p++) = 0;
151
0
  return s;
152
0
}
153
154
155
/************* Functions for handling MODULEs(groups) of stats ***************/
156
157
module_stats* get_stat_module( str *module)
158
0
{
159
0
  int i;
160
161
0
  if ( (module==0) || module->s==0 || module->len==0 )
162
0
    return 0;
163
164
0
  for( i=0 ; i<collector->mod_no ; i++ ) {
165
0
    if ( (collector->amodules[i].name.len == module->len) &&
166
0
    (strncasecmp(collector->amodules[i].name.s,module->s,module->len)==0) )
167
0
      return &collector->amodules[i];
168
0
  }
169
170
0
  return 0;
171
0
}
172
173
module_stats *module_stats_iterate(module_stats *mod)
174
0
{
175
0
  int m;
176
0
  if (!mod) {
177
    /* first iteration - return head */
178
0
    return ((collector->mod_no > 0)?
179
0
        &collector->amodules[0]:NULL);
180
0
  }
181
0
  for (m = 0; m < collector->mod_no - 1; m++)
182
0
    if (&collector->amodules[m] == mod)
183
0
      return &collector->amodules[m + 1];
184
0
  return NULL;
185
0
}
186
187
static inline module_stats* __add_stat_module(str *module, int unsafe)
188
0
{
189
0
  module_stats *amods;
190
0
  module_stats *mods;
191
192
0
  if ( (module==0) || module->len==0 )
193
0
    return NULL;
194
195
0
  amods = unsafe ?
196
0
    (module_stats*)shm_realloc_unsafe( collector->amodules,
197
0
    (collector->mod_no+1)*sizeof(module_stats))
198
0
    :
199
0
    (module_stats*)shm_realloc( collector->amodules,
200
0
    (collector->mod_no+1)*sizeof(module_stats));
201
202
0
  if (amods==0) {
203
0
    LM_ERR("no more shm memory\n");
204
0
    return NULL;
205
0
  }
206
207
0
  collector->amodules = amods;
208
0
  collector->mod_no++;
209
210
0
  mods = &amods[collector->mod_no-1];
211
0
  memset( mods, 0, sizeof(module_stats) );
212
213
0
  mods->name.s = unsafe ? shm_malloc_unsafe(module->len) : shm_malloc(module->len);
214
0
  if (!mods->name.s) {
215
0
      LM_ERR("oom\n");
216
0
      return NULL;
217
0
  }
218
0
  memcpy(mods->name.s, module->s, module->len);
219
0
  mods->name.len = module->len;
220
221
0
  mods->idx = collector->mod_no-1;
222
223
0
  return mods;
224
0
}
225
226
module_stats *add_stat_module(char *module)
227
0
{
228
0
  str smodule;
229
0
  init_str(&smodule, module);
230
0
  return __add_stat_module(&smodule, 0);
231
0
}
232
233
234
/***************** Init / Destroy STATS support functions *******************/
235
236
237
int clone_pv_stat_name(const str *name, str *clone)
238
0
{
239
0
  clone->s = (char*)shm_malloc(name->len);
240
0
  if (clone->s==NULL) {
241
0
    LM_ERR("failed to allocated more shm mem (%d)\n",name->len);
242
0
    return -1;
243
0
  }
244
0
  clone->len = name->len;
245
0
  memcpy(clone->s,name->s,name->len);
246
247
0
  return 0;
248
0
}
249
250
251
int init_stats_collector(void)
252
0
{
253
0
  module_stats *dy_mod;
254
255
  /* init the collector */
256
0
  collector = (stats_collector*)shm_malloc_unsafe(sizeof(stats_collector));
257
0
  if (collector==0) {
258
0
    LM_ERR("no more shm mem\n");
259
0
    goto error;
260
0
  }
261
0
  memset( collector, 0 , sizeof(stats_collector));
262
263
  /*
264
   * register shm statistics in an unsafe manner, as some allocators
265
   * would actually attempt to update these statistics
266
   * during their "safe" allocations -- Liviu
267
   */
268
0
  if (__register_module_stats( "shmem", shm_stats, 1) != 0) {
269
0
    LM_ERR("failed to register sh_mem statistics\n");
270
0
    goto error;
271
0
  }
272
273
0
  if (__register_module_stats( "rpmem", rpm_stats, 1) != 0) {
274
0
    LM_ERR("failed to register rp_mem statistics\n");
275
0
    goto error;
276
0
  }
277
278
0
  stats_ready = 1;
279
280
#ifdef NO_ATOMIC_OPS
281
  /* init BIG (really BIG) lock */
282
  stat_lock = lock_alloc();
283
  if (stat_lock==0 || lock_init( stat_lock )==0 ) {
284
    LM_ERR("failed to init the really BIG lock\n");
285
    goto error;
286
  }
287
#endif
288
289
0
  collector->rwl = (void*)lock_init_rw();
290
0
  if (collector->rwl==NULL) {
291
0
    LM_ERR("failed to create RW lock dynamic stats\n");
292
0
    goto error;
293
0
  }
294
295
  /* register MI commands */
296
0
  if (register_mi_mod( "statistics", mi_stat_cmds)<0) {
297
0
    LM_ERR("unable to register MI cmds\n");
298
0
    goto error;
299
0
  }
300
301
  /* register core statistics */
302
0
  if (register_module_stats( "core", core_stats)!=0 ) {
303
0
    LM_ERR("failed to register core statistics\n");
304
0
    goto error;
305
0
  }
306
307
  /* register network-level statistics */
308
0
  if (register_module_stats( "net", net_stats)!=0 ) {
309
0
    LM_ERR("failed to register network statistics\n");
310
0
    goto error;
311
0
  }
312
313
  /* create the module for "dynamic" statistics */
314
0
  dy_mod = add_stat_module( DYNAMIC_MODULE_NAME );
315
0
  if (dy_mod==NULL) {
316
0
    LM_ERR("failed to create <%s> module\n",DYNAMIC_MODULE_NAME);
317
0
    goto error;
318
0
  }
319
  /* mark it as dynamic, so it will require locking */
320
0
  dy_mod->is_dyn = 1 ;
321
322
0
  LM_DBG("statistics manager successfully initialized\n");
323
324
0
  return 0;
325
0
error:
326
0
  return -1;
327
0
}
328
329
330
void destroy_stats_collector(void)
331
0
{
332
0
  stat_var *stat;
333
0
  stat_var *tmp_stat;
334
0
  group_stats *grp, *grp_next;
335
0
  int i, idx;
336
337
#ifdef NO_ATOMIC_OPS
338
  /* destroy big lock */
339
  if (stat_lock)
340
    lock_destroy( stat_lock );
341
#endif
342
343
0
  if (collector) {
344
    /* destroy hash tables */
345
0
    for( i=0 ; i<STATS_HASH_SIZE ; i++ ) {
346
      /* static stats */
347
0
      for( stat=collector->hstats[i] ; stat ; ) {
348
0
        tmp_stat = stat;
349
0
        stat = stat->hnext;
350
0
        if ((tmp_stat->flags&STAT_IS_FUNC)==0 && tmp_stat->u.val && !(tmp_stat->flags&STAT_NOT_ALLOCATED))
351
0
          shm_free(tmp_stat->u.val);
352
0
        if ( (tmp_stat->flags&STAT_SHM_NAME) && tmp_stat->name.s)
353
0
          shm_free(tmp_stat->name.s);
354
0
        if (!(tmp_stat->flags&STAT_NOT_ALLOCATED))
355
0
          shm_free(tmp_stat);
356
0
      }
357
      /* dynamic stats*/
358
0
      for( stat=collector->dy_hstats[i] ; stat ; ) {
359
0
        tmp_stat = stat;
360
0
        stat = stat->hnext;
361
0
        if ((tmp_stat->flags&STAT_IS_FUNC)==0 && tmp_stat->u.val && !(tmp_stat->flags&STAT_NOT_ALLOCATED))
362
0
          shm_free(tmp_stat->u.val);
363
0
        if ( (tmp_stat->flags&STAT_SHM_NAME) && tmp_stat->name.s)
364
0
          shm_free(tmp_stat->name.s);
365
0
        if (!(tmp_stat->flags&STAT_NOT_ALLOCATED))
366
0
          shm_free(tmp_stat);
367
0
      }
368
0
    }
369
370
0
    for (idx = 0; idx < collector->mod_no; idx++) {
371
0
      shm_free(collector->amodules[idx].name.s);
372
0
    }
373
374
    /* destroy sts_module array */
375
0
    if (collector->amodules)
376
0
      shm_free(collector->amodules);
377
378
    /* destroy the stat's groups */
379
0
    for (grp = collector->groups; grp; grp = grp_next) {
380
0
      grp_next = grp->next;
381
0
      shm_free(grp->vars);
382
0
      shm_free(grp);
383
0
    }
384
385
    /* destroy the RW lock */
386
0
    if (collector->rwl)
387
0
      lock_destroy_rw( (rw_lock_t *)collector->rwl);
388
389
    /* destroy the collector */
390
0
    shm_free(collector);
391
0
  }
392
393
0
  return;
394
0
}
395
396
int stats_are_ready(void)
397
0
{
398
0
  return stats_ready;
399
0
}
400
401
/********************* Create/Register STATS functions ***********************/
402
403
/**
404
 * Note: certain statistics (e.g. shm statistics) require different handling,
405
 * hence the <unsafe> parameter
406
 */
407
static int __register_stat(str *module, str *name, stat_var **pvar,
408
          unsigned short flags, void *ctx, int unsafe)
409
0
{
410
0
  module_stats* mods;
411
0
  stat_var **shash;
412
0
  stat_var *stat;
413
0
  stat_var *it;
414
0
  int hash;
415
416
0
  if (module==0 || name==0 || pvar==0) {
417
0
    LM_ERR("invalid parameters module=%p, name=%p, pvar=%p \n",
418
0
        module, name, pvar);
419
0
    goto error;
420
0
  }
421
422
0
  if(flags&STAT_NOT_ALLOCATED){
423
0
    stat = *pvar;
424
0
    goto do_register;
425
0
  }
426
0
  stat = unsafe ?
427
0
      (stat_var*)shm_malloc_unsafe(sizeof(stat_var) +
428
0
      (((flags&STAT_SHM_NAME)==0)?name->len:0))
429
0
      :
430
0
      (stat_var*)shm_malloc(sizeof(stat_var) +
431
0
      (((flags&STAT_SHM_NAME)==0)?name->len:0));
432
433
0
  if (stat==0) {
434
0
    LM_ERR("no more shm memory\n");
435
0
    goto error;
436
0
  }
437
0
  memset( stat, 0, sizeof(stat_var) );
438
439
0
  if ( (flags&STAT_IS_FUNC)==0 ) {
440
0
    stat->u.val = unsafe ?
441
0
      (stat_val*)shm_malloc_unsafe(sizeof(stat_val)) :
442
0
      (stat_val*)shm_malloc(sizeof(stat_val));
443
0
    if (stat->u.val==0) {
444
0
      LM_ERR("no more shm memory\n");
445
0
      goto error1;
446
0
    }
447
#ifdef NO_ATOMIC_OPS
448
    *(stat->u.val) = 0;
449
#else
450
0
    atomic_init(stat->u.val, 0);
451
0
#endif
452
0
    *pvar = stat;
453
0
  } else {
454
0
    stat->u.f = (stat_function)(pvar);
455
0
  }
456
457
  /* is the module already recorded? */
458
0
do_register:
459
0
  mods = get_stat_module(module);
460
0
  if (mods==0) {
461
0
    mods = __add_stat_module(module, unsafe);
462
0
    if (mods==0) {
463
0
      LM_ERR("failed to add new module\n");
464
0
      goto error2;
465
0
    }
466
0
  }
467
468
  /* fill the stat record */
469
0
  stat->mod_idx = mods->idx;
470
471
0
  stat->name.len = name->len;
472
0
  if ( (flags&STAT_SHM_NAME)==0 ) {
473
0
    if(flags&STAT_NOT_ALLOCATED)
474
0
      stat->name.s = shm_malloc_unsafe(name->len);
475
0
    else
476
0
      stat->name.s = (char*)(stat+1);
477
      
478
0
    memcpy(stat->name.s, name->s, name->len);
479
0
  } else {
480
0
    stat->name.s = name->s;
481
0
  }
482
0
  stat->flags = flags;
483
0
  stat->context = ctx;
484
485
  /* compute the hash by name */
486
0
  hash = stat_hash( &stat->name );
487
488
  /* link it into appropriate hash table , with or without locking */
489
0
  if (mods->is_dyn) {
490
0
    lock_start_write((rw_lock_t *)collector->rwl);
491
0
    shash = collector->dy_hstats;
492
    /* double check for duplicates (due race conditions) */
493
0
    for( it=shash[hash] ; it ; it=stat->hnext ) {
494
0
      if ( (it->name.len==stat->name.len) &&
495
0
      (strncasecmp( it->name.s, stat->name.s, stat->name.len)==0) ) {
496
        /* duplicate found -> drop current stat and return the
497
         * found one */
498
0
        lock_stop_write((rw_lock_t *)collector->rwl);
499
500
0
        if (unsafe) {
501
0
          if (flags&STAT_SHM_NAME)
502
0
            shm_free_unsafe(stat->name.s);
503
504
0
          if ((flags&STAT_IS_FUNC)==0)
505
0
            shm_free_unsafe(stat->u.val);
506
507
0
          shm_free_unsafe(stat);
508
        
509
0
        } else {
510
0
          if (flags&STAT_SHM_NAME)
511
0
            shm_free(stat->name.s);
512
513
0
          if ((flags&STAT_IS_FUNC)==0)
514
0
            shm_free(stat->u.val);
515
516
0
          shm_free(stat);
517
0
        }
518
519
0
        if ((flags&STAT_IS_FUNC)==0)
520
0
          *pvar = it;
521
0
        return 0;
522
0
      }
523
0
    }
524
    /* new genuin stat-> continue */
525
0
  } else {
526
0
    shash = collector->hstats;
527
0
  }
528
529
0
  if (shash[hash]==0) {
530
0
    shash[hash] = stat;
531
0
  } else {
532
0
    it = shash[hash];
533
0
    while(it->hnext)
534
0
      it = it->hnext;
535
0
    it->hnext = stat;
536
0
  }
537
0
  collector->stats_no++;
538
539
  /* add the statistic also to the module statistic list */
540
0
  if (mods->tail) {
541
0
    mods->tail->lnext = stat;
542
0
  } else {
543
0
    mods->head = stat;
544
0
  }
545
0
  mods->tail = stat;
546
0
  mods->no++;
547
548
0
  if (mods->is_dyn)
549
0
    lock_stop_write((rw_lock_t *)collector->rwl);
550
551
0
  return 0;
552
553
0
error2:
554
0
  if ( (flags&STAT_IS_FUNC)==0 ) {
555
0
    if (unsafe)
556
0
      shm_free_unsafe(*pvar);
557
0
    else
558
0
      shm_free(*pvar);
559
0
    *pvar = 0;
560
0
  }
561
0
error1:
562
0
    if (unsafe)
563
0
      shm_free_unsafe(stat);
564
0
    else
565
0
      shm_free(stat);
566
0
error:
567
0
  if ( (flags&STAT_IS_FUNC)==0 && pvar!=NULL)
568
0
    *pvar = 0;
569
570
0
  return -1;
571
0
}
572
573
int register_stat2(const char *module, char *name, stat_var **pvar,
574
          unsigned short flags, void *ctx, int unsafe)
575
0
{
576
0
  str smodule, sname;
577
0
  init_str(&smodule, module);
578
0
  init_str(&sname, name);
579
0
  return __register_stat(&smodule, &sname, pvar, flags, ctx, unsafe);
580
0
}
581
582
int __register_dynamic_stat(str *group, str *name, stat_var **pvar)
583
0
{
584
0
  str dynamic_grp = str_init(DYNAMIC_MODULE_NAME);
585
586
0
  if (!group)
587
0
    group = &dynamic_grp;
588
589
0
  return __register_stat(group, name, pvar, 0/*flags*/, NULL, 0);
590
0
}
591
592
int register_dynamic_stat( str *name, stat_var **pvar)
593
0
{
594
0
  return __register_dynamic_stat (NULL, name, pvar);
595
0
}
596
597
int __register_module_stats(const char *module, const stat_export_t *stats, int unsafe)
598
0
{
599
0
  int ret;
600
601
0
  if (module==0 || module[0]==0 || !stats || !stats[0].name)
602
0
    return 0;
603
604
0
  for( ; stats->name ; stats++) {
605
0
    ret = register_stat2( module, stats->name, stats->stat_pointer,
606
0
      stats->flags, NULL, unsafe);
607
0
    if (ret!=0) {
608
0
      LM_CRIT("failed to add statistic\n");
609
0
      return -1;
610
0
    }
611
0
  }
612
613
0
  return 0;
614
0
}
615
616
617
stat_var* __get_stat( const str *name, int mod_idx )
618
0
{
619
0
  stat_var *stat;
620
0
  int hash;
621
622
0
  if (collector==NULL || name==0 || name->s==0 || name->len==0)
623
0
    return 0;
624
625
  /* compute the hash by name */
626
0
  hash = stat_hash( name );
627
628
  /* and look for it , first in the hash for static stats */
629
0
  for( stat=collector->hstats[hash] ; stat ; stat=stat->hnext ) {
630
0
    if ( !stat_is_hidden(stat) && (stat->name.len==name->len) &&
631
0
    (strncasecmp( stat->name.s, name->s, name->len)==0) &&
632
0
    (mod_idx < 0 || stat->mod_idx == mod_idx))
633
0
      return stat;
634
0
  }
635
  /* and then in the hash for dynamic stats */
636
0
  lock_start_read((rw_lock_t *)collector->rwl);
637
0
  for( stat=collector->dy_hstats[hash] ; stat ; stat=stat->hnext ) {
638
0
    if ( !stat_is_hidden(stat) && (stat->name.len==name->len) &&
639
0
    (strncasecmp( stat->name.s, name->s, name->len)==0) &&
640
0
    (mod_idx < 0 || stat->mod_idx == mod_idx)) {
641
0
      lock_stop_read((rw_lock_t *)collector->rwl);
642
0
      return stat;
643
0
    }
644
0
  }
645
0
  lock_stop_read((rw_lock_t *)collector->rwl);
646
647
0
  return 0;
648
0
}
649
650
stat_var* get_stat( const str *name )
651
0
{
652
0
  return __get_stat(name, -1);
653
0
}
654
655
int mi_stat_name(str *mod, str *stat, str *out)
656
0
{
657
0
  static str tmp_buf = {0, 0};
658
0
  char *tmp;
659
660
0
  if (mod) {
661
0
    tmp = pkg_realloc(tmp_buf.s, mod->len + stat->len + 1);
662
0
    if (!tmp) {
663
0
      LM_ERR("no more pkg memory\n");
664
0
      return -1;
665
0
    }
666
0
    tmp_buf.s = tmp;
667
668
0
    memcpy(tmp_buf.s, mod->s, mod->len);
669
0
    tmp_buf.len = mod->len;
670
0
    tmp_buf.s[tmp_buf.len++] = ':';
671
672
0
    memcpy(tmp_buf.s + tmp_buf.len, stat->s, stat->len);
673
0
    tmp_buf.len += stat->len;
674
675
0
    out->len = tmp_buf.len;
676
0
    out->s = tmp_buf.s;
677
0
  } else {
678
0
    out->len = stat->len;
679
0
    out->s = stat->s;
680
0
  }
681
0
  return 0;
682
0
}
683
684
int mi_print_stat(mi_item_t *resp_obj, str *mod, str *stat, unsigned long val)
685
0
{
686
0
  str tmp_buf;
687
688
0
  if (mi_stat_name(mod, stat, &tmp_buf) < 0) {
689
0
    LM_ERR("cannot get stat name\n");
690
0
    return -1;
691
0
  }
692
693
0
  if (add_mi_number(resp_obj, tmp_buf.s, tmp_buf.len, val) < 0) {
694
0
    LM_ERR("cannot add stat\n");
695
0
    return -1;
696
0
  }
697
0
  return 0;
698
0
}
699
700
701
str *get_stat_module_name(stat_var *stat)
702
0
{
703
0
  return &collector->amodules[stat->mod_idx].name;
704
0
}
705
706
707
/***************************** MI STUFF ********************************/
708
709
inline static int mi_add_stat(mi_item_t *resp_obj, stat_var *stat)
710
0
{
711
0
  return mi_print_stat(resp_obj, &collector->amodules[stat->mod_idx].name,
712
0
          &stat->name, get_stat_val(stat));
713
0
}
714
715
inline static int mi_list_stat(mi_item_t *resp_obj, str *mod, stat_var *stat)
716
0
{
717
0
  str tmp_buf;
718
0
  char *buf;
719
720
0
  if (mi_stat_name(mod, &stat->name, &tmp_buf) < 0) {
721
0
    LM_ERR("cannot get stat name\n");
722
0
    return -1;
723
0
  }
724
725
0
  if (stat->flags & (STAT_IS_FUNC|STAT_NO_RESET))
726
0
    buf = "non-incremental";
727
0
  else
728
0
    buf = "incremental";
729
730
0
  if (add_mi_string_fmt(resp_obj, tmp_buf.s, tmp_buf.len, "%s", buf)<0) {
731
0
    LM_ERR("cannot add stat\n");
732
0
    return -1;
733
0
  }
734
0
  return 0;
735
0
}
736
737
inline static int mi_add_module_stats(mi_item_t *resp_obj, module_stats *mods)
738
0
{
739
0
  stat_var *stat;
740
0
  int ret = 0;
741
742
0
  if (mods->is_dyn)
743
0
    lock_start_read((rw_lock_t *)collector->rwl);
744
745
0
  for( stat=mods->head ; stat ; stat=stat->lnext) {
746
0
    if (stat_is_hidden(stat))
747
0
      continue;
748
0
    ret = mi_print_stat(resp_obj, &mods->name, &stat->name,
749
0
        get_stat_val(stat));
750
0
    if (ret < 0)
751
0
      break;
752
0
  }
753
754
0
  if (mods->is_dyn)
755
0
    lock_stop_read((rw_lock_t *)collector->rwl);
756
757
0
  return ret;
758
0
}
759
760
inline static int mi_list_module_stats(mi_item_t *resp_obj, module_stats *mods)
761
0
{
762
0
  stat_var *stat;
763
0
  int ret = 0;
764
765
0
  if (mods->is_dyn)
766
0
    lock_start_read((rw_lock_t *)collector->rwl);
767
768
0
  for( stat=mods->head ; stat ; stat=stat->lnext) {
769
0
    if (stat_is_hidden(stat))
770
0
      continue;
771
0
    ret = mi_list_stat(resp_obj, &mods->name, stat);
772
0
    if (ret < 0)
773
0
      break;
774
0
  }
775
776
0
  if (mods->is_dyn)
777
0
    lock_stop_read((rw_lock_t *)collector->rwl);
778
779
0
  return ret;
780
0
}
781
782
783
static mi_response_t *mi_get_stats(const mi_params_t *params,
784
                struct mi_handler *async_hdl)
785
0
{
786
0
  mi_response_t *resp;
787
0
  mi_item_t *resp_obj;
788
0
  mi_item_t *params_arr;
789
0
  int i, j, no_params;
790
0
  int found;
791
0
  module_stats *mods;
792
0
  stat_var *stat;
793
0
  str val;
794
795
0
  resp = init_mi_result_object(&resp_obj);
796
0
  if (!resp)
797
0
    return 0;
798
799
0
  if (get_mi_array_param(params, "statistics", &params_arr, &no_params) < 0) {
800
0
    free_mi_response(resp);
801
0
    return init_mi_param_error();
802
0
  }
803
804
0
  for (i = 0; i < no_params; i++) {
805
0
    if (get_mi_arr_param_string(params_arr, i, &val.s, &val.len) < 0) {
806
0
      free_mi_response(resp);
807
0
      return init_mi_param_error();
808
0
    }
809
810
0
    if ( val.len==3 && memcmp(val.s,"all",3)==0) {
811
      /* add all statistic variables */
812
0
      for( j=0 ; j<collector->mod_no ;j++ ) {
813
0
        if (mi_add_module_stats(resp_obj, &collector->amodules[j] )!=0)
814
0
          goto error;
815
0
      }
816
817
0
      found = 1;
818
0
    } else if ( val.len>1 && val.s[val.len-1]==':') {
819
      /* add module statistics */
820
0
      val.len--;
821
0
      mods = get_stat_module( &val );
822
0
      if (mods==0)
823
0
        continue;
824
0
      if (mi_add_module_stats(resp_obj, mods)!=0)
825
0
        goto error;
826
827
0
      found = 1;
828
0
    } else {
829
      /* add only one statistic */
830
0
      stat = get_stat( &val );
831
0
      if (stat==0)
832
0
        continue;
833
0
      if (mi_add_stat(resp_obj, stat)!=0)
834
0
        goto error;
835
836
0
      found = 1;
837
0
    }
838
0
  }
839
840
0
  if (!found) {
841
0
    free_mi_response(resp);
842
0
    return init_mi_error(404, MI_SSTR("Statistics Not Found"));
843
0
  }
844
845
0
  return resp;
846
847
0
error:
848
0
  free_mi_response(resp);
849
0
  return 0;
850
0
}
851
852
static mi_response_t *w_mi_list_stats(const mi_params_t *params,
853
                struct mi_handler *async_hdl)
854
0
{
855
0
  mi_response_t *resp;
856
0
  mi_item_t *resp_obj;
857
0
  int i;
858
859
0
  resp = init_mi_result_object(&resp_obj);
860
0
  if (!resp)
861
0
    return 0;
862
863
0
  for( i=0 ; i<collector->mod_no ;i++ ) {
864
0
    if (mi_list_module_stats(resp_obj, &collector->amodules[i] )!=0) {
865
0
      free_mi_response(resp);
866
0
      return 0;
867
0
    }
868
0
  }
869
870
0
  return resp;
871
0
}
872
873
static mi_response_t *w_mi_list_stats_1(const mi_params_t *params,
874
                struct mi_handler *async_hdl)
875
0
{
876
0
  mi_response_t *resp;
877
0
  mi_item_t *resp_obj;
878
0
  mi_item_t *params_arr;
879
0
  int i, no_params;
880
0
  int found;
881
0
  module_stats   *mods;
882
0
  stat_var       *stat;
883
0
  str val;
884
885
0
  resp = init_mi_result_object(&resp_obj);
886
0
  if (!resp)
887
0
    return 0;
888
889
0
  if (get_mi_array_param(params, "statistics", &params_arr, &no_params) < 0) {
890
0
    free_mi_response(resp);
891
0
    return init_mi_param_error();
892
0
  }
893
894
0
  for (i = 0; i < no_params; i++) {
895
0
    if (get_mi_arr_param_string(params_arr, i, &val.s, &val.len) < 0) {
896
0
      free_mi_response(resp);
897
0
      return init_mi_param_error();
898
0
    }
899
900
0
    if ( val.len>1 && val.s[val.len-1]==':') {
901
      /* add module statistics */
902
0
      val.len--;
903
0
      mods = get_stat_module( &val );
904
0
      if (mods==0)
905
0
        continue;
906
0
      if (mi_list_module_stats(resp_obj, mods)!=0)
907
0
        goto error;
908
909
0
      found = 1;
910
0
    } else {
911
      /* add only one statistic */
912
0
      stat = get_stat( &val );
913
0
      if (stat==0)
914
0
        continue;
915
0
      if (mi_list_stat(resp_obj,NULL, stat)!=0)
916
0
        goto error;
917
918
0
      found = 1;
919
0
    }
920
0
  }
921
922
0
  if (!found) {
923
0
    free_mi_response(resp);
924
0
    return init_mi_error(404, MI_SSTR("Statistics Not Found"));
925
0
  }
926
927
0
  return resp;
928
929
0
error:
930
0
  free_mi_response(resp);
931
0
  return 0;
932
0
}
933
934
static mi_response_t *mi_reset_stats(const mi_params_t *params,
935
                struct mi_handler *async_hdl)
936
0
{
937
0
  mi_item_t *params_arr;
938
0
  int i, no_params;
939
0
  str val;
940
0
  stat_var *stat;
941
0
  int found = 0;
942
943
0
  if (get_mi_array_param(params, "statistics", &params_arr, &no_params) < 0)
944
0
    return init_mi_param_error();
945
946
0
  for (i = 0; i < no_params; i++) {
947
0
    if (get_mi_arr_param_string(params_arr, i, &val.s, &val.len) < 0)
948
0
      return init_mi_param_error();
949
950
0
    stat = get_stat(&val);
951
0
    if (stat==0)
952
0
      continue;
953
954
0
    reset_stat( stat );
955
0
    found = 1;
956
0
  }
957
958
0
  if (!found)
959
0
    return init_mi_error(404, MI_SSTR("Statistics Not Found"));
960
961
0
  return init_mi_result_ok();
962
0
}
963
964
static mi_response_t *mi_reset_all_stats(const mi_params_t *params,
965
                struct mi_handler *async_hdl)
966
0
{
967
0
  int i;
968
0
  stat_var *stat;
969
970
0
  if (collector==NULL)
971
0
    return 0;
972
973
  /* static stats */
974
0
  for (i=0;i<STATS_HASH_SIZE;i++) {
975
0
    for( stat=collector->hstats[i] ; stat ; stat=stat->hnext ) {
976
0
      if ( !stat_is_hidden(stat)) {
977
0
        reset_stat( stat );
978
0
      }
979
0
    }
980
0
  }
981
982
  /* dynamic stats */
983
0
  lock_start_read((rw_lock_t *)collector->rwl);
984
0
  for (i=0;i<STATS_HASH_SIZE;i++) {
985
0
    for( stat=collector->dy_hstats[i] ; stat ; stat=stat->hnext ) {
986
0
      if ( !stat_is_hidden(stat)) {
987
0
        reset_stat( stat );
988
0
      }
989
0
    }
990
0
  }
991
0
  lock_stop_read((rw_lock_t *)collector->rwl);
992
993
  /* module stats */
994
0
  for( i=0 ; i<collector->mod_no ; i++ ) {
995
0
    for( stat=collector->amodules[i].head ; stat ; stat=stat->hnext ) {
996
0
      if ( !stat_is_hidden(stat)) {
997
0
        reset_stat( stat );
998
0
      }
999
0
    }
1000
0
  }
1001
1002
0
  return init_mi_result_ok();
1003
0
}
1004
1005
void stats_mod_lock(module_stats *mod)
1006
0
{
1007
0
  if (mod->is_dyn)
1008
0
    lock_start_read((rw_lock_t *)collector->rwl);
1009
0
}
1010
1011
void stats_mod_unlock(module_stats *mod)
1012
0
{
1013
0
  if (mod->is_dyn)
1014
0
    lock_stop_read((rw_lock_t *)collector->rwl);
1015
0
}
1016
1017
group_stats *register_stats_group(const char *name)
1018
0
{
1019
0
  group_stats *grp = shm_malloc(sizeof *grp);
1020
0
  if (!grp)
1021
0
    return NULL;
1022
0
  memset(grp, 0, sizeof *grp);
1023
0
  init_str(&grp->name, name);
1024
0
  grp->vars = shm_malloc(sizeof(stat_var *));
1025
0
  if (!grp->vars) {
1026
0
    shm_free(grp);
1027
0
    return NULL;
1028
0
  }
1029
1030
0
  grp->next = collector->groups;
1031
0
  collector->groups = grp;
1032
1033
0
  return grp;
1034
0
}
1035
1036
int add_stats_group(group_stats *grp, stat_var *stat)
1037
0
{
1038
0
  stat_var **stats = shm_realloc(grp->vars, sizeof(stat_var) * grp->no + 1);
1039
0
  if (!stats)
1040
0
    return -1;
1041
0
  grp->vars = stats;
1042
0
  grp->vars[grp->no++] = stat;
1043
0
  stat->flags |= STAT_HAS_GROUP;
1044
0
  return 0;
1045
0
}
1046
1047
group_stats *get_stat_group(stat_var *stat)
1048
0
{
1049
0
  int s;
1050
0
  group_stats *grp;
1051
0
  for (grp = collector->groups; grp; grp = grp->next)
1052
0
    for (s = 0; s < grp->no; s++)
1053
0
      if (grp->vars[s] == stat)
1054
0
        return grp;
1055
0
  return NULL;
1056
0
}
1057
1058
group_stats *find_stat_group(str *name)
1059
0
{
1060
0
  group_stats *grp;
1061
0
  for (grp = collector->groups; grp; grp = grp->next)
1062
0
    if (str_strcmp(name, &grp->name) == 0)
1063
0
      return grp;
1064
0
  return NULL;
1065
0
}
1066
1067
#endif /*STATISTICS*/