| /* |
| * 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); |
| } |