1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * This code provides functions to handle gcc's profiling data format |
4 | * introduced with gcc 4.7. |
5 | * |
6 | * This file is based heavily on gcc_3_4.c file. |
7 | * |
8 | * For a better understanding, refer to gcc source: |
9 | * gcc/gcov-io.h |
10 | * libgcc/libgcov.c |
11 | * |
12 | * Uses gcc-internal data definitions. |
13 | */ |
14 | |
15 | #include <linux/errno.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/string.h> |
18 | #include <linux/mm.h> |
19 | #include "gcov.h" |
20 | |
21 | #if (__GNUC__ >= 10) |
22 | #define GCOV_COUNTERS 8 |
23 | #elif (__GNUC__ >= 7) |
24 | #define GCOV_COUNTERS 9 |
25 | #elif (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1) |
26 | #define GCOV_COUNTERS 10 |
27 | #else |
28 | #define GCOV_COUNTERS 9 |
29 | #endif |
30 | |
31 | #define GCOV_TAG_FUNCTION_LENGTH 3 |
32 | |
33 | /* Since GCC 12.1 sizes are in BYTES and not in WORDS (4B). */ |
34 | #if (__GNUC__ >= 12) |
35 | #define GCOV_UNIT_SIZE 4 |
36 | #else |
37 | #define GCOV_UNIT_SIZE 1 |
38 | #endif |
39 | |
40 | static struct gcov_info *gcov_info_head; |
41 | |
42 | /** |
43 | * struct gcov_ctr_info - information about counters for a single function |
44 | * @num: number of counter values for this type |
45 | * @values: array of counter values for this type |
46 | * |
47 | * This data is generated by gcc during compilation and doesn't change |
48 | * at run-time with the exception of the values array. |
49 | */ |
50 | struct gcov_ctr_info { |
51 | unsigned int num; |
52 | gcov_type *values; |
53 | }; |
54 | |
55 | /** |
56 | * struct gcov_fn_info - profiling meta data per function |
57 | * @key: comdat key |
58 | * @ident: unique ident of function |
59 | * @lineno_checksum: function lineo_checksum |
60 | * @cfg_checksum: function cfg checksum |
61 | * @ctrs: instrumented counters |
62 | * |
63 | * This data is generated by gcc during compilation and doesn't change |
64 | * at run-time. |
65 | * |
66 | * Information about a single function. This uses the trailing array |
67 | * idiom. The number of counters is determined from the merge pointer |
68 | * array in gcov_info. The key is used to detect which of a set of |
69 | * comdat functions was selected -- it points to the gcov_info object |
70 | * of the object file containing the selected comdat function. |
71 | */ |
72 | struct gcov_fn_info { |
73 | const struct gcov_info *key; |
74 | unsigned int ident; |
75 | unsigned int lineno_checksum; |
76 | unsigned int cfg_checksum; |
77 | struct gcov_ctr_info ctrs[]; |
78 | }; |
79 | |
80 | /** |
81 | * struct gcov_info - profiling data per object file |
82 | * @version: gcov version magic indicating the gcc version used for compilation |
83 | * @next: list head for a singly-linked list |
84 | * @stamp: uniquifying time stamp |
85 | * @checksum: unique object checksum |
86 | * @filename: name of the associated gcov data file |
87 | * @merge: merge functions (null for unused counter type) |
88 | * @n_functions: number of instrumented functions |
89 | * @functions: pointer to pointers to function information |
90 | * |
91 | * This data is generated by gcc during compilation and doesn't change |
92 | * at run-time with the exception of the next pointer. |
93 | */ |
94 | struct gcov_info { |
95 | unsigned int version; |
96 | struct gcov_info *next; |
97 | unsigned int stamp; |
98 | /* Since GCC 12.1 a checksum field is added. */ |
99 | #if (__GNUC__ >= 12) |
100 | unsigned int checksum; |
101 | #endif |
102 | const char *filename; |
103 | void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int); |
104 | unsigned int n_functions; |
105 | struct gcov_fn_info **functions; |
106 | }; |
107 | |
108 | /** |
109 | * gcov_info_filename - return info filename |
110 | * @info: profiling data set |
111 | */ |
112 | const char *gcov_info_filename(struct gcov_info *info) |
113 | { |
114 | return info->filename; |
115 | } |
116 | |
117 | /** |
118 | * gcov_info_version - return info version |
119 | * @info: profiling data set |
120 | */ |
121 | unsigned int gcov_info_version(struct gcov_info *info) |
122 | { |
123 | return info->version; |
124 | } |
125 | |
126 | /** |
127 | * gcov_info_next - return next profiling data set |
128 | * @info: profiling data set |
129 | * |
130 | * Returns next gcov_info following @info or first gcov_info in the chain if |
131 | * @info is %NULL. |
132 | */ |
133 | struct gcov_info *gcov_info_next(struct gcov_info *info) |
134 | { |
135 | if (!info) |
136 | return gcov_info_head; |
137 | |
138 | return info->next; |
139 | } |
140 | |
141 | /** |
142 | * gcov_info_link - link/add profiling data set to the list |
143 | * @info: profiling data set |
144 | */ |
145 | void gcov_info_link(struct gcov_info *info) |
146 | { |
147 | info->next = gcov_info_head; |
148 | gcov_info_head = info; |
149 | } |
150 | |
151 | /** |
152 | * gcov_info_unlink - unlink/remove profiling data set from the list |
153 | * @prev: previous profiling data set |
154 | * @info: profiling data set |
155 | */ |
156 | void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info) |
157 | { |
158 | if (prev) |
159 | prev->next = info->next; |
160 | else |
161 | gcov_info_head = info->next; |
162 | } |
163 | |
164 | /** |
165 | * gcov_info_within_module - check if a profiling data set belongs to a module |
166 | * @info: profiling data set |
167 | * @mod: module |
168 | * |
169 | * Returns true if profiling data belongs module, false otherwise. |
170 | */ |
171 | bool gcov_info_within_module(struct gcov_info *info, struct module *mod) |
172 | { |
173 | return within_module(addr: (unsigned long)info, mod); |
174 | } |
175 | |
176 | /* Symbolic links to be created for each profiling data file. */ |
177 | const struct gcov_link gcov_link[] = { |
178 | { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */ |
179 | { 0, NULL}, |
180 | }; |
181 | |
182 | /* |
183 | * Determine whether a counter is active. Doesn't change at run-time. |
184 | */ |
185 | static int counter_active(struct gcov_info *info, unsigned int type) |
186 | { |
187 | return info->merge[type] ? 1 : 0; |
188 | } |
189 | |
190 | /* Determine number of active counters. Based on gcc magic. */ |
191 | static unsigned int num_counter_active(struct gcov_info *info) |
192 | { |
193 | unsigned int i; |
194 | unsigned int result = 0; |
195 | |
196 | for (i = 0; i < GCOV_COUNTERS; i++) { |
197 | if (counter_active(info, type: i)) |
198 | result++; |
199 | } |
200 | return result; |
201 | } |
202 | |
203 | /** |
204 | * gcov_info_reset - reset profiling data to zero |
205 | * @info: profiling data set |
206 | */ |
207 | void gcov_info_reset(struct gcov_info *info) |
208 | { |
209 | struct gcov_ctr_info *ci_ptr; |
210 | unsigned int fi_idx; |
211 | unsigned int ct_idx; |
212 | |
213 | for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) { |
214 | ci_ptr = info->functions[fi_idx]->ctrs; |
215 | |
216 | for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) { |
217 | if (!counter_active(info, type: ct_idx)) |
218 | continue; |
219 | |
220 | memset(ci_ptr->values, 0, |
221 | sizeof(gcov_type) * ci_ptr->num); |
222 | ci_ptr++; |
223 | } |
224 | } |
225 | } |
226 | |
227 | /** |
228 | * gcov_info_is_compatible - check if profiling data can be added |
229 | * @info1: first profiling data set |
230 | * @info2: second profiling data set |
231 | * |
232 | * Returns non-zero if profiling data can be added, zero otherwise. |
233 | */ |
234 | int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2) |
235 | { |
236 | return (info1->stamp == info2->stamp); |
237 | } |
238 | |
239 | /** |
240 | * gcov_info_add - add up profiling data |
241 | * @dst: profiling data set to which data is added |
242 | * @src: profiling data set which is added |
243 | * |
244 | * Adds profiling counts of @src to @dst. |
245 | */ |
246 | void gcov_info_add(struct gcov_info *dst, struct gcov_info *src) |
247 | { |
248 | struct gcov_ctr_info *dci_ptr; |
249 | struct gcov_ctr_info *sci_ptr; |
250 | unsigned int fi_idx; |
251 | unsigned int ct_idx; |
252 | unsigned int val_idx; |
253 | |
254 | for (fi_idx = 0; fi_idx < src->n_functions; fi_idx++) { |
255 | dci_ptr = dst->functions[fi_idx]->ctrs; |
256 | sci_ptr = src->functions[fi_idx]->ctrs; |
257 | |
258 | for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) { |
259 | if (!counter_active(info: src, type: ct_idx)) |
260 | continue; |
261 | |
262 | for (val_idx = 0; val_idx < sci_ptr->num; val_idx++) |
263 | dci_ptr->values[val_idx] += |
264 | sci_ptr->values[val_idx]; |
265 | |
266 | dci_ptr++; |
267 | sci_ptr++; |
268 | } |
269 | } |
270 | } |
271 | |
272 | /** |
273 | * gcov_info_dup - duplicate profiling data set |
274 | * @info: profiling data set to duplicate |
275 | * |
276 | * Return newly allocated duplicate on success, %NULL on error. |
277 | */ |
278 | struct gcov_info *gcov_info_dup(struct gcov_info *info) |
279 | { |
280 | struct gcov_info *dup; |
281 | struct gcov_ctr_info *dci_ptr; /* dst counter info */ |
282 | struct gcov_ctr_info *sci_ptr; /* src counter info */ |
283 | unsigned int active; |
284 | unsigned int fi_idx; /* function info idx */ |
285 | unsigned int ct_idx; /* counter type idx */ |
286 | size_t fi_size; /* function info size */ |
287 | size_t cv_size; /* counter values size */ |
288 | |
289 | dup = kmemdup(p: info, size: sizeof(*dup), GFP_KERNEL); |
290 | if (!dup) |
291 | return NULL; |
292 | |
293 | dup->next = NULL; |
294 | dup->filename = NULL; |
295 | dup->functions = NULL; |
296 | |
297 | dup->filename = kstrdup(s: info->filename, GFP_KERNEL); |
298 | if (!dup->filename) |
299 | goto err_free; |
300 | |
301 | dup->functions = kcalloc(n: info->n_functions, |
302 | size: sizeof(struct gcov_fn_info *), GFP_KERNEL); |
303 | if (!dup->functions) |
304 | goto err_free; |
305 | |
306 | active = num_counter_active(info); |
307 | fi_size = sizeof(struct gcov_fn_info); |
308 | fi_size += sizeof(struct gcov_ctr_info) * active; |
309 | |
310 | for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) { |
311 | dup->functions[fi_idx] = kzalloc(size: fi_size, GFP_KERNEL); |
312 | if (!dup->functions[fi_idx]) |
313 | goto err_free; |
314 | |
315 | *(dup->functions[fi_idx]) = *(info->functions[fi_idx]); |
316 | |
317 | sci_ptr = info->functions[fi_idx]->ctrs; |
318 | dci_ptr = dup->functions[fi_idx]->ctrs; |
319 | |
320 | for (ct_idx = 0; ct_idx < active; ct_idx++) { |
321 | |
322 | cv_size = sizeof(gcov_type) * sci_ptr->num; |
323 | |
324 | dci_ptr->values = kvmalloc(size: cv_size, GFP_KERNEL); |
325 | |
326 | if (!dci_ptr->values) |
327 | goto err_free; |
328 | |
329 | dci_ptr->num = sci_ptr->num; |
330 | memcpy(dci_ptr->values, sci_ptr->values, cv_size); |
331 | |
332 | sci_ptr++; |
333 | dci_ptr++; |
334 | } |
335 | } |
336 | |
337 | return dup; |
338 | err_free: |
339 | gcov_info_free(info: dup); |
340 | return NULL; |
341 | } |
342 | |
343 | /** |
344 | * gcov_info_free - release memory for profiling data set duplicate |
345 | * @info: profiling data set duplicate to free |
346 | */ |
347 | void gcov_info_free(struct gcov_info *info) |
348 | { |
349 | unsigned int active; |
350 | unsigned int fi_idx; |
351 | unsigned int ct_idx; |
352 | struct gcov_ctr_info *ci_ptr; |
353 | |
354 | if (!info->functions) |
355 | goto free_info; |
356 | |
357 | active = num_counter_active(info); |
358 | |
359 | for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) { |
360 | if (!info->functions[fi_idx]) |
361 | continue; |
362 | |
363 | ci_ptr = info->functions[fi_idx]->ctrs; |
364 | |
365 | for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++) |
366 | kvfree(addr: ci_ptr->values); |
367 | |
368 | kfree(objp: info->functions[fi_idx]); |
369 | } |
370 | |
371 | free_info: |
372 | kfree(objp: info->functions); |
373 | kfree(objp: info->filename); |
374 | kfree(objp: info); |
375 | } |
376 | |
377 | /** |
378 | * convert_to_gcda - convert profiling data set to gcda file format |
379 | * @buffer: the buffer to store file data or %NULL if no data should be stored |
380 | * @info: profiling data set to be converted |
381 | * |
382 | * Returns the number of bytes that were/would have been stored into the buffer. |
383 | */ |
384 | size_t convert_to_gcda(char *buffer, struct gcov_info *info) |
385 | { |
386 | struct gcov_fn_info *fi_ptr; |
387 | struct gcov_ctr_info *ci_ptr; |
388 | unsigned int fi_idx; |
389 | unsigned int ct_idx; |
390 | unsigned int cv_idx; |
391 | size_t pos = 0; |
392 | |
393 | /* File header. */ |
394 | pos += store_gcov_u32(buffer, off: pos, GCOV_DATA_MAGIC); |
395 | pos += store_gcov_u32(buffer, off: pos, v: info->version); |
396 | pos += store_gcov_u32(buffer, off: pos, v: info->stamp); |
397 | |
398 | #if (__GNUC__ >= 12) |
399 | /* Use zero as checksum of the compilation unit. */ |
400 | pos += store_gcov_u32(buffer, pos, 0); |
401 | #endif |
402 | |
403 | for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) { |
404 | fi_ptr = info->functions[fi_idx]; |
405 | |
406 | /* Function record. */ |
407 | pos += store_gcov_u32(buffer, off: pos, GCOV_TAG_FUNCTION); |
408 | pos += store_gcov_u32(buffer, off: pos, |
409 | GCOV_TAG_FUNCTION_LENGTH * GCOV_UNIT_SIZE); |
410 | pos += store_gcov_u32(buffer, off: pos, v: fi_ptr->ident); |
411 | pos += store_gcov_u32(buffer, off: pos, v: fi_ptr->lineno_checksum); |
412 | pos += store_gcov_u32(buffer, off: pos, v: fi_ptr->cfg_checksum); |
413 | |
414 | ci_ptr = fi_ptr->ctrs; |
415 | |
416 | for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) { |
417 | if (!counter_active(info, type: ct_idx)) |
418 | continue; |
419 | |
420 | /* Counter record. */ |
421 | pos += store_gcov_u32(buffer, off: pos, |
422 | GCOV_TAG_FOR_COUNTER(ct_idx)); |
423 | pos += store_gcov_u32(buffer, off: pos, |
424 | v: ci_ptr->num * 2 * GCOV_UNIT_SIZE); |
425 | |
426 | for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) { |
427 | pos += store_gcov_u64(buffer, off: pos, |
428 | v: ci_ptr->values[cv_idx]); |
429 | } |
430 | |
431 | ci_ptr++; |
432 | } |
433 | } |
434 | |
435 | return pos; |
436 | } |
437 | |