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