285 lines
6.8 KiB
C
285 lines
6.8 KiB
C
/*
|
|
* Copyright Runtime.io 2018. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <zephyr/types.h>
|
|
#include <stats/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;
|
|
uint16_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 uint16_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, uint8_t size, uint16_t cnt,
|
|
const struct stats_name_map *map, uint16_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, uint8_t size, uint16_t cnt,
|
|
const struct stats_name_map *map, uint16_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)
|
|
{
|
|
(void)memset((uint8_t *)hdr + sizeof(*hdr), 0, hdr->s_size * hdr->s_cnt);
|
|
}
|