blob: f4c0b623cce09d3827438c3939d2cead17e8a1cb [file] [log] [blame]
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02001/*
2 * (C) 2015 by Sysmocom s.f.m.c. GmbH
3 *
4 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
5 *
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24#include <osmocom/core/stats.h>
25
26#include <unistd.h>
27#include <string.h>
28#include <stdint.h>
29#include <errno.h>
30#include <stdio.h>
31#include <sys/socket.h>
32#include <netinet/ip.h>
33#include <arpa/inet.h>
34
35#include <osmocom/core/utils.h>
36#include <osmocom/core/logging.h>
37#include <osmocom/core/rate_ctr.h>
38#include <osmocom/core/stat_item.h>
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010039#include <osmocom/core/timer.h>
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +010040#include <osmocom/core/statistics.h>
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010041#include <osmocom/core/msgb.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020042
43/* TODO: register properly */
44#define DSTATS DLGLOBAL
45
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010046#define STATS_DEFAULT_INTERVAL 5 /* secs */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010047#define STATS_DEFAULT_STATSD_BUFLEN 256
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020048
49static LLIST_HEAD(stats_reporter_list);
50static void *stats_ctx = NULL;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010051static int is_initialised = 0;
52static int32_t current_stat_item_index = 0;
53
54static struct stats_config s_stats_config = {
55 .interval = STATS_DEFAULT_INTERVAL,
56};
57struct stats_config *stats_config = &s_stats_config;
58
59static struct osmo_timer_list stats_timer;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020060
61static int stats_reporter_statsd_open(struct stats_reporter *srep);
62static int stats_reporter_statsd_close(struct stats_reporter *srep);
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010063static int stats_reporter_statsd_send_counter(struct stats_reporter *srep,
64 const struct rate_ctr_group *ctrg,
65 const struct rate_ctr_desc *desc,
66 int64_t value, int64_t delta);
67static int stats_reporter_statsd_send_item(struct stats_reporter *srep,
68 const struct stat_item_group *statg,
69 const struct stat_item_desc *desc, int value);
70
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020071static int stats_reporter_send(struct stats_reporter *srep, const char *data,
72 int data_len);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010073static int stats_reporter_send_buffer(struct stats_reporter *srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020074
75static int update_srep_config(struct stats_reporter *srep)
76{
77 int rc = 0;
78
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020079 if (srep->running) {
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010080 if (srep->close)
81 rc = srep->close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020082 srep->running = 0;
83 }
84
85 if (!srep->enabled)
86 return rc;
87
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010088 if (srep->open)
89 rc = srep->open(srep);
90 else
91 rc = 0;
92
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020093 if (rc < 0)
94 srep->enabled = 0;
95 else
96 srep->running = 1;
97
98 return rc;
99}
100
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100101static void stats_timer_cb(void *data)
102{
103 int interval = stats_config->interval;
104
105 if (!llist_empty(&stats_reporter_list))
106 stats_report();
107
108 osmo_timer_schedule(&stats_timer, interval, 0);
109}
110
111static int start_timer()
112{
113 if (!is_initialised)
114 return -ESRCH;
115
116 stats_timer.cb = stats_timer_cb;
117 osmo_timer_schedule(&stats_timer, 0, 1);
118
119 return 0;
120}
121
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200122struct stats_reporter *stats_reporter_alloc(enum stats_reporter_type type,
123 const char *name)
124{
125 struct stats_reporter *srep;
126 srep = talloc_zero(stats_ctx, struct stats_reporter);
127 OSMO_ASSERT(srep);
128 srep->type = type;
129 if (name)
130 srep->name = talloc_strdup(srep, name);
131 srep->fd = -1;
132
133 llist_add(&srep->list, &stats_reporter_list);
134
135 return srep;
136}
137
138void stats_reporter_free(struct stats_reporter *srep)
139{
140 stats_reporter_disable(srep);
141 llist_del(&srep->list);
142 talloc_free(srep);
143}
144
145void stats_init(void *ctx)
146{
147 stats_ctx = ctx;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100148 stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100149
150 is_initialised = 1;
151 start_timer();
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200152}
153
154struct stats_reporter *stats_reporter_find(enum stats_reporter_type type,
155 const char *name)
156{
157 struct stats_reporter *srep;
158 llist_for_each_entry(srep, &stats_reporter_list, list) {
159 if (srep->type != type)
160 continue;
161 if (srep->name != name) {
162 if (name == NULL || srep->name == NULL ||
163 strcmp(name, srep->name) != 0)
164 continue;
165 }
166 return srep;
167 }
168 return NULL;
169}
170
171int stats_reporter_set_remote_addr(struct stats_reporter *srep, const char *addr)
172{
173 int rc;
174 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
175 struct in_addr inaddr;
176
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100177 if (!srep->have_net_config)
178 return -ENOTSUP;
179
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200180 OSMO_ASSERT(addr != NULL);
181
182 rc = inet_pton(AF_INET, addr, &inaddr);
183 if (rc <= 0)
184 return -EINVAL;
185
186 sock_addr->sin_addr = inaddr;
187 sock_addr->sin_family = AF_INET;
188 srep->dest_addr_len = sizeof(*sock_addr);
189
190 talloc_free(srep->dest_addr_str);
191 srep->dest_addr_str = talloc_strdup(srep, addr);
192
193 return update_srep_config(srep);
194}
195
196int stats_reporter_set_remote_port(struct stats_reporter *srep, int port)
197{
198 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
199
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100200 if (!srep->have_net_config)
201 return -ENOTSUP;
202
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200203 srep->dest_port = port;
204 sock_addr->sin_port = htons(port);
205
206 return update_srep_config(srep);
207}
208
209int stats_reporter_set_local_addr(struct stats_reporter *srep, const char *addr)
210{
211 int rc;
212 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
213 struct in_addr inaddr;
214
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100215 if (!srep->have_net_config)
216 return -ENOTSUP;
217
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200218 if (addr) {
219 rc = inet_pton(AF_INET, addr, &inaddr);
220 if (rc <= 0)
221 return -EINVAL;
222 } else {
223 addr = INADDR_ANY;
224 }
225
226 sock_addr->sin_addr = inaddr;
227 sock_addr->sin_family = AF_INET;
228 srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
229
230 talloc_free(srep->bind_addr_str);
231 srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
232
233 return update_srep_config(srep);
234}
235
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100236int stats_reporter_set_mtu(struct stats_reporter *srep, int mtu)
237{
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100238 if (!srep->have_net_config)
239 return -ENOTSUP;
240
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100241 if (mtu < 0)
242 return -EINVAL;
243
244 srep->mtu = mtu;
245
246 return update_srep_config(srep);
247}
248
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100249int stats_set_interval(int interval)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200250{
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100251 if (interval <= 0)
252 return -EINVAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200253
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100254 stats_config->interval = interval;
255 if (is_initialised)
256 start_timer();
257
258 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200259}
260
261int stats_reporter_set_name_prefix(struct stats_reporter *srep, const char *prefix)
262{
263 talloc_free(srep->name_prefix);
264 srep->name_prefix = prefix ? talloc_strdup(srep, prefix) : NULL;
265
266 return update_srep_config(srep);
267}
268
269int stats_reporter_enable(struct stats_reporter *srep)
270{
271 srep->enabled = 1;
272
273 return update_srep_config(srep);
274}
275
276int stats_reporter_disable(struct stats_reporter *srep)
277{
278 srep->enabled = 0;
279
280 return update_srep_config(srep);
281}
282
283static int stats_reporter_send(struct stats_reporter *srep, const char *data,
284 int data_len)
285{
286 int rc;
287
288 rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT,
289 &srep->dest_addr, srep->dest_addr_len);
290
291 if (rc == -1)
292 rc = -errno;
293
294 return rc;
295}
296
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100297static int stats_reporter_send_buffer(struct stats_reporter *srep)
298{
299 int rc;
300
301 if (!srep->buffer || msgb_length(srep->buffer) == 0)
302 return 0;
303
304 rc = stats_reporter_send(srep,
305 (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
306
307 msgb_trim(srep->buffer, 0);
308
309 return rc;
310}
311
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200312/*** statsd reporter ***/
313
314struct stats_reporter *stats_reporter_create_statsd(const char *name)
315{
316 struct stats_reporter *srep;
317 srep = stats_reporter_alloc(STATS_REPORTER_STATSD, name);
318
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100319 srep->have_net_config = 1;
320
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100321 srep->open = stats_reporter_statsd_open;
322 srep->close = stats_reporter_statsd_close;
323 srep->send_counter = stats_reporter_statsd_send_counter;
324 srep->send_item = stats_reporter_statsd_send_item;
325
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200326 return srep;
327}
328
329static int stats_reporter_statsd_open(struct stats_reporter *srep)
330{
331 int sock;
332 int rc;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100333 int buffer_size = STATS_DEFAULT_STATSD_BUFLEN;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200334
335 if (srep->fd != -1)
336 stats_reporter_statsd_close(srep);
337
338 sock = socket(AF_INET, SOCK_DGRAM, 0);
339 if (sock == -1)
340 return -errno;
341
342 if (srep->bind_addr_len > 0) {
343 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
344 if (rc == -1)
345 goto failed;
346 }
347
348 srep->fd = sock;
349
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100350 if (srep->mtu > 0) {
351 buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
352 srep->agg_enabled = 1;
353 }
354
355 srep->buffer = msgb_alloc(buffer_size, "stats buffer");
356
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200357 return 0;
358
359failed:
360 rc = -errno;
361 close(sock);
362
363 return rc;
364}
365
366static int stats_reporter_statsd_close(struct stats_reporter *srep)
367{
368 int rc;
369 if (srep->fd == -1)
370 return -EBADF;
371
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100372 stats_reporter_send_buffer(srep);
373
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200374 rc = close(srep->fd);
375 srep->fd = -1;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100376 msgb_free(srep->buffer);
377 srep->buffer = NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200378 return rc == -1 ? -errno : 0;
379}
380
381static int stats_reporter_statsd_send(struct stats_reporter *srep,
382 const char *name1, int index1, const char *name2, int value,
383 const char *unit)
384{
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100385 char *buf;
386 int buf_size;
387 int nchars, rc = 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200388 char *fmt = NULL;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100389 int old_len = msgb_length(srep->buffer);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200390
391 if (name1) {
392 if (index1 > 0)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100393 fmt = "%1$s.%2$s.%6$d.%3$s:%4$d|%5$s";
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200394 else
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100395 fmt = "%1$s.%2$s.%3$s:%4$d|%5$s";
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200396 } else {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100397 fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s";
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200398 }
399 if (!srep->name_prefix)
400 fmt += 5; /* skip prefix part */
401
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100402 if (srep->agg_enabled) {
403 if (msgb_length(srep->buffer) > 0 &&
404 msgb_tailroom(srep->buffer) > 0)
405 {
406 msgb_put_u8(srep->buffer, '\n');
407 }
408 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200409
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100410 buf = (char *)msgb_put(srep->buffer, 0);
411 buf_size = msgb_tailroom(srep->buffer);
412
413 nchars = snprintf(buf, buf_size, fmt,
414 srep->name_prefix, name1, name2,
415 value, unit, index1);
416
417 if (nchars >= buf_size) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200418 /* Truncated */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100419 /* Restore original buffer (without trailing LF) */
420 msgb_trim(srep->buffer, old_len);
421 /* Send it */
422 rc = stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200423
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100424 /* Try again */
425 buf = (char *)msgb_put(srep->buffer, 0);
426 buf_size = msgb_tailroom(srep->buffer);
427
428 nchars = snprintf(buf, buf_size, fmt,
429 srep->name_prefix, name1, name2,
430 value, unit, index1);
431
432 if (nchars >= buf_size)
433 return -EMSGSIZE;
434 }
435
436 if (nchars > 0)
437 msgb_trim(srep->buffer, msgb_length(srep->buffer) + nchars);
438
439 if (!srep->agg_enabled)
440 rc = stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200441
442 return rc;
443}
444
445static int stats_reporter_statsd_send_counter(struct stats_reporter *srep,
446 const struct rate_ctr_group *ctrg,
447 const struct rate_ctr_desc *desc,
448 int64_t value, int64_t delta)
449{
450 if (ctrg)
451 return stats_reporter_statsd_send(srep,
452 ctrg->desc->group_name_prefix,
453 ctrg->idx,
454 desc->name, delta, "c");
455 else
456 return stats_reporter_statsd_send(srep,
457 NULL, -1,
458 desc->name, delta, "c");
459}
460
461static int stats_reporter_statsd_send_item(struct stats_reporter *srep,
462 const struct stat_item_group *statg,
463 const struct stat_item_desc *desc, int value)
464{
465 return stats_reporter_statsd_send(srep,
466 statg->desc->group_name_prefix, statg->idx,
467 desc->name, value, desc->unit);
468}
469
470/*** generic rate counter support ***/
471
472static int stats_reporter_send_counter(struct stats_reporter *srep,
473 const struct rate_ctr_group *ctrg,
474 const struct rate_ctr_desc *desc,
475 int64_t value, int64_t delta)
476{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100477 if (!srep->send_counter)
478 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200479
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100480 return srep->send_counter(srep, ctrg, desc, value, delta);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200481}
482
483static int rate_ctr_handler(
484 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
485 const struct rate_ctr_desc *desc, void *sctx_)
486{
487 struct stats_reporter *srep;
488 int rc;
489 int64_t delta = rate_ctr_difference(ctr);
490
491 if (delta == 0)
492 return 0;
493
494 llist_for_each_entry(srep, &stats_reporter_list, list) {
495 if (!srep->running)
496 continue;
497
498 rc = stats_reporter_send_counter(srep, ctrg, desc,
499 ctr->current, delta);
500
501 /* TODO: handle rc (log?, inc counter(!)?) or remove it */
502 }
503
504 return 0;
505}
506
507static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
508{
509 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
510
511 return 0;
512}
513
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100514/*** stat item support ***/
515
516static int stats_reporter_send_item(struct stats_reporter *srep,
517 const struct stat_item_group *statg,
518 const struct stat_item_desc *desc,
519 int32_t value)
520{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100521 if (!srep->send_item)
522 return 0;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100523
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100524 return srep->send_item(srep, statg, desc, value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100525}
526
527static int stat_item_handler(
528 struct stat_item_group *statg, struct stat_item *item, void *sctx_)
529{
530 struct stats_reporter *srep;
531 int rc;
532 int32_t idx = current_stat_item_index;
533 int32_t value;
534
535 while (stat_item_get_next(item, &idx, &value) > 0) {
536 llist_for_each_entry(srep, &stats_reporter_list, list) {
537 if (!srep->running)
538 continue;
539
540 rc = stats_reporter_send_item(srep, statg,
541 item->desc, value);
542 }
543 }
544
545 return 0;
546}
547
548static int stat_item_group_handler(struct stat_item_group *statg, void *sctx_)
549{
550 stat_item_for_each_item(statg, stat_item_handler, sctx_);
551 stat_item_discard_all(&current_stat_item_index);
552
553 return 0;
554}
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200555
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100556/*** osmo counter support ***/
557
558static int handle_counter(struct osmo_counter *counter, void *sctx_)
559{
560 struct stats_reporter *srep;
561 int rc;
562 struct rate_ctr_desc desc = {0};
563 /* Fake a rate counter description */
564 desc.name = counter->name;
565 desc.description = counter->description;
566
567 int delta = osmo_counter_difference(counter);
568
569 if (delta == 0)
570 return 0;
571
572 llist_for_each_entry(srep, &stats_reporter_list, list) {
573 if (!srep->running)
574 continue;
575
576 rc = stats_reporter_send_counter(srep, NULL, &desc,
577 counter->value, delta);
578
579 /* TODO: handle rc (log?, inc counter(!)?) */
580 }
581
582 return 0;
583}
584
585
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200586/*** main reporting function ***/
587
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100588static void flush_all_reporters()
589{
590 struct stats_reporter *srep;
591
592 llist_for_each_entry(srep, &stats_reporter_list, list) {
593 if (!srep->running)
594 continue;
595
596 stats_reporter_send_buffer(srep);
597 }
598}
599
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200600int stats_report()
601{
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100602 osmo_counters_for_each(handle_counter, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200603 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100604 stat_item_for_each_group(stat_item_group_handler, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200605
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100606 flush_all_reporters();
607
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200608 return 0;
609}