1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <stdio.h>
9 #include <stdint.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <zephyr/posix/fnmatch.h>
13 #include "coverage.h"
14 #include <zephyr/arch/common/semihost.h>
15 #include <sys/types.h>
16 
17 K_HEAP_DEFINE(gcov_heap, CONFIG_COVERAGE_GCOV_HEAP_SIZE);
18 
19 static struct gcov_info *gcov_info_head;
20 
21 /**
22  * Is called by gcc-generated constructor code for each object file compiled
23  * with -fprofile-arcs.
24  */
__gcov_init(struct gcov_info * info)25 void __gcov_init(struct gcov_info *info)
26 {
27 	info->next = gcov_info_head;
28 	gcov_info_head = info;
29 }
30 
__gcov_merge_add(gcov_type * counters,unsigned int n_counters)31 void __gcov_merge_add(gcov_type *counters, unsigned int n_counters)
32 {
33 	/* Unused. */
34 }
35 
__gcov_exit(void)36 void __gcov_exit(void)
37 {
38 	/* Unused. */
39 }
40 
41 /**
42  * print_u8 - Print 8 bit of gcov data
43  */
print_u8(uint8_t v)44 static inline void print_u8(uint8_t v)
45 {
46 	printk("%02x", v);
47 }
48 
49 /**
50  * print_u32 - Print 32 bit of gcov data
51  */
print_u32(uint32_t v)52 static inline void print_u32(uint32_t v)
53 {
54 	uint8_t *ptr = (uint8_t *)&v;
55 
56 	print_u8(*ptr);
57 	print_u8(*(ptr+1));
58 	print_u8(*(ptr+2));
59 	print_u8(*(ptr+3));
60 }
61 
62 /**
63  * write_u64 - Store 64 bit data on a buffer and return the size
64  */
65 
write_u64(void * buffer,size_t * off,uint64_t v)66 static inline void write_u64(void *buffer, size_t *off, uint64_t v)
67 {
68 	if (buffer != NULL) {
69 		memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v));
70 	} else {
71 		print_u32(*((uint32_t *)&v));
72 		print_u32(*(((uint32_t *)&v) + 1));
73 	}
74 	*off = *off + sizeof(uint64_t);
75 }
76 
77 /**
78  * write_u32 - Store 32 bit data on a buffer and return the size
79  */
write_u32(void * buffer,size_t * off,uint32_t v)80 static inline void write_u32(void *buffer, size_t *off, uint32_t v)
81 {
82 	if (buffer != NULL) {
83 		memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v));
84 	} else {
85 		print_u32(v);
86 	}
87 	*off = *off + sizeof(uint32_t);
88 }
89 
gcov_calculate_buff_size(struct gcov_info * info)90 size_t gcov_calculate_buff_size(struct gcov_info *info)
91 {
92 	uint32_t iter;
93 	uint32_t iter_1;
94 	uint32_t iter_2;
95 
96 	/* Few fixed values at the start: magic number,
97 	 * version, stamp, and checksum.
98 	 */
99 #ifdef GCOV_12_FORMAT
100 	uint32_t size = sizeof(uint32_t) * 4;
101 #else
102 	uint32_t size = sizeof(uint32_t) * 3;
103 #endif
104 
105 	for (iter = 0U; iter < info->n_functions; iter++) {
106 		/* space for TAG_FUNCTION and FUNCTION_LENGTH
107 		 * ident
108 		 * lineno_checksum
109 		 * cfg_checksum
110 		 */
111 		size += (sizeof(uint32_t) * 5);
112 
113 		for (iter_1 = 0U; iter_1 < GCOV_COUNTERS; iter_1++) {
114 			if (!info->merge[iter_1]) {
115 				continue;
116 			}
117 
118 			/*  for function counter and number of values  */
119 			size += (sizeof(uint32_t) * 2);
120 
121 			for (iter_2 = 0U;
122 			     iter_2 < info->functions[iter]->ctrs->num;
123 			     iter_2++) {
124 
125 				/* Iter for values which is uint64_t */
126 				size += (sizeof(uint64_t));
127 			}
128 
129 		}
130 
131 
132 	}
133 
134 	return size;
135 }
136 
137 /**
138  * gcov_populate_buffer - convert from gcov data set (info) to
139  * .gcda file format.
140  * This buffer will now have info similar to a regular gcda
141  * format.
142  */
gcov_populate_buffer(uint8_t * buffer,struct gcov_info * info)143 size_t gcov_populate_buffer(uint8_t *buffer, struct gcov_info *info)
144 {
145 	struct gcov_fn_info *functions;
146 	struct gcov_ctr_info *counters_per_func;
147 	uint32_t iter_functions;
148 	uint32_t iter_counts;
149 	uint32_t iter_counter_values;
150 	size_t buffer_write_position = 0;
151 
152 	/* File header. */
153 	write_u32(buffer,
154 		      &buffer_write_position,
155 		      GCOV_DATA_MAGIC);
156 
157 	write_u32(buffer,
158 		      &buffer_write_position,
159 		      info->version);
160 
161 	write_u32(buffer,
162 		      &buffer_write_position,
163 		      info->stamp);
164 
165 #ifdef GCOV_12_FORMAT
166 	write_u32(buffer,
167 		      &buffer_write_position,
168 		      info->checksum);
169 #endif
170 
171 	for (iter_functions = 0U;
172 	     iter_functions < info->n_functions;
173 	     iter_functions++) {
174 
175 		functions = info->functions[iter_functions];
176 
177 
178 		write_u32(buffer,
179 			      &buffer_write_position,
180 			      GCOV_TAG_FUNCTION);
181 
182 		write_u32(buffer,
183 			      &buffer_write_position,
184 			      GCOV_TAG_FUNCTION_LENGTH);
185 
186 		write_u32(buffer,
187 			      &buffer_write_position,
188 			      functions->ident);
189 
190 		write_u32(buffer,
191 			      &buffer_write_position,
192 			      functions->lineno_checksum);
193 
194 		write_u32(buffer,
195 			      &buffer_write_position,
196 			      functions->cfg_checksum);
197 
198 		counters_per_func = functions->ctrs;
199 
200 		for (iter_counts = 0U;
201 		     iter_counts < GCOV_COUNTERS;
202 		     iter_counts++) {
203 
204 			if (!info->merge[iter_counts]) {
205 				continue;
206 			}
207 
208 			write_u32(buffer,
209 				      &buffer_write_position,
210 				      GCOV_TAG_FOR_COUNTER(iter_counts));
211 
212 #ifdef GCOV_12_FORMAT
213 			/* GCOV 12 counts the length by bytes */
214 			write_u32(buffer,
215 				      &buffer_write_position,
216 				      counters_per_func->num * 2U * 4);
217 #else
218 			write_u32(buffer,
219 				      &buffer_write_position,
220 				      counters_per_func->num * 2U);
221 #endif
222 
223 			for (iter_counter_values = 0U;
224 			     iter_counter_values < counters_per_func->num;
225 			     iter_counter_values++) {
226 
227 				write_u64(buffer,
228 					      &buffer_write_position,
229 					      counters_per_func->values[iter_counter_values]);
230 			}
231 
232 			counters_per_func++;
233 		}
234 	}
235 	return buffer_write_position;
236 }
237 
gcov_reset_counts(struct gcov_info * info)238 void gcov_reset_counts(struct gcov_info *info)
239 {
240 	struct gcov_fn_info *functions;
241 	struct gcov_ctr_info *counters_per_func;
242 	uint32_t iter_functions;
243 	uint32_t iter_counts;
244 	uint32_t iter_counter_values;
245 
246 	for (iter_functions = 0U;
247 		iter_functions < info->n_functions;
248 		iter_functions++) {
249 
250 		functions = info->functions[iter_functions];
251 		counters_per_func = functions->ctrs;
252 
253 		for (iter_counts = 0U;
254 			iter_counts < GCOV_COUNTERS;
255 			iter_counts++) {
256 			for (iter_counter_values = 0U;
257 				iter_counter_values < counters_per_func->num;
258 				iter_counter_values++) {
259 				counters_per_func->values[iter_counter_values] = 0;
260 			}
261 		}
262 	}
263 }
264 
gcov_reset_all_counts(void)265 void gcov_reset_all_counts(void)
266 {
267 	struct gcov_info *gcov_list = NULL;
268 
269 #ifdef CONFIG_MULTITHREADING
270 	k_sched_lock();
271 #endif
272 
273 	gcov_list = gcov_get_list_head();
274 
275 	while (gcov_list) {
276 		gcov_reset_counts(gcov_list);
277 		gcov_list = gcov_list->next;
278 	}
279 
280 #ifdef CONFIG_MULTITHREADING
281 	k_sched_unlock();
282 #endif
283 }
284 
285 #ifdef CONFIG_COVERAGE_DUMP
dump_on_console_start(const char * filename)286 void dump_on_console_start(const char *filename)
287 {
288 	printk("\n%c", FILE_START_INDICATOR);
289 	while (*filename != '\0') {
290 		printk("%c", *filename++);
291 	}
292 	printk("%c", GCOV_DUMP_SEPARATOR);
293 }
294 
dump_on_console_data(char * ptr,size_t len)295 void dump_on_console_data(char *ptr, size_t len)
296 {
297 	if (ptr != NULL) {
298 		for (size_t iter = 0U; iter < len; iter++) {
299 			print_u8((uint8_t)*ptr++);
300 		}
301 	}
302 }
303 
304 
305 /**
306  * Retrieves gcov coverage data and sends it over the given interface.
307  */
gcov_coverage_dump(void)308 void gcov_coverage_dump(void)
309 {
310 	uint8_t *buffer;
311 	size_t size;
312 	size_t written_size;
313 	struct gcov_info *gcov_list_first = gcov_info_head;
314 	struct gcov_info *gcov_list = gcov_info_head;
315 
316 #ifdef CONFIG_MULTITHREADING
317 	if (!k_is_in_isr()) {
318 		k_sched_lock();
319 	}
320 #endif
321 	printk("\nGCOV_COVERAGE_DUMP_START");
322 	while (gcov_list) {
323 		if ((strlen(CONFIG_COVERAGE_DUMP_PATH_EXCLUDE) > 0) &&
324 		    (fnmatch(CONFIG_COVERAGE_DUMP_PATH_EXCLUDE, gcov_list->filename, 0) == 0)) {
325 			/* Don't print a note here, it would be interpreted as dump data */
326 			goto file_dump_end;
327 		}
328 
329 		dump_on_console_start(gcov_list->filename);
330 		size = gcov_calculate_buff_size(gcov_list);
331 
332 		buffer = k_heap_alloc(&gcov_heap, size, K_NO_WAIT);
333 		if (CONFIG_COVERAGE_GCOV_HEAP_SIZE > 0 && !buffer) {
334 			printk("No memory available to continue dump\n");
335 			k_heap_free(&gcov_heap, buffer);
336 			goto coverage_dump_end;
337 		}
338 
339 		written_size = gcov_populate_buffer(buffer, gcov_list);
340 		if (written_size != size) {
341 			printk("Write Error on buffer\n");
342 			k_heap_free(&gcov_heap, buffer);
343 			goto coverage_dump_end;
344 		}
345 
346 		dump_on_console_data(buffer, size);
347 
348 		k_heap_free(&gcov_heap, buffer);
349 file_dump_end:
350 		gcov_list = gcov_list->next;
351 		if (gcov_list_first == gcov_list) {
352 			goto coverage_dump_end;
353 		}
354 	}
355 coverage_dump_end:
356 	printk("\nGCOV_COVERAGE_DUMP_END\n");
357 #ifdef CONFIG_MULTITHREADING
358 	if (!k_is_in_isr()) {
359 		k_sched_unlock();
360 	}
361 #endif
362 	return;
363 }
364 
365 #elif CONFIG_COVERAGE_SEMIHOST
366 /**
367  * Retrieves gcov coverage data and sends it over the given interface.
368  */
gcov_coverage_semihost(void)369 void gcov_coverage_semihost(void)
370 {
371 	uint8_t *buffer;
372 	size_t size;
373 	size_t written_size;
374 	struct gcov_info *gcov_list_first = gcov_info_head;
375 	struct gcov_info *gcov_list = gcov_info_head;
376 
377 #ifdef CONFIG_MULTITHREADING
378 	if (!k_is_in_isr()) {
379 		k_sched_lock();
380 	}
381 #endif
382 	while (gcov_list) {
383 
384 		int fd = semihost_open(gcov_list->filename, SEMIHOST_OPEN_WB);
385 
386 		if (fd < 0) {
387 			printk("Failed to open file: %s\n", gcov_list->filename);
388 			goto coverage_dump_end;
389 		}
390 
391 		size = gcov_calculate_buff_size(gcov_list);
392 
393 		buffer = k_heap_alloc(&gcov_heap, size, K_NO_WAIT);
394 		if (CONFIG_COVERAGE_GCOV_HEAP_SIZE > 0 && !buffer) {
395 			printk("No memory available to continue dump\n");
396 			semihost_close(fd);
397 			k_heap_free(&gcov_heap, buffer);
398 			goto coverage_dump_end;
399 		}
400 
401 		written_size = gcov_populate_buffer(buffer, gcov_list);
402 		if (written_size != size) {
403 			printk("Write Error on buffer\n");
404 			semihost_close(fd);
405 			k_heap_free(&gcov_heap, buffer);
406 			goto coverage_dump_end;
407 		}
408 
409 		int ret = semihost_write(fd, (const void *)buffer, size);
410 
411 		if (ret < 0) {
412 			printk("Failed to write data to file: %s\n", gcov_list->filename);
413 			semihost_close(fd);
414 			k_heap_free(&gcov_heap, buffer);
415 			goto coverage_dump_end;
416 		}
417 
418 		k_heap_free(&gcov_heap, buffer);
419 		gcov_list = gcov_list->next;
420 		if (gcov_list_first == gcov_list) {
421 			semihost_close(fd);
422 			goto coverage_dump_end;
423 		}
424 	}
425 coverage_dump_end:
426 #ifdef CONFIG_MULTITHREADING
427 	if (!k_is_in_isr()) {
428 		k_sched_unlock();
429 	}
430 #endif
431 }
432 #endif
433 
gcov_get_list_head(void)434 struct gcov_info *gcov_get_list_head(void)
435 {
436 	/* Locking someway before getting this is recommended. */
437 	return gcov_info_head;
438 }
439 
440 /* Initialize the gcov by calling the required constructors */
gcov_static_init(void)441 void gcov_static_init(void)
442 {
443 	extern uintptr_t __init_array_start, __init_array_end;
444 	uintptr_t func_pointer_start = (uintptr_t) &__init_array_start;
445 	uintptr_t func_pointer_end = (uintptr_t) &__init_array_end;
446 
447 	while (func_pointer_start < func_pointer_end) {
448 		void (**p)(void);
449 		/* get function pointer */
450 		p = (void (**)(void)) func_pointer_start;
451 		(*p)();
452 		func_pointer_start += sizeof(p);
453 	}
454 }
455