Coverage Report

Created: 2025-07-11 07:06

/src/rtpproxy/src/rtpp_stats.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014-2015 Sippy Software, Inc., http://www.sippysoft.com
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
 * SUCH DAMAGE.
25
 *
26
 */
27
28
#include <sys/types.h>
29
#include <assert.h>
30
#include <inttypes.h>
31
#include <pthread.h>
32
#include <stdatomic.h>
33
#include <stddef.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
38
#include "rtpp_types.h"
39
#include "rtpp_pearson_perfect.h"
40
#include "rtpp_codeptr.h"
41
#include "rtpp_refcnt.h"
42
#include "rtpp_stats.h"
43
#include "rtpp_stats_fin.h"
44
#include "rtpp_time.h"
45
#include "rtpp_mallocs.h"
46
#include "rtpp_command_reply.h"
47
48
struct rtpp_stat_derived;
49
50
enum rtpp_cnt_type {
51
    RTPP_CNT_U64,
52
    RTPP_CNT_DBL
53
};
54
55
struct rtpp_stat_descr
56
{
57
    const char *name;
58
    const char *descr;
59
    enum rtpp_cnt_type type;
60
    const char *derive_from;
61
};
62
63
union rtpp_stat_cnt {
64
    _Atomic(uint64_t) u64;
65
    double d;
66
};
67
68
struct rtpp_stat
69
{
70
    struct rtpp_stat_descr *descr;
71
    pthread_mutex_t mutex;
72
    union rtpp_stat_cnt cnt;
73
};
74
75
struct rtpp_stat_derived
76
{
77
    struct rtpp_stat *derive_from;
78
    struct rtpp_stat *derive_to;
79
    double last_ts;
80
    union rtpp_stat_cnt last_val;
81
};
82
83
static struct rtpp_stat_descr default_stats[] = {
84
    {.name = "nsess_created",        .descr = "Number of RTP sessions created", .type = RTPP_CNT_U64},
85
    {.name = "nsess_destroyed",      .descr = "Number of RTP sessions destroyed", .type = RTPP_CNT_U64},
86
    {.name = "nsess_timeout",        .descr = "Number of RTP sessions ended due to media timeout", .type = RTPP_CNT_U64},
87
    {.name = "nsess_complete",       .descr = "Number of RTP sessions fully setup", .type = RTPP_CNT_U64},
88
    {.name = "nsess_nortp",          .descr = "Number of sessions that had no RTP neither in nor out", .type = RTPP_CNT_U64},
89
    {.name = "nsess_owrtp",          .descr = "Number of sessions that had one-way RTP only", .type = RTPP_CNT_U64},
90
    {.name = "nsess_nortcp",         .descr = "Number of sessions that had no RTCP neither in nor out", .type = RTPP_CNT_U64},
91
    {.name = "nsess_owrtcp",         .descr = "Number of sessions that had one-way RTCP only", .type = RTPP_CNT_U64}, 
92
    {.name = "nplrs_created",        .descr = "Number of RTP players created", .type = RTPP_CNT_U64},
93
    {.name = "nplrs_destroyed",      .descr = "Number of RTP players destroyed", .type = RTPP_CNT_U64},
94
    {.name = "npkts_rcvd",           .descr = "Total number of RTP/RTPC packets received", .type = RTPP_CNT_U64},
95
    {.name = "npkts_played",         .descr = "Total number of RTP packets locally generated (played out)", .type = RTPP_CNT_U64},
96
    {.name = "npkts_relayed",        .descr = "Total number of RTP/RTPC packets relayed", .type = RTPP_CNT_U64},
97
    {.name = "npkts_resizer_in",     .descr = "Total number of RTP packets ingress into resizer (re-packetizer)", .type = RTPP_CNT_U64},
98
    {.name = "npkts_resizer_out",    .descr = "Total number of RTP packets egress out of resizer (re-packetizer)", .type = RTPP_CNT_U64},
99
    {.name = "npkts_resizer_discard",.descr = "Total number of RTP packets dropped by the resizer (re-packetizer)", .type = RTPP_CNT_U64},
100
    {.name = "npkts_discard",        .descr = "Total number of RTP/RTPC packets discarded", .type = RTPP_CNT_U64},
101
    {.name = "total_duration",       .descr = "Cumulative duration of all sessions", .type = RTPP_CNT_DBL},
102
    {.name = "ncmds_rcvd",           .descr = "Total number of control commands received", .type = RTPP_CNT_U64},
103
    {.name = "ncmds_rcvd_ndups",     .descr = "Total number of duplicate control commands received", .type = RTPP_CNT_U64},
104
    {.name = "ncmds_succd",          .descr = "Total number of control commands successfully processed", .type = RTPP_CNT_U64},
105
    {.name = "ncmds_errs",           .descr = "Total number of control commands ended up with an error", .type = RTPP_CNT_U64},
106
    {.name = "ncmds_repld",          .descr = "Total number of control commands that had a reply generated", .type = RTPP_CNT_U64},
107
    {.name = "rtpa_nsent",           .descr = "Total number of uniqie RTP packets sent to us based on SEQ tracking", .type = RTPP_CNT_U64},
108
    {.name = "rtpa_nrcvd",           .descr = "Total number of unique RTP packets received by us based on SEQ tracking", .type = RTPP_CNT_U64},
109
    {.name = "rtpa_ndups",           .descr = "Total number of duplicate RTP packets received by us based on SEQ tracking", .type = RTPP_CNT_U64},
110
    {.name = "rtpa_nlost",           .descr = "Total number of lost RTP packets based on SEQ tracking", .type = RTPP_CNT_U64},
111
    {.name = "rtpa_perrs",           .descr = "Total number of RTP packets that failed RTP parse routine in SEQ tracking", .type = RTPP_CNT_U64},
112
    {.name = "pps_in",               .descr = "Rate at which RTP/RTPC packets are received (packets per second)", .type = RTPP_CNT_DBL, .derive_from = "npkts_rcvd"},
113
    {.name = NULL}
114
};
115
116
struct rtpp_stats_priv
117
{
118
    int nstats;
119
    int nstats_derived;
120
    struct rtpp_stat *stats;
121
    struct rtpp_stat_derived *dstats;
122
    struct rtpp_pearson_perfect *rppp;
123
};
124
125
struct rtpp_stats_full
126
{
127
    struct rtpp_stats pub;
128
    struct rtpp_stats_priv pvt;
129
};
130
131
static void rtpp_stats_dtor(struct rtpp_stats_full *);
132
static int rtpp_stats_getidxbyname(struct rtpp_stats *, const char *);
133
static int rtpp_stats_updatebyidx(struct rtpp_stats *, int, uint64_t);
134
static int rtpp_stats_updatebyname(struct rtpp_stats *, const char *, uint64_t);
135
static int rtpp_stats_updatebyname_d(struct rtpp_stats *, const char *, double);
136
static int64_t rtpp_stats_getlvalbyname(struct rtpp_stats *, const char *);
137
static int rtpp_stats_nstr(struct rtpp_stats *, const char *, struct rtpc_reply *);
138
static int rtpp_stats_getnstats(struct rtpp_stats *);
139
static void rtpp_stats_update_derived(struct rtpp_stats *, double);
140
141
DEFINE_SMETHODS(rtpp_stats,
142
    .getidxbyname = &rtpp_stats_getidxbyname,
143
    .updatebyidx = &rtpp_stats_updatebyidx,
144
    .updatebyname = &rtpp_stats_updatebyname,
145
    .updatebyname_d = &rtpp_stats_updatebyname_d,
146
    .getlvalbyname = &rtpp_stats_getlvalbyname,
147
    .getnstats = &rtpp_stats_getnstats,
148
    .nstr = &rtpp_stats_nstr,
149
    .update_derived = &rtpp_stats_update_derived
150
);
151
152
static const char *
153
getdstat(void *p, int n)
154
204k
{
155
204k
    struct rtpp_stats_priv *pvt;
156
157
204k
    pvt = (struct rtpp_stats_priv *)p;
158
204k
    if (n >= pvt->nstats) {
159
2
        return (NULL);
160
2
    }
161
162
204k
    return (pvt->stats[n].descr->name);
163
204k
}
164
165
static int
166
count_rtpp_stats(struct rtpp_stat_descr *sp)
167
2
{
168
2
    int nstats, i;
169
170
2
    nstats = 0;
171
60
    for (i = 0; sp[i].name != NULL; i++) {
172
58
        nstats += 1;
173
58
    }
174
2
    return (nstats);
175
2
}
176
177
static int
178
count_rtpp_stats_derived(struct rtpp_stat_descr *sp)
179
2
{
180
2
    int nstats, i;
181
182
2
    nstats = 0;
183
60
    for (i = 0; sp[i].name != NULL; i++) {
184
58
        if (sp[i].derive_from == NULL)
185
56
            continue;
186
2
        nstats += 1;
187
2
    }
188
2
    return (nstats);
189
2
}
190
191
struct rtpp_stats *
192
rtpp_stats_ctor(void)
193
2
{
194
2
    struct rtpp_stats_full *fp;
195
2
    struct rtpp_stats *pub;
196
2
    struct rtpp_stats_priv *pvt;
197
2
    struct rtpp_stat *st;
198
2
    struct rtpp_stat_derived *dst;
199
2
    int i, idx;
200
201
2
    fp = rtpp_rzmalloc(sizeof(struct rtpp_stats_full), PVT_RCOFFS(fp));
202
2
    if (fp == NULL) {
203
0
        goto e0;
204
0
    }
205
2
    pub = &(fp->pub);
206
2
    pvt = &(fp->pvt);
207
2
    pvt->stats = rtpp_zmalloc(sizeof(struct rtpp_stat) *
208
2
      count_rtpp_stats(default_stats));
209
2
    if (pvt->stats == NULL) {
210
0
        goto e1;
211
0
    }
212
2
    i = count_rtpp_stats_derived(default_stats);
213
2
    if (i > 0) {
214
2
        pvt->dstats = rtpp_zmalloc(sizeof(struct rtpp_stat_derived) * i);
215
2
        if (pvt->dstats == NULL)
216
0
            goto e2;
217
2
    }
218
60
    for (i = 0; default_stats[i].name != NULL; i++) {
219
58
        st = &pvt->stats[pvt->nstats];
220
58
        st->descr = &default_stats[i];
221
58
        if (pthread_mutex_init(&st->mutex, NULL) != 0) {
222
0
            while ((pvt->nstats - 1) >= 0) {
223
0
                st = &pvt->stats[pvt->nstats - 1];
224
0
                pthread_mutex_destroy(&st->mutex);
225
0
                pvt->nstats -= 1;
226
0
            }
227
0
            goto e2;
228
0
        }
229
58
        if (default_stats[i].type == RTPP_CNT_U64) {
230
54
            atomic_init(&st->cnt.u64, 0);
231
54
        } else {
232
4
            st->cnt.d = 0.0;
233
4
        }
234
58
        pvt->nstats += 1;
235
58
    }
236
2
    pvt->rppp = rtpp_pearson_perfect_ctor(getdstat, pvt);
237
2
    if (pvt->rppp == NULL) {
238
0
        goto e2;
239
0
    }
240
2
    pub->pvt = pvt;
241
60
    for (i = 0; default_stats[i].name != NULL; i++) {
242
58
        if (default_stats[i].derive_from == NULL)
243
56
            continue;
244
2
        dst = &pvt->dstats[pvt->nstats_derived];
245
2
        idx = rtpp_stats_getidxbyname(pub, default_stats[i].name);
246
2
        dst->derive_to = &pvt->stats[idx];
247
2
        idx = rtpp_stats_getidxbyname(pub, default_stats[i].derive_from);
248
2
        dst->derive_from = &pvt->stats[idx];
249
2
        pvt->nstats_derived += 1;
250
2
        dst->last_ts = getdtime();
251
2
    }
252
2
    PUBINST_FININIT(pub, fp, rtpp_stats_dtor);
253
2
    return (pub);
254
0
e2:
255
0
    if (pvt->dstats != NULL)
256
0
        free(pvt->dstats);
257
0
    free(pvt->stats);
258
0
e1:
259
0
    RTPP_OBJ_DECREF(pub);
260
0
e0:
261
0
    return (NULL);
262
0
}
263
264
static int
265
rtpp_stats_getidxbyname(struct rtpp_stats *self, const char *name)
266
204k
{
267
204k
    struct rtpp_stats_priv *pvt;
268
269
204k
    pvt = self->pvt;
270
204k
    return (CALL_SMETHOD(pvt->rppp, hash, name));
271
204k
}
272
273
static int
274
rtpp_stats_updatebyidx_internal(struct rtpp_stats *self, int idx,
275
  enum rtpp_cnt_type type, void *argp)
276
306k
{
277
306k
    struct rtpp_stats_priv *pvt;
278
306k
    struct rtpp_stat *st;
279
280
306k
    pvt = self->pvt;
281
306k
    if (idx < 0 || idx >= pvt->nstats)
282
0
        return (-1);
283
306k
    st = &pvt->stats[idx];
284
306k
    if (type == RTPP_CNT_U64) {
285
288k
        atomic_fetch_add_explicit(&st->cnt.u64, *(uint64_t *)argp, memory_order_relaxed);
286
288k
    } else {
287
18.5k
        pthread_mutex_lock(&st->mutex);
288
18.5k
        st->cnt.d += *(double *)argp;
289
18.5k
        pthread_mutex_unlock(&st->mutex);
290
18.5k
    }
291
306k
    return (0);
292
306k
}
293
294
static int
295
rtpp_stats_updatebyidx(struct rtpp_stats *self, int idx, uint64_t incr)
296
213k
{
297
298
213k
    return rtpp_stats_updatebyidx_internal(self, idx, RTPP_CNT_U64, &incr);
299
213k
}
300
301
static int
302
rtpp_stats_updatebyname(struct rtpp_stats *self, const char *name, uint64_t incr)
303
75.0k
{
304
75.0k
    int idx;
305
306
75.0k
    idx = rtpp_stats_getidxbyname(self, name);
307
75.0k
    return rtpp_stats_updatebyidx_internal(self, idx, RTPP_CNT_U64, &incr);
308
75.0k
}
309
310
static int
311
rtpp_stats_updatebyname_d(struct rtpp_stats *self, const char *name, double incr)
312
18.5k
{
313
18.5k
    int idx;
314
315
18.5k
    idx = rtpp_stats_getidxbyname(self, name);
316
18.5k
    return rtpp_stats_updatebyidx_internal(self, idx, RTPP_CNT_DBL, &incr);
317
18.5k
}
318
319
static int64_t
320
rtpp_stats_getlvalbyname(struct rtpp_stats *self, const char *name)
321
0
{
322
0
    struct rtpp_stats_priv *pvt;
323
0
    struct rtpp_stat *st;
324
0
    uint64_t rval;
325
0
    int idx;
326
327
0
    idx = rtpp_stats_getidxbyname(self, name);
328
0
    if (idx < 0) {
329
0
        return (-1);
330
0
    }
331
0
    pvt = self->pvt;
332
0
    st = &pvt->stats[idx];
333
0
    rval = atomic_load_explicit(&st->cnt.u64, memory_order_relaxed);
334
0
    return (rval);
335
0
}
336
337
static int
338
rtpp_stats_nstr(struct rtpp_stats *self, const char *name, struct rtpc_reply *rrp)
339
0
{
340
0
    struct rtpp_stats_priv *pvt;
341
0
    struct rtpp_stat *st;
342
0
    int idx, rval;
343
0
    uint64_t uval;
344
0
    double dval;
345
346
0
    idx = rtpp_stats_getidxbyname(self, name);
347
0
    if (idx < 0) {
348
0
        return (-1);
349
0
    }
350
0
    pvt = self->pvt;
351
0
    st = &pvt->stats[idx];
352
0
    if (pvt->stats[idx].descr->type == RTPP_CNT_U64) {
353
0
        uval = atomic_load_explicit(&st->cnt.u64, memory_order_relaxed);
354
0
        rval = CALL_SMETHOD(rrp, appendf, "%" PRIu64, uval);
355
0
    } else {
356
0
        pthread_mutex_lock(&st->mutex);
357
0
        dval = st->cnt.d;
358
0
        pthread_mutex_unlock(&st->mutex);
359
0
        rval = CALL_SMETHOD(rrp, appendf, "%f", dval);
360
0
    }
361
0
    return (rval);
362
0
}
363
364
static void
365
rtpp_stats_dtor(struct rtpp_stats_full *fp)
366
2
{
367
2
    int i;
368
2
    struct rtpp_stats_priv *pvt;
369
2
    struct rtpp_stat *st;
370
371
2
    pvt = &fp->pvt;
372
60
    for (i = 0; i < pvt->nstats; i++) {
373
58
        st = &pvt->stats[i];
374
58
        pthread_mutex_destroy(&st->mutex);
375
58
    }
376
2
    RTPP_OBJ_DECREF(pvt->rppp);
377
2
    if (pvt->dstats != NULL) {
378
2
        free(pvt->dstats);
379
2
    }
380
2
    free(pvt->stats);
381
2
    rtpp_stats_fin(&fp->pub);
382
2
}
383
384
static int
385
rtpp_stats_getnstats(struct rtpp_stats *self)
386
0
{
387
388
0
    return (self->pvt->nstats);
389
0
}
390
391
static void
392
rtpp_stats_update_derived(struct rtpp_stats *self, double dtime)
393
7
{
394
7
    struct rtpp_stats_priv *pvt;
395
7
    int i;
396
7
    struct rtpp_stat_derived *dst;
397
7
    double ival, dval;
398
7
    union rtpp_stat_cnt last_val;
399
400
7
    pvt = self->pvt;
401
14
    for (i = 0; i < pvt->nstats_derived; i++) {
402
7
        dst = &pvt->dstats[i];
403
7
        assert(dst->last_ts < dtime);
404
7
        ival = dtime - dst->last_ts;
405
7
        if (dst->derive_from->descr->type == RTPP_CNT_U64) {
406
7
            last_val.u64 = dst->last_val.u64;
407
7
            dst->last_val.u64 = atomic_load_explicit(&dst->derive_from->cnt.u64, memory_order_relaxed);
408
7
            dval = (dst->last_val.u64 - last_val.u64) / ival;
409
7
        } else {
410
0
            last_val.d = dst->last_val.d;
411
0
            pthread_mutex_lock(&dst->derive_from->mutex);
412
0
            dst->last_val.d = dst->derive_from->cnt.d;
413
0
            pthread_mutex_unlock(&dst->derive_from->mutex);
414
0
            dval = (dst->last_val.d - last_val.d) / ival;
415
0
        }
416
7
        pthread_mutex_lock(&dst->derive_to->mutex);
417
7
        dst->derive_to->cnt.d = dval;
418
7
        pthread_mutex_unlock(&dst->derive_to->mutex);
419
7
        dst->last_ts = dtime;
420
7
    }
421
7
}