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 "coverage.h"
13 
14 K_HEAP_DEFINE(gcov_heap, CONFIG_COVERAGE_GCOV_HEAP_SIZE);
15 
16 static struct gcov_info *gcov_info_head;
17 
18 /**
19  * Is called by gcc-generated constructor code for each object file compiled
20  * with -fprofile-arcs.
21  */
__gcov_init(struct gcov_info * info)22 void __gcov_init(struct gcov_info *info)
23 {
24 	info->next = gcov_info_head;
25 	gcov_info_head = info;
26 }
27 
__gcov_merge_add(gcov_type * counters,unsigned int n_counters)28 void __gcov_merge_add(gcov_type *counters, unsigned int n_counters)
29 {
30 	/* Unused. */
31 }
32 
__gcov_exit(void)33 void __gcov_exit(void)
34 {
35 	/* Unused. */
36 }
37 
38 /**
39  * print_u8 - Print 8 bit of gcov data
40  */
print_u8(uint8_t v)41 static inline void print_u8(uint8_t v)
42 {
43 	printk("%02x", v);
44 }
45 
46 /**
47  * print_u32 - Print 32 bit of gcov data
48  */
print_u32(uint32_t v)49 static inline void print_u32(uint32_t v)
50 {
51 	uint8_t *ptr = (uint8_t *)&v;
52 
53 	print_u8(*ptr);
54 	print_u8(*(ptr+1));
55 	print_u8(*(ptr+2));
56 	print_u8(*(ptr+3));
57 }
58 
59 /**
60  * write_u64 - Store 64 bit data on a buffer and return the size
61  */
62 
write_u64(void * buffer,size_t * off,uint64_t v)63 static inline void write_u64(void *buffer, size_t *off, uint64_t v)
64 {
65 	if (buffer != NULL) {
66 		memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v));
67 	} else {
68 		print_u32(*((uint32_t *)&v));
69 		print_u32(*(((uint32_t *)&v) + 1));
70 	}
71 	*off = *off + sizeof(uint64_t);
72 }
73 
74 /**
75  * write_u32 - Store 32 bit data on a buffer and return the size
76  */
write_u32(void * buffer,size_t * off,uint32_t v)77 static inline void write_u32(void *buffer, size_t *off, uint32_t v)
78 {
79 	if (buffer != NULL) {
80 		memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v));
81 	} else {
82 		print_u32(v);
83 	}
84 	*off = *off + sizeof(uint32_t);
85 }
86 
gcov_calculate_buff_size(struct gcov_info * info)87 size_t gcov_calculate_buff_size(struct gcov_info *info)
88 {
89 	uint32_t iter;
90 	uint32_t iter_1;
91 	uint32_t iter_2;
92 
93 	/* Few fixed values at the start: magic number,
94 	 * version, stamp, and checksum.
95 	 */
96 #ifdef GCOV_12_FORMAT
97 	uint32_t size = sizeof(uint32_t) * 4;
98 #else
99 	uint32_t size = sizeof(uint32_t) * 3;
100 #endif
101 
102 	for (iter = 0U; iter < info->n_functions; iter++) {
103 		/* space for TAG_FUNCTION and FUNCTION_LENGTH
104 		 * ident
105 		 * lineno_checksum
106 		 * cfg_checksum
107 		 */
108 		size += (sizeof(uint32_t) * 5);
109 
110 		for (iter_1 = 0U; iter_1 < GCOV_COUNTERS; iter_1++) {
111 			if (!info->merge[iter_1]) {
112 				continue;
113 			}
114 
115 			/*  for function counter and number of values  */
116 			size += (sizeof(uint32_t) * 2);
117 
118 			for (iter_2 = 0U;
119 			     iter_2 < info->functions[iter]->ctrs->num;
120 			     iter_2++) {
121 
122 				/* Iter for values which is uint64_t */
123 				size += (sizeof(uint64_t));
124 			}
125 
126 		}
127 
128 
129 	}
130 
131 	return size;
132 }
133 
134 /**
135  * gcov_to_gcda - convert from gcov data set (info) to
136  * .gcda file format.
137  * This buffer will now have info similar to a regular gcda
138  * format.
139  */
gcov_to_gcda(uint8_t * buffer,struct gcov_info * info)140 size_t gcov_to_gcda(uint8_t *buffer, struct gcov_info *info)
141 {
142 	struct gcov_fn_info *functions;
143 	struct gcov_ctr_info *counters_per_func;
144 	uint32_t iter_functions;
145 	uint32_t iter_counts;
146 	uint32_t iter_counter_values;
147 	size_t buffer_write_position = 0;
148 
149 	/* File header. */
150 	write_u32(buffer,
151 		      &buffer_write_position,
152 		      GCOV_DATA_MAGIC);
153 
154 	write_u32(buffer,
155 		      &buffer_write_position,
156 		      info->version);
157 
158 	write_u32(buffer,
159 		      &buffer_write_position,
160 		      info->stamp);
161 
162 #ifdef GCOV_12_FORMAT
163 	write_u32(buffer,
164 		      &buffer_write_position,
165 		      info->checksum);
166 #endif
167 
168 	for (iter_functions = 0U;
169 	     iter_functions < info->n_functions;
170 	     iter_functions++) {
171 
172 		functions = info->functions[iter_functions];
173 
174 
175 		write_u32(buffer,
176 			      &buffer_write_position,
177 			      GCOV_TAG_FUNCTION);
178 
179 		write_u32(buffer,
180 			      &buffer_write_position,
181 			      GCOV_TAG_FUNCTION_LENGTH);
182 
183 		write_u32(buffer,
184 			      &buffer_write_position,
185 			      functions->ident);
186 
187 		write_u32(buffer,
188 			      &buffer_write_position,
189 			      functions->lineno_checksum);
190 
191 		write_u32(buffer,
192 			      &buffer_write_position,
193 			      functions->cfg_checksum);
194 
195 		counters_per_func = functions->ctrs;
196 
197 		for (iter_counts = 0U;
198 		     iter_counts < GCOV_COUNTERS;
199 		     iter_counts++) {
200 
201 			if (!info->merge[iter_counts]) {
202 				continue;
203 			}
204 
205 			write_u32(buffer,
206 				      &buffer_write_position,
207 				      GCOV_TAG_FOR_COUNTER(iter_counts));
208 
209 #ifdef GCOV_12_FORMAT
210 			/* GCOV 12 counts the length by bytes */
211 			write_u32(buffer,
212 				      &buffer_write_position,
213 				      counters_per_func->num * 2U * 4);
214 #else
215 			write_u32(buffer,
216 				      &buffer_write_position,
217 				      counters_per_func->num * 2U);
218 #endif
219 
220 			for (iter_counter_values = 0U;
221 			     iter_counter_values < counters_per_func->num;
222 			     iter_counter_values++) {
223 
224 				write_u64(buffer,
225 					      &buffer_write_position,
226 					      counters_per_func->values[iter_counter_values]);
227 			}
228 
229 			counters_per_func++;
230 		}
231 	}
232 	return buffer_write_position;
233 }
234 
dump_on_console_start(const char * filename)235 void dump_on_console_start(const char *filename)
236 {
237 	printk("\n%c", FILE_START_INDICATOR);
238 	while (*filename != '\0') {
239 		printk("%c", *filename++);
240 	}
241 	printk("%c", GCOV_DUMP_SEPARATOR);
242 }
243 
dump_on_console_data(char * ptr,size_t len)244 void dump_on_console_data(char *ptr, size_t len)
245 {
246 	if (ptr != NULL) {
247 		for (size_t iter = 0U; iter < len; iter++) {
248 			print_u8((uint8_t)*ptr++);
249 		}
250 	}
251 }
252 
253 /**
254  * Retrieves gcov coverage data and sends it over the given interface.
255  */
gcov_coverage_dump(void)256 void gcov_coverage_dump(void)
257 {
258 	uint8_t *buffer;
259 	size_t size;
260 	size_t written_size;
261 	struct gcov_info *gcov_list_first = gcov_info_head;
262 	struct gcov_info *gcov_list = gcov_info_head;
263 
264 	k_sched_lock();
265 	printk("\nGCOV_COVERAGE_DUMP_START");
266 	while (gcov_list) {
267 
268 		dump_on_console_start(gcov_list->filename);
269 		size = gcov_calculate_buff_size(gcov_list);
270 
271 		buffer = k_heap_alloc(&gcov_heap, size, K_NO_WAIT);
272 		if (CONFIG_COVERAGE_GCOV_HEAP_SIZE > 0 && !buffer) {
273 			printk("No Mem available to continue dump\n");
274 			goto coverage_dump_end;
275 		}
276 
277 		written_size = gcov_to_gcda(buffer, gcov_list);
278 		if (written_size != size) {
279 			printk("Write Error on buff\n");
280 			goto coverage_dump_end;
281 		}
282 
283 		dump_on_console_data(buffer, size);
284 
285 		k_heap_free(&gcov_heap, buffer);
286 		gcov_list = gcov_list->next;
287 		if (gcov_list_first == gcov_list) {
288 			goto coverage_dump_end;
289 		}
290 	}
291 coverage_dump_end:
292 	printk("\nGCOV_COVERAGE_DUMP_END\n");
293 	k_sched_unlock();
294 	return;
295 }
296 
gcov_get_list_head(void)297 struct gcov_info *gcov_get_list_head(void)
298 {
299 	/* Locking someway before getting this is recommended. */
300 	return gcov_info_head;
301 }
302 
303 /* Initialize the gcov by calling the required constructors */
gcov_static_init(void)304 void gcov_static_init(void)
305 {
306 	extern uintptr_t __init_array_start, __init_array_end;
307 	uintptr_t func_pointer_start = (uintptr_t) &__init_array_start;
308 	uintptr_t func_pointer_end = (uintptr_t) &__init_array_end;
309 
310 	while (func_pointer_start < func_pointer_end) {
311 		void (**p)(void);
312 		/* get function pointer */
313 		p = (void (**)(void)) func_pointer_start;
314 		(*p)();
315 		func_pointer_start += sizeof(p);
316 	}
317 }
318