Coverage Report

Created: 2025-08-29 06:53

/src/opensips/pt_load.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2018 OpenSIPS Solutions
3
 *
4
 * This file is part of opensips, a free SIP server.
5
 *
6
 * opensips is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version
10
 *
11
 * opensips is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
22
#include <string.h>
23
#include <sys/time.h>
24
#include <stdio.h>
25
26
#include "dprint.h"
27
#include "pt_load.h"
28
#include "pt.h"
29
#include "ut.h"
30
31
0
#define PT_LOAD(_pno)    pt[_pno].load
32
33
0
#define MY_LOAD          PT_LOAD(process_no)
34
35
#define FOR_ALL_INDEXES(_it, _old, _new, _TYPE) \
36
0
  for( _it=(_old+1)%_TYPE##_WINDOW_SIZE ; _it!=_new ;\
37
0
    _it=(_it+1)%_TYPE##_WINDOW_SIZE )
38
39
40
#define MARK_AS_IDLE( _now, _TYPE) \
41
0
  do { \
42
0
    /* check if entire time window was idle */ \
43
0
    if (_now-MY_LOAD.last_time >= _TYPE##_WINDOW_TIME) { \
44
0
      /* all was idle, so make it zero */ \
45
0
      MY_LOAD._TYPE##_window[0] = 0; \
46
0
      FOR_ALL_INDEXES( i, 0, 0, _TYPE) \
47
0
        MY_LOAD._TYPE##_window[i] = 0; \
48
0
    } else { \
49
0
      /* get the index inside window for the last time update */ \
50
0
      idx_old = (MY_LOAD.last_time / _TYPE##_WINDOW_UNIT) % \
51
0
        _TYPE##_WINDOW_SIZE; \
52
0
      idx_new = (_now / _TYPE##_WINDOW_UNIT) % _TYPE##_WINDOW_SIZE; \
53
0
      if (idx_old!=idx_new) { \
54
0
        FOR_ALL_INDEXES( i, idx_old, idx_new, _TYPE) { \
55
0
          MY_LOAD._TYPE##_window[i] = 0; \
56
0
        } \
57
0
        MY_LOAD._TYPE##_window[idx_new] = 0; \
58
0
      }\
59
0
    } \
60
0
  }while(0)
61
62
63
void pt_become_active(void)
64
0
{
65
0
  utime_t usec_now;
66
0
  struct timeval tv;
67
0
  int idx_old, idx_new, i;
68
69
0
  gettimeofday( &tv, NULL);
70
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
71
0
  if (usec_now==MY_LOAD.last_time) {
72
0
    MY_LOAD.is_busy = 1;
73
0
    return;
74
0
  }
75
76
0
  MARK_AS_IDLE( usec_now, ST);
77
0
  MARK_AS_IDLE( usec_now, LT);
78
79
0
  MY_LOAD.last_time = usec_now;
80
0
  MY_LOAD.is_busy = 1;
81
0
}
82
83
84
#define MARK_AS_ACTIVE( _now, _TYPE) \
85
0
  do { \
86
0
    /* check if the entire time window was active */ \
87
0
    if ((_now-MY_LOAD.last_time) >= _TYPE##_WINDOW_TIME) { \
88
0
      /* all was busy, so make it "full" */ \
89
0
      MY_LOAD._TYPE##_window[0] = _TYPE##_WINDOW_UNIT; \
90
0
      FOR_ALL_INDEXES( i, 0, 0, _TYPE) \
91
0
        MY_LOAD._TYPE##_window[i] = _TYPE##_WINDOW_UNIT; \
92
0
    } else { \
93
0
      /* get the index inside window for the last time update */ \
94
0
      idx_old = (MY_LOAD.last_time / _TYPE##_WINDOW_UNIT) % \
95
0
        _TYPE##_WINDOW_SIZE; \
96
0
      idx_new = (_now / _TYPE##_WINDOW_UNIT) % \
97
0
        _TYPE##_WINDOW_SIZE; \
98
0
      if (idx_old!=idx_new) { \
99
0
        /* do partial update on the last used index */ \
100
0
        MY_LOAD._TYPE##_window[idx_old] += _TYPE##_WINDOW_UNIT - \
101
0
          (MY_LOAD.last_time % _TYPE##_WINDOW_UNIT); \
102
0
        /* update the fully used */ \
103
0
        FOR_ALL_INDEXES( i, idx_old, idx_new, _TYPE) { \
104
0
          MY_LOAD._TYPE##_window[i] = _TYPE##_WINDOW_UNIT; \
105
0
        } \
106
0
        /* do partial update on the last used index */ \
107
0
        MY_LOAD._TYPE##_window[i] = _now % _TYPE##_WINDOW_UNIT; \
108
0
      } else { \
109
0
        MY_LOAD._TYPE##_window[idx_old] += \
110
0
          (_now % _TYPE##_WINDOW_UNIT) \
111
0
           - \
112
0
          (MY_LOAD.last_time % _TYPE##_WINDOW_UNIT); \
113
0
      } \
114
0
    } \
115
0
  }while(0)
116
117
void pt_become_idle(void)
118
0
{
119
0
  utime_t usec_now;
120
0
  struct timeval tv;
121
0
  int idx_old, idx_new, i;
122
123
0
  gettimeofday( &tv, NULL);
124
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
125
0
  if (usec_now==MY_LOAD.last_time) {
126
0
    MY_LOAD.is_busy = 0;
127
0
    return;
128
0
  }
129
130
0
  MARK_AS_ACTIVE( usec_now, ST);
131
0
  MARK_AS_ACTIVE( usec_now, LT);
132
133
0
  MY_LOAD.last_time = usec_now;
134
0
  MY_LOAD.is_busy = 0;
135
0
}
136
137
138
#define SUM_UP_LOAD(_now, _pno, _TYPE, _ratio) \
139
0
  do { \
140
0
    /* check if the entire time window has the same status */ \
141
0
    if (((long long)_now-(long long)PT_LOAD(_pno).last_time) >= \
142
0
        (_TYPE##_WINDOW_TIME)*(_ratio)){\
143
0
      /* nothing recorded in the last time window */ \
144
0
      used += PT_LOAD(_pno).is_busy?_TYPE##_WINDOW_TIME*_ratio:0; \
145
0
    } else { \
146
0
      /* get the index inside window for the last time update */ \
147
0
      idx_old = (PT_LOAD(_pno).last_time / _TYPE##_WINDOW_UNIT) % \
148
0
        _TYPE##_WINDOW_SIZE; \
149
0
      idx_new = (_now / _TYPE##_WINDOW_UNIT) % \
150
0
        _TYPE##_WINDOW_SIZE; \
151
0
      /* ajust the index where we start counting the past used-time \
152
0
       * based on the "ratio" option, if present */  \
153
0
      if (_ratio!=1) { \
154
0
        idx_start = (idx_new+(int)(_TYPE##_WINDOW_SIZE*(1-_ratio))) % \
155
0
          _TYPE##_WINDOW_SIZE; \
156
0
        /* the start is between [new,old], so no used recorded yet */ \
157
0
        if (idx_start>=idx_old && idx_start<=idx_new) {\
158
0
          used+= PT_LOAD(_pno).is_busy?_TYPE##_WINDOW_TIME*_ratio:0;\
159
0
          break; \
160
0
        }\
161
0
      } else { \
162
0
        idx_start = idx_new; \
163
0
      }\
164
0
      /* sum up the already accounted used time */ \
165
0
      FOR_ALL_INDEXES( i, idx_start, idx_old, _TYPE) { \
166
0
        used += PT_LOAD(_pno)._TYPE##_window[i]; \
167
0
      } \
168
0
      /* add what is not accounted since last update */ \
169
0
      if (PT_LOAD(_pno).is_busy) { \
170
0
        if (idx_old!=idx_new) { \
171
0
          /* count the last used index (existing + new) */ \
172
0
          used += PT_LOAD(_pno)._TYPE##_window[idx_old] + \
173
0
            (_TYPE##_WINDOW_UNIT - \
174
0
            (PT_LOAD(_pno).last_time % _TYPE##_WINDOW_UNIT)); \
175
0
          /* update the fully used */ \
176
0
          FOR_ALL_INDEXES( i, idx_old, idx_new, _TYPE) { \
177
0
            used += _TYPE##_WINDOW_UNIT; \
178
0
          } \
179
0
          /* do partial update on current index */ \
180
0
          used += _now % _TYPE##_WINDOW_UNIT; \
181
0
        } else { \
182
0
          used += \
183
0
            (_now % _TYPE##_WINDOW_UNIT) \
184
0
             - \
185
0
            (PT_LOAD(_pno).last_time % _TYPE##_WINDOW_UNIT); \
186
0
        } \
187
0
      } \
188
0
    } \
189
0
  } while(0)
190
191
unsigned int pt_get_rt_proc_load( int pno )
192
0
{
193
0
  utime_t usec_now;
194
0
  struct timeval tv;
195
0
  int idx_old, idx_new, idx_start, i; /* used inside the macro */
196
0
  unsigned long long used = 0;
197
198
0
  gettimeofday( &tv, NULL);
199
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
200
201
0
  SUM_UP_LOAD( usec_now, pno, ST, 1);
202
203
0
  return (used*100/ST_WINDOW_TIME);
204
0
}
205
206
207
unsigned int pt_get_1m_proc_load( int pno )
208
0
{
209
0
  utime_t usec_now;
210
0
  struct timeval tv;
211
0
  int idx_old, idx_new, idx_start, i; /* used inside the macro */
212
0
  unsigned long long used = 0;
213
214
0
  gettimeofday( &tv, NULL);
215
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
216
217
0
  SUM_UP_LOAD( usec_now, pno, LT, LT_1m_RATIO);
218
219
0
  return (used*100/(LT_WINDOW_TIME*LT_1m_RATIO));
220
0
}
221
222
223
unsigned int pt_get_10m_proc_load( int pno )
224
0
{
225
0
  utime_t usec_now;
226
0
  struct timeval tv;
227
0
  int idx_old, idx_new, idx_start, i; /* used inside the macro */
228
0
  unsigned long long used = 0;
229
230
0
  gettimeofday( &tv, NULL);
231
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
232
233
0
  SUM_UP_LOAD( usec_now, pno, LT, 1);
234
235
0
  return (used*100/LT_WINDOW_TIME);
236
0
}
237
238
239
unsigned int pt_get_rt_load(int _)
240
0
{
241
0
  utime_t usec_now;
242
0
  struct timeval tv;
243
0
  int idx_old, idx_new, idx_start, i; /* used inside the macro */
244
0
  unsigned int n, summed_procs=0;
245
0
  unsigned long long used = 0;
246
247
0
  gettimeofday( &tv, NULL);
248
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
249
250
0
  for( n=0 ; n<counted_max_processes; n++)
251
0
    if ( is_process_running(n) &&
252
0
    (pt[n].flags&(OSS_PROC_NO_LOAD|OSS_PROC_IS_EXTRA))==0 ) {
253
0
      SUM_UP_LOAD( usec_now, n, ST, 1);
254
0
      summed_procs++;
255
0
    }
256
0
  if (!summed_procs)
257
0
    return 0;
258
259
0
  return (used*100/(ST_WINDOW_TIME*summed_procs));
260
0
}
261
262
263
unsigned int pt_get_1m_load(int _)
264
0
{
265
0
  utime_t usec_now;
266
0
  struct timeval tv;
267
0
  int idx_old, idx_new, idx_start, i; /* used inside the macro */
268
0
  unsigned int n, summed_procs=0;
269
0
  unsigned long long used = 0;
270
271
0
  gettimeofday( &tv, NULL);
272
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
273
274
0
  for( n=0 ; n<counted_max_processes; n++)
275
0
    if ( is_process_running(n) &&
276
0
    (pt[n].flags&(OSS_PROC_NO_LOAD|OSS_PROC_IS_EXTRA))==0 ) {
277
0
      SUM_UP_LOAD( usec_now, n, LT, LT_1m_RATIO);
278
0
      summed_procs++;
279
0
    }
280
0
  if (!summed_procs)
281
0
    return 0;
282
283
0
  return (used*100/((long long)LT_WINDOW_TIME*summed_procs*LT_1m_RATIO));
284
0
}
285
286
287
unsigned int pt_get_10m_load(int _)
288
0
{
289
0
  utime_t usec_now;
290
0
  struct timeval tv;
291
0
  int idx_old, idx_new, idx_start, i; /* used inside the macro */
292
0
  unsigned int n, summed_procs=0;
293
0
  unsigned long long used = 0;
294
295
0
  gettimeofday( &tv, NULL);
296
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
297
298
0
  for( n=0 ; n<counted_max_processes; n++)
299
0
    if ( is_process_running(n) &&
300
0
    (pt[n].flags&(OSS_PROC_NO_LOAD|OSS_PROC_IS_EXTRA))==0 ) {
301
0
      SUM_UP_LOAD( usec_now, n, LT, 1);
302
0
      summed_procs++;
303
0
    }
304
0
  if (!summed_procs)
305
0
    return 0;
306
307
0
  return (used*100/((long long)LT_WINDOW_TIME*summed_procs));
308
0
}
309
310
311
unsigned int pt_get_rt_loadall(int _)
312
0
{
313
0
  utime_t usec_now;
314
0
  struct timeval tv;
315
0
  int idx_old, idx_new, idx_start, i; /* used inside the macro */
316
0
  unsigned int n, summed_procs=0;
317
0
  unsigned long long used = 0;
318
319
0
  gettimeofday( &tv, NULL);
320
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
321
322
0
  for( n=0 ; n<counted_max_processes; n++)
323
0
    if ( is_process_running(n) && (pt[n].flags&OSS_PROC_NO_LOAD)==0 ) {
324
0
      SUM_UP_LOAD( usec_now, n, ST, 1);
325
0
      summed_procs++;
326
0
    }
327
0
  if (!summed_procs)
328
0
    return 0;
329
330
0
  return (used*100/((long long)ST_WINDOW_TIME*summed_procs));
331
0
}
332
333
334
unsigned int pt_get_1m_loadall(int _)
335
0
{
336
0
  utime_t usec_now;
337
0
  struct timeval tv;
338
0
  int idx_old, idx_new, idx_start, i; /* used inside the macro */
339
0
  unsigned int n, summed_procs=0;
340
0
  unsigned long long used = 0;
341
342
0
  gettimeofday( &tv, NULL);
343
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
344
345
0
  for( n=0 ; n<counted_max_processes; n++)
346
0
    if ( is_process_running(n) && (pt[n].flags&OSS_PROC_NO_LOAD)==0 ) {
347
0
      SUM_UP_LOAD( usec_now, n, LT, LT_1m_RATIO);
348
0
      summed_procs++;
349
0
    }
350
0
  if (!summed_procs)
351
0
    return 0;
352
353
0
  return (used*100/((long long)LT_WINDOW_TIME*summed_procs*LT_1m_RATIO));
354
0
}
355
356
357
unsigned int pt_get_10m_loadall(int _)
358
0
{
359
0
  utime_t usec_now;
360
0
  struct timeval tv;
361
0
  int idx_old, idx_new, idx_start, i; /* used inside the macro */
362
0
  unsigned int n, summed_procs=0;
363
0
  unsigned long long used = 0;
364
365
0
  gettimeofday( &tv, NULL);
366
0
  usec_now = ((utime_t)(tv.tv_sec)) * 1000000 + tv.tv_usec;
367
368
0
  for( n=0 ; n<counted_max_processes; n++)
369
0
    if ( is_process_running(n) && (pt[n].flags&OSS_PROC_NO_LOAD)==0 ) {
370
0
      SUM_UP_LOAD( usec_now, n, LT, 1);
371
0
      summed_procs++;
372
0
    }
373
0
  if (!summed_procs)
374
0
    return 0;
375
376
0
  return (used*100/((long long)LT_WINDOW_TIME*summed_procs));
377
0
}
378
379
380
int register_processes_load_stats(int procs_no)
381
0
{
382
0
  char *stat_name;
383
0
  str stat_prefix;
384
0
  char *pno_s;
385
0
  str name;
386
0
  int pno;
387
388
0
  group_stats *load_proc_grp, *load_proc_1m_grp, *load_proc_10m_grp;
389
390
0
  load_proc_grp = register_stats_group("proc_load");
391
0
  if (!load_proc_grp) {
392
0
    LM_ERR("could not register stats group proc_load");
393
0
    return -1;
394
0
  }
395
0
  load_proc_1m_grp = register_stats_group("proc_load1m");
396
0
  if (!load_proc_1m_grp) {
397
0
    LM_ERR("could not register stats group proc_load1m");
398
0
    return -1;
399
0
  }
400
0
  load_proc_10m_grp = register_stats_group("proc_load10m");
401
0
  if (!load_proc_10m_grp) {
402
0
    LM_ERR("could not register stats group proc_load10m");
403
0
    return -1;
404
0
  }
405
406
  /* build the stats and register them for each potential process
407
   * skipp the attendant, id 0 */
408
0
  for( pno=1 ; pno<procs_no ; pno++) {
409
410
0
    pno_s = int2str( (unsigned int)pno, NULL);
411
412
0
    stat_prefix.s = "load-proc";
413
0
    stat_prefix.len = sizeof("load-proc")-1;
414
0
    if ( (stat_name = build_stat_name( &stat_prefix, pno_s)) == 0 ||
415
0
    register_stat2( "load", stat_name, (stat_var**)pt_get_rt_proc_load,
416
0
    STAT_IS_FUNC|STAT_PER_PROC, (void*)(long)pno, 0) != 0) {
417
0
      LM_ERR("failed to add RT load stat for process %d\n",pno);
418
0
    return -1;
419
0
    }
420
0
    name.s = stat_name;
421
0
    name.len = strlen(stat_name);
422
0
    pt[pno].load_rt = get_stat(&name);
423
0
    pt[pno].load_rt->flags |= STAT_HIDDEN;
424
0
    add_stats_group(load_proc_grp, pt[pno].load_rt);
425
426
0
    stat_prefix.s = "load1m-proc";
427
0
    stat_prefix.len = sizeof("load1m-proc")-1;
428
0
    if ( (stat_name = build_stat_name( &stat_prefix, pno_s)) == 0 ||
429
0
    register_stat2( "load", stat_name, (stat_var**)pt_get_1m_proc_load,
430
0
    STAT_IS_FUNC|STAT_PER_PROC, (void*)(long)pno, 0) != 0) {
431
0
      LM_ERR("failed to add RT load stat for process %d\n",pno);
432
0
      return -1;
433
0
    }
434
0
    name.s = stat_name;
435
0
    name.len = strlen(stat_name);
436
0
    pt[pno].load_1m = get_stat(&name);
437
0
    pt[pno].load_1m->flags |= STAT_HIDDEN;
438
0
    add_stats_group(load_proc_1m_grp, pt[pno].load_1m);
439
440
0
    stat_prefix.s = "load10m-proc";
441
0
    stat_prefix.len = sizeof("load10m-proc")-1;
442
0
    if ( (stat_name = build_stat_name( &stat_prefix, pno_s)) == 0 ||
443
0
    register_stat2( "load", stat_name, (stat_var**)pt_get_10m_proc_load,
444
0
    STAT_IS_FUNC|STAT_PER_PROC, (void*)(long)pno, 0) != 0) {
445
0
      LM_ERR("failed to add RT load stat for process %d\n",pno);
446
0
      return -1;
447
0
    }
448
0
    name.s = stat_name;
449
0
    name.len = strlen(stat_name);
450
0
    pt[pno].load_10m = get_stat(&name);
451
0
    pt[pno].load_10m->flags |= STAT_HIDDEN;
452
0
    add_stats_group(load_proc_10m_grp, pt[pno].load_10m);
453
0
  }
454
455
0
  return 0;
456
0
}