Coverage Report

Created: 2025-07-11 06:28

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