blob: b5adbf29ff22b5f4dda4c887051bcb9aa1a9543f [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
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200329/*! Set the reporting interval of a given stats_reporter (in seconds).
330 * \param[in] srep stats_reporter whose remote address is to be set
331 * \param[in] interval Reporting interval in seconds
332 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100333int osmo_stats_set_interval(int interval)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200334{
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100335 if (interval <= 0)
336 return -EINVAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200337
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100338 osmo_stats_config->interval = interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100339 if (is_initialised)
340 start_timer();
341
342 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200343}
344
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200345/*! Set the name prefix of a given stats_reporter.
346 * \param[in] srep stats_reporter whose name prefix is to be set
347 * \param[in] prefix NAme perfix to pre-pend for any reported value
348 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100349int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200350{
351 talloc_free(srep->name_prefix);
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100352 srep->name_prefix = prefix && strlen(prefix) > 0 ?
353 talloc_strdup(srep, prefix) : NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200354
355 return update_srep_config(srep);
356}
357
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200358
359/*! Enable the given stats_reporter.
360 * \param[in] srep stats_reporter who is to be enabled
361 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100362int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200363{
364 srep->enabled = 1;
365
366 return update_srep_config(srep);
367}
368
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200369/*! Disable the given stats_reporter.
370 * \param[in] srep stats_reporter who is to be disabled
371 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100372int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200373{
374 srep->enabled = 0;
375
376 return update_srep_config(srep);
377}
378
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100379/*** i/o helper functions ***/
380
Harald Welte67bdd802017-01-15 17:56:11 +0100381#ifdef HAVE_SYS_SOCKET_H
382
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200383/*! Open the UDP socket for given stats_reporter.
384 * \param[in] srep stats_reporter whose UDP socket is to be opened
385 * ]returns 0 on success; negative otherwise */
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100386int osmo_stats_reporter_udp_open(struct osmo_stats_reporter *srep)
387{
388 int sock;
389 int rc;
390 int buffer_size = STATS_DEFAULT_BUFLEN;
391
392 if (srep->fd != -1 && srep->close)
393 srep->close(srep);
394
395 sock = socket(AF_INET, SOCK_DGRAM, 0);
396 if (sock == -1)
397 return -errno;
398
Arran Cudbard-Bellcc3694b2016-05-18 16:02:19 -0400399#if defined(__APPLE__) && !defined(MSG_NOSIGNAL)
400 {
401 static int val = 1;
402
403 rc = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val));
404 goto failed;
405 }
406#endif
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100407 if (srep->bind_addr_len > 0) {
408 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
409 if (rc == -1)
410 goto failed;
411 }
412
413 srep->fd = sock;
414
415 if (srep->mtu > 0) {
416 buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
417 srep->agg_enabled = 1;
418 }
419
420 srep->buffer = msgb_alloc(buffer_size, "stats buffer");
421
422 return 0;
423
424failed:
425 rc = -errno;
426 close(sock);
427
428 return rc;
429}
430
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200431/*! Closee the UDP socket for given stats_reporter.
432 * \param[in] srep stats_reporter whose UDP socket is to be closed
433 * ]returns 0 on success; negative otherwise */
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100434int osmo_stats_reporter_udp_close(struct osmo_stats_reporter *srep)
435{
436 int rc;
437 if (srep->fd == -1)
438 return -EBADF;
439
440 osmo_stats_reporter_send_buffer(srep);
441
442 rc = close(srep->fd);
443 srep->fd = -1;
444 msgb_free(srep->buffer);
445 srep->buffer = NULL;
446 return rc == -1 ? -errno : 0;
447}
448
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200449/*! Send given date to given stats_reporter.
450 * \param[in] srep stats_reporter whose UDP socket is to be opened
451 * \param[in] data string data to be sent
452 * \param[in] data_len Length of \a data in bytes
453 * \returns number of bytes on success; negative otherwise */
Jacob Erlbeckb6e6bea2015-11-09 15:33:44 +0100454int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200455 int data_len)
456{
457 int rc;
458
Arran Cudbard-Bellcc3694b2016-05-18 16:02:19 -0400459 rc = sendto(srep->fd, data, data_len,
460#ifdef MSG_NOSIGNAL
461 MSG_NOSIGNAL |
462#endif
463 MSG_DONTWAIT,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200464 &srep->dest_addr, srep->dest_addr_len);
465
466 if (rc == -1)
467 rc = -errno;
468
469 return rc;
470}
471
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200472/*! Send current accumulated buffer to given stats_reporter.
473 * \param[in] srep stats_reporter whose UDP socket is to be opened
474 * \returns number of bytes on success; negative otherwise */
Jacob Erlbeckb6e6bea2015-11-09 15:33:44 +0100475int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100476{
477 int rc;
478
479 if (!srep->buffer || msgb_length(srep->buffer) == 0)
480 return 0;
481
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100482 rc = osmo_stats_reporter_send(srep,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100483 (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
484
485 msgb_trim(srep->buffer, 0);
486
487 return rc;
488}
Harald Welte67bdd802017-01-15 17:56:11 +0100489#endif /* HAVE_SYS_SOCKET_H */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100490
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100491/*** log reporter ***/
492
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200493/*! Create a stats_reporter that logs via libosmocore logging.
494 * A stats_reporter created via this function will simply print the statistics
495 * via the libosmocore logging framework, using DLSTATS subsystem and LOGL_INFO
496 * priority. The configuration of the libosmocore log targets define where this
497 * information will end up (ignored, text file, stderr, syslog, ...).
498 * \param[in] name Name of the to-be-created stats_reporter
499 * \returns stats_reporter on success; NULL on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100500struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100501{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100502 struct osmo_stats_reporter *srep;
503 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100504
505 srep->have_net_config = 0;
506
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100507 srep->send_counter = osmo_stats_reporter_log_send_counter;
508 srep->send_item = osmo_stats_reporter_log_send_item;
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100509
510 return srep;
511}
512
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100513static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100514 const char *type,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100515 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100516 const char *unit)
517{
Jacob Erlbeck79125ec2015-11-02 15:17:50 +0100518 LOGP(DLSTATS, LOGL_INFO,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100519 "stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100520 type, srep->name_prefix ? srep->name_prefix : "",
521 name1 ? name1 : "", index1,
522 name2, value, unit ? unit : "");
523
524 return 0;
525}
526
527
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100528static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100529 const struct rate_ctr_group *ctrg,
530 const struct rate_ctr_desc *desc,
531 int64_t value, int64_t delta)
532{
533 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100534 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100535 ctrg->desc->group_name_prefix,
536 ctrg->idx,
537 desc->name, value, NULL);
538 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100539 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100540 NULL, 0,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100541 desc->name, value, NULL);
542}
543
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100544static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
545 const struct osmo_stat_item_group *statg,
Harald Welte1554f802016-11-11 15:06:06 +0100546 const struct osmo_stat_item_desc *desc, int64_t value)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100547{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100548 return osmo_stats_reporter_log_send(srep, "i",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100549 statg->desc->group_name_prefix, statg->idx,
550 desc->name, value, desc->unit);
551}
552
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100553/*** helper for reporting ***/
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200554
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100555static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep,
556 unsigned int index, int class_id)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200557{
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100558 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
559 class_id = index != 0 ?
560 OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200561
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100562 return class_id <= srep->max_class;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200563}
564
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200565/*** generic rate counter support ***/
566
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100567static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200568 const struct rate_ctr_group *ctrg,
569 const struct rate_ctr_desc *desc,
570 int64_t value, int64_t delta)
571{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100572 if (!srep->send_counter)
573 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200574
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100575 return srep->send_counter(srep, ctrg, desc, value, delta);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200576}
577
578static int rate_ctr_handler(
579 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
580 const struct rate_ctr_desc *desc, void *sctx_)
581{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100582 struct osmo_stats_reporter *srep;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200583 int64_t delta = rate_ctr_difference(ctr);
584
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100585 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200586 if (!srep->running)
587 continue;
588
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100589 if (delta == 0 && !srep->force_single_flush)
590 continue;
591
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100592 if (!osmo_stats_reporter_check_config(srep,
593 ctrg->idx, ctrg->desc->class_id))
Jacob Erlbeck8a97cb92015-11-09 11:39:42 +0100594 continue;
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100595
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100596 osmo_stats_reporter_send_counter(srep, ctrg, desc,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200597 ctr->current, delta);
598
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100599 /* TODO: handle result (log?, inc counter(!)?) or remove it */
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200600 }
601
602 return 0;
603}
604
605static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
606{
607 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
608
609 return 0;
610}
611
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100612/*** stat item support ***/
613
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100614static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep,
615 const struct osmo_stat_item_group *statg,
616 const struct osmo_stat_item_desc *desc,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100617 int32_t value)
618{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100619 if (!srep->send_item)
620 return 0;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100621
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100622 return srep->send_item(srep, statg, desc, value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100623}
624
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100625static int osmo_stat_item_handler(
626 struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100627{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100628 struct osmo_stats_reporter *srep;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100629 int32_t idx = current_stat_item_index;
630 int32_t value;
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100631 int have_value;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100632
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100633 have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
634 if (!have_value)
635 /* Send the last value in case a flush is requested */
636 value = osmo_stat_item_get_last(item);
637
638 do {
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100639 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100640 if (!srep->running)
641 continue;
642
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100643 if (!have_value && !srep->force_single_flush)
644 continue;
645
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100646 if (!osmo_stats_reporter_check_config(srep,
647 statg->idx, statg->desc->class_id))
Jacob Erlbeck8a97cb92015-11-09 11:39:42 +0100648 continue;
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100649
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100650 osmo_stats_reporter_send_item(srep, statg,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100651 item->desc, value);
652 }
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100653
654 if (!have_value)
655 break;
656
657 have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
658 } while (have_value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100659
660 return 0;
661}
662
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100663static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100664{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100665 osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100666
667 return 0;
668}
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200669
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100670/*** osmo counter support ***/
671
672static int handle_counter(struct osmo_counter *counter, void *sctx_)
673{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100674 struct osmo_stats_reporter *srep;
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100675 struct rate_ctr_desc desc = {0};
676 /* Fake a rate counter description */
677 desc.name = counter->name;
678 desc.description = counter->description;
679
680 int delta = osmo_counter_difference(counter);
681
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100682 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100683 if (!srep->running)
684 continue;
685
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100686 if (delta == 0 && !srep->force_single_flush)
687 continue;
688
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100689 osmo_stats_reporter_send_counter(srep, NULL, &desc,
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100690 counter->value, delta);
691
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100692 /* TODO: handle result (log?, inc counter(!)?) */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100693 }
694
695 return 0;
696}
697
698
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200699/*** main reporting function ***/
700
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100701static void flush_all_reporters()
702{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100703 struct osmo_stats_reporter *srep;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100704
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100705 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100706 if (!srep->running)
707 continue;
708
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100709 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100710 srep->force_single_flush = 0;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100711 }
712}
713
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100714int osmo_stats_report()
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200715{
Jacob Erlbeck01e8c912015-11-09 14:13:23 +0100716 /* per group actions */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100717 osmo_counters_for_each(handle_counter, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200718 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100719 osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200720
Jacob Erlbeck01e8c912015-11-09 14:13:23 +0100721 /* global actions */
722 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100723 flush_all_reporters();
724
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200725 return 0;
726}
Harald Welte67bdd802017-01-15 17:56:11 +0100727
728#endif /* !EMBEDDED */
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200729
730/*! @} */