subsys: stats - Statistics support

Implements the definition, manipulation, and delivery of statistics.

Signed-off-by: Christopher Collins <ccollins@apache.org>
This commit is contained in:
Christopher Collins 2018-01-29 14:19:50 -08:00 committed by Carles Cufí
parent 3986ef2770
commit 5ed30f0360
4 changed files with 691 additions and 0 deletions

386
include/stats.h Normal file
View file

@ -0,0 +1,386 @@
/*
* Copyright Runtime.io 2018. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Statistics.
*
* Statistics are per-module event counters for troubleshooting, maintenance,
* and usage monitoring. Statistics are organized into named "groups", with
* each group consisting of a set of "entries". An entry corresponds to an
* individual counter. Each entry can optionally be named if the STATS_NAMES
* setting is enabled. Statistics can be retrieved with the mcumgr management
* subsystem.
*
* There are two, largely duplicated, statistics sections here, in order to
* provide the optional ability to name statistics.
*
* STATS_SECT_START/END actually declare the statistics structure definition,
* STATS_SECT_DECL() creates the structure declaration so you can declare
* these statistics as a global structure, and STATS_NAME_START/END are how
* you name the statistics themselves.
*
* Statistics entries can be declared as any of several integer types.
* However, all statistics in a given structure must be of the same size, and
* they are all unsigned.
*
* - STATS_SECT_ENTRY(): default statistic entry, 32-bits.
*
* - STATS_SECT_ENTRY16(): 16-bits. Smaller statistics if you need to fit into
* specific RAM or code size numbers.
*
* - STATS_SECT_ENTRY32(): 32-bits.
*
* - STATS_SECT_ENTRY64(): 64-bits. Useful for storing chunks of data.
*
* Following the static entry declaration is the statistic names declaration.
* This is compiled out when the CONFIGURE_STATS_NAME setting is undefined.
*
* When CONFIG_STATS_NAMES is defined, the statistics names are stored and
* returned to the management APIs. When the setting is undefined, temporary
* names are generated as needed with the following format:
*
* s<stat-idx>
*
* E.g., "s0", "s1", etc.
*/
#ifndef H_STATS_
#define H_STATS_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct stats_name_map {
u16_t snm_off;
const char *snm_name;
} __packed;
struct stats_hdr {
const char *s_name;
u8_t s_size;
u8_t s_cnt;
u16_t s_pad1;
#ifdef CONFIG_STATS_NAMES
const struct stats_name_map *s_map;
int s_map_cnt;
#endif
struct stats_hdr *s_next;
};
/**
* @brief Declares a stat group struct.
*
* @param group__ The name to assign to the structure tag.
*/
#define STATS_SECT_DECL(group__) \
struct stats_ ## group__
/**
* @brief Ends a stats group struct definition.
*/
#define STATS_SECT_END }
/* The following macros depend on whether CONFIG_STATS is defined. If it is
* not defined, then invocations of these macros get compiled out.
*/
#ifdef CONFIG_STATS
/**
* @brief Begins a stats group struct definition.
*
* @param group__ The stats group struct name.
*/
#define STATS_SECT_START(group__) \
STATS_SECT_DECL(group__) { \
struct stats_hdr s_hdr;
/**
* @brief Declares a 32-bit stat entry inside a group struct.
*
* @param var__ The name ot assign to the entry.
*/
#define STATS_SECT_ENTRY(var__) u32_t var__;
/**
* @brief Declares a 16-bit stat entry inside a group struct.
*
* @param var__ The name ot assign to the entry.
*/
#define STATS_SECT_ENTRY16(var__) u16_t var__;
/**
* @brief Declares a 32-bit stat entry inside a group struct.
*
* @param var__ The name ot assign to the entry.
*/
#define STATS_SECT_ENTRY32(var__) u32_t var__;
/**
* @brief Declares a 64-bit stat entry inside a group struct.
*
* @param var__ The name ot assign to the entry.
*/
#define STATS_SECT_ENTRY64(var__) u64_t var__;
/**
* @brief Increases a statistic entry by the specified amount.
*
* Increases a statistic entry by the specified amount. Compiled out if
* CONFIG_STATS is not defined.
*
* @param group__ The group containing the entry to increase.
* @param var__ The statistic entry to increase.
* @param n__ The amount to increase the statistic entry by.
*/
#define STATS_INCN(group__, var__, n__) \
((group__).var__ += (n__))
/**
* @brief Increments a statistic entry.
*
* Increments a statistic entry by one. Compiled out if CONFIG_STATS is not
* defined.
*
* @param group__ The group containing the entry to increase.
* @param var__ The statistic entry to increase.
*/
#define STATS_INC(group__, var__) \
STATS_INCN(group__, var__, 1)
/**
* @brief Sets a statistic entry to zero.
*
* Sets a statistic entry to zero. Compiled out if CONFIG_STATS is not
* defined.
*
* @param group__ The group containing the entry to clear.
* @param var__ The statistic entry to clear.
*/
#define STATS_CLEAR(group__, var__) \
((group__).var__ = 0)
#define STATS_SIZE_16 (sizeof(u16_t))
#define STATS_SIZE_32 (sizeof(u32_t))
#define STATS_SIZE_64 (sizeof(u64_t))
#define STATS_SIZE_INIT_PARMS(group__, size__) \
(size__), \
((sizeof(group__)) - sizeof(struct stats_hdr)) / (size__)
/**
* @brief Initializes and registers a statistics group.
*
* @param group__ The statistics group to initialize and
* register.
* @param size__ The size of each entry in the statistics group,
* in bytes. Must be one of: 2 (16-bits), 4
* (32-bits) or 8 (64-bits).
* @param name__ The name of the statistics group to register.
* This name must be unique among all
* statistics groups.
*
* @return 0 on success; negative error code on failure.
*/
#define STATS_INIT_AND_REG(group__, size__, name__) \
stats_init_and_reg( \
&(group__).s_hdr, \
(size__), \
(sizeof(group__) - sizeof(struct stats_hdr)) / (size__), \
STATS_NAME_INIT_PARMS(group__), \
(name__))
/**
* @brief Initializes a statistics group.
*
* @param hdr The header of the statistics structure,
* contains things like statistic section
* name, size of statistics entries, number of
* statistics, etc.
* @param size The size of each individual statistics
* element, in bytes. Must be one of: 2
* (16-bits), 4 (32-bits) or 8 (64-bits).
* @param cnt The number of elements in the stats group.
* @param map The mapping of stat offset to name.
* @param map_cnt The number of items in the statistics map
*
* @param group__ The group containing the entry to clear.
* @param var__ The statistic entry to clear.
*/
void stats_init(struct stats_hdr *shdr, uint8_t size, uint8_t cnt,
const struct stats_name_map *map, uint8_t map_cnt);
/**
* @brief Registers a statistics group to be managed.
*
* @param name The name of the statistics group to register.
* This name must be unique among all
* statistics groups. If the name is a
* duplicate, this function will return
* -EALREADY.
* @param shdr The statistics group to register.
*
* @return 0 on success, non-zero error code on failure.
*/
int stats_register(const char *name, struct stats_hdr *shdr);
/**
* @brief Initializes and registers a statistics group.
*
* Initializes and registers a statistics group. Note: it is recommended to
* use the STATS_INIT_AND_REG macro instead of this function.
*
* @param hdr The header of the statistics group to
* initialize and register.
* @param size The size of each individual statistics
* element, in bytes. Must be one of: 2
* (16-bits), 4 (32-bits) or 8 (64-bits).
* @param cnt The number of elements in the stats group.
* @param map The mapping of stat offset to name.
* @param map_cnt The number of items in the statistics map
* @param name The name of the statistics group to register.
* This name must be unique among all
* statistics groups. If the name is a
* duplicate, this function will return
* -EALREADY.
*
* @return 0 on success; negative error code on failure.
*
* @see STATS_INIT_AND_REG
*/
int stats_init_and_reg(struct stats_hdr *hdr, uint8_t size, uint8_t cnt,
const struct stats_name_map *map, uint8_t map_cnt,
const char *name);
/**
* Zeroes the specified statistics group.
*
* @param shdr The statistics group to clear.
*/
void stats_reset(struct stats_hdr *shdr);
/** @typedef stats_walk_fn
* @brief Function that gets applied to every stat entry during a walk.
*
* @param hdr The group containing the stat entry being
* walked.
* @param arg Optional argument.
* @param name The name of the statistic entry to process
* @param off The offset of the entry, from `hdr`.
*
* @return 0 if the walk should proceed;
* nonzero to abort the walk.
*/
typedef int stats_walk_fn(struct stats_hdr *hdr, void *arg,
const char *name, uint16_t off);
/**
* @brief Applies a function to every stat entry in a group.
*
* @param hdr The stats group to operate on.
* @param walk_cb The function to apply to each stat entry.
* @param arg Optional argument to pass to the callback.
*
* @return 0 if the walk completed;
* nonzero if the walk was aborted.
*/
int stats_walk(struct stats_hdr *hdr, stats_walk_fn *walk_cb, void *arg);
/** @typedef stats_group_walk_fn
* @brief Function that gets applied to every registered stats group.
*
* @param hdr The stats group being walked.
* @param arg Optional argument.
*
* @return 0 if the walk should proceed;
* nonzero to abort the walk.
*/
typedef int stats_group_walk_fn(struct stats_hdr *hdr, void *arg);
/**
* @brief Applies a function every registered statistics group.
*
* @param walk_cb The function to apply to each stat group.
* @param arg Optional argument to pass to the callback.
*
* @return 0 if the walk completed;
* nonzero if the walk was aborted.
*/
int stats_group_walk(stats_group_walk_fn *walk_cb, void *arg);
/**
* @brief Retrieves the next registered statistics group.
*
* @param cur The group whose sucessor is being retrieved, or
* NULL to retrieve the first group.
*
* @return Pointer to the retrieved group on success;
* NULL if no more groups remain.
*/
struct stats_hdr *stats_group_get_next(const struct stats_hdr *cur);
/**
* @brief Retrieves the statistics group with the specified name.
*
* @param name The name of the statistics group to look up.
*
* @return Pointer to the retrieved group on success;
* NULL if there is no matching registered group.
*/
struct stats_hdr *stats_group_find(const char *name);
#else /* CONFIG_STATS */
#define STATS_SECT_START(group__) \
STATS_SECT_DECL(group__) {
#define STATS_SECT_ENTRY(var__)
#define STATS_SECT_ENTRY16(var__)
#define STATS_SECT_ENTRY32(var__)
#define STATS_SECT_ENTRY64(var__)
#define STATS_RESET(var__)
#define STATS_SIZE_INIT_PARMS(group__, size__)
#define STATS_INCN(group__, var__, n__)
#define STATS_INC(group__, var__)
#define STATS_CLEAR(group__, var__)
#define STATS_INIT_AND_REG(group__, size__, name__) (0)
#endif /* !CONFIG_STATS */
#ifdef CONFIG_STATS_NAMES
#define STATS_NAME_MAP_NAME(sectname__) stats_map_ ## sectname__
#define STATS_NAME_START(sectname__) \
const struct stats_name_map STATS_NAME_MAP_NAME(sectname__)[] = {
#define STATS_NAME(sectname__, entry__) \
{ offsetof(STATS_SECT_DECL(sectname__), entry__), #entry__ },
#define STATS_NAME_END(sectname__) }
#define STATS_NAME_INIT_PARMS(name__) \
&(STATS_NAME_MAP_NAME(name__)[0]), \
(sizeof(STATS_NAME_MAP_NAME(name__)) / sizeof(struct stats_name_map))
#else /* CONFIG_STATS_NAMES */
#define STATS_NAME_START(name__)
#define STATS_NAME(name__, entry__)
#define STATS_NAME_END(name__)
#define STATS_NAME_INIT_PARMS(name__) NULL, 0
#endif /* CONFIG_STATS_NAMES */
#ifdef __cplusplus
}
#endif
#endif /* H_STATS_ */

View file

@ -1,2 +1,3 @@
zephyr_sources_if_kconfig(printk.c)
zephyr_sources_if_kconfig(reboot.c)
zephyr_sources_if_kconfig(stats.c)

View file

@ -288,6 +288,25 @@ config CPU_CLOCK_FREQ_MHZ
This option specifies the CPU Clock Frequency in MHz in order to
convert Intel RDTSC timestamp to microseconds.
config STATS
bool
prompt "Statistics support"
default n
help
Enable per-module event counters for troubleshooting, maintenance,
and usage monitoring. Statistics can be retrieved with the mcumgr
management subsystem.
config STATS_NAMES
bool
prompt "Statistic names"
depends on STATS
default n
help
Include a full name string for each statistic in the build. If this
setting is disabled, statistics are assigned generic names of the
form "s0", "s1", etc. Enabling this setting simplifies debugging,
but results in a larger code size.
endmenu
menu "Boot Options"

285
misc/stats.c Normal file
View file

@ -0,0 +1,285 @@
/*
* Copyright Runtime.io 2018. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <zephyr/types.h>
#include <stats.h>
#define STATS_GEN_NAME_MAX_LEN (sizeof("s255"))
/* The global list of registered statistic groups. */
static struct stats_hdr *stats_list;
static const char *
stats_get_name(const struct stats_hdr *hdr, int idx)
{
#ifdef CONFIG_STATS_NAMES
const struct stats_name_map *cur;
u16_t off;
int i;
/* The stats name map contains two elements, an offset into the
* statistics entry structure, and the name corresponding to that
* offset. This annotation allows for naming only certain statistics,
* and doesn't enforce ordering restrictions on the stats name map.
*/
off = sizeof(*hdr) + idx * hdr->s_size;
for (i = 0; i < hdr->s_map_cnt; i++) {
cur = hdr->s_map + i;
if (cur->snm_off == off) {
return cur->snm_name;
}
}
#endif
return NULL;
}
static u16_t
stats_get_off(const struct stats_hdr *hdr, int idx)
{
return sizeof(*hdr) + idx * hdr->s_size;
}
/**
* Creates a generic name for an unnamed stat. The name has the form:
* s<idx>
*
* This function assumes the supplied destination buffer is large enough to
* accommodate the name.
*/
static void
stats_gen_name(int idx, char *dst)
{
char c;
int len;
int i;
/* Encode the stat name backwards (e.g., "321s" for index 123). */
len = 0;
do {
dst[len++] = '0' + idx % 10;
idx /= 10;
} while (idx > 0);
dst[len++] = 's';
/* Reverse the string to its proper order. */
for (i = 0; i < len / 2; i++) {
c = dst[i];
dst[i] = dst[len - i - 1];
dst[len - i - 1] = c;
}
dst[len] = '\0';
}
/**
* Walk a specific statistic entry, and call walk_func with arg for
* each field within that entry.
*
* Walk func takes the following parameters:
*
* - The header of the statistics section (stats_hdr)
* - The user supplied argument
* - The name of the statistic (if STATS_NAME_ENABLE = 0, this is
* ("s%d", n), where n is the number of the statistic in the structure.
* - A pointer to the current entry.
*
* @return 0 on success, the return code of the walk_func on abort.
*
*/
int
stats_walk(struct stats_hdr *hdr, stats_walk_fn *walk_func, void *arg)
{
const char *name;
char name_buf[STATS_GEN_NAME_MAX_LEN];
int rc;
int i;
for (i = 0; i < hdr->s_cnt; i++) {
name = stats_get_name(hdr, i);
if (name == NULL) {
/* No assigned name; generate a temporary s<#> name. */
stats_gen_name(i, name_buf);
name = name_buf;
}
rc = walk_func(hdr, arg, name, stats_get_off(hdr, i));
if (rc != 0) {
return rc;
}
}
return 0;
}
/**
* Initialize a statistics structure, pointed to by hdr.
*
* @param hdr The header of the statistics structure, contains things
* like statistic section name, size of statistics entries,
* number of statistics, etc.
* @param size The size of the individual statistics elements, either
* 2 (16-bits), 4 (32-bits) or 8 (64-bits).
* @param cnt The number of elements in the statistics structure
* @param map The mapping of statistics name to statistic entry
* @param map_cnt The number of items in the statistics map
*/
void
stats_init(struct stats_hdr *hdr, u8_t size, u8_t cnt,
const struct stats_name_map *map, u8_t map_cnt)
{
hdr->s_size = size;
hdr->s_cnt = cnt;
#ifdef CONFIG_STATS_NAMES
hdr->s_map = map;
hdr->s_map_cnt = map_cnt;
#endif
stats_reset(hdr);
}
/**
* Walk the group of registered statistics and call walk_func() for
* each element in the list. This function _DOES NOT_ lock the statistics
* list, and assumes that the list is not being changed by another task.
* (assumption: all statistics are registered prior to OS start.)
*
* @param walk_func The walk function to call, with a statistics header
* and arg.
* @param arg The argument to call the walk function with.
*
* @return 0 on success, non-zero error code on failure
*/
int
stats_group_walk(stats_group_walk_fn *walk_func, void *arg)
{
struct stats_hdr *hdr;
int rc;
for (hdr = stats_list; hdr != NULL; hdr = hdr->s_next) {
rc = walk_func(hdr, arg);
if (rc != 0) {
return rc;
}
}
return 0;
}
struct stats_hdr *
stats_group_get_next(const struct stats_hdr *cur)
{
if (cur == NULL) {
return stats_list;
}
/* Cast away const. */
return cur->s_next;
}
/**
* Find a statistics structure by name, this is not thread-safe.
* (assumption: all statistics are registered prior ot OS start.)
*
* @param name The statistic structure name to find
*
* @return statistic structure if found, NULL if not found.
*/
struct stats_hdr *
stats_group_find(const char *name)
{
struct stats_hdr *hdr;
for (hdr = stats_list; hdr != NULL; hdr = hdr->s_next) {
if (strcmp(hdr->s_name, name) == 0) {
return hdr;
}
}
return NULL;
}
/**
* Register the statistics pointed to by shdr, with the name of "name."
*
* @param name The name of the statistic to register. This name is guaranteed
* unique in the statistics map. If already exists, this function
* will return an error.
* @param shdr The statistics header to register into the statistic map under
* name.
*
* @return 0 on success, non-zero error code on failure.
*/
int
stats_register(const char *name, struct stats_hdr *hdr)
{
struct stats_hdr *prev;
struct stats_hdr *cur;
/* Don't allow duplicate entries. */
prev = NULL;
for (cur = stats_list; cur != NULL; cur = cur->s_next) {
if (strcmp(cur->s_name, name) == 0) {
return -EALREADY;
}
prev = cur;
}
if (prev == NULL) {
stats_list = hdr;
} else {
prev->s_next = hdr;
}
hdr->s_name = name;
return 0;
}
/**
* Initializes and registers the specified statistics section.
*
* @param shdr The statistics header to register
* @param size The entry size of the statistics to register either 2 (16-bit),
* 4 (32-bit) or 8 (64-bit).
* @param cnt The number of statistics entries in the statistics structure.
* @param map The map of statistics entry to statistics name, only used when
* STATS_NAMES is enabled.
* @param map_cnt The number of elements in the statistics name map.
* @param name The name of the statistics element to register with the system.
*
* @return 0 on success, non-zero error code on failure.
*/
int
stats_init_and_reg(struct stats_hdr *shdr, u8_t size, u8_t cnt,
const struct stats_name_map *map, u8_t map_cnt,
const char *name)
{
int rc;
stats_init(shdr, size, cnt, map, map_cnt);
rc = stats_register(name, shdr);
if (rc != 0) {
return rc;
}
return 0;
}
/**
* Resets and zeroes the specified statistics section.
*
* @param shdr The statistics header to zero
*/
void
stats_reset(struct stats_hdr *hdr)
{
memset(hdr + 1, 0, hdr->s_size * hdr->s_cnt);
}