/* * Copyright Runtime.io 2018. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #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 * * 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); }