blob: cbcdebc62a56829715a7df4ef248972a36c75f2a [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/*
4 * (C) 2015 by Sysmocom s.f.m.c. GmbH
Jacob Erlbeck9732cb42015-10-01 20:43:53 +02005 * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
6 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 */
24
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010025/*! \addtogroup osmo_stat_item
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020026 * @{
Harald Welte781951b2017-10-15 19:24:57 +020027 *
28 * This osmo_stat_item module adds instrumentation capabilities to
29 * gather measurement and statistical values in a similar fashion to
30 * what we have as \ref osmo_counter_group.
31 *
32 * As opposed to counters, osmo_stat_item do not increment but consist
33 * of a configurable-sized FIFO, which can store not only the current
34 * (most recent) value, but also historic values.
35 *
36 * The only supported value type is an int32_t.
37 *
38 * Getting values from the osmo_stat_item does not modify its state to
39 * allow for multiple independent back-ends retrieving values (e.g. VTY
40 * and statd).
41 *
42 * Each value stored in the FIFO of an osmo_stat_item has an associated
43 * value_id. The value_id is derived from an application-wide globally
44 * incrementing counter, so (until the counter wraps) more recent
45 * values will have higher values.
46 *
47 * When a new value is set, the oldest value in the FIFO gets silently
48 * overwritten. Lost values are skipped when getting values from the
49 * item.
50 *
51 */
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020052
53#include <stdint.h>
54#include <string.h>
55
56#include <osmocom/core/utils.h>
57#include <osmocom/core/linuxlist.h>
58#include <osmocom/core/talloc.h>
59#include <osmocom/core/timer.h>
60#include <osmocom/core/stat_item.h>
61
Harald Welte781951b2017-10-15 19:24:57 +020062/*! global list of stat_item groups */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010063static LLIST_HEAD(osmo_stat_item_groups);
Harald Welte781951b2017-10-15 19:24:57 +020064/*! counter for assigning globally unique value identifiers */
Jacob Erlbeckb27b3522015-10-12 18:47:09 +020065static int32_t global_value_id = 0;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020066
Harald Welte781951b2017-10-15 19:24:57 +020067/*! talloc context from which we allocate */
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020068static void *tall_stat_item_ctx;
69
Harald Welte781951b2017-10-15 19:24:57 +020070/*! Allocate a new group of counters according to description.
71 * Allocate a group of stat items described in \a desc from talloc context \a ctx,
72 * giving the new group the index \a idx.
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020073 * \param[in] ctx \ref talloc context
74 * \param[in] desc Statistics item group description
75 * \param[in] idx Index of new stat item group
76 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010077struct osmo_stat_item_group *osmo_stat_item_group_alloc(void *ctx,
78 const struct osmo_stat_item_group_desc *desc,
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020079 unsigned int idx)
80{
81 unsigned int group_size;
Harald Welteb32a1942015-11-20 10:22:14 +010082 unsigned long items_size = 0;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020083 unsigned int item_idx;
84 void *items;
85
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010086 struct osmo_stat_item_group *group;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020087
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010088 group_size = sizeof(struct osmo_stat_item_group) +
89 desc->num_items * sizeof(struct osmo_stat_item *);
Jacob Erlbeck9732cb42015-10-01 20:43:53 +020090
91 if (!ctx)
92 ctx = tall_stat_item_ctx;
93
94 group = talloc_zero_size(ctx, group_size);
95 if (!group)
96 return NULL;
97
98 group->desc = desc;
99 group->idx = idx;
100
101 /* Get combined size of all items */
102 for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
103 unsigned int size;
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100104 size = sizeof(struct osmo_stat_item) +
105 sizeof(struct osmo_stat_item_value) *
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200106 desc->item_desc[item_idx].num_values;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200107 /* Align to pointer size */
108 size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
109
110 /* Store offsets into the item array */
111 group->items[item_idx] = (void *)items_size;
112
113 items_size += size;
114 }
115
116 items = talloc_zero_size(group, items_size);
117 if (!items) {
118 talloc_free(group);
119 return NULL;
120 }
121
122 /* Update item pointers */
123 for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100124 struct osmo_stat_item *item = (struct osmo_stat_item *)
Harald Welteb32a1942015-11-20 10:22:14 +0100125 ((uint8_t *)items + (unsigned long)group->items[item_idx]);
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200126 unsigned int i;
127
128 group->items[item_idx] = item;
129 item->last_offs = desc->item_desc[item_idx].num_values - 1;
130 item->last_value_index = -1;
131 item->desc = &desc->item_desc[item_idx];
132
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200133 for (i = 0; i <= item->last_offs; i++) {
134 item->values[i].value = desc->item_desc[item_idx].default_value;
Jacob Erlbeckac4ed172015-12-08 10:29:16 +0100135 item->values[i].id = OSMO_STAT_ITEM_NOVALUE_ID;
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200136 }
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200137 }
138
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100139 llist_add(&group->list, &osmo_stat_item_groups);
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200140
141 return group;
142}
143
Harald Welte781951b2017-10-15 19:24:57 +0200144/*! Free the memory for the specified group of stat items */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100145void osmo_stat_item_group_free(struct osmo_stat_item_group *grp)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200146{
147 llist_del(&grp->list);
148 talloc_free(grp);
149}
150
Harald Welte781951b2017-10-15 19:24:57 +0200151/*! Set the a given stat_item to the given value.
152 * This function adds a new value for the given stat_item at the end of
153 * the FIFO.
154 * \param[in] item The stat_item whose \a value we want to set
155 * \param[in] value The numeric value we want to store at end of FIFO
156 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100157void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200158{
159 item->last_offs += 1;
160 if (item->last_offs >= item->desc->num_values)
161 item->last_offs = 0;
162
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200163 global_value_id += 1;
Jacob Erlbeckac4ed172015-12-08 10:29:16 +0100164 if (global_value_id == OSMO_STAT_ITEM_NOVALUE_ID)
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200165 global_value_id += 1;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200166
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200167 item->values[item->last_offs].value = value;
168 item->values[item->last_offs].id = global_value_id;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200169}
170
Harald Welte781951b2017-10-15 19:24:57 +0200171/*! Retrieve the next value from the osmo_stat_item object.
172 * If a new value has been set, it is returned. The idx is used to decide
173 * which value to return.
174 * On success, *idx is updated to refer to the next unread value. If
175 * values have been missed due to FIFO overflow, *idx is incremented by
176 * (1 + num_lost).
177 * This way, the osmo_stat_item object can be kept stateless from the reader's
178 * perspective and therefore be used by several backends simultaneously.
179 *
180 * \param val the osmo_stat_item object
181 * \param idx identifies the next value to be read
182 * \param value a pointer to store the value
183 * \returns the increment of the index (0: no value has been read,
184 * 1: one value has been taken,
185 * (1+n): n values have been skipped, one has been taken)
186 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100187int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_idx,
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200188 int32_t *value)
189{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100190 const struct osmo_stat_item_value *next_value;
191 const struct osmo_stat_item_value *item_value = NULL;
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200192 int idx_delta;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200193 int next_offs;
194
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200195 next_offs = item->last_offs;
196 next_value = &item->values[next_offs];
197
198 while (next_value->id - *next_idx >= 0 &&
Jacob Erlbeckac4ed172015-12-08 10:29:16 +0100199 next_value->id != OSMO_STAT_ITEM_NOVALUE_ID)
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200200 {
201 item_value = next_value;
202
203 next_offs -= 1;
204 if (next_offs < 0)
205 next_offs = item->desc->num_values - 1;
206 if (next_offs == item->last_offs)
207 break;
208 next_value = &item->values[next_offs];
209 }
210
211 if (!item_value)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200212 /* All items have been read */
213 return 0;
214
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200215 *value = item_value->value;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200216
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200217 idx_delta = item_value->id + 1 - *next_idx;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200218
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200219 *next_idx = item_value->id + 1;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200220
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200221 return idx_delta;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200222}
223
Harald Welte781951b2017-10-15 19:24:57 +0200224/*! Skip/discard all values of this item and update \a idx accordingly */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100225int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200226{
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200227 int discarded = item->values[item->last_offs].id + 1 - *idx;
228 *idx = item->values[item->last_offs].id + 1;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200229
230 return discarded;
231}
232
Harald Welte781951b2017-10-15 19:24:57 +0200233/*! Skip all values of all items and update \a idx accordingly */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100234int osmo_stat_item_discard_all(int32_t *idx)
Jacob Erlbeckb27b3522015-10-12 18:47:09 +0200235{
236 int discarded = global_value_id + 1 - *idx;
237 *idx = global_value_id + 1;
238
239 return discarded;
240}
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200241
Harald Welte781951b2017-10-15 19:24:57 +0200242/*! Initialize the stat item module. Call this once from your program.
243 * \param[in] tall_ctx Talloc context from which this module allocates */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100244int osmo_stat_item_init(void *tall_ctx)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200245{
246 tall_stat_item_ctx = tall_ctx;
247
248 return 0;
249}
250
Harald Welte781951b2017-10-15 19:24:57 +0200251/*! Search for item group based on group name and index
252 * \param[in] name Name of stats_item_group we want to find
253 * \param[in] idx Index of the group we want to find
254 * \returns pointer to group, if found; NULL otherwise */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100255struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200256 const char *name, const unsigned int idx)
257{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100258 struct osmo_stat_item_group *statg;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200259
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100260 llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200261 if (!statg->desc)
262 continue;
263
264 if (!strcmp(statg->desc->group_name_prefix, name) &&
265 statg->idx == idx)
266 return statg;
267 }
268 return NULL;
269}
270
Harald Welte781951b2017-10-15 19:24:57 +0200271/*! Search for item based on group + item name
272 * \param[in] statg group in which to search for the item
273 * \param[in] name name of item to search within \a statg
274 * \returns pointer to item, if found; NULL otherwise */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100275const struct osmo_stat_item *osmo_stat_item_get_by_name(
276 const struct osmo_stat_item_group *statg, const char *name)
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200277{
278 int i;
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100279 const struct osmo_stat_item_desc *item_desc;
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200280
281 if (!statg->desc)
282 return NULL;
283
284 for (i = 0; i < statg->desc->num_items; i++) {
285 item_desc = &statg->desc->item_desc[i];
286
287 if (!strcmp(item_desc->name, name)) {
288 return statg->items[i];
289 }
290 }
291 return NULL;
292}
293
Harald Welte781951b2017-10-15 19:24:57 +0200294/*! Iterate over all items in group, call user-supplied function on each
295 * \param[in] statg stat_item group over whose items to iterate
296 * \param[in] handle_item Call-back function, aborts if rc < 0
297 * \param[in] data Private data handed through to \a handle_item
298 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100299int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
300 osmo_stat_item_handler_t handle_item, void *data)
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200301{
302 int rc = 0;
303 int i;
304
305 for (i = 0; i < statg->desc->num_items; i++) {
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100306 struct osmo_stat_item *item = statg->items[i];
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200307 rc = handle_item(statg, item, data);
308 if (rc < 0)
309 return rc;
310 }
311
312 return rc;
313}
314
Harald Welte781951b2017-10-15 19:24:57 +0200315/*! Iterate over all stat_item groups in system, call user-supplied function on each
316 * \param[in] handle_group Call-back function, aborts if rc < 0
317 * \param[in] data Private data handed through to \a handle_group
318 */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100319int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data)
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200320{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100321 struct osmo_stat_item_group *statg;
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200322 int rc = 0;
323
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100324 llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
Jacob Erlbeckc6a71082015-10-19 14:04:38 +0200325 rc = handle_group(statg, data);
326 if (rc < 0)
327 return rc;
328 }
329
330 return rc;
331}
332
Jacob Erlbeck9732cb42015-10-01 20:43:53 +0200333/*! @} */