1 /*
2 * Copyright Runtime.io 2018. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <string.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <zephyr/types.h>
11 #include <zephyr/stats/stats.h>
12
13 #define STATS_GEN_NAME_MAX_LEN (sizeof("s255"))
14
15 /* The global list of registered statistic groups. */
16 static struct stats_hdr *stats_list;
17
18 static const char *
stats_get_name(const struct stats_hdr * hdr,int idx)19 stats_get_name(const struct stats_hdr *hdr, int idx)
20 {
21 #ifdef CONFIG_STATS_NAMES
22 const struct stats_name_map *cur;
23 uint16_t off;
24 int i;
25
26 /* The stats name map contains two elements, an offset into the
27 * statistics entry structure, and the name corresponding to that
28 * offset. This annotation allows for naming only certain statistics,
29 * and doesn't enforce ordering restrictions on the stats name map.
30 */
31 off = sizeof(*hdr) + idx * hdr->s_size;
32 for (i = 0; i < hdr->s_map_cnt; i++) {
33 cur = hdr->s_map + i;
34 if (cur->snm_off == off) {
35 return cur->snm_name;
36 }
37 }
38 #endif
39
40 return NULL;
41 }
42
43 static uint16_t
stats_get_off(const struct stats_hdr * hdr,int idx)44 stats_get_off(const struct stats_hdr *hdr, int idx)
45 {
46 return (uint16_t) (sizeof(*hdr) + idx * (int) hdr->s_size);
47 }
48
49 /**
50 * Creates a generic name for an unnamed stat. The name has the form:
51 * s<idx>
52 *
53 * This function assumes the supplied destination buffer is large enough to
54 * accommodate the name.
55 */
56 static void
stats_gen_name(int idx,char * dst)57 stats_gen_name(int idx, char *dst)
58 {
59 char c;
60 int len;
61 int i;
62
63 /* Encode the stat name backwards (e.g., "321s" for index 123). */
64 len = 0;
65 do {
66 dst[len++] = '0' + idx % 10;
67 idx /= 10;
68 } while (idx > 0);
69 dst[len++] = 's';
70
71 /* Reverse the string to its proper order. */
72 for (i = 0; i < len / 2; i++) {
73 c = dst[i];
74 dst[i] = dst[len - i - 1];
75 dst[len - i - 1] = c;
76 }
77 dst[len] = '\0';
78 }
79
80 /**
81 * Walk a specific statistic entry, and call walk_func with arg for
82 * each field within that entry.
83 *
84 * Walk func takes the following parameters:
85 *
86 * - The header of the statistics section (stats_hdr)
87 * - The user supplied argument
88 * - The name of the statistic (if STATS_NAME_ENABLE = 0, this is
89 * ("s%d", n), where n is the number of the statistic in the structure.
90 * - A pointer to the current entry.
91 *
92 * @return 0 on success, the return code of the walk_func on abort.
93 *
94 */
95 int
stats_walk(struct stats_hdr * hdr,stats_walk_fn * walk_func,void * arg)96 stats_walk(struct stats_hdr *hdr, stats_walk_fn *walk_func, void *arg)
97 {
98 const char *name;
99 char name_buf[STATS_GEN_NAME_MAX_LEN];
100 int rc;
101 int i;
102
103 for (i = 0; i < hdr->s_cnt; i++) {
104 name = stats_get_name(hdr, i);
105 if (name == NULL) {
106 /* No assigned name; generate a temporary s<#> name. */
107 stats_gen_name(i, name_buf);
108 name = name_buf;
109 }
110
111 rc = walk_func(hdr, arg, name, stats_get_off(hdr, i));
112 if (rc != 0) {
113 return rc;
114 }
115 }
116
117 return 0;
118 }
119
120 /**
121 * Initialize a statistics structure, pointed to by hdr.
122 *
123 * @param hdr The header of the statistics structure, contains things
124 * like statistic section name, size of statistics entries,
125 * number of statistics, etc.
126 * @param size The size of the individual statistics elements, either
127 * 2 (16-bits), 4 (32-bits) or 8 (64-bits).
128 * @param cnt The number of elements in the statistics structure
129 * @param map The mapping of statistics name to statistic entry
130 * @param map_cnt The number of items in the statistics map
131 */
132 void
stats_init(struct stats_hdr * hdr,uint8_t size,uint16_t cnt,const struct stats_name_map * map,uint16_t map_cnt)133 stats_init(struct stats_hdr *hdr, uint8_t size, uint16_t cnt,
134 const struct stats_name_map *map, uint16_t map_cnt)
135 {
136 hdr->s_size = size;
137 hdr->s_cnt = cnt;
138 #ifdef CONFIG_STATS_NAMES
139 hdr->s_map = map;
140 hdr->s_map_cnt = map_cnt;
141 #endif
142
143 stats_reset(hdr);
144 }
145
146 /**
147 * Walk the group of registered statistics and call walk_func() for
148 * each element in the list. This function _DOES NOT_ lock the statistics
149 * list, and assumes that the list is not being changed by another task.
150 * (assumption: all statistics are registered prior to OS start.)
151 *
152 * @param walk_func The walk function to call, with a statistics header
153 * and arg.
154 * @param arg The argument to call the walk function with.
155 *
156 * @return 0 on success, non-zero error code on failure
157 */
158 int
stats_group_walk(stats_group_walk_fn * walk_func,void * arg)159 stats_group_walk(stats_group_walk_fn *walk_func, void *arg)
160 {
161 struct stats_hdr *hdr;
162 int rc;
163
164 for (hdr = stats_list; hdr != NULL; hdr = hdr->s_next) {
165 rc = walk_func(hdr, arg);
166 if (rc != 0) {
167 return rc;
168 }
169 }
170
171 return 0;
172 }
173
174 struct stats_hdr *
stats_group_get_next(const struct stats_hdr * cur)175 stats_group_get_next(const struct stats_hdr *cur)
176 {
177 if (cur == NULL) {
178 return stats_list;
179 }
180
181 /* Cast away const. */
182 return cur->s_next;
183 }
184
185 /**
186 * Find a statistics structure by name, this is not thread-safe.
187 * (assumption: all statistics are registered prior ot OS start.)
188 *
189 * @param name The statistic structure name to find
190 *
191 * @return statistic structure if found, NULL if not found.
192 */
193 struct stats_hdr *
stats_group_find(const char * name)194 stats_group_find(const char *name)
195 {
196 struct stats_hdr *hdr;
197
198 for (hdr = stats_list; hdr != NULL; hdr = hdr->s_next) {
199 if (strcmp(hdr->s_name, name) == 0) {
200 return hdr;
201 }
202 }
203
204 return NULL;
205 }
206
207 /**
208 * Register the statistics pointed to by shdr, with the name of "name."
209 *
210 * @param name The name of the statistic to register. This name is guaranteed
211 * unique in the statistics map. If already exists, this function
212 * will return an error.
213 * @param shdr The statistics header to register into the statistic map under
214 * name.
215 *
216 * @return 0 on success, non-zero error code on failure.
217 */
218 int
stats_register(const char * name,struct stats_hdr * hdr)219 stats_register(const char *name, struct stats_hdr *hdr)
220 {
221 struct stats_hdr *prev;
222 struct stats_hdr *cur;
223
224 /* Don't allow duplicate entries. */
225 prev = NULL;
226 for (cur = stats_list; cur != NULL; cur = cur->s_next) {
227 if (strcmp(cur->s_name, name) == 0) {
228 return -EALREADY;
229 }
230
231 prev = cur;
232 }
233
234 if (prev == NULL) {
235 stats_list = hdr;
236 } else {
237 prev->s_next = hdr;
238 }
239 hdr->s_name = name;
240
241 return 0;
242 }
243
244 /**
245 * Initializes and registers the specified statistics section.
246 *
247 * @param shdr The statistics header to register
248 * @param size The entry size of the statistics to register either 2 (16-bit),
249 * 4 (32-bit) or 8 (64-bit).
250 * @param cnt The number of statistics entries in the statistics structure.
251 * @param map The map of statistics entry to statistics name, only used when
252 * STATS_NAMES is enabled.
253 * @param map_cnt The number of elements in the statistics name map.
254 * @param name The name of the statistics element to register with the system.
255 *
256 * @return 0 on success, non-zero error code on failure.
257 */
258 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)259 stats_init_and_reg(struct stats_hdr *shdr, uint8_t size, uint16_t cnt,
260 const struct stats_name_map *map, uint16_t map_cnt,
261 const char *name)
262 {
263 int rc;
264
265 stats_init(shdr, size, cnt, map, map_cnt);
266
267 rc = stats_register(name, shdr);
268 if (rc != 0) {
269 return rc;
270 }
271
272 return 0;
273 }
274
275 /**
276 * Resets and zeroes the specified statistics section.
277 *
278 * @param shdr The statistics header to zero
279 */
280 void
stats_reset(struct stats_hdr * hdr)281 stats_reset(struct stats_hdr *hdr)
282 {
283 (void)memset((uint8_t *)hdr + sizeof(*hdr), 0, hdr->s_size * hdr->s_cnt);
284 }
285