|  | /* | 
|  | * 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 <zephyr/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 (uint16_t) (sizeof(*hdr) + idx * (int) 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); | 
|  | } |