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