blob: ba36464062bdd9bae8972cf6f42af6844a784e05 [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 *
40 * Getting values from the osmo_stat_item does not modify its state to
41 * allow for multiple independent back-ends retrieving values (e.g. VTY
42 * and statd).
43 *
44 * Each value stored in the FIFO of an osmo_stat_item has an associated
45 * value_id. The value_id is derived from an application-wide globally
46 * incrementing counter, so (until the counter wraps) more recent
47 * values will have higher values.
48 *
49 * When a new value is set, the oldest value in the FIFO gets silently
50 * overwritten. Lost values are skipped when getting values from the
51 * item.
52 *
53 */
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020054
55#include <stdint.h>
56#include <string.h>
57
58#include <osmocom/core/utils.h>
59#include <osmocom/core/linuxlist.h>
60#include <osmocom/core/talloc.h>
61#include <osmocom/core/timer.h>
62#include <osmocom/core/stat_item.h>
63
Harald Welte781951b2017-10-15 19:24:57 +020064/*! global list of stat_item groups */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010065static LLIST_HEAD(osmo_stat_item_groups);
Harald Welte781951b2017-10-15 19:24:57 +020066/*! counter for assigning globally unique value identifiers */
Jacob Erlbeckb27b3522015-10-12 18:47:09 +020067static int32_t global_value_id = 0;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020068
Harald Welte781951b2017-10-15 19:24:57 +020069/*! talloc context from which we allocate */
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020070static void *tall_stat_item_ctx;
71
Harald Welte781951b2017-10-15 19:24:57 +020072/*! Allocate a new group of counters according to description.
73 * Allocate a group of stat items described in \a desc from talloc context \a ctx,
74 * giving the new group the index \a idx.
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020075 * \param[in] ctx \ref talloc context
76 * \param[in] desc Statistics item group description
77 * \param[in] idx Index of new stat item group
78 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010079struct osmo_stat_item_group *osmo_stat_item_group_alloc(void *ctx,
80 const struct osmo_stat_item_group_desc *desc,
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020081 unsigned int idx)
82{
83 unsigned int group_size;
Harald Welteb32a1942015-11-20 10:22:14 +010084 unsigned long items_size = 0;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020085 unsigned int item_idx;
86 void *items;
87
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010088 struct osmo_stat_item_group *group;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020089
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010090 group_size = sizeof(struct osmo_stat_item_group) +
91 desc->num_items * sizeof(struct osmo_stat_item *);
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020092
93 if (!ctx)
94 ctx = tall_stat_item_ctx;
95
96 group = talloc_zero_size(ctx, group_size);
97 if (!group)
98 return NULL;
99
100 group->desc = desc;
101 group->idx = idx;
102
103 /* Get combined size of all items */
104 for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
105 unsigned int size;
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100106 size = sizeof(struct osmo_stat_item) +
107 sizeof(struct osmo_stat_item_value) *
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200108 desc->item_desc[item_idx].num_values;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200109 /* Align to pointer size */
110 size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
111
112 /* Store offsets into the item array */
113 group->items[item_idx] = (void *)items_size;
114
115 items_size += size;
116 }
117
118 items = talloc_zero_size(group, items_size);
119 if (!items) {
120 talloc_free(group);
121 return NULL;
122 }
123
124 /* Update item pointers */
125 for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100126 struct osmo_stat_item *item = (struct osmo_stat_item *)
Harald Welteb32a1942015-11-20 10:22:14 +0100127 ((uint8_t *)items + (unsigned long)group->items[item_idx]);
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200128 unsigned int i;
129
130 group->items[item_idx] = item;
131 item->last_offs = desc->item_desc[item_idx].num_values - 1;
132 item->last_value_index = -1;
133 item->desc = &desc->item_desc[item_idx];
134
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200135 for (i = 0; i <= item->last_offs; i++) {
136 item->values[i].value = desc->item_desc[item_idx].default_value;
Jacob Erlbeckac4ed172015-12-08 10:29:16 +0100137 item->values[i].id = OSMO_STAT_ITEM_NOVALUE_ID;
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200138 }
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200139 }
140
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100141 llist_add(&group->list, &osmo_stat_item_groups);
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200142
143 return group;
144}
145
Harald Welte781951b2017-10-15 19:24:57 +0200146/*! Free the memory for the specified group of stat items */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100147void osmo_stat_item_group_free(struct osmo_stat_item_group *grp)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200148{
149 llist_del(&grp->list);
150 talloc_free(grp);
151}
152
Alexander Couzenscc72cc42019-04-27 23:19:55 +0200153/*! Increase the stat_item to the given value.
154 * This function adds a new value for the given stat_item at the end of
155 * the FIFO.
156 * \param[in] item The stat_item whose \a value we want to set
157 * \param[in] value The numeric value we want to store at end of FIFO
158 */
159void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value)
160{
161 int32_t oldvalue = item->values[item->last_offs].value;
162 osmo_stat_item_set(item, oldvalue + value);
163}
164
165/*! Descrease the stat_item to the given value.
166 * This function adds a new value for the given stat_item at the end of
167 * the FIFO.
168 * \param[in] item The stat_item whose \a value we want to set
169 * \param[in] value The numeric value we want to store at end of FIFO
170 */
171void osmo_stat_item_dec(struct osmo_stat_item *item, int32_t value)
172{
173 int32_t oldvalue = item->values[item->last_offs].value;
174 osmo_stat_item_set(item, oldvalue - value);
175}
176
Harald Welte781951b2017-10-15 19:24:57 +0200177/*! Set the a given stat_item to the given value.
178 * This function adds a new value for the given stat_item at the end of
179 * the FIFO.
180 * \param[in] item The stat_item whose \a value we want to set
181 * \param[in] value The numeric value we want to store at end of FIFO
182 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100183void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200184{
185 item->last_offs += 1;
186 if (item->last_offs >= item->desc->num_values)
187 item->last_offs = 0;
188
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200189 global_value_id += 1;
Jacob Erlbeckac4ed172015-12-08 10:29:16 +0100190 if (global_value_id == OSMO_STAT_ITEM_NOVALUE_ID)
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200191 global_value_id += 1;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200192
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200193 item->values[item->last_offs].value = value;
194 item->values[item->last_offs].id = global_value_id;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200195}
196
Harald Welte781951b2017-10-15 19:24:57 +0200197/*! Retrieve the next value from the osmo_stat_item object.
198 * If a new value has been set, it is returned. The idx is used to decide
199 * which value to return.
200 * On success, *idx is updated to refer to the next unread value. If
201 * values have been missed due to FIFO overflow, *idx is incremented by
202 * (1 + num_lost).
203 * This way, the osmo_stat_item object can be kept stateless from the reader's
204 * perspective and therefore be used by several backends simultaneously.
205 *
206 * \param val the osmo_stat_item object
207 * \param idx identifies the next value to be read
208 * \param value a pointer to store the value
209 * \returns the increment of the index (0: no value has been read,
210 * 1: one value has been taken,
211 * (1+n): n values have been skipped, one has been taken)
212 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100213int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_idx,
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200214 int32_t *value)
215{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100216 const struct osmo_stat_item_value *next_value;
217 const struct osmo_stat_item_value *item_value = NULL;
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200218 int idx_delta;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200219 int next_offs;
220
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200221 next_offs = item->last_offs;
222 next_value = &item->values[next_offs];
223
224 while (next_value->id - *next_idx >= 0 &&
Jacob Erlbeckac4ed172015-12-08 10:29:16 +0100225 next_value->id != OSMO_STAT_ITEM_NOVALUE_ID)
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200226 {
227 item_value = next_value;
228
229 next_offs -= 1;
230 if (next_offs < 0)
231 next_offs = item->desc->num_values - 1;
232 if (next_offs == item->last_offs)
233 break;
234 next_value = &item->values[next_offs];
235 }
236
237 if (!item_value)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200238 /* All items have been read */
239 return 0;
240
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200241 *value = item_value->value;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200242
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200243 idx_delta = item_value->id + 1 - *next_idx;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200244
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200245 *next_idx = item_value->id + 1;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200246
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200247 return idx_delta;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200248}
249
Harald Welte781951b2017-10-15 19:24:57 +0200250/*! Skip/discard all values of this item and update \a idx accordingly */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100251int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200252{
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200253 int discarded = item->values[item->last_offs].id + 1 - *idx;
254 *idx = item->values[item->last_offs].id + 1;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200255
256 return discarded;
257}
258
Harald Welte781951b2017-10-15 19:24:57 +0200259/*! Skip all values of all items and update \a idx accordingly */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100260int osmo_stat_item_discard_all(int32_t *idx)
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200261{
262 int discarded = global_value_id + 1 - *idx;
263 *idx = global_value_id + 1;
264
265 return discarded;
266}
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200267
Harald Welte781951b2017-10-15 19:24:57 +0200268/*! Initialize the stat item module. Call this once from your program.
269 * \param[in] tall_ctx Talloc context from which this module allocates */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100270int osmo_stat_item_init(void *tall_ctx)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200271{
272 tall_stat_item_ctx = tall_ctx;
273
274 return 0;
275}
276
Harald Welte781951b2017-10-15 19:24:57 +0200277/*! Search for item group based on group name and index
278 * \param[in] name Name of stats_item_group we want to find
279 * \param[in] idx Index of the group we want to find
280 * \returns pointer to group, if found; NULL otherwise */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100281struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200282 const char *name, const unsigned int idx)
283{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100284 struct osmo_stat_item_group *statg;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200285
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100286 llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200287 if (!statg->desc)
288 continue;
289
290 if (!strcmp(statg->desc->group_name_prefix, name) &&
291 statg->idx == idx)
292 return statg;
293 }
294 return NULL;
295}
296
Harald Welte781951b2017-10-15 19:24:57 +0200297/*! Search for item based on group + item name
298 * \param[in] statg group in which to search for the item
299 * \param[in] name name of item to search within \a statg
300 * \returns pointer to item, if found; NULL otherwise */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100301const struct osmo_stat_item *osmo_stat_item_get_by_name(
302 const struct osmo_stat_item_group *statg, const char *name)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200303{
304 int i;
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100305 const struct osmo_stat_item_desc *item_desc;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200306
307 if (!statg->desc)
308 return NULL;
309
310 for (i = 0; i < statg->desc->num_items; i++) {
311 item_desc = &statg->desc->item_desc[i];
312
313 if (!strcmp(item_desc->name, name)) {
314 return statg->items[i];
315 }
316 }
317 return NULL;
318}
319
Harald Welte781951b2017-10-15 19:24:57 +0200320/*! Iterate over all items in group, call user-supplied function on each
321 * \param[in] statg stat_item group over whose items to iterate
322 * \param[in] handle_item Call-back function, aborts if rc < 0
323 * \param[in] data Private data handed through to \a handle_item
324 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100325int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
326 osmo_stat_item_handler_t handle_item, void *data)
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200327{
328 int rc = 0;
329 int i;
330
331 for (i = 0; i < statg->desc->num_items; i++) {
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100332 struct osmo_stat_item *item = statg->items[i];
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200333 rc = handle_item(statg, item, data);
334 if (rc < 0)
335 return rc;
336 }
337
338 return rc;
339}
340
Harald Welte781951b2017-10-15 19:24:57 +0200341/*! Iterate over all stat_item groups in system, call user-supplied function on each
342 * \param[in] handle_group Call-back function, aborts if rc < 0
343 * \param[in] data Private data handed through to \a handle_group
344 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100345int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data)
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200346{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100347 struct osmo_stat_item_group *statg;
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200348 int rc = 0;
349
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100350 llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200351 rc = handle_group(statg, data);
352 if (rc < 0)
353 return rc;
354 }
355
356 return rc;
357}
358
Daniel Willmannea71b432020-07-14 18:10:20 +0200359
360/*! Remove all values of a stat item
361 * \param[in] item stat item to reset
362 */
363void osmo_stat_item_reset(struct osmo_stat_item *item)
364{
365 unsigned int i;
366
367 item->last_offs = item->desc->num_values - 1;
368 item->last_value_index = -1;
369
370 for (i = 0; i <= item->last_offs; i++) {
371 item->values[i].value = item->desc->default_value;
372 item->values[i].id = OSMO_STAT_ITEM_NOVALUE_ID;
373 }
374}
375
376/*! Reset all osmo stat items in a group
377 * \param[in] statg stat item group to reset
378 */
379void osmo_stat_item_group_reset(struct osmo_stat_item_group *statg)
380{
381 int i;
382
383 for (i = 0; i < statg->desc->num_items; i++) {
384 struct osmo_stat_item *item = statg->items[i];
385 osmo_stat_item_reset(item);
386 }
387}
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200388/*! @} */