blob: 5954167a4ddae3d5f452955752a806b13c15b0a4 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file stats.c */
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02002/*
Harald Weltee08da972017-11-13 01:00:26 +09003 * (C) 2015 by sysmocom - s.f.m.c. GmbH
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02004 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02005 * All Rights Reserved
6 *
Harald Weltee08da972017-11-13 01:00:26 +09007 * SPDX-License-Identifier: GPL-2.0+
8 *
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02009 * 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
Harald Welteeb5b6ce2017-10-15 20:03:24 +020025/*! \addtogroup stats
26 * @{
27 *
28 * This module implements periodic reporting of statistics / counters.
29 * It supports the notion of multiple \ref osmo_stats_reporter objects
30 * which independently of each other can report statistics at different
31 * configurable intervals to different destinations.
32 *
33 * In order to use this facility, you have to call \ref
34 * osmo_stats_init() once at application start-up and then create one or
35 * more \ref osmo_stats_reporter, either using the direct API functions
36 * or by using the optional VTY bindings:
37 *
38 * - reporting to any of the libosmocore log targets
39 * \ref osmo_stats_reporter_create_log() creates a new stats_reporter
40 * which reports to the libosmcoore \ref logging subsystem.
41 *
42 * - reporting to statsd (a front-end proxy for the Graphite/Carbon
43 * metrics server
44 * \ref osmo_stats_reporter_create_statsd() creates a new stats_reporter
45 * which reports via UDP to statsd.
46 *
47 * You can either use the above API functions directly to create \ref
48 * osmo_stats_reporter instances, or you can use the VTY support
49 * contained in libosmovty. See the "stats" configuration node
50 * installed by osmo_stats_vty_Add_cmds().
51 *
52 * An \ref osmo_stats_reporter reports statistics on all of the following
53 * libosmocore internal counter/statistics objects:
54 * - \ref osmo_counter
55 * - \ref rate_ctr
56 * - \ref osmo_stat_item
57 *
58 * You do not need to do anything in particular to expose a given
59 * counter or stat_item, they are all exported automatically via any
60 * \ref osmo_stats_reporter. If you have multiple \ref
61 * osmo_stats_reporter, they will each report all counters/stat_items.
62 *
63 * \file stats.c */
64
Harald Welte67bdd802017-01-15 17:56:11 +010065#include "config.h"
66#if !defined(EMBEDDED)
67
Harald Welte95871da2017-05-15 12:11:36 +020068#include <osmocom/core/byteswap.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020069#include <osmocom/core/stats.h>
70
71#include <unistd.h>
72#include <string.h>
73#include <stdint.h>
74#include <errno.h>
75#include <stdio.h>
Holger Hans Peter Freytherc3376932015-08-21 19:56:54 +000076#include <sys/types.h>
Harald Welte67bdd802017-01-15 17:56:11 +010077
78#ifdef HAVE_SYS_SOCKET_H
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020079#include <sys/socket.h>
Holger Hans Peter Freytherc3376932015-08-21 19:56:54 +000080#include <netinet/in.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020081#include <arpa/inet.h>
Harald Welte67bdd802017-01-15 17:56:11 +010082#endif
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020083
84#include <osmocom/core/utils.h>
85#include <osmocom/core/logging.h>
86#include <osmocom/core/rate_ctr.h>
87#include <osmocom/core/stat_item.h>
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010088#include <osmocom/core/timer.h>
Harald Welte216338c2017-10-15 19:46:19 +020089#include <osmocom/core/counter.h>
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010090#include <osmocom/core/msgb.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020091
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010092#define STATS_DEFAULT_INTERVAL 5 /* secs */
Jacob Erlbeckb6e6bea2015-11-09 15:33:44 +010093#define STATS_DEFAULT_BUFLEN 256
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020094
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010095static LLIST_HEAD(osmo_stats_reporter_list);
96static void *osmo_stats_ctx = NULL;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010097static int is_initialised = 0;
98static int32_t current_stat_item_index = 0;
99
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100100static struct osmo_stats_config s_stats_config = {
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100101 .interval = STATS_DEFAULT_INTERVAL,
102};
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100103struct osmo_stats_config *osmo_stats_config = &s_stats_config;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100104
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100105static struct osmo_timer_list osmo_stats_timer;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200106
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100107static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100108 const struct rate_ctr_group *ctrg,
109 const struct rate_ctr_desc *desc,
110 int64_t value, int64_t delta);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100111static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
112 const struct osmo_stat_item_group *statg,
Harald Welte1554f802016-11-11 15:06:06 +0100113 const struct osmo_stat_item_desc *desc, int64_t value);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100114
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100115static int update_srep_config(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200116{
117 int rc = 0;
118
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200119 if (srep->running) {
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100120 if (srep->close)
121 rc = srep->close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200122 srep->running = 0;
123 }
124
125 if (!srep->enabled)
126 return rc;
127
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100128 if (srep->open)
129 rc = srep->open(srep);
130 else
131 rc = 0;
132
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200133 if (rc < 0)
134 srep->enabled = 0;
135 else
136 srep->running = 1;
137
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100138 srep->force_single_flush = 1;
139
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200140 return rc;
141}
142
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100143static void osmo_stats_timer_cb(void *data)
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100144{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100145 int interval = osmo_stats_config->interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100146
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100147 if (!llist_empty(&osmo_stats_reporter_list))
148 osmo_stats_report();
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100149
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100150 osmo_timer_schedule(&osmo_stats_timer, interval, 0);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100151}
152
153static int start_timer()
154{
155 if (!is_initialised)
156 return -ESRCH;
157
Pablo Neira Ayuso44f423f2017-05-08 18:00:28 +0200158 osmo_timer_setup(&osmo_stats_timer, osmo_stats_timer_cb, NULL);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100159 osmo_timer_schedule(&osmo_stats_timer, 0, 1);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100160
161 return 0;
162}
163
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100164struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200165 const char *name)
166{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100167 struct osmo_stats_reporter *srep;
168 srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200169 OSMO_ASSERT(srep);
170 srep->type = type;
171 if (name)
172 srep->name = talloc_strdup(srep, name);
173 srep->fd = -1;
174
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100175 llist_add(&srep->list, &osmo_stats_reporter_list);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200176
177 return srep;
178}
179
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200180/*! Destroy a given stats_reporter. Takes care of first disabling it.
181 * \param[in] srep stats_reporter that shall be disabled + destroyed */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100182void osmo_stats_reporter_free(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200183{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100184 osmo_stats_reporter_disable(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200185 llist_del(&srep->list);
186 talloc_free(srep);
187}
188
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200189/*! Initilize the stats reporting module; call this once in your program
190 * \param[in] ctx Talloc context from which stats related memory is allocated */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100191void osmo_stats_init(void *ctx)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200192{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100193 osmo_stats_ctx = ctx;
194 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100195
196 is_initialised = 1;
197 start_timer();
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200198}
199
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200200/*! Find a stats_reporter of given \a type and \a name.
201 * \param[in] type Type of stats_reporter to find
202 * \param[in] name Name of stats_reporter to find
203 * \returns stats_reporter matching \a type and \a name; NULL otherwise */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100204struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200205 const char *name)
206{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100207 struct osmo_stats_reporter *srep;
208 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200209 if (srep->type != type)
210 continue;
211 if (srep->name != name) {
212 if (name == NULL || srep->name == NULL ||
213 strcmp(name, srep->name) != 0)
214 continue;
215 }
216 return srep;
217 }
218 return NULL;
219}
220
Harald Welte67bdd802017-01-15 17:56:11 +0100221#ifdef HAVE_SYS_SOCKET_H
222
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200223/*! Set the remote (IP) address of a given stats_reporter.
224 * \param[in] srep stats_reporter whose remote address is to be set
225 * \param[in] addr String representation of remote IPv4 address
226 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100227int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200228{
229 int rc;
230 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
231 struct in_addr inaddr;
232
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100233 if (!srep->have_net_config)
234 return -ENOTSUP;
235
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200236 OSMO_ASSERT(addr != NULL);
237
238 rc = inet_pton(AF_INET, addr, &inaddr);
239 if (rc <= 0)
240 return -EINVAL;
241
242 sock_addr->sin_addr = inaddr;
243 sock_addr->sin_family = AF_INET;
244 srep->dest_addr_len = sizeof(*sock_addr);
245
246 talloc_free(srep->dest_addr_str);
247 srep->dest_addr_str = talloc_strdup(srep, addr);
248
249 return update_srep_config(srep);
250}
251
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200252/*! Set the remote (UDP) port of a given stats_reporter
253 * \param[in] srep stats_reporter whose remote address is to be set
254 * \param[in] port UDP port of remote statsd to which we report
255 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100256int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200257{
258 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
259
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100260 if (!srep->have_net_config)
261 return -ENOTSUP;
262
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200263 srep->dest_port = port;
Harald Welte95871da2017-05-15 12:11:36 +0200264 sock_addr->sin_port = osmo_htons(port);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200265
266 return update_srep_config(srep);
267}
268
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200269/*! Set the local (IP) address of a given stats_reporter.
270 * \param[in] srep stats_reporter whose remote address is to be set
271 * \param[in] addr String representation of local IP address
272 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100273int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200274{
275 int rc;
276 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
277 struct in_addr inaddr;
278
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100279 if (!srep->have_net_config)
280 return -ENOTSUP;
281
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200282 if (addr) {
283 rc = inet_pton(AF_INET, addr, &inaddr);
284 if (rc <= 0)
285 return -EINVAL;
286 } else {
Holger Hans Peter Freyther79219752015-11-02 15:50:32 +0100287 inaddr.s_addr = INADDR_ANY;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200288 }
289
290 sock_addr->sin_addr = inaddr;
291 sock_addr->sin_family = AF_INET;
292 srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
293
294 talloc_free(srep->bind_addr_str);
295 srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
296
297 return update_srep_config(srep);
298}
299
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200300/*! Set the maximum transmission unit of a given stats_reporter.
301 * \param[in] srep stats_reporter whose remote address is to be set
302 * \param[in] mtu Maximum Transmission Unit of \a srep
303 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100304int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100305{
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100306 if (!srep->have_net_config)
307 return -ENOTSUP;
308
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100309 if (mtu < 0)
310 return -EINVAL;
311
312 srep->mtu = mtu;
313
314 return update_srep_config(srep);
315}
Harald Welte67bdd802017-01-15 17:56:11 +0100316#endif /* HAVE_SYS_SOCKETS_H */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100317
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100318int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
319 enum osmo_stats_class class_id)
320{
321 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
322 return -EINVAL;
323
324 srep->max_class = class_id;
325
326 return 0;
327}
328
Alexander Chemerisf203ed32020-05-08 19:09:22 +0300329/*! Set the reporting interval (common for all reporters)
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200330 * \param[in] interval Reporting interval in seconds
331 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100332int osmo_stats_set_interval(int interval)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200333{
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100334 if (interval <= 0)
335 return -EINVAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200336
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100337 osmo_stats_config->interval = interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100338 if (is_initialised)
339 start_timer();
340
341 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200342}
343
Alexander Chemerisdfebf402020-05-08 19:10:40 +0300344/*! Set the regular flush period for a given stats_reporter
345 *
346 * Send all stats even if they have not changed (i.e. force the flush)
347 * every N-th reporting interval. Set to 0 to disable regular flush,
348 * set to 1 to flush every time, set to 2 to flush every 2nd time, etc.
349 * \param[in] srep stats_reporter to set flush period for
350 * \param[in] period Reporting interval in seconds
351 * \returns 0 on success; negative on error */
352int osmo_stats_reporter_set_flush_period(struct osmo_stats_reporter *srep, unsigned int period)
353{
354 srep->flush_period = period;
355 srep->flush_period_counter = 0;
356 /* force the flush now if it's not disabled by period=0 */
357 if (period > 0)
358 srep->force_single_flush = 1;
359
360 return 0;
361}
362
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200363/*! Set the name prefix of a given stats_reporter.
364 * \param[in] srep stats_reporter whose name prefix is to be set
365 * \param[in] prefix NAme perfix to pre-pend for any reported value
366 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100367int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200368{
369 talloc_free(srep->name_prefix);
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100370 srep->name_prefix = prefix && strlen(prefix) > 0 ?
371 talloc_strdup(srep, prefix) : NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200372
373 return update_srep_config(srep);
374}
375
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200376
377/*! Enable the given stats_reporter.
378 * \param[in] srep stats_reporter who is to be enabled
379 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100380int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200381{
382 srep->enabled = 1;
383
384 return update_srep_config(srep);
385}
386
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200387/*! Disable the given stats_reporter.
388 * \param[in] srep stats_reporter who is to be disabled
389 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100390int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200391{
392 srep->enabled = 0;
393
394 return update_srep_config(srep);
395}
396
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100397/*** i/o helper functions ***/
398
Harald Welte67bdd802017-01-15 17:56:11 +0100399#ifdef HAVE_SYS_SOCKET_H
400
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200401/*! Open the UDP socket for given stats_reporter.
402 * \param[in] srep stats_reporter whose UDP socket is to be opened
403 * ]returns 0 on success; negative otherwise */
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100404int osmo_stats_reporter_udp_open(struct osmo_stats_reporter *srep)
405{
406 int sock;
407 int rc;
408 int buffer_size = STATS_DEFAULT_BUFLEN;
409
410 if (srep->fd != -1 && srep->close)
411 srep->close(srep);
412
413 sock = socket(AF_INET, SOCK_DGRAM, 0);
414 if (sock == -1)
415 return -errno;
416
Arran Cudbard-Bellcc3694b2016-05-18 16:02:19 -0400417#if defined(__APPLE__) && !defined(MSG_NOSIGNAL)
418 {
419 static int val = 1;
420
421 rc = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val));
422 goto failed;
423 }
424#endif
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100425 if (srep->bind_addr_len > 0) {
426 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
427 if (rc == -1)
428 goto failed;
429 }
430
431 srep->fd = sock;
432
433 if (srep->mtu > 0) {
434 buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
435 srep->agg_enabled = 1;
436 }
437
438 srep->buffer = msgb_alloc(buffer_size, "stats buffer");
439
440 return 0;
441
442failed:
443 rc = -errno;
444 close(sock);
445
446 return rc;
447}
448
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200449/*! Closee the UDP socket for given stats_reporter.
450 * \param[in] srep stats_reporter whose UDP socket is to be closed
451 * ]returns 0 on success; negative otherwise */
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100452int osmo_stats_reporter_udp_close(struct osmo_stats_reporter *srep)
453{
454 int rc;
455 if (srep->fd == -1)
456 return -EBADF;
457
458 osmo_stats_reporter_send_buffer(srep);
459
460 rc = close(srep->fd);
461 srep->fd = -1;
462 msgb_free(srep->buffer);
463 srep->buffer = NULL;
464 return rc == -1 ? -errno : 0;
465}
466
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200467/*! Send given date to given stats_reporter.
468 * \param[in] srep stats_reporter whose UDP socket is to be opened
469 * \param[in] data string data to be sent
470 * \param[in] data_len Length of \a data in bytes
471 * \returns number of bytes on success; negative otherwise */
Jacob Erlbeckb6e6bea2015-11-09 15:33:44 +0100472int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200473 int data_len)
474{
475 int rc;
476
Arran Cudbard-Bellcc3694b2016-05-18 16:02:19 -0400477 rc = sendto(srep->fd, data, data_len,
478#ifdef MSG_NOSIGNAL
479 MSG_NOSIGNAL |
480#endif
481 MSG_DONTWAIT,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200482 &srep->dest_addr, srep->dest_addr_len);
483
484 if (rc == -1)
485 rc = -errno;
486
487 return rc;
488}
489
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200490/*! Send current accumulated buffer to given stats_reporter.
491 * \param[in] srep stats_reporter whose UDP socket is to be opened
492 * \returns number of bytes on success; negative otherwise */
Jacob Erlbeckb6e6bea2015-11-09 15:33:44 +0100493int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100494{
495 int rc;
496
497 if (!srep->buffer || msgb_length(srep->buffer) == 0)
498 return 0;
499
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100500 rc = osmo_stats_reporter_send(srep,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100501 (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
502
503 msgb_trim(srep->buffer, 0);
504
505 return rc;
506}
Harald Welte67bdd802017-01-15 17:56:11 +0100507#endif /* HAVE_SYS_SOCKET_H */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100508
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100509/*** log reporter ***/
510
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200511/*! Create a stats_reporter that logs via libosmocore logging.
512 * A stats_reporter created via this function will simply print the statistics
513 * via the libosmocore logging framework, using DLSTATS subsystem and LOGL_INFO
514 * priority. The configuration of the libosmocore log targets define where this
515 * information will end up (ignored, text file, stderr, syslog, ...).
516 * \param[in] name Name of the to-be-created stats_reporter
517 * \returns stats_reporter on success; NULL on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100518struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100519{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100520 struct osmo_stats_reporter *srep;
521 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100522
523 srep->have_net_config = 0;
524
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100525 srep->send_counter = osmo_stats_reporter_log_send_counter;
526 srep->send_item = osmo_stats_reporter_log_send_item;
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100527
528 return srep;
529}
530
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100531static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100532 const char *type,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100533 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100534 const char *unit)
535{
Jacob Erlbeck79125ec2015-11-02 15:17:50 +0100536 LOGP(DLSTATS, LOGL_INFO,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100537 "stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100538 type, srep->name_prefix ? srep->name_prefix : "",
539 name1 ? name1 : "", index1,
540 name2, value, unit ? unit : "");
541
542 return 0;
543}
544
545
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100546static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100547 const struct rate_ctr_group *ctrg,
548 const struct rate_ctr_desc *desc,
549 int64_t value, int64_t delta)
550{
551 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100552 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100553 ctrg->desc->group_name_prefix,
554 ctrg->idx,
555 desc->name, value, NULL);
556 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100557 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100558 NULL, 0,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100559 desc->name, value, NULL);
560}
561
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100562static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
563 const struct osmo_stat_item_group *statg,
Harald Welte1554f802016-11-11 15:06:06 +0100564 const struct osmo_stat_item_desc *desc, int64_t value)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100565{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100566 return osmo_stats_reporter_log_send(srep, "i",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100567 statg->desc->group_name_prefix, statg->idx,
568 desc->name, value, desc->unit);
569}
570
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100571/*** helper for reporting ***/
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200572
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100573static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep,
574 unsigned int index, int class_id)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200575{
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100576 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
577 class_id = index != 0 ?
578 OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200579
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100580 return class_id <= srep->max_class;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200581}
582
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200583/*** generic rate counter support ***/
584
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100585static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200586 const struct rate_ctr_group *ctrg,
587 const struct rate_ctr_desc *desc,
588 int64_t value, int64_t delta)
589{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100590 if (!srep->send_counter)
591 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200592
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100593 return srep->send_counter(srep, ctrg, desc, value, delta);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200594}
595
596static int rate_ctr_handler(
597 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
598 const struct rate_ctr_desc *desc, void *sctx_)
599{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100600 struct osmo_stats_reporter *srep;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200601 int64_t delta = rate_ctr_difference(ctr);
602
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100603 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200604 if (!srep->running)
605 continue;
606
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100607 if (delta == 0 && !srep->force_single_flush)
608 continue;
609
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100610 if (!osmo_stats_reporter_check_config(srep,
611 ctrg->idx, ctrg->desc->class_id))
Jacob Erlbeck8a97cb92015-11-09 11:39:42 +0100612 continue;
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100613
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100614 osmo_stats_reporter_send_counter(srep, ctrg, desc,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200615 ctr->current, delta);
616
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100617 /* TODO: handle result (log?, inc counter(!)?) or remove it */
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200618 }
619
620 return 0;
621}
622
623static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
624{
625 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
626
627 return 0;
628}
629
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100630/*** stat item support ***/
631
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100632static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep,
633 const struct osmo_stat_item_group *statg,
634 const struct osmo_stat_item_desc *desc,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100635 int32_t value)
636{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100637 if (!srep->send_item)
638 return 0;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100639
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100640 return srep->send_item(srep, statg, desc, value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100641}
642
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100643static int osmo_stat_item_handler(
644 struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100645{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100646 struct osmo_stats_reporter *srep;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100647 int32_t idx = current_stat_item_index;
648 int32_t value;
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100649 int have_value;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100650
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100651 have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
652 if (!have_value)
653 /* Send the last value in case a flush is requested */
654 value = osmo_stat_item_get_last(item);
655
656 do {
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100657 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100658 if (!srep->running)
659 continue;
660
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100661 if (!have_value && !srep->force_single_flush)
662 continue;
663
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100664 if (!osmo_stats_reporter_check_config(srep,
665 statg->idx, statg->desc->class_id))
Jacob Erlbeck8a97cb92015-11-09 11:39:42 +0100666 continue;
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100667
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100668 osmo_stats_reporter_send_item(srep, statg,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100669 item->desc, value);
670 }
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100671
672 if (!have_value)
673 break;
674
675 have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
676 } while (have_value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100677
678 return 0;
679}
680
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100681static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100682{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100683 osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100684
685 return 0;
686}
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200687
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100688/*** osmo counter support ***/
689
690static int handle_counter(struct osmo_counter *counter, void *sctx_)
691{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100692 struct osmo_stats_reporter *srep;
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100693 struct rate_ctr_desc desc = {0};
694 /* Fake a rate counter description */
695 desc.name = counter->name;
696 desc.description = counter->description;
697
698 int delta = osmo_counter_difference(counter);
699
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100700 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100701 if (!srep->running)
702 continue;
703
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100704 if (delta == 0 && !srep->force_single_flush)
705 continue;
706
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100707 osmo_stats_reporter_send_counter(srep, NULL, &desc,
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100708 counter->value, delta);
709
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100710 /* TODO: handle result (log?, inc counter(!)?) */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100711 }
712
713 return 0;
714}
715
716
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200717/*** main reporting function ***/
718
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100719static void flush_all_reporters()
720{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100721 struct osmo_stats_reporter *srep;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100722
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100723 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100724 if (!srep->running)
725 continue;
726
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100727 osmo_stats_reporter_send_buffer(srep);
Alexander Chemerisdfebf402020-05-08 19:10:40 +0300728
729 /* reset force_single_flush first */
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100730 srep->force_single_flush = 0;
Alexander Chemerisdfebf402020-05-08 19:10:40 +0300731 /* and schedule a new flush if it's time for it */
732 if (srep->flush_period > 0) {
733 srep->flush_period_counter++;
734 if (srep->flush_period_counter >= srep->flush_period) {
735 srep->force_single_flush = 1;
736 srep->flush_period_counter = 0;
737 }
738 }
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100739 }
740}
741
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100742int osmo_stats_report()
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200743{
Jacob Erlbeck01e8c912015-11-09 14:13:23 +0100744 /* per group actions */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100745 osmo_counters_for_each(handle_counter, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200746 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100747 osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200748
Jacob Erlbeck01e8c912015-11-09 14:13:23 +0100749 /* global actions */
750 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100751 flush_all_reporters();
752
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200753 return 0;
754}
Harald Welte67bdd802017-01-15 17:56:11 +0100755
756#endif /* !EMBEDDED */
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200757
758/*! @} */