Merge branch 'jerlbeck/wip/stats'

* This adds a new counter type (to measure time or delay)
* A statsd reporting backend. This can be fed into graphite
or similar tools.
* A periodic log backend for performance values
This commit is contained in:
Holger Hans Peter Freyther 2015-11-02 15:57:34 +01:00
commit d7b0577d7d
29 changed files with 2135 additions and 27 deletions

1
.gitignore vendored
View File

@ -56,6 +56,7 @@ tests/testsuite.dir/
tests/testsuite.log
tests/utils/utils_test
tests/stats/stats_test
tests/kasumi/kasumi_test
tests/sms/sms_test
tests/timer/timer_test

View File

@ -1 +1,3 @@
#library what description / commit summary line
libosmovty abi-change stats/vty: Add stats configuration (enum node_type has changed)
libosmovty abi-change vty: Add reserved nodes to enum node_type

View File

@ -22,12 +22,14 @@ nobase_include_HEADERS = \
osmocom/core/linuxrbtree.h \
osmocom/core/logging.h \
osmocom/core/loggingrb.h \
osmocom/core/stats.h \
osmocom/core/macaddr.h \
osmocom/core/msgb.h \
osmocom/core/panic.h \
osmocom/core/prim.h \
osmocom/core/process.h \
osmocom/core/rate_ctr.h \
osmocom/core/stat_item.h \
osmocom/core/select.h \
osmocom/core/signal.h \
osmocom/core/socket.h \
@ -112,6 +114,7 @@ nobase_include_HEADERS += \
osmocom/vty/buffer.h \
osmocom/vty/command.h \
osmocom/vty/logging.h \
osmocom/vty/stats.h \
osmocom/vty/misc.h \
osmocom/vty/telnet_interface.h \
osmocom/vty/vector.h \

View File

@ -69,7 +69,8 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
#define DLSMS -7
#define DLCTRL -8
#define DLGTP -9
#define OSMO_NUM_DLIB 9
#define DLSTATS -10
#define OSMO_NUM_DLIB 10
struct log_category {
uint8_t loglevel;

View File

@ -30,6 +30,7 @@ struct rate_ctr_per_intv {
/*! \brief data we keep for each actual value */
struct rate_ctr {
uint64_t current; /*!< \brief current value */
uint64_t previous; /*!< \brief previous value, used for delta */
/*! \brief per-interval data */
struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM];
};
@ -46,6 +47,8 @@ struct rate_ctr_group_desc {
const char *group_name_prefix;
/*! \brief The human-readable description of the group */
const char *group_description;
/*! \brief The class to which this group belongs */
int class_id;
/*! \brief The number of counters in this group */
const unsigned int num_ctr;
/*! \brief Pointer to array of counter names */
@ -78,9 +81,27 @@ static inline void rate_ctr_inc(struct rate_ctr *ctr)
rate_ctr_add(ctr, 1);
}
/*! \brief Return the counter difference since the last call to this function */
int64_t rate_ctr_difference(struct rate_ctr *ctr);
int rate_ctr_init(void *tall_ctx);
struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx);
const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name);
typedef int (*rate_ctr_handler_t)(
struct rate_ctr_group *, struct rate_ctr *,
const struct rate_ctr_desc *, void *);
typedef int (*rate_ctr_group_handler_t)(struct rate_ctr_group *, void *);
/*! \brief Iterate over all counters
* \param[in] handle_item Call-back function, aborts if rc < 0
* \param[in] data Private data handed through to \a handle_counter
*/
int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg,
rate_ctr_handler_t handle_counter, void *data);
int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data);
/*! @} */

View File

@ -0,0 +1,130 @@
#pragma once
/*! \defgroup osmo_stat_item Statistics value item
* @{
*/
/*! \file stat_item.h */
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
struct osmo_stat_item_desc;
#define STAT_ITEM_NOVALUE_ID 0
struct osmo_stat_item_value {
int32_t id;
int32_t value;
};
/*! \brief data we keep for each actual value */
struct osmo_stat_item {
const struct osmo_stat_item_desc *desc;
/*! \brief the index of the freshest value */
int32_t last_value_index;
/*! \brief offset to the freshest value in the value fifo */
int16_t last_offs;
/*! \brief value fifo */
struct osmo_stat_item_value values[0];
};
/*! \brief statistics value description */
struct osmo_stat_item_desc {
const char *name; /*!< \brief name of the item */
const char *description;/*!< \brief description of the item */
const char *unit; /*!< \brief unit of a value */
unsigned int num_values;/*!< \brief number of values to store */
int32_t default_value;
};
/*! \brief description of a statistics value group */
struct osmo_stat_item_group_desc {
/*! \brief The prefix to the name of all values in this group */
const char *group_name_prefix;
/*! \brief The human-readable description of the group */
const char *group_description;
/*! \brief The class to which this group belongs */
int class_id;
/*! \brief The number of values in this group */
const unsigned int num_items;
/*! \brief Pointer to array of value names */
const struct osmo_stat_item_desc *item_desc;
};
/*! \brief One instance of a counter group class */
struct osmo_stat_item_group {
/*! \brief Linked list of all value groups in the system */
struct llist_head list;
/*! \brief Pointer to the counter group class */
const struct osmo_stat_item_group_desc *desc;
/*! \brief The index of this value group within its class */
unsigned int idx;
/*! \brief Actual counter structures below */
struct osmo_stat_item *items[0];
};
struct osmo_stat_item_group *osmo_stat_item_group_alloc(
void *ctx,
const struct osmo_stat_item_group_desc *desc,
unsigned int idx);
void osmo_stat_item_group_free(struct osmo_stat_item_group *statg);
void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value);
int osmo_stat_item_init(void *tall_ctx);
struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
const char *name, const unsigned int idx);
const struct osmo_stat_item *osmo_stat_item_get_by_name(
const struct osmo_stat_item_group *statg, const char *name);
/*! \brief Retrieve the next value from the osmo_stat_item object.
* If a new value has been set, it is returned. The idx is used to decide
* which value to return.
* On success, *idx is updated to refer to the next unread value. If
* values have been missed due to FIFO overflow, *idx is incremented by
* (1 + num_lost).
* This way, the osmo_stat_item object can be kept stateless from the reader's
* perspective and therefore be used by several backends simultaneously.
*
* \param val the osmo_stat_item object
* \param idx identifies the next value to be read
* \param value a pointer to store the value
* \returns the increment of the index (0: no value has been read,
* 1: one value has been taken,
* (1+n): n values have been skipped, one has been taken)
*/
int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *idx, int32_t *value);
/*! \brief Get the last (freshest) value */
static int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item);
/*! \brief Skip all values of the item and update idx accordingly */
int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx);
/*! \brief Skip all values of all items and update idx accordingly */
int osmo_stat_item_discard_all(int32_t *idx);
typedef int (*osmo_stat_item_handler_t)(
struct osmo_stat_item_group *, struct osmo_stat_item *, void *);
typedef int (*osmo_stat_item_group_handler_t)(struct osmo_stat_item_group *, void *);
/*! \brief Iteate over all items
* \param[in] handle_item Call-back function, aborts if rc < 0
* \param[in] data Private data handed through to \a handle_item
*/
int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
osmo_stat_item_handler_t handle_item, void *data);
int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data);
static inline int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item)
{
return item->values[item->last_offs].value;
}
/*! @} */

View File

@ -9,6 +9,7 @@ struct osmo_counter {
const char *name; /*!< \brief human-readable name */
const char *description; /*!< \brief humn-readable description */
unsigned long value; /*!< \brief current value */
unsigned long previous; /*!< \brief previous value */
};
/*! \brief Increment counter */
@ -37,8 +38,8 @@ struct osmo_counter *osmo_counter_alloc(const char *name);
*/
void osmo_counter_free(struct osmo_counter *ctr);
/*! \brief Iteate over all counters
* \param[in] handle_counter Call-back function
/*! \brief Iterate over all counters
* \param[in] handle_counter Call-back function, aborts if rc < 0
* \param[in] data Private dtata handed through to \a handle_counter
*/
int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data);
@ -48,3 +49,6 @@ int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *),
* \returns pointer to counter (\ref osmo_counter) or NULL otherwise
*/
struct osmo_counter *osmo_counter_get_by_name(const char *name);
/*! \brief Return the counter difference since the last call to this function */
int osmo_counter_difference(struct osmo_counter *ctr);

View File

@ -0,0 +1,110 @@
/* (C) 2015 by Sysmocom s.f.m.c. GmbH
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#pragma once
#include <sys/socket.h>
#include <osmocom/core/linuxlist.h>
struct msgb;
struct osmo_stat_item_group;
struct osmo_stat_item_desc;
struct rate_ctr_group;
struct rate_ctr_desc;
enum osmo_stats_class {
OSMO_STATS_CLASS_UNKNOWN,
OSMO_STATS_CLASS_GLOBAL,
OSMO_STATS_CLASS_PEER,
OSMO_STATS_CLASS_SUBSCRIBER,
};
enum osmo_stats_reporter_type {
OSMO_STATS_REPORTER_STATSD,
OSMO_STATS_REPORTER_LOG,
};
struct osmo_stats_reporter {
enum osmo_stats_reporter_type type;
char *name;
unsigned int have_net_config : 1;
/* config */
int enabled;
char *name_prefix;
char *dest_addr_str;
char *bind_addr_str;
int dest_port;
int mtu;
enum osmo_stats_class max_class;
/* state */
int running;
struct sockaddr dest_addr;
int dest_addr_len;
struct sockaddr bind_addr;
int bind_addr_len;
int fd;
struct msgb *buffer;
int agg_enabled;
struct llist_head list;
int (*open)(struct osmo_stats_reporter *srep);
int (*close)(struct osmo_stats_reporter *srep);
int (*send_counter)(struct osmo_stats_reporter *srep,
const struct rate_ctr_group *ctrg,
const struct rate_ctr_desc *desc,
int64_t value, int64_t delta);
int (*send_item)(struct osmo_stats_reporter *srep,
const struct osmo_stat_item_group *statg,
const struct osmo_stat_item_desc *desc,
int32_t value);
};
struct osmo_stats_config {
int interval;
};
extern struct osmo_stats_config *osmo_stats_config;
void osmo_stats_init(void *ctx);
int osmo_stats_report();
int osmo_stats_set_interval(int interval);
struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type,
const char *name);
void osmo_stats_reporter_free(struct osmo_stats_reporter *srep);
struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name);
struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name);
struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type,
const char *name);
int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr);
int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port);
int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr);
int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu);
int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
enum osmo_stats_class class_id);
int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix);
int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep);
int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep);

View File

@ -118,6 +118,7 @@ struct gprs_nsvc {
struct osmo_timer_list timer;
enum nsvc_timer_mode timer_mode;
struct timeval timer_started;
int alive_retries;
unsigned int remote_end_is_sgsn:1;
@ -125,6 +126,7 @@ struct gprs_nsvc {
unsigned int nsvci_is_valid:1;
struct rate_ctr_group *ctrg;
struct osmo_stat_item_group *statg;
/*! \brief which link-layer are we based on? */
enum gprs_ns_ll ll;

View File

@ -75,6 +75,7 @@ enum node_type {
SERVICE_NODE, /*!< \brief Service node. */
DEBUG_NODE, /*!< \brief Debug node. */
CFG_LOG_NODE, /*!< \brief Configure the logging */
CFG_STATS_NODE, /*!< \brief Configure the statistics */
VTY_NODE, /*!< \brief Vty node. */
@ -83,6 +84,15 @@ enum node_type {
L_NS_NODE, /*!< \brief NS node in libosmo-gb. */
L_BSSGP_NODE, /*!< \brief BSSGP node in libosmo-gb. */
/*
* When adding new nodes to the libosmocore project, these nodes can be
* used to avoid ABI changes for unrelated projects.
*/
RESERVED1_NODE, /*!< \brief Reserved for later extensions */
RESERVED2_NODE, /*!< \brief Reserved for later extensions */
RESERVED3_NODE, /*!< \brief Reserved for later extensions */
RESERVED4_NODE, /*!< \brief Reserved for later extensions */
_LAST_OSMOVTY_NODE
};

View File

@ -2,6 +2,7 @@
#include <osmocom/vty/vty.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/core/utils.h>
#define VTY_DO_LOWER 1
@ -10,7 +11,12 @@ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals,
const char *end, int do_lower);
void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
struct rate_ctr_group *ctrg);
struct rate_ctr_group *ctrg);
void vty_out_stat_item_group(struct vty *vty, const char *prefix,
struct osmo_stat_item_group *statg);
void vty_out_statistics_full(struct vty *vty, const char *prefix);
int osmo_vty_write_config_file(const char *filename);
int osmo_vty_save_config_file(void);

View File

@ -0,0 +1,3 @@
#pragma once
void osmo_stats_vty_add_cmds();

View File

@ -15,7 +15,7 @@ libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \
gsmtap_util.c crc16.c panic.c backtrace.c \
conv.c application.c rbtree.c strrb.c \
loggingrb.c crc8gen.c crc16gen.c crc32gen.c crc64gen.c \
macaddr.c
macaddr.c stat_item.c stats.c
BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c

View File

@ -31,6 +31,7 @@
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stats.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns.h>
@ -54,6 +55,7 @@ static const struct rate_ctr_group_desc bssgp_ctrg_desc = {
.group_description = "BSSGP Peer Statistics",
.num_ctr = ARRAY_SIZE(bssgp_ctr_description),
.ctr_desc = bssgp_ctr_description,
.class_id = OSMO_STATS_CLASS_PEER,
};
LLIST_HEAD(bssgp_bvc_ctxts);

View File

@ -75,6 +75,8 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/signal.h>
#include <osmocom/gprs/gprs_ns.h>
@ -104,6 +106,8 @@ enum ns_ctr {
NS_CTR_NSEI_CHG,
NS_CTR_INV_VCI,
NS_CTR_INV_NSEI,
NS_CTR_LOST_ALIVE,
NS_CTR_LOST_RESET,
};
static const struct rate_ctr_desc nsvc_ctr_description[] = {
@ -117,6 +121,8 @@ static const struct rate_ctr_desc nsvc_ctr_description[] = {
{ "nsei-chg", "NS-VC changed NSEI count " },
{ "inv-nsvci", "NS-VCI was invalid count " },
{ "inv-nsei", "NSEI was invalid count " },
{ "lost.alive", "ALIVE ACK missing count " },
{ "lost.reset", "RESET ACK missing count " },
};
static const struct rate_ctr_group_desc nsvc_ctrg_desc = {
@ -126,6 +132,22 @@ static const struct rate_ctr_group_desc nsvc_ctrg_desc = {
.ctr_desc = nsvc_ctr_description,
};
enum ns_stat {
NS_STAT_ALIVE_DELAY,
};
static const struct osmo_stat_item_desc nsvc_stat_description[] = {
{ "alive.delay", "ALIVE reponse time ", "ms", 16, 0 },
};
static const struct osmo_stat_item_group_desc nsvc_statg_desc = {
.group_name_prefix = "ns.nsvc",
.group_description = "NSVC Peer Statistics",
.num_items = ARRAY_SIZE(nsvc_stat_description),
.item_desc = nsvc_stat_description,
.class_id = OSMO_STATS_CLASS_PEER,
};
#define CHECK_TX_RC(rc, nsvc) \
if (rc < 0) \
LOGP(DNS, LOGL_ERROR, "TX failed (%d) to peer %s\n", \
@ -218,6 +240,7 @@ struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci)
nsvc->timer.cb = gprs_ns_timer_cb;
nsvc->timer.data = nsvc;
nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, nsvci);
nsvc->statg = osmo_stat_item_group_alloc(nsvc, &nsvc_statg_desc, nsvci);
llist_add(&nsvc->list, &nsi->gprs_nsvcs);
@ -531,10 +554,20 @@ static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode)
if (osmo_timer_pending(&nsvc->timer))
osmo_timer_del(&nsvc->timer);
gettimeofday(&nsvc->timer_started, NULL);
nsvc->timer_mode = mode;
osmo_timer_schedule(&nsvc->timer, seconds, 0);
}
static int nsvc_timer_elapsed_ms(struct gprs_nsvc *nsvc)
{
struct timeval now, elapsed;
gettimeofday(&now, NULL);
timersub(&now, &nsvc->timer_started, &elapsed);
return 1000 * elapsed.tv_sec + elapsed.tv_usec / 1000;
}
static void gprs_ns_timer_cb(void *data)
{
struct gprs_nsvc *nsvc = data;
@ -549,6 +582,7 @@ static void gprs_ns_timer_cb(void *data)
switch (nsvc->timer_mode) {
case NSVC_TIMER_TNS_ALIVE:
/* Tns-alive case: we expired without response ! */
rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_LOST_ALIVE]);
nsvc->alive_retries++;
if (nsvc->alive_retries >
nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
@ -578,6 +612,7 @@ static void gprs_ns_timer_cb(void *data)
nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE);
break;
case NSVC_TIMER_TNS_RESET:
rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_LOST_RESET]);
/* Chapter 7.3: Re-send the RESET */
gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION);
/* Re-start Tns-reset timer */
@ -1272,6 +1307,9 @@ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
rc = gprs_ns_tx_alive_ack(*nsvc);
break;
case NS_PDUT_ALIVE_ACK:
if ((*nsvc)->timer_mode == NSVC_TIMER_TNS_ALIVE)
osmo_stat_item_set((*nsvc)->statg->items[NS_STAT_ALIVE_DELAY],
nsvc_timer_elapsed_ms(*nsvc));
/* stop Tns-alive and start Tns-test */
nsvc_start_timer(*nsvc, NSVC_TIMER_TNS_TEST);
if ((*nsvc)->remote_end_is_sgsn) {

View File

@ -167,8 +167,10 @@ static void dump_nse(struct vty *vty, struct gprs_nsvc *nsvc, int stats)
inet_ntoa(nsvc->ip.bts_addr.sin_addr),
ntohs(nsvc->ip.bts_addr.sin_port));
vty_out(vty, "%s", VTY_NEWLINE);
if (stats)
if (stats) {
vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
vty_out_stat_item_group(vty, " ", nsvc->statg);
}
}
static void dump_ns(struct vty *vty, struct gprs_ns_inst *nsi, int stats)

View File

@ -117,6 +117,11 @@ static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
.description = "GPRS GTP library",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[INT2IDX(DLSTATS)] = {
.name = "DLSTATS",
.description = "Statistics messages and logging",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
/*! \brief descriptive string for each log level */

View File

@ -83,6 +83,15 @@ void rate_ctr_add(struct rate_ctr *ctr, int inc)
ctr->current += inc;
}
/*! \brief Return the counter difference since the last call to this function */
int64_t rate_ctr_difference(struct rate_ctr *ctr)
{
int64_t result = ctr->current - ctr->previous;
ctr->previous = ctr->current;
return result;
}
static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
{
/* calculate rate over last interval */
@ -177,4 +186,36 @@ const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, c
return NULL;
}
int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg,
rate_ctr_handler_t handle_counter, void *data)
{
int rc = 0;
int i;
for (i = 0; i < ctrg->desc->num_ctr; i++) {
struct rate_ctr *ctr = &ctrg->ctr[i];
rc = handle_counter(ctrg,
ctr, &ctrg->desc->ctr_desc[i], data);
if (rc < 0)
return rc;
}
return rc;
}
int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data)
{
struct rate_ctr_group *statg;
int rc = 0;
llist_for_each_entry(statg, &rate_ctr_groups, list) {
rc = handle_group(statg, data);
if (rc < 0)
return rc;
}
return rc;
}
/*! @} */

268
src/stat_item.c Normal file
View File

@ -0,0 +1,268 @@
/* utility routines for keeping conters about events and the event rates */
/* (C) 2015 by Sysmocom s.f.m.c. GmbH
* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*! \addtogroup osmo_stat_item
* @{
*/
/*! \file stat_item.c */
#include <stdint.h>
#include <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/stat_item.h>
static LLIST_HEAD(osmo_stat_item_groups);
static int32_t global_value_id = 0;
static void *tall_stat_item_ctx;
/*! \brief Allocate a new group of counters according to description
* \param[in] ctx \ref talloc context
* \param[in] desc Statistics item group description
* \param[in] idx Index of new stat item group
*/
struct osmo_stat_item_group *osmo_stat_item_group_alloc(void *ctx,
const struct osmo_stat_item_group_desc *desc,
unsigned int idx)
{
unsigned int group_size;
unsigned int items_size = 0;
unsigned int item_idx;
void *items;
struct osmo_stat_item_group *group;
group_size = sizeof(struct osmo_stat_item_group) +
desc->num_items * sizeof(struct osmo_stat_item *);
if (!ctx)
ctx = tall_stat_item_ctx;
group = talloc_zero_size(ctx, group_size);
if (!group)
return NULL;
group->desc = desc;
group->idx = idx;
/* Get combined size of all items */
for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
unsigned int size;
size = sizeof(struct osmo_stat_item) +
sizeof(struct osmo_stat_item_value) *
desc->item_desc[item_idx].num_values;
/* Align to pointer size */
size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
/* Store offsets into the item array */
group->items[item_idx] = (void *)items_size;
items_size += size;
}
items = talloc_zero_size(group, items_size);
if (!items) {
talloc_free(group);
return NULL;
}
/* Update item pointers */
for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
struct osmo_stat_item *item = (struct osmo_stat_item *)
((uint8_t *)items + (int)group->items[item_idx]);
unsigned int i;
group->items[item_idx] = item;
item->last_offs = desc->item_desc[item_idx].num_values - 1;
item->last_value_index = -1;
item->desc = &desc->item_desc[item_idx];
for (i = 0; i <= item->last_offs; i++) {
item->values[i].value = desc->item_desc[item_idx].default_value;
item->values[i].id = STAT_ITEM_NOVALUE_ID;
}
}
llist_add(&group->list, &osmo_stat_item_groups);
return group;
}
/*! \brief Free the memory for the specified group of counters */
void osmo_stat_item_group_free(struct osmo_stat_item_group *grp)
{
llist_del(&grp->list);
talloc_free(grp);
}
void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)
{
item->last_offs += 1;
if (item->last_offs >= item->desc->num_values)
item->last_offs = 0;
global_value_id += 1;
if (global_value_id == STAT_ITEM_NOVALUE_ID)
global_value_id += 1;
item->values[item->last_offs].value = value;
item->values[item->last_offs].id = global_value_id;
}
int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_idx,
int32_t *value)
{
const struct osmo_stat_item_value *next_value;
const struct osmo_stat_item_value *item_value = NULL;
int idx_delta;
int next_offs;
next_offs = item->last_offs;
next_value = &item->values[next_offs];
while (next_value->id - *next_idx >= 0 &&
next_value->id != STAT_ITEM_NOVALUE_ID)
{
item_value = next_value;
next_offs -= 1;
if (next_offs < 0)
next_offs = item->desc->num_values - 1;
if (next_offs == item->last_offs)
break;
next_value = &item->values[next_offs];
}
if (!item_value)
/* All items have been read */
return 0;
*value = item_value->value;
idx_delta = item_value->id + 1 - *next_idx;
*next_idx = item_value->id + 1;
return idx_delta;
}
/*! \brief Skip all values of this item and update idx accordingly */
int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx)
{
int discarded = item->values[item->last_offs].id + 1 - *idx;
*idx = item->values[item->last_offs].id + 1;
return discarded;
}
/*! \brief Skip all values of all items and update idx accordingly */
int osmo_stat_item_discard_all(int32_t *idx)
{
int discarded = global_value_id + 1 - *idx;
*idx = global_value_id + 1;
return discarded;
}
/*! \brief Initialize the stat item module */
int osmo_stat_item_init(void *tall_ctx)
{
tall_stat_item_ctx = tall_ctx;
return 0;
}
/*! \brief Search for item group based on group name and index */
struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
const char *name, const unsigned int idx)
{
struct osmo_stat_item_group *statg;
llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
if (!statg->desc)
continue;
if (!strcmp(statg->desc->group_name_prefix, name) &&
statg->idx == idx)
return statg;
}
return NULL;
}
/*! \brief Search for item group based on group name */
const struct osmo_stat_item *osmo_stat_item_get_by_name(
const struct osmo_stat_item_group *statg, const char *name)
{
int i;
const struct osmo_stat_item_desc *item_desc;
if (!statg->desc)
return NULL;
for (i = 0; i < statg->desc->num_items; i++) {
item_desc = &statg->desc->item_desc[i];
if (!strcmp(item_desc->name, name)) {
return statg->items[i];
}
}
return NULL;
}
int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
osmo_stat_item_handler_t handle_item, void *data)
{
int rc = 0;
int i;
for (i = 0; i < statg->desc->num_items; i++) {
struct osmo_stat_item *item = statg->items[i];
rc = handle_item(statg, item, data);
if (rc < 0)
return rc;
}
return rc;
}
int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data)
{
struct osmo_stat_item_group *statg;
int rc = 0;
llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
rc = handle_group(statg, data);
if (rc < 0)
return rc;
}
return rc;
}
/*! @} */

View File

@ -74,3 +74,11 @@ struct osmo_counter *osmo_counter_get_by_name(const char *name)
}
return NULL;
}
int osmo_counter_difference(struct osmo_counter *ctr)
{
int delta = ctr->value - ctr->previous;
ctr->previous = ctr->value;
return delta;
}

696
src/stats.c Normal file
View File

@ -0,0 +1,696 @@
/*
* (C) 2015 by Sysmocom s.f.m.c. GmbH
*
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <osmocom/core/stats.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/statistics.h>
#include <osmocom/core/msgb.h>
#define STATS_DEFAULT_INTERVAL 5 /* secs */
#define STATS_DEFAULT_STATSD_BUFLEN 256
static LLIST_HEAD(osmo_stats_reporter_list);
static void *osmo_stats_ctx = NULL;
static int is_initialised = 0;
static int32_t current_stat_item_index = 0;
static struct osmo_stats_config s_stats_config = {
.interval = STATS_DEFAULT_INTERVAL,
};
struct osmo_stats_config *osmo_stats_config = &s_stats_config;
static struct osmo_timer_list osmo_stats_timer;
static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep);
static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep);
static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
const struct rate_ctr_group *ctrg,
const struct rate_ctr_desc *desc,
int64_t value, int64_t delta);
static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
const struct osmo_stat_item_group *statg,
const struct osmo_stat_item_desc *desc, int value);
static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
const struct rate_ctr_group *ctrg,
const struct rate_ctr_desc *desc,
int64_t value, int64_t delta);
static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
const struct osmo_stat_item_group *statg,
const struct osmo_stat_item_desc *desc, int value);
static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
int data_len);
static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep);
static int update_srep_config(struct osmo_stats_reporter *srep)
{
int rc = 0;
if (srep->running) {
if (srep->close)
rc = srep->close(srep);
srep->running = 0;
}
if (!srep->enabled)
return rc;
if (srep->open)
rc = srep->open(srep);
else
rc = 0;
if (rc < 0)
srep->enabled = 0;
else
srep->running = 1;
return rc;
}
static void osmo_stats_timer_cb(void *data)
{
int interval = osmo_stats_config->interval;
if (!llist_empty(&osmo_stats_reporter_list))
osmo_stats_report();
osmo_timer_schedule(&osmo_stats_timer, interval, 0);
}
static int start_timer()
{
if (!is_initialised)
return -ESRCH;
osmo_stats_timer.cb = osmo_stats_timer_cb;
osmo_timer_schedule(&osmo_stats_timer, 0, 1);
return 0;
}
struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type,
const char *name)
{
struct osmo_stats_reporter *srep;
srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter);
OSMO_ASSERT(srep);
srep->type = type;
if (name)
srep->name = talloc_strdup(srep, name);
srep->fd = -1;
llist_add(&srep->list, &osmo_stats_reporter_list);
return srep;
}
void osmo_stats_reporter_free(struct osmo_stats_reporter *srep)
{
osmo_stats_reporter_disable(srep);
llist_del(&srep->list);
talloc_free(srep);
}
void osmo_stats_init(void *ctx)
{
osmo_stats_ctx = ctx;
osmo_stat_item_discard_all(&current_stat_item_index);
is_initialised = 1;
start_timer();
}
struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type,
const char *name)
{
struct osmo_stats_reporter *srep;
llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
if (srep->type != type)
continue;
if (srep->name != name) {
if (name == NULL || srep->name == NULL ||
strcmp(name, srep->name) != 0)
continue;
}
return srep;
}
return NULL;
}
int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr)
{
int rc;
struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
struct in_addr inaddr;
if (!srep->have_net_config)
return -ENOTSUP;
OSMO_ASSERT(addr != NULL);
rc = inet_pton(AF_INET, addr, &inaddr);
if (rc <= 0)
return -EINVAL;
sock_addr->sin_addr = inaddr;
sock_addr->sin_family = AF_INET;
srep->dest_addr_len = sizeof(*sock_addr);
talloc_free(srep->dest_addr_str);
srep->dest_addr_str = talloc_strdup(srep, addr);
return update_srep_config(srep);
}
int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port)
{
struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
if (!srep->have_net_config)
return -ENOTSUP;
srep->dest_port = port;
sock_addr->sin_port = htons(port);
return update_srep_config(srep);
}
int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr)
{
int rc;
struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
struct in_addr inaddr;
if (!srep->have_net_config)
return -ENOTSUP;
if (addr) {
rc = inet_pton(AF_INET, addr, &inaddr);
if (rc <= 0)
return -EINVAL;
} else {
inaddr.s_addr = INADDR_ANY;
}
sock_addr->sin_addr = inaddr;
sock_addr->sin_family = AF_INET;
srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
talloc_free(srep->bind_addr_str);
srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
return update_srep_config(srep);
}
int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu)
{
if (!srep->have_net_config)
return -ENOTSUP;
if (mtu < 0)
return -EINVAL;
srep->mtu = mtu;
return update_srep_config(srep);
}
int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
enum osmo_stats_class class_id)
{
if (class_id == OSMO_STATS_CLASS_UNKNOWN)
return -EINVAL;
srep->max_class = class_id;
return 0;
}
int osmo_stats_set_interval(int interval)
{
if (interval <= 0)
return -EINVAL;
osmo_stats_config->interval = interval;
if (is_initialised)
start_timer();
return 0;
}
int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
{
talloc_free(srep->name_prefix);
srep->name_prefix = prefix ? talloc_strdup(srep, prefix) : NULL;
return update_srep_config(srep);
}
int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep)
{
srep->enabled = 1;
return update_srep_config(srep);
}
int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep)
{
srep->enabled = 0;
return update_srep_config(srep);
}
static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
int data_len)
{
int rc;
rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT,
&srep->dest_addr, srep->dest_addr_len);
if (rc == -1)
rc = -errno;
return rc;
}
static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep)
{
int rc;
if (!srep->buffer || msgb_length(srep->buffer) == 0)
return 0;
rc = osmo_stats_reporter_send(srep,
(const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
msgb_trim(srep->buffer, 0);
return rc;
}
static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep,
unsigned int index, int class_id)
{
if (class_id == OSMO_STATS_CLASS_UNKNOWN)
class_id = index != 0 ?
OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL;
return class_id <= srep->max_class;
}
/*** log reporter ***/
struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
srep->have_net_config = 0;
srep->send_counter = osmo_stats_reporter_log_send_counter;
srep->send_item = osmo_stats_reporter_log_send_item;
return srep;
}
static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep,
const char *type,
const char *name1, unsigned int index1, const char *name2, int value,
const char *unit)
{
LOGP(DLSTATS, LOGL_INFO,
"stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n",
type, srep->name_prefix ? srep->name_prefix : "",
name1 ? name1 : "", index1,
name2, value, unit ? unit : "");
return 0;
}
static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
const struct rate_ctr_group *ctrg,
const struct rate_ctr_desc *desc,
int64_t value, int64_t delta)
{
if (ctrg)
return osmo_stats_reporter_log_send(srep, "c",
ctrg->desc->group_name_prefix,
ctrg->idx,
desc->name, value, NULL);
else
return osmo_stats_reporter_log_send(srep, "c",
NULL, 0,
desc->name, value, NULL);
}
static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
const struct osmo_stat_item_group *statg,
const struct osmo_stat_item_desc *desc, int value)
{
return osmo_stats_reporter_log_send(srep, "i",
statg->desc->group_name_prefix, statg->idx,
desc->name, value, desc->unit);
}
/*** statsd reporter ***/
struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name)
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_STATSD, name);
srep->have_net_config = 1;
srep->open = osmo_stats_reporter_statsd_open;
srep->close = osmo_stats_reporter_statsd_close;
srep->send_counter = osmo_stats_reporter_statsd_send_counter;
srep->send_item = osmo_stats_reporter_statsd_send_item;
return srep;
}
static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep)
{
int sock;
int rc;
int buffer_size = STATS_DEFAULT_STATSD_BUFLEN;
if (srep->fd != -1)
osmo_stats_reporter_statsd_close(srep);
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1)
return -errno;
if (srep->bind_addr_len > 0) {
rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
if (rc == -1)
goto failed;
}
srep->fd = sock;
if (srep->mtu > 0) {
buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
srep->agg_enabled = 1;
}
srep->buffer = msgb_alloc(buffer_size, "stats buffer");
return 0;
failed:
rc = -errno;
close(sock);
return rc;
}
static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep)
{
int rc;
if (srep->fd == -1)
return -EBADF;
osmo_stats_reporter_send_buffer(srep);
rc = close(srep->fd);
srep->fd = -1;
msgb_free(srep->buffer);
srep->buffer = NULL;
return rc == -1 ? -errno : 0;
}
static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
const char *name1, unsigned int index1, const char *name2, int value,
const char *unit)
{
char *buf;
int buf_size;
int nchars, rc = 0;
char *fmt = NULL;
int old_len = msgb_length(srep->buffer);
if (name1) {
if (index1 != 0)
fmt = "%1$s.%2$s.%6$u.%3$s:%4$d|%5$s";
else
fmt = "%1$s.%2$s.%3$s:%4$d|%5$s";
} else {
fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s";
}
if (!srep->name_prefix)
fmt += 5; /* skip prefix part */
if (srep->agg_enabled) {
if (msgb_length(srep->buffer) > 0 &&
msgb_tailroom(srep->buffer) > 0)
{
msgb_put_u8(srep->buffer, '\n');
}
}
buf = (char *)msgb_put(srep->buffer, 0);
buf_size = msgb_tailroom(srep->buffer);
nchars = snprintf(buf, buf_size, fmt,
srep->name_prefix, name1, name2,
value, unit, index1);
if (nchars >= buf_size) {
/* Truncated */
/* Restore original buffer (without trailing LF) */
msgb_trim(srep->buffer, old_len);
/* Send it */
rc = osmo_stats_reporter_send_buffer(srep);
/* Try again */
buf = (char *)msgb_put(srep->buffer, 0);
buf_size = msgb_tailroom(srep->buffer);
nchars = snprintf(buf, buf_size, fmt,
srep->name_prefix, name1, name2,
value, unit, index1);
if (nchars >= buf_size)
return -EMSGSIZE;
}
if (nchars > 0)
msgb_trim(srep->buffer, msgb_length(srep->buffer) + nchars);
if (!srep->agg_enabled)
rc = osmo_stats_reporter_send_buffer(srep);
return rc;
}
static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
const struct rate_ctr_group *ctrg,
const struct rate_ctr_desc *desc,
int64_t value, int64_t delta)
{
if (ctrg)
return osmo_stats_reporter_statsd_send(srep,
ctrg->desc->group_name_prefix,
ctrg->idx,
desc->name, delta, "c");
else
return osmo_stats_reporter_statsd_send(srep,
NULL, 0,
desc->name, delta, "c");
}
static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
const struct osmo_stat_item_group *statg,
const struct osmo_stat_item_desc *desc, int value)
{
return osmo_stats_reporter_statsd_send(srep,
statg->desc->group_name_prefix,
statg->idx,
desc->name, value, desc->unit);
}
/*** generic rate counter support ***/
static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep,
const struct rate_ctr_group *ctrg,
const struct rate_ctr_desc *desc,
int64_t value, int64_t delta)
{
if (!srep->send_counter)
return 0;
return srep->send_counter(srep, ctrg, desc, value, delta);
}
static int rate_ctr_handler(
struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
const struct rate_ctr_desc *desc, void *sctx_)
{
struct osmo_stats_reporter *srep;
int64_t delta = rate_ctr_difference(ctr);
if (delta == 0)
return 0;
llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
if (!srep->running)
continue;
if (!osmo_stats_reporter_check_config(srep,
ctrg->idx, ctrg->desc->class_id))
return 0;
osmo_stats_reporter_send_counter(srep, ctrg, desc,
ctr->current, delta);
/* TODO: handle result (log?, inc counter(!)?) or remove it */
}
return 0;
}
static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
{
rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
return 0;
}
/*** stat item support ***/
static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep,
const struct osmo_stat_item_group *statg,
const struct osmo_stat_item_desc *desc,
int32_t value)
{
if (!srep->send_item)
return 0;
return srep->send_item(srep, statg, desc, value);
}
static int osmo_stat_item_handler(
struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
{
struct osmo_stats_reporter *srep;
int32_t idx = current_stat_item_index;
int32_t value;
while (osmo_stat_item_get_next(item, &idx, &value) > 0) {
llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
if (!srep->running)
continue;
if (!osmo_stats_reporter_check_config(srep,
statg->idx, statg->desc->class_id))
return 0;
osmo_stats_reporter_send_item(srep, statg,
item->desc, value);
}
}
return 0;
}
static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
{
osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_);
osmo_stat_item_discard_all(&current_stat_item_index);
return 0;
}
/*** osmo counter support ***/
static int handle_counter(struct osmo_counter *counter, void *sctx_)
{
struct osmo_stats_reporter *srep;
struct rate_ctr_desc desc = {0};
/* Fake a rate counter description */
desc.name = counter->name;
desc.description = counter->description;
int delta = osmo_counter_difference(counter);
if (delta == 0)
return 0;
llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
if (!srep->running)
continue;
osmo_stats_reporter_send_counter(srep, NULL, &desc,
counter->value, delta);
/* TODO: handle result (log?, inc counter(!)?) */
}
return 0;
}
/*** main reporting function ***/
static void flush_all_reporters()
{
struct osmo_stats_reporter *srep;
llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
if (!srep->running)
continue;
osmo_stats_reporter_send_buffer(srep);
}
}
int osmo_stats_report()
{
osmo_counters_for_each(handle_counter, NULL);
rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
flush_all_reporters();
return 0;
}

View File

@ -9,7 +9,7 @@ if ENABLE_VTY
lib_LTLIBRARIES = libosmovty.la
libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \
telnet_interface.c logging_vty.c
telnet_interface.c logging_vty.c stats_vty.c
libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la
endif

430
src/vty/stats_vty.c Normal file
View File

@ -0,0 +1,430 @@
/* OpenBSC stats helper for the VTY */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2009-2014 by Holger Hans Peter Freyther
* (C) 2015 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <stdlib.h>
#include <string.h>
#include "../../config.h"
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/stats.h>
#define CFG_STATS_STR "Configure stats sub-system\n"
#define CFG_REPORTER_STR "Configure a stats reporter\n"
#define SHOW_STATS_STR "Show statistical values\n"
struct cmd_node cfg_stats_node = {
CFG_STATS_NODE,
"%s(config-stats)# ",
1
};
static const struct value_string stats_class_strs[] = {
{ OSMO_STATS_CLASS_GLOBAL, "global" },
{ OSMO_STATS_CLASS_PEER, "peer" },
{ OSMO_STATS_CLASS_SUBSCRIBER, "subscriber" },
{ 0, NULL }
};
static struct osmo_stats_reporter *osmo_stats_vty2srep(struct vty *vty)
{
if (vty->node == CFG_STATS_NODE)
return vty->index;
return NULL;
}
static int set_srep_parameter_str(struct vty *vty,
int (*fun)(struct osmo_stats_reporter *, const char *),
const char *val, const char *param_name)
{
int rc;
struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
OSMO_ASSERT(srep);
rc = fun(srep, val);
if (rc < 0) {
vty_out(vty, "%% Unable to set %s: %s%s",
param_name, strerror(-rc), VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
static int set_srep_parameter_int(struct vty *vty,
int (*fun)(struct osmo_stats_reporter *, int),
const char *val, const char *param_name)
{
int rc;
int int_val;
struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
OSMO_ASSERT(srep);
int_val = atoi(val);
rc = fun(srep, int_val);
if (rc < 0) {
vty_out(vty, "%% Unable to set %s: %s%s",
param_name, strerror(-rc), VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_stats_reporter_local_ip, cfg_stats_reporter_local_ip_cmd,
"local-ip ADDR",
"Set the IP address to which we bind locally\n"
"IP Address\n")
{
return set_srep_parameter_str(vty, osmo_stats_reporter_set_local_addr,
argv[0], "local address");
}
DEFUN(cfg_no_stats_reporter_local_ip, cfg_no_stats_reporter_local_ip_cmd,
"no local-ip",
NO_STR
"Set the IP address to which we bind locally\n")
{
return set_srep_parameter_str(vty, osmo_stats_reporter_set_local_addr,
NULL, "local address");
}
DEFUN(cfg_stats_reporter_remote_ip, cfg_stats_reporter_remote_ip_cmd,
"remote-ip ADDR",
"Set the remote IP address to which we connect\n"
"IP Address\n")
{
return set_srep_parameter_str(vty, osmo_stats_reporter_set_remote_addr,
argv[0], "remote address");
}
DEFUN(cfg_stats_reporter_remote_port, cfg_stats_reporter_remote_port_cmd,
"remote-port <1-65535>",
"Set the remote port to which we connect\n"
"Remote port number\n")
{
return set_srep_parameter_int(vty, osmo_stats_reporter_set_remote_port,
argv[0], "remote port");
}
DEFUN(cfg_stats_reporter_mtu, cfg_stats_reporter_mtu_cmd,
"mtu <100-65535>",
"Set the maximum packet size\n"
"Size in byte\n")
{
return set_srep_parameter_int(vty, osmo_stats_reporter_set_mtu,
argv[0], "mtu");
}
DEFUN(cfg_no_stats_reporter_mtu, cfg_no_stats_reporter_mtu_cmd,
"no mtu",
NO_STR "Set the maximum packet size\n")
{
return set_srep_parameter_int(vty, osmo_stats_reporter_set_mtu,
"0", "mtu");
}
DEFUN(cfg_stats_reporter_prefix, cfg_stats_reporter_prefix_cmd,
"prefix PREFIX",
"Set the item name prefix\n"
"The prefix string\n")
{
return set_srep_parameter_str(vty, osmo_stats_reporter_set_name_prefix,
argv[0], "prefix string");
}
DEFUN(cfg_no_stats_reporter_prefix, cfg_no_stats_reporter_prefix_cmd,
"no prefix",
NO_STR
"Set the item name prefix\n")
{
return set_srep_parameter_str(vty, osmo_stats_reporter_set_name_prefix,
"", "prefix string");
}
DEFUN(cfg_stats_reporter_level, cfg_stats_reporter_level_cmd,
"level (global|peer|subscriber)",
"Set the maximum group level\n"
"Report global groups only\n"
"Report global and network peer related groups\n"
"Report global, peer, and subscriber groups\n")
{
int level = get_string_value(stats_class_strs, argv[0]);
int rc;
struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
OSMO_ASSERT(srep);
rc = osmo_stats_reporter_set_max_class(srep, level);
if (rc < 0) {
vty_out(vty, "%% Unable to set level: %s%s",
strerror(-rc), VTY_NEWLINE);
return CMD_WARNING;
}
return 0;
}
DEFUN(cfg_stats_reporter_enable, cfg_stats_reporter_enable_cmd,
"enable",
"Enable the reporter\n")
{
int rc;
struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
OSMO_ASSERT(srep);
rc = osmo_stats_reporter_enable(srep);
if (rc < 0) {
vty_out(vty, "%% Unable to enable the reporter: %s%s",
strerror(-rc), VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_stats_reporter_disable, cfg_stats_reporter_disable_cmd,
"disable",
"Disable the reporter\n")
{
int rc;
struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
OSMO_ASSERT(srep);
rc = osmo_stats_reporter_disable(srep);
if (rc < 0) {
vty_out(vty, "%% Unable to disable the reporter: %s%s",
strerror(-rc), VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_stats_reporter_statsd, cfg_stats_reporter_statsd_cmd,
"stats reporter statsd",
CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n")
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
if (!srep) {
srep = osmo_stats_reporter_create_statsd(NULL);
if (!srep) {
vty_out(vty, "%% Unable to create statsd reporter%s",
VTY_NEWLINE);
return CMD_WARNING;
}
srep->max_class = OSMO_STATS_CLASS_GLOBAL;
/* TODO: if needed, add osmo_stats_add_reporter(srep); */
}
vty->index = srep;
vty->node = CFG_STATS_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_stats_interval, cfg_stats_interval_cmd,
"stats interval <1-65535>",
CFG_STATS_STR "Set the reporting interval\n"
"Interval in seconds\n")
{
int rc;
int interval = atoi(argv[0]);
rc = osmo_stats_set_interval(interval);
if (rc < 0) {
vty_out(vty, "%% Unable to set interval: %s%s",
strerror(-rc), VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(cfg_no_stats_reporter_statsd, cfg_no_stats_reporter_statsd_cmd,
"no stats reporter statsd",
NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n")
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
if (!srep) {
vty_out(vty, "%% No statsd logging active%s",
VTY_NEWLINE);
return CMD_WARNING;
}
osmo_stats_reporter_free(srep);
return CMD_SUCCESS;
}
DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd,
"stats reporter log",
CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n")
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
if (!srep) {
srep = osmo_stats_reporter_create_log(NULL);
if (!srep) {
vty_out(vty, "%% Unable to create log reporter%s",
VTY_NEWLINE);
return CMD_WARNING;
}
srep->max_class = OSMO_STATS_CLASS_GLOBAL;
/* TODO: if needed, add osmo_stats_add_reporter(srep); */
}
vty->index = srep;
vty->node = CFG_STATS_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_no_stats_reporter_log, cfg_no_stats_reporter_log_cmd,
"no stats reporter log",
NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n")
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
if (!srep) {
vty_out(vty, "%% No log reporting active%s",
VTY_NEWLINE);
return CMD_WARNING;
}
osmo_stats_reporter_free(srep);
return CMD_SUCCESS;
}
DEFUN(show_stats,
show_stats_cmd,
"show stats",
SHOW_STR SHOW_STATS_STR)
{
vty_out_statistics_full(vty, "");
return CMD_SUCCESS;
}
static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_reporter *srep)
{
if (srep == NULL)
return 0;
switch (srep->type) {
case OSMO_STATS_REPORTER_STATSD:
vty_out(vty, "stats reporter statsd%s", VTY_NEWLINE);
break;
case OSMO_STATS_REPORTER_LOG:
vty_out(vty, "stats reporter log%s", VTY_NEWLINE);
break;
}
vty_out(vty, " disable%s", VTY_NEWLINE);
if (srep->have_net_config) {
if (srep->dest_addr_str)
vty_out(vty, " remote-ip %s%s",
srep->dest_addr_str, VTY_NEWLINE);
if (srep->dest_port)
vty_out(vty, " remote-port %d%s",
srep->dest_port, VTY_NEWLINE);
if (srep->bind_addr_str)
vty_out(vty, " local-ip %s%s",
srep->bind_addr_str, VTY_NEWLINE);
if (srep->mtu)
vty_out(vty, " mtu %d%s",
srep->mtu, VTY_NEWLINE);
}
if (srep->max_class)
vty_out(vty, " level %s%s",
get_value_string(stats_class_strs, srep->max_class),
VTY_NEWLINE);
if (srep->name_prefix && *srep->name_prefix)
vty_out(vty, " prefix %s%s",
srep->name_prefix, VTY_NEWLINE);
else
vty_out(vty, " no prefix%s", VTY_NEWLINE);
if (srep->enabled)
vty_out(vty, " enable%s", VTY_NEWLINE);
return 1;
}
static int config_write_stats(struct vty *vty)
{
struct osmo_stats_reporter *srep;
/* TODO: loop through all reporters */
srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
config_write_stats_reporter(vty, srep);
srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
config_write_stats_reporter(vty, srep);
vty_out(vty, "stats interval %d%s", osmo_stats_config->interval, VTY_NEWLINE);
return 1;
}
void osmo_stats_vty_add_cmds()
{
install_element_ve(&show_stats_cmd);
install_element(CONFIG_NODE, &cfg_stats_reporter_statsd_cmd);
install_element(CONFIG_NODE, &cfg_no_stats_reporter_statsd_cmd);
install_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd);
install_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd);
install_element(CONFIG_NODE, &cfg_stats_interval_cmd);
install_node(&cfg_stats_node, config_write_stats);
vty_install_default(CFG_STATS_NODE);
install_element(CFG_STATS_NODE, &cfg_stats_reporter_local_ip_cmd);
install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_local_ip_cmd);
install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_ip_cmd);
install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_port_cmd);
install_element(CFG_STATS_NODE, &cfg_stats_reporter_mtu_cmd);
install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_mtu_cmd);
install_element(CFG_STATS_NODE, &cfg_stats_reporter_prefix_cmd);
install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_prefix_cmd);
install_element(CFG_STATS_NODE, &cfg_stats_reporter_level_cmd);
install_element(CFG_STATS_NODE, &cfg_stats_reporter_enable_cmd);
install_element(CFG_STATS_NODE, &cfg_stats_reporter_disable_cmd);
}

View File

@ -29,7 +29,9 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/statistics.h>
#include <osmocom/vty/vty.h>
@ -39,6 +41,30 @@
* @{
*/
struct vty_out_context {
struct vty *vty;
const char *prefix;
};
static int rate_ctr_handler(
struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
const struct rate_ctr_desc *desc, void *vctx_)
{
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
vty_out(vty, " %s%s: %8" PRIu64 " "
"(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
vctx->prefix, desc->description, ctr->current,
ctr->intv[RATE_CTR_INTV_SEC].rate,
ctr->intv[RATE_CTR_INTV_MIN].rate,
ctr->intv[RATE_CTR_INTV_HOUR].rate,
ctr->intv[RATE_CTR_INTV_DAY].rate,
VTY_NEWLINE);
return 0;
}
/*! \brief print a rate counter group to given VTY
* \param[in] vty The VTY to which it should be printed
* \param[in] prefix Any additional log prefix ahead of each line
@ -47,20 +73,97 @@
void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
struct rate_ctr_group *ctrg)
{
unsigned int i;
struct vty_out_context vctx = {vty, prefix};
vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE);
for (i = 0; i < ctrg->desc->num_ctr; i++) {
struct rate_ctr *ctr = &ctrg->ctr[i];
vty_out(vty, " %s%s: %8" PRIu64 " "
"(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
prefix, ctrg->desc->ctr_desc[i].description, ctr->current,
ctr->intv[RATE_CTR_INTV_SEC].rate,
ctr->intv[RATE_CTR_INTV_MIN].rate,
ctr->intv[RATE_CTR_INTV_HOUR].rate,
ctr->intv[RATE_CTR_INTV_DAY].rate,
rate_ctr_for_each_counter(ctrg, rate_ctr_handler, &vctx);
}
static int osmo_stat_item_handler(
struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *vctx_)
{
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
vty_out(vty, " %s%s: %8" PRIi32 " %s%s",
vctx->prefix, item->desc->description,
osmo_stat_item_get_last(item),
item->desc->unit, VTY_NEWLINE);
return 0;
}
/*! \brief print a stat item group to given VTY
* \param[in] vty The VTY to which it should be printed
* \param[in] prefix Any additional log prefix ahead of each line
* \param[in] statg Stat item group to be printed
*/
void vty_out_stat_item_group(struct vty *vty, const char *prefix,
struct osmo_stat_item_group *statg)
{
struct vty_out_context vctx = {vty, prefix};
vty_out(vty, "%s%s:%s", prefix, statg->desc->group_description,
VTY_NEWLINE);
osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, &vctx);
}
static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vctx_)
{
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
if (statg->idx)
vty_out(vty, "%s%s (%d):%s", vctx->prefix,
statg->desc->group_description, statg->idx,
VTY_NEWLINE);
};
else
vty_out(vty, "%s%s:%s", vctx->prefix,
statg->desc->group_description, VTY_NEWLINE);
osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vctx);
return 0;
}
static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_)
{
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
if (ctrg->idx)
vty_out(vty, "%s%s (%d):%s", vctx->prefix,
ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE);
else
vty_out(vty, "%s%s:%s", vctx->prefix,
ctrg->desc->group_description, VTY_NEWLINE);
rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vctx);
return 0;
}
static int handle_counter(struct osmo_counter *counter, void *vctx_)
{
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
vty_out(vty, " %s%s: %8lu%s",
vctx->prefix, counter->description,
osmo_counter_get(counter), VTY_NEWLINE);
return 0;
}
void vty_out_statistics_full(struct vty *vty, const char *prefix)
{
struct vty_out_context vctx = {vty, prefix};
vty_out(vty, "%sUngrouped counters:%s", prefix, VTY_NEWLINE);
osmo_counters_for_each(handle_counter, &vctx);
rate_ctr_for_each_group(rate_ctr_group_handler, &vctx);
osmo_stat_item_for_each_group(osmo_stat_item_group_handler, &vctx);
}
/*! \brief Generate a VTY command string from value_string */

View File

@ -9,7 +9,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
kasumi/kasumi_test logging/logging_test fr/fr_test \
loggingrb/loggingrb_test strrb/strrb_test \
vty/vty_test comp128/comp128_test utils/utils_test \
smscb/gsm0341_test
smscb/gsm0341_test stats/stats_test
if ENABLE_MSGFILE
check_PROGRAMS += msgfile/msgfile_test
@ -18,6 +18,9 @@ endif
utils_utils_test_SOURCES = utils/utils_test.c
utils_utils_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
stats_stats_test_SOURCES = stats/stats_test.c
stats_stats_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
a5_a5_test_SOURCES = a5/a5_test.c
a5_a5_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libgsmint.la
@ -64,19 +67,19 @@ ussd_ussd_test_SOURCES = ussd/ussd_test.c
ussd_ussd_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
gb_bssgp_fc_test_SOURCES = gb/bssgp_fc_test.c
gb_bssgp_fc_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la
gb_bssgp_fc_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(top_builddir)/src/vty/libosmovty.la
gb_gprs_bssgp_test_SOURCES = gb/gprs_bssgp_test.c
gb_gprs_bssgp_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DL)
gb_gprs_bssgp_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(top_builddir)/src/vty/libosmovty.la $(LIBRARY_DL)
gb_gprs_ns_test_SOURCES = gb/gprs_ns_test.c
gb_gprs_ns_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DL)
gb_gprs_ns_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(top_builddir)/src/vty/libosmovty.la $(LIBRARY_DL)
logging_logging_test_SOURCES = logging/logging_test.c
logging_logging_test_LDADD = $(top_builddir)/src/libosmocore.la
fr_fr_test_SOURCES = fr/fr_test.c
fr_fr_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DL)
fr_fr_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(top_builddir)/src/vty/libosmovty.la $(LIBRARY_DL)
loggingrb_loggingrb_test_SOURCES = loggingrb/loggingrb_test.c
loggingrb_loggingrb_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/vty/libosmovty.la
@ -120,7 +123,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
fr/fr_test.ok loggingrb/logging_test.ok \
loggingrb/logging_test.err strrb/strrb_test.ok \
vty/vty_test.ok comp128/comp128_test.ok \
utils/utils_test.ok
utils/utils_test.ok stats/stats_test.ok
DISTCLEANFILES = atconfig

213
tests/stats/stats_test.c Normal file
View File

@ -0,0 +1,213 @@
/* tests for statistics */
/*
* (C) 2015 Sysmocom s.m.f.c. GmbH
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/stat_item.h>
#include <stdio.h>
static void stat_test(void)
{
enum test_items {
TEST_A_ITEM,
TEST_B_ITEM,
};
static const struct osmo_stat_item_desc item_description[] = {
{ "item.a", "The A value", "ma", 4, -1 },
{ "item.b", "The B value", "kb", 7, -1 },
};
static const struct osmo_stat_item_group_desc statg_desc = {
.group_name_prefix = "test.one",
.group_description = "Test number 1",
.num_items = ARRAY_SIZE(item_description),
.item_desc = item_description,
};
struct osmo_stat_item_group *statg =
osmo_stat_item_group_alloc(NULL, &statg_desc, 0);
struct osmo_stat_item_group *sgrp2;
const struct osmo_stat_item *sitem1, *sitem2;
int rc;
int32_t value;
int32_t rd_a = 0;
int32_t rd_b = 0;
int i;
OSMO_ASSERT(statg != NULL);
sgrp2 = osmo_stat_item_get_group_by_name_idx("test.one", 0);
OSMO_ASSERT(sgrp2 == statg);
sgrp2 = osmo_stat_item_get_group_by_name_idx("test.one", 1);
OSMO_ASSERT(sgrp2 == NULL);
sgrp2 = osmo_stat_item_get_group_by_name_idx("test.two", 0);
OSMO_ASSERT(sgrp2 == NULL);
sitem1 = osmo_stat_item_get_by_name(statg, "item.c");
OSMO_ASSERT(sitem1 == NULL);
sitem1 = osmo_stat_item_get_by_name(statg, "item.a");
OSMO_ASSERT(sitem1 != NULL);
OSMO_ASSERT(sitem1 == statg->items[TEST_A_ITEM]);
sitem2 = osmo_stat_item_get_by_name(statg, "item.b");
OSMO_ASSERT(sitem2 != NULL);
OSMO_ASSERT(sitem2 != sitem1);
OSMO_ASSERT(sitem2 == statg->items[TEST_B_ITEM]);
value = osmo_stat_item_get_last(statg->items[TEST_A_ITEM]);
OSMO_ASSERT(value == -1);
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc == 0);
osmo_stat_item_set(statg->items[TEST_A_ITEM], 1);
value = osmo_stat_item_get_last(statg->items[TEST_A_ITEM]);
OSMO_ASSERT(value == 1);
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == 1);
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc == 0);
for (i = 2; i <= 32; i++) {
osmo_stat_item_set(statg->items[TEST_A_ITEM], i);
osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i);
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == i);
rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == 1000 + i);
}
/* Keep 2 in FIFO */
osmo_stat_item_set(statg->items[TEST_A_ITEM], 33);
osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + 33);
for (i = 34; i <= 64; i++) {
osmo_stat_item_set(statg->items[TEST_A_ITEM], i);
osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i);
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == i-1);
rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == 1000 + i-1);
}
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == 64);
rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == 1000 + 64);
/* Overrun FIFOs */
for (i = 65; i <= 96; i++) {
osmo_stat_item_set(statg->items[TEST_A_ITEM], i);
osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i);
}
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == 93);
for (i = 94; i <= 96; i++) {
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == i);
}
rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == 1000 + 90);
for (i = 91; i <= 96; i++) {
rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == 1000 + i);
}
/* Test Discard (single item) */
osmo_stat_item_set(statg->items[TEST_A_ITEM], 97);
rc = osmo_stat_item_discard(statg->items[TEST_A_ITEM], &rd_a);
OSMO_ASSERT(rc > 0);
rc = osmo_stat_item_discard(statg->items[TEST_A_ITEM], &rd_a);
OSMO_ASSERT(rc == 0);
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc == 0);
osmo_stat_item_set(statg->items[TEST_A_ITEM], 98);
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc > 0);
OSMO_ASSERT(value == 98);
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc == 0);
/* Test Discard (all items) */
osmo_stat_item_set(statg->items[TEST_A_ITEM], 99);
osmo_stat_item_set(statg->items[TEST_A_ITEM], 100);
osmo_stat_item_set(statg->items[TEST_A_ITEM], 101);
osmo_stat_item_set(statg->items[TEST_B_ITEM], 99);
osmo_stat_item_set(statg->items[TEST_B_ITEM], 100);
rc = osmo_stat_item_discard_all(&rd_a);
rc = osmo_stat_item_discard_all(&rd_b);
rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
OSMO_ASSERT(rc == 0);
rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
OSMO_ASSERT(rc == 0);
osmo_stat_item_group_free(statg);
sgrp2 = osmo_stat_item_get_group_by_name_idx("test.one", 0);
OSMO_ASSERT(sgrp2 == NULL);
}
int main(int argc, char **argv)
{
static const struct log_info log_info = {};
log_init(&log_info, NULL);
osmo_stat_item_init(NULL);
stat_test();
return 0;
}

View File

View File

@ -136,6 +136,12 @@ cat $abs_srcdir/utils/utils_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/utils/utils_test], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([stats])
AT_KEYWORDS([stats])
cat $abs_srcdir/stats/stats_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/stats/stats_test], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([bssgp-fc])
AT_KEYWORDS([bssgp-fc])
cat $abs_srcdir/gb/bssgp_fc_tests.ok > expout

View File

@ -24,11 +24,11 @@ Returned: 0, Current node: 3 '%s# '
Going to execute 'configure terminal'
Returned: 0, Current node: 4 '%s(config)# '
Going to execute 'line vty'
Returned: 0, Current node: 8 '%s(config-line)# '
Returned: 0, Current node: 9 '%s(config-line)# '
Going to execute 'exit'
Returned: 0, Current node: 4 '%s(config)# '
Going to execute 'line vty'
Returned: 0, Current node: 8 '%s(config-line)# '
Returned: 0, Current node: 9 '%s(config-line)# '
Going to execute 'end'
Returned: 0, Current node: 3 '%s# '
Going to execute 'configure terminal'
@ -36,7 +36,7 @@ Returned: 0, Current node: 4 '%s(config)# '
Going to execute 'log stderr'
Returned: 0, Current node: 7 '%s(config-log)# '
Going to execute 'line vty'
Returned: 0, Current node: 8 '%s(config-line)# '
Returned: 0, Current node: 9 '%s(config-line)# '
Going to execute 'log stderr'
Returned: 0, Current node: 7 '%s(config-log)# '
Going to execute 'end'