blob: a04a7760dec1737376b6c8a5111f9817140839f8 [file] [log] [blame]
Harald Welte7b45d602010-05-13 11:35:30 +02001/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
Harald Welte9327c6d2011-08-17 16:06:06 +020021/*! \addtogroup rate_ctr
22 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020023 * Counters about events and their event rates.
24 *
Harald Welte53de0d32017-10-16 14:09:16 +020025 * As \ref osmo_counter and \ref osmo_stat_item are concerned only with
26 * a single given value that may be increased/decreased, or the difference
27 * to one given previous value, this module adds some support for keeping
28 * long term information about a given event rate.
29 *
30 * A \ref rate_ctr keeps information on the amount of events per second,
31 * per minute, per hour and per day.
32 *
33 * \ref rate_ctr come in groups: An application describes a group of counters
34 * with their names and identities once in a (typically const) \ref
35 * rate_ctr_group_desc.
36 *
37 * As objects (such as e.g. a subscriber or a PDP context) are
38 * allocated dynamically at runtime, the application calls \ref
39 * rate_ctr_group_alloc with a refernce to the \ref
40 * rate_ctr_group_desc, which causes the library to allocate one set of
41 * \ref rate_ctr: One for each in the group.
42 *
43 * The application then uses functions like \ref rate_ctr_add or \ref
44 * rate_ctr_inc to increment the value as certain events (e.g. location
45 * update) happens.
46 *
47 * The library internally keeps a timer once per second which iterates
48 * over all registered counters and which updates the per-second,
49 * per-minute, per-hour and per-day averages based on the current
50 * value.
51 *
52 * The counters can be reported using \ref stats or by VTY
53 * introspection, as well as by any application-specific code accessing
54 * the \ref rate_ctr.intv array directly.
55 *
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020056 * \file rate_ctr.c */
Harald Welte9327c6d2011-08-17 16:06:06 +020057
Harald Welte7b45d602010-05-13 11:35:30 +020058#include <stdint.h>
Harald Welte7b45d602010-05-13 11:35:30 +020059#include <string.h>
60
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010061#include <osmocom/core/utils.h>
62#include <osmocom/core/linuxlist.h>
63#include <osmocom/core/talloc.h>
64#include <osmocom/core/timer.h>
65#include <osmocom/core/rate_ctr.h>
Harald Welte7b45d602010-05-13 11:35:30 +020066
67static LLIST_HEAD(rate_ctr_groups);
68
69static void *tall_rate_ctr_ctx;
70
Neels Hofmeyr87e45502017-06-20 00:17:59 +020071/*! Allocate a new group of counters according to description
Harald Welte9327c6d2011-08-17 16:06:06 +020072 * \param[in] ctx \ref talloc context
73 * \param[in] desc Rate counter group description
74 * \param[in] idx Index of new counter group
75 */
Harald Welte7b45d602010-05-13 11:35:30 +020076struct rate_ctr_group *rate_ctr_group_alloc(void *ctx,
77 const struct rate_ctr_group_desc *desc,
78 unsigned int idx)
79{
80 unsigned int size;
81 struct rate_ctr_group *group;
82
83 size = sizeof(struct rate_ctr_group) +
84 desc->num_ctr * sizeof(struct rate_ctr);
85
86 if (!ctx)
87 ctx = tall_rate_ctr_ctx;
88
89 group = talloc_zero_size(ctx, size);
90 if (!group)
91 return NULL;
92
93 group->desc = desc;
Harald Welte087fcff2010-05-13 12:16:17 +020094 group->idx = idx;
Harald Welte7b45d602010-05-13 11:35:30 +020095
96 llist_add(&group->list, &rate_ctr_groups);
97
98 return group;
99}
100
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200101/*! Free the memory for the specified group of counters */
Harald Welte7b45d602010-05-13 11:35:30 +0200102void rate_ctr_group_free(struct rate_ctr_group *grp)
103{
104 llist_del(&grp->list);
105 talloc_free(grp);
106}
107
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200108/*! Add a number to the counter */
Harald Welte7b45d602010-05-13 11:35:30 +0200109void rate_ctr_add(struct rate_ctr *ctr, int inc)
110{
111 ctr->current += inc;
112}
113
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200114/*! Return the counter difference since the last call to this function */
Jacob Erlbeck423c1e52015-10-19 13:45:42 +0200115int64_t rate_ctr_difference(struct rate_ctr *ctr)
116{
117 int64_t result = ctr->current - ctr->previous;
118 ctr->previous = ctr->current;
119
120 return result;
121}
122
Jacob Erlbeckee702cd2015-11-10 11:38:25 +0100123/* TODO: support update intervals > 1s */
124/* TODO: implement this as a special stats reporter */
125
Harald Welte7b45d602010-05-13 11:35:30 +0200126static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
127{
128 /* calculate rate over last interval */
129 ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last;
130 /* save current counter for next interval */
131 ctr->intv[intv].last = ctr->current;
Harald Welted2dce6d2010-05-13 13:28:12 +0200132
133 /* update the rate of the next bigger interval. This will
134 * be overwritten when that next larger interval expires */
135 if (intv + 1 < ARRAY_SIZE(ctr->intv))
136 ctr->intv[intv+1].rate += ctr->intv[intv].rate;
Harald Welte7b45d602010-05-13 11:35:30 +0200137}
138
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200139static struct osmo_timer_list rate_ctr_timer;
Harald Welte7b45d602010-05-13 11:35:30 +0200140static uint64_t timer_ticks;
141
142/* The one-second interval has expired */
143static void rate_ctr_group_intv(struct rate_ctr_group *grp)
144{
145 unsigned int i;
146
147 for (i = 0; i < grp->desc->num_ctr; i++) {
148 struct rate_ctr *ctr = &grp->ctr[i];
149
150 interval_expired(ctr, RATE_CTR_INTV_SEC);
151 if ((timer_ticks % 60) == 0)
152 interval_expired(ctr, RATE_CTR_INTV_MIN);
153 if ((timer_ticks % (60*60)) == 0)
154 interval_expired(ctr, RATE_CTR_INTV_HOUR);
155 if ((timer_ticks % (24*60*60)) == 0)
156 interval_expired(ctr, RATE_CTR_INTV_DAY);
157 }
158}
159
160static void rate_ctr_timer_cb(void *data)
161{
162 struct rate_ctr_group *ctrg;
163
164 /* Increment number of ticks before we calculate intervals,
165 * as a counter value of 0 would already wrap all counters */
166 timer_ticks++;
167
168 llist_for_each_entry(ctrg, &rate_ctr_groups, list)
169 rate_ctr_group_intv(ctrg);
170
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200171 osmo_timer_schedule(&rate_ctr_timer, 1, 0);
Harald Welte7b45d602010-05-13 11:35:30 +0200172}
173
Harald Welte53de0d32017-10-16 14:09:16 +0200174/*! Initialize the counter module. Call this once from your application.
175 * \param[in] tall_ctx Talloc context from which rate_ctr_group will be allocated
176 * \returns 0 on success; negative on error */
Harald Welte7b45d602010-05-13 11:35:30 +0200177int rate_ctr_init(void *tall_ctx)
178{
179 tall_rate_ctr_ctx = tall_ctx;
Pablo Neira Ayuso44f423f2017-05-08 18:00:28 +0200180 osmo_timer_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL);
Pablo Neira Ayuso0b21c1c2011-05-07 12:42:28 +0200181 osmo_timer_schedule(&rate_ctr_timer, 1, 0);
Harald Welte7b45d602010-05-13 11:35:30 +0200182
183 return 0;
184}
Daniel Willmann2d42dde2011-04-08 10:46:18 +0200185
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200186/*! Search for counter group based on group name and index
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200187 * \param[in] name Name of the counter group you're looking for
188 * \param[in] idx Index inside the counter group
189 * \returns \ref rate_ctr_group or NULL in case of error */
Daniel Willmann2d42dde2011-04-08 10:46:18 +0200190struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx)
191{
192 struct rate_ctr_group *ctrg;
193
194 llist_for_each_entry(ctrg, &rate_ctr_groups, list) {
195 if (!ctrg->desc)
196 continue;
197
198 if (!strcmp(ctrg->desc->group_name_prefix, name) &&
199 ctrg->idx == idx) {
200 return ctrg;
201 }
202 }
203 return NULL;
204}
205
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200206/*! Search for counter based on group + name
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200207 * \param[in] ctrg pointer to \ref rate_ctr_group
208 * \param[in] name name of counter inside group
Harald Welte53de0d32017-10-16 14:09:16 +0200209 * \returns \ref rate_ctr or NULL in case of error
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200210 */
Holger Hans Peter Freythera9f526a2011-04-18 16:45:45 +0200211const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name)
Daniel Willmann2d42dde2011-04-08 10:46:18 +0200212{
213 int i;
Holger Hans Peter Freythera9f526a2011-04-18 16:45:45 +0200214 const struct rate_ctr_desc *ctr_desc;
Daniel Willmann2d42dde2011-04-08 10:46:18 +0200215
216 if (!ctrg->desc)
217 return NULL;
218
219 for (i = 0; i < ctrg->desc->num_ctr; i++) {
220 ctr_desc = &ctrg->desc->ctr_desc[i];
221
222 if (!strcmp(ctr_desc->name, name)) {
223 return &ctrg->ctr[i];
224 }
225 }
226 return NULL;
227}
Harald Welte9327c6d2011-08-17 16:06:06 +0200228
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200229/*! Iterate over each counter in group and call function
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200230 * \param[in] counter group over whose counter to iterate
231 * \param[in] handle_counter function pointer
232 * \param[in] data Data to hand transparently to \ref handle_counter
233 * \returns 0 on success; negative otherwise
234 */
Jacob Erlbeck423c1e52015-10-19 13:45:42 +0200235int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg,
236 rate_ctr_handler_t handle_counter, void *data)
237{
238 int rc = 0;
239 int i;
240
241 for (i = 0; i < ctrg->desc->num_ctr; i++) {
242 struct rate_ctr *ctr = &ctrg->ctr[i];
243 rc = handle_counter(ctrg,
244 ctr, &ctrg->desc->ctr_desc[i], data);
245 if (rc < 0)
246 return rc;
247 }
248
249 return rc;
250}
251
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200252/*! Iterate over all counter groups
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200253 * \param[in] handle_group function pointer of callback function
254 * \param[in] data Data to hand transparently to \ref handle_group
255 * \returns 0 on success; negative otherwise
256 */
Jacob Erlbeck423c1e52015-10-19 13:45:42 +0200257int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data)
258{
259 struct rate_ctr_group *statg;
260 int rc = 0;
261
262 llist_for_each_entry(statg, &rate_ctr_groups, list) {
263 rc = handle_group(statg, data);
264 if (rc < 0)
265 return rc;
266 }
267
268 return rc;
269}
270
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200271/*! @} */