/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 | } |