blob: 59f59357ebbfdb7fb65af3e2b3094356f0cf0967 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file stat_item.c
Harald Welte781951b2017-10-15 19:24:57 +02002 * utility routines for keeping statistical values */
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02003/*
Jacob Erlbeck9732cb42015-10-01 20:43:53 +02004 * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
Harald Weltee08da972017-11-13 01:00:26 +09005 * (C) 2015 by sysmocom - s.f.m.c. GmbH
Jacob Erlbeck9732cb42015-10-01 20:43:53 +02006 *
7 * All Rights Reserved
8 *
Harald Weltee08da972017-11-13 01:00:26 +09009 * SPDX-License-Identifier: GPL-2.0+
10 *
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 *
25 */
26
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010027/*! \addtogroup osmo_stat_item
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020028 * @{
Harald Welte781951b2017-10-15 19:24:57 +020029 *
30 * This osmo_stat_item module adds instrumentation capabilities to
31 * gather measurement and statistical values in a similar fashion to
32 * what we have as \ref osmo_counter_group.
33 *
34 * As opposed to counters, osmo_stat_item do not increment but consist
35 * of a configurable-sized FIFO, which can store not only the current
36 * (most recent) value, but also historic values.
37 *
38 * The only supported value type is an int32_t.
39 *
Oliver Smith61401942021-03-26 10:18:37 +010040 * Getting values from osmo_stat_item is usually done at a high level
41 * through the stats API (stats.c). It uses item->stats_next_id to
42 * store what has been sent to all enabled reporters. It is also
43 * possible to read from osmo_stat_item directly, without modifying
44 * its state, by storing next_id outside of osmo_stat_item.
Harald Welte781951b2017-10-15 19:24:57 +020045 *
46 * Each value stored in the FIFO of an osmo_stat_item has an associated
Oliver Smith61401942021-03-26 10:18:37 +010047 * value_id. The value_id is increased with each value, so (until the
48 * counter wraps) more recent values will have higher values.
Harald Welte781951b2017-10-15 19:24:57 +020049 *
50 * When a new value is set, the oldest value in the FIFO gets silently
51 * overwritten. Lost values are skipped when getting values from the
52 * item.
53 *
54 */
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020055
Oliver Smith43686da2021-03-26 14:33:21 +010056/* Struct overview:
Oliver Smith43686da2021-03-26 14:33:21 +010057 *
Neels Hofmeyre90c7172021-09-14 14:37:38 +020058 * Group and item descriptions:
59 * Each group description exists once as osmo_stat_item_group_desc,
60 * each such group description lists N osmo_stat_item_desc, i.e. describes N stat items.
61 *
62 * Actual stats:
63 * The global osmo_stat_item_groups llist contains all group instances, each points at a group description.
64 * This list mixes all types of groups in a single llist, where each instance points at its group desc and has an index.
65 * There are one or more instances of each group, each storing stats for a distinct object (for example, one description
66 * for a BTS group, and any number of BTS instances with independent stats). A group is identified by a group index nr
67 * and possibly also a given name for that particular index (e.g. in osmo-mgw, a group instance is named
68 * "virtual-trunk-0" and can be looked up by that name instead of its more or less arbitrary group index number).
69 *
70 * Each group instance contains one osmo_stat_item instance per global stat item description.
71 * Each osmo_stat_item keeps track of the values for the current reporting period (min, last, max, sum, n),
72 * and also stores the set of values reported at the end of the previous reporting period.
73 *
74 * const osmo_stat_item_group_desc foo
75 * +-- group_name_prefix = "foo"
76 * +-- item_desc[] (array of osmo_stat_item_desc)
77 * +-- osmo_stat_item_desc bar
78 * | +-- name = "bar"
79 * | +-- description
80 * | +-- unit
81 * | +-- default_value
82 * |
83 * +-- osmo_stat_item_desc: baz
84 * +-- ...
85 *
86 * const osmo_stat_item_group_desc moo
87 * +-- group_name_prefix = "moo"
88 * +-- item_desc[]
89 * +-- osmo_stat_item_desc goo
90 * | +-- name = "goo"
91 * | +-- description
92 * | +-- unit
93 * | +-- default_value
94 * |
95 * +-- osmo_stat_item_desc: loo
96 * +-- ...
97 *
98 * osmo_stat_item_groups (llist of osmo_stat_item_group)
99 * |
100 * +-- group: foo[0]
101 * | +-- desc --> osmo_stat_item_group_desc foo
102 * | +-- idx = 0
103 * | +-- name = NULL (no given name for this group instance)
104 * | +-- items[]
105 * | |
106 * | [0] --> osmo_stat_item instance for "bar"
107 * | | +-- desc --> osmo_stat_item_desc bar (see above)
108 * | | +-- value.{min, last, max, n, sum}
109 * | | +-- reported.{min, last, max, n, sum}
110 * | |
111 * | [1] --> osmo_stat_item instance for "baz"
112 * | | +-- desc --> osmo_stat_item_desc baz
113 * | | +-- value.{min, last, max, n, sum}
114 * | | +-- reported.{min, last, max, n, sum}
115 * | .
116 * | :
117 * |
118 * +-- group: foo[1]
119 * | +-- desc --> osmo_stat_item_group_desc foo
120 * | +-- idx = 1
121 * | +-- name = "special-foo" (instance can be looked up by this index-name)
122 * | +-- items[]
123 * | |
124 * | [0] --> osmo_stat_item instance for "bar"
125 * | | +-- desc --> osmo_stat_item_desc bar
126 * | | +-- value.{min, last, max, n, sum}
127 * | | +-- reported.{min, last, max, n, sum}
128 * | |
129 * | [1] --> osmo_stat_item instance for "baz"
130 * | | +-- desc --> osmo_stat_item_desc baz
131 * | | +-- value.{min, last, max, n, sum}
132 * | | +-- reported.{min, last, max, n, sum}
133 * | .
134 * | :
135 * |
136 * +-- group: moo[0]
137 * | +-- desc --> osmo_stat_item_group_desc moo
138 * | +-- idx = 0
139 * | +-- name = NULL
140 * | +-- items[]
141 * | |
142 * | [0] --> osmo_stat_item instance for "goo"
143 * | | +-- desc --> osmo_stat_item_desc goo
144 * | | +-- value.{min, last, max, n, sum}
145 * | | +-- reported.{min, last, max, n, sum}
146 * | |
147 * | [1] --> osmo_stat_item instance for "loo"
148 * | | +-- desc --> osmo_stat_item_desc loo
149 * | | +-- value.{min, last, max, n, sum}
150 * | | +-- reported.{min, last, max, n, sum}
151 * | .
152 * | :
153 * .
154 * :
155 *
Oliver Smith43686da2021-03-26 14:33:21 +0100156 */
157
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200158#include <stdint.h>
159#include <string.h>
160
161#include <osmocom/core/utils.h>
162#include <osmocom/core/linuxlist.h>
Oliver Smith61401942021-03-26 10:18:37 +0100163#include <osmocom/core/logging.h>
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200164#include <osmocom/core/talloc.h>
165#include <osmocom/core/timer.h>
166#include <osmocom/core/stat_item.h>
167
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200168#include <stat_item_internal.h>
169
Harald Welte781951b2017-10-15 19:24:57 +0200170/*! global list of stat_item groups */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100171static LLIST_HEAD(osmo_stat_item_groups);
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200172
Harald Welte781951b2017-10-15 19:24:57 +0200173/*! talloc context from which we allocate */
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200174static void *tall_stat_item_ctx;
175
Harald Welte781951b2017-10-15 19:24:57 +0200176/*! Allocate a new group of counters according to description.
177 * Allocate a group of stat items described in \a desc from talloc context \a ctx,
178 * giving the new group the index \a idx.
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200179 * \param[in] ctx \ref talloc context
180 * \param[in] desc Statistics item group description
181 * \param[in] idx Index of new stat item group
182 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100183struct osmo_stat_item_group *osmo_stat_item_group_alloc(void *ctx,
Neels Hofmeyr049fd5c2021-09-14 14:39:18 +0200184 const struct osmo_stat_item_group_desc *group_desc,
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200185 unsigned int idx)
186{
187 unsigned int group_size;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200188 unsigned int item_idx;
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200189 struct osmo_stat_item *items;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200190
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100191 struct osmo_stat_item_group *group;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200192
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100193 group_size = sizeof(struct osmo_stat_item_group) +
Neels Hofmeyr049fd5c2021-09-14 14:39:18 +0200194 group_desc->num_items * sizeof(struct osmo_stat_item *);
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200195
196 if (!ctx)
197 ctx = tall_stat_item_ctx;
198
199 group = talloc_zero_size(ctx, group_size);
200 if (!group)
201 return NULL;
202
Neels Hofmeyr049fd5c2021-09-14 14:39:18 +0200203 group->desc = group_desc;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200204 group->idx = idx;
205
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200206 items = talloc_array(group, struct osmo_stat_item, group_desc->num_items);
207 OSMO_ASSERT(items);
Neels Hofmeyr049fd5c2021-09-14 14:39:18 +0200208 for (item_idx = 0; item_idx < group_desc->num_items; item_idx++) {
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200209 struct osmo_stat_item *item = &items[item_idx];
210 const struct osmo_stat_item_desc *item_desc = &group_desc->item_desc[item_idx];
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200211 group->items[item_idx] = item;
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200212 *item = (struct osmo_stat_item){
213 .desc = item_desc,
214 .value = {
215 .n = 0,
216 .last = item_desc->default_value,
217 .min = item_desc->default_value,
218 .max = item_desc->default_value,
219 .sum = 0,
220 },
221 };
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200222 }
223
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100224 llist_add(&group->list, &osmo_stat_item_groups);
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200225 return group;
226}
227
Harald Welte781951b2017-10-15 19:24:57 +0200228/*! Free the memory for the specified group of stat items */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100229void osmo_stat_item_group_free(struct osmo_stat_item_group *grp)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200230{
231 llist_del(&grp->list);
232 talloc_free(grp);
233}
234
Pau Espin Pedrol5fe3de52021-05-31 13:39:07 +0200235/*! Get statistics item from group, identified by index idx
236 * \param[in] grp Rate counter group
237 * \param[in] idx Index of the counter to retrieve
238 * \returns rate counter requested
239 */
240struct osmo_stat_item *osmo_stat_item_group_get_item(struct osmo_stat_item_group *grp, unsigned int idx)
241{
242 return grp->items[idx];
243}
244
Pau Espin Pedrol09f075f2021-05-31 13:10:24 +0200245/*! Set a name for the statistics item group to be used instead of index value
246 at report time.
247 * \param[in] statg Statistics item group
248 * \param[in] name Name identifier to assign to the statistics item group
249 */
250void osmo_stat_item_group_set_name(struct osmo_stat_item_group *statg, const char *name)
251{
252 osmo_talloc_replace_string(statg, &statg->name, name);
253}
254
Alexander Couzenscc72cc42019-04-27 23:19:55 +0200255/*! Increase the stat_item to the given value.
256 * This function adds a new value for the given stat_item at the end of
257 * the FIFO.
258 * \param[in] item The stat_item whose \a value we want to set
259 * \param[in] value The numeric value we want to store at end of FIFO
260 */
261void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value)
262{
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200263 osmo_stat_item_set(item, item->value.last + value);
Alexander Couzenscc72cc42019-04-27 23:19:55 +0200264}
265
266/*! Descrease the stat_item to the given value.
267 * This function adds a new value for the given stat_item at the end of
268 * the FIFO.
269 * \param[in] item The stat_item whose \a value we want to set
270 * \param[in] value The numeric value we want to store at end of FIFO
271 */
272void osmo_stat_item_dec(struct osmo_stat_item *item, int32_t value)
273{
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200274 osmo_stat_item_set(item, item->value.last - value);
Alexander Couzenscc72cc42019-04-27 23:19:55 +0200275}
276
Harald Welte781951b2017-10-15 19:24:57 +0200277/*! Set the a given stat_item to the given value.
278 * This function adds a new value for the given stat_item at the end of
279 * the FIFO.
280 * \param[in] item The stat_item whose \a value we want to set
281 * \param[in] value The numeric value we want to store at end of FIFO
282 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100283void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200284{
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200285 item->value.last = value;
286 if (item->value.n == 0) {
287 /* No values recorded yet, clamp min and max to this first value. */
288 item->value.min = item->value.max = value;
289 /* Overwrite any cruft remaining in value.sum */
290 item->value.sum = value;
291 item->value.n = 1;
292 } else {
293 item->value.min = OSMO_MIN(item->value.min, value);
294 item->value.max = OSMO_MAX(item->value.max, value);
295 item->value.sum += value;
296 item->value.n++;
297 }
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200298}
299
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200300/*! Indicate that a reporting period has elapsed, and prepare the stat item for a new period of collecting min/max/avg.
301 * \param item Stat item to flush.
Harald Welte781951b2017-10-15 19:24:57 +0200302 */
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200303void osmo_stat_item_flush(struct osmo_stat_item *item)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200304{
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200305 item->reported = item->value;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200306
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200307 /* Indicate a new reporting period: no values have been received, but the previous value.last remains unchanged
308 * for the case that an entire period elapses without a new value appearing. */
309 item->value.n = 0;
310 item->value.sum = 0;
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200311
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200312 /* Also for the case that an entire period elapses without any osmo_stat_item_set(), put the min and max to the
313 * last value. As soon as one osmo_stat_item_set() occurs, these are both set to the new value (when n is still
314 * zero from above). */
315 item->value.min = item->value.max = item->value.last;
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200316}
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200317
Harald Welte781951b2017-10-15 19:24:57 +0200318/*! Initialize the stat item module. Call this once from your program.
319 * \param[in] tall_ctx Talloc context from which this module allocates */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100320int osmo_stat_item_init(void *tall_ctx)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200321{
322 tall_stat_item_ctx = tall_ctx;
323
324 return 0;
325}
326
Harald Welte781951b2017-10-15 19:24:57 +0200327/*! Search for item group based on group name and index
328 * \param[in] name Name of stats_item_group we want to find
329 * \param[in] idx Index of the group we want to find
330 * \returns pointer to group, if found; NULL otherwise */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100331struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200332 const char *name, const unsigned int idx)
333{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100334 struct osmo_stat_item_group *statg;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200335
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100336 llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200337 if (!statg->desc)
338 continue;
339
340 if (!strcmp(statg->desc->group_name_prefix, name) &&
341 statg->idx == idx)
342 return statg;
343 }
344 return NULL;
345}
346
Neels Hofmeyr7fcfefb2021-09-05 16:22:23 +0200347/*! Search for item group based on group name and index's name.
348 * \param[in] name Name of stats_item_group we want to find.
349 * \param[in] idx_name Index of the group we want to find, by the index's name (osmo_stat_item_group->name).
350 * \returns pointer to group, if found; NULL otherwise. */
351struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idxname(const char *group_name, const char *idx_name)
352{
353 struct osmo_stat_item_group *statg;
354
355 llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
356 if (!statg->desc || !statg->name)
357 continue;
358 if (strcmp(statg->desc->group_name_prefix, group_name))
359 continue;
360 if (strcmp(statg->name, idx_name))
361 continue;
362 return statg;
363 }
364 return NULL;
365}
366
Harald Welte781951b2017-10-15 19:24:57 +0200367/*! Search for item based on group + item name
368 * \param[in] statg group in which to search for the item
369 * \param[in] name name of item to search within \a statg
370 * \returns pointer to item, if found; NULL otherwise */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100371const struct osmo_stat_item *osmo_stat_item_get_by_name(
372 const struct osmo_stat_item_group *statg, const char *name)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200373{
374 int i;
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100375 const struct osmo_stat_item_desc *item_desc;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200376
377 if (!statg->desc)
378 return NULL;
379
380 for (i = 0; i < statg->desc->num_items; i++) {
381 item_desc = &statg->desc->item_desc[i];
382
383 if (!strcmp(item_desc->name, name)) {
384 return statg->items[i];
385 }
386 }
387 return NULL;
388}
389
Harald Welte781951b2017-10-15 19:24:57 +0200390/*! Iterate over all items in group, call user-supplied function on each
391 * \param[in] statg stat_item group over whose items to iterate
392 * \param[in] handle_item Call-back function, aborts if rc < 0
393 * \param[in] data Private data handed through to \a handle_item
394 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100395int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
396 osmo_stat_item_handler_t handle_item, void *data)
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200397{
398 int rc = 0;
399 int i;
400
401 for (i = 0; i < statg->desc->num_items; i++) {
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100402 struct osmo_stat_item *item = statg->items[i];
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200403 rc = handle_item(statg, item, data);
404 if (rc < 0)
405 return rc;
406 }
407
408 return rc;
409}
410
Harald Welte781951b2017-10-15 19:24:57 +0200411/*! Iterate over all stat_item groups in system, call user-supplied function on each
412 * \param[in] handle_group Call-back function, aborts if rc < 0
413 * \param[in] data Private data handed through to \a handle_group
414 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100415int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data)
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200416{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100417 struct osmo_stat_item_group *statg;
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200418 int rc = 0;
419
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100420 llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200421 rc = handle_group(statg, data);
422 if (rc < 0)
423 return rc;
424 }
425
426 return rc;
427}
428
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200429/*! Get the last (freshest) value. */
430int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item)
431{
432 return item->value.last;
433}
Daniel Willmannea71b432020-07-14 18:10:20 +0200434
435/*! Remove all values of a stat item
436 * \param[in] item stat item to reset
437 */
438void osmo_stat_item_reset(struct osmo_stat_item *item)
439{
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200440 item->value.sum = 0;
441 item->value.n = 0;
442 item->value.last = item->value.min = item->value.max = item->desc->default_value;
Daniel Willmannea71b432020-07-14 18:10:20 +0200443}
444
445/*! Reset all osmo stat items in a group
446 * \param[in] statg stat item group to reset
447 */
448void osmo_stat_item_group_reset(struct osmo_stat_item_group *statg)
449{
450 int i;
451
452 for (i = 0; i < statg->desc->num_items; i++) {
453 struct osmo_stat_item *item = statg->items[i];
454 osmo_stat_item_reset(item);
455 }
456}
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200457
458/*! Return the description for an osmo_stat_item. */
459const struct osmo_stat_item_desc *osmo_stat_item_get_desc(struct osmo_stat_item *item)
460{
461 return item->desc;
462}
463
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200464/*! @} */