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