xref: /openbmc/linux/kernel/gcov/gcc_4_7.c (revision 74ce1896)
1 /*
2  *  This code provides functions to handle gcc's profiling data format
3  *  introduced with gcc 4.7.
4  *
5  *  This file is based heavily on gcc_3_4.c file.
6  *
7  *  For a better understanding, refer to gcc source:
8  *  gcc/gcov-io.h
9  *  libgcc/libgcov.c
10  *
11  *  Uses gcc-internal data definitions.
12  */
13 
14 #include <linux/errno.h>
15 #include <linux/slab.h>
16 #include <linux/string.h>
17 #include <linux/seq_file.h>
18 #include <linux/vmalloc.h>
19 #include "gcov.h"
20 
21 #if (__GNUC__ >= 7)
22 #define GCOV_COUNTERS			9
23 #elif (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
24 #define GCOV_COUNTERS			10
25 #elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9
26 #define GCOV_COUNTERS			9
27 #else
28 #define GCOV_COUNTERS			8
29 #endif
30 
31 #define GCOV_TAG_FUNCTION_LENGTH	3
32 
33 static struct gcov_info *gcov_info_head;
34 
35 /**
36  * struct gcov_ctr_info - information about counters for a single function
37  * @num: number of counter values for this type
38  * @values: array of counter values for this type
39  *
40  * This data is generated by gcc during compilation and doesn't change
41  * at run-time with the exception of the values array.
42  */
43 struct gcov_ctr_info {
44 	unsigned int num;
45 	gcov_type *values;
46 };
47 
48 /**
49  * struct gcov_fn_info - profiling meta data per function
50  * @key: comdat key
51  * @ident: unique ident of function
52  * @lineno_checksum: function lineo_checksum
53  * @cfg_checksum: function cfg checksum
54  * @ctrs: instrumented counters
55  *
56  * This data is generated by gcc during compilation and doesn't change
57  * at run-time.
58  *
59  * Information about a single function.  This uses the trailing array
60  * idiom. The number of counters is determined from the merge pointer
61  * array in gcov_info.  The key is used to detect which of a set of
62  * comdat functions was selected -- it points to the gcov_info object
63  * of the object file containing the selected comdat function.
64  */
65 struct gcov_fn_info {
66 	const struct gcov_info *key;
67 	unsigned int ident;
68 	unsigned int lineno_checksum;
69 	unsigned int cfg_checksum;
70 	struct gcov_ctr_info ctrs[0];
71 };
72 
73 /**
74  * struct gcov_info - profiling data per object file
75  * @version: gcov version magic indicating the gcc version used for compilation
76  * @next: list head for a singly-linked list
77  * @stamp: uniquifying time stamp
78  * @filename: name of the associated gcov data file
79  * @merge: merge functions (null for unused counter type)
80  * @n_functions: number of instrumented functions
81  * @functions: pointer to pointers to function information
82  *
83  * This data is generated by gcc during compilation and doesn't change
84  * at run-time with the exception of the next pointer.
85  */
86 struct gcov_info {
87 	unsigned int version;
88 	struct gcov_info *next;
89 	unsigned int stamp;
90 	const char *filename;
91 	void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
92 	unsigned int n_functions;
93 	struct gcov_fn_info **functions;
94 };
95 
96 /**
97  * gcov_info_filename - return info filename
98  * @info: profiling data set
99  */
100 const char *gcov_info_filename(struct gcov_info *info)
101 {
102 	return info->filename;
103 }
104 
105 /**
106  * gcov_info_version - return info version
107  * @info: profiling data set
108  */
109 unsigned int gcov_info_version(struct gcov_info *info)
110 {
111 	return info->version;
112 }
113 
114 /**
115  * gcov_info_next - return next profiling data set
116  * @info: profiling data set
117  *
118  * Returns next gcov_info following @info or first gcov_info in the chain if
119  * @info is %NULL.
120  */
121 struct gcov_info *gcov_info_next(struct gcov_info *info)
122 {
123 	if (!info)
124 		return gcov_info_head;
125 
126 	return info->next;
127 }
128 
129 /**
130  * gcov_info_link - link/add profiling data set to the list
131  * @info: profiling data set
132  */
133 void gcov_info_link(struct gcov_info *info)
134 {
135 	info->next = gcov_info_head;
136 	gcov_info_head = info;
137 }
138 
139 /**
140  * gcov_info_unlink - unlink/remove profiling data set from the list
141  * @prev: previous profiling data set
142  * @info: profiling data set
143  */
144 void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
145 {
146 	if (prev)
147 		prev->next = info->next;
148 	else
149 		gcov_info_head = info->next;
150 }
151 
152 /* Symbolic links to be created for each profiling data file. */
153 const struct gcov_link gcov_link[] = {
154 	{ OBJ_TREE, "gcno" },	/* Link to .gcno file in $(objtree). */
155 	{ 0, NULL},
156 };
157 
158 /*
159  * Determine whether a counter is active. Doesn't change at run-time.
160  */
161 static int counter_active(struct gcov_info *info, unsigned int type)
162 {
163 	return info->merge[type] ? 1 : 0;
164 }
165 
166 /* Determine number of active counters. Based on gcc magic. */
167 static unsigned int num_counter_active(struct gcov_info *info)
168 {
169 	unsigned int i;
170 	unsigned int result = 0;
171 
172 	for (i = 0; i < GCOV_COUNTERS; i++) {
173 		if (counter_active(info, i))
174 			result++;
175 	}
176 	return result;
177 }
178 
179 /**
180  * gcov_info_reset - reset profiling data to zero
181  * @info: profiling data set
182  */
183 void gcov_info_reset(struct gcov_info *info)
184 {
185 	struct gcov_ctr_info *ci_ptr;
186 	unsigned int fi_idx;
187 	unsigned int ct_idx;
188 
189 	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
190 		ci_ptr = info->functions[fi_idx]->ctrs;
191 
192 		for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
193 			if (!counter_active(info, ct_idx))
194 				continue;
195 
196 			memset(ci_ptr->values, 0,
197 					sizeof(gcov_type) * ci_ptr->num);
198 			ci_ptr++;
199 		}
200 	}
201 }
202 
203 /**
204  * gcov_info_is_compatible - check if profiling data can be added
205  * @info1: first profiling data set
206  * @info2: second profiling data set
207  *
208  * Returns non-zero if profiling data can be added, zero otherwise.
209  */
210 int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
211 {
212 	return (info1->stamp == info2->stamp);
213 }
214 
215 /**
216  * gcov_info_add - add up profiling data
217  * @dest: profiling data set to which data is added
218  * @source: profiling data set which is added
219  *
220  * Adds profiling counts of @source to @dest.
221  */
222 void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
223 {
224 	struct gcov_ctr_info *dci_ptr;
225 	struct gcov_ctr_info *sci_ptr;
226 	unsigned int fi_idx;
227 	unsigned int ct_idx;
228 	unsigned int val_idx;
229 
230 	for (fi_idx = 0; fi_idx < src->n_functions; fi_idx++) {
231 		dci_ptr = dst->functions[fi_idx]->ctrs;
232 		sci_ptr = src->functions[fi_idx]->ctrs;
233 
234 		for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
235 			if (!counter_active(src, ct_idx))
236 				continue;
237 
238 			for (val_idx = 0; val_idx < sci_ptr->num; val_idx++)
239 				dci_ptr->values[val_idx] +=
240 					sci_ptr->values[val_idx];
241 
242 			dci_ptr++;
243 			sci_ptr++;
244 		}
245 	}
246 }
247 
248 /**
249  * gcov_info_dup - duplicate profiling data set
250  * @info: profiling data set to duplicate
251  *
252  * Return newly allocated duplicate on success, %NULL on error.
253  */
254 struct gcov_info *gcov_info_dup(struct gcov_info *info)
255 {
256 	struct gcov_info *dup;
257 	struct gcov_ctr_info *dci_ptr; /* dst counter info */
258 	struct gcov_ctr_info *sci_ptr; /* src counter info */
259 	unsigned int active;
260 	unsigned int fi_idx; /* function info idx */
261 	unsigned int ct_idx; /* counter type idx */
262 	size_t fi_size; /* function info size */
263 	size_t cv_size; /* counter values size */
264 
265 	dup = kmemdup(info, sizeof(*dup), GFP_KERNEL);
266 	if (!dup)
267 		return NULL;
268 
269 	dup->next = NULL;
270 	dup->filename = NULL;
271 	dup->functions = NULL;
272 
273 	dup->filename = kstrdup(info->filename, GFP_KERNEL);
274 	if (!dup->filename)
275 		goto err_free;
276 
277 	dup->functions = kcalloc(info->n_functions,
278 				 sizeof(struct gcov_fn_info *), GFP_KERNEL);
279 	if (!dup->functions)
280 		goto err_free;
281 
282 	active = num_counter_active(info);
283 	fi_size = sizeof(struct gcov_fn_info);
284 	fi_size += sizeof(struct gcov_ctr_info) * active;
285 
286 	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
287 		dup->functions[fi_idx] = kzalloc(fi_size, GFP_KERNEL);
288 		if (!dup->functions[fi_idx])
289 			goto err_free;
290 
291 		*(dup->functions[fi_idx]) = *(info->functions[fi_idx]);
292 
293 		sci_ptr = info->functions[fi_idx]->ctrs;
294 		dci_ptr = dup->functions[fi_idx]->ctrs;
295 
296 		for (ct_idx = 0; ct_idx < active; ct_idx++) {
297 
298 			cv_size = sizeof(gcov_type) * sci_ptr->num;
299 
300 			dci_ptr->values = vmalloc(cv_size);
301 
302 			if (!dci_ptr->values)
303 				goto err_free;
304 
305 			dci_ptr->num = sci_ptr->num;
306 			memcpy(dci_ptr->values, sci_ptr->values, cv_size);
307 
308 			sci_ptr++;
309 			dci_ptr++;
310 		}
311 	}
312 
313 	return dup;
314 err_free:
315 	gcov_info_free(dup);
316 	return NULL;
317 }
318 
319 /**
320  * gcov_info_free - release memory for profiling data set duplicate
321  * @info: profiling data set duplicate to free
322  */
323 void gcov_info_free(struct gcov_info *info)
324 {
325 	unsigned int active;
326 	unsigned int fi_idx;
327 	unsigned int ct_idx;
328 	struct gcov_ctr_info *ci_ptr;
329 
330 	if (!info->functions)
331 		goto free_info;
332 
333 	active = num_counter_active(info);
334 
335 	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
336 		if (!info->functions[fi_idx])
337 			continue;
338 
339 		ci_ptr = info->functions[fi_idx]->ctrs;
340 
341 		for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++)
342 			vfree(ci_ptr->values);
343 
344 		kfree(info->functions[fi_idx]);
345 	}
346 
347 free_info:
348 	kfree(info->functions);
349 	kfree(info->filename);
350 	kfree(info);
351 }
352 
353 #define ITER_STRIDE	PAGE_SIZE
354 
355 /**
356  * struct gcov_iterator - specifies current file position in logical records
357  * @info: associated profiling data
358  * @buffer: buffer containing file data
359  * @size: size of buffer
360  * @pos: current position in file
361  */
362 struct gcov_iterator {
363 	struct gcov_info *info;
364 	void *buffer;
365 	size_t size;
366 	loff_t pos;
367 };
368 
369 /**
370  * store_gcov_u32 - store 32 bit number in gcov format to buffer
371  * @buffer: target buffer or NULL
372  * @off: offset into the buffer
373  * @v: value to be stored
374  *
375  * Number format defined by gcc: numbers are recorded in the 32 bit
376  * unsigned binary form of the endianness of the machine generating the
377  * file. Returns the number of bytes stored. If @buffer is %NULL, doesn't
378  * store anything.
379  */
380 static size_t store_gcov_u32(void *buffer, size_t off, u32 v)
381 {
382 	u32 *data;
383 
384 	if (buffer) {
385 		data = buffer + off;
386 		*data = v;
387 	}
388 
389 	return sizeof(*data);
390 }
391 
392 /**
393  * store_gcov_u64 - store 64 bit number in gcov format to buffer
394  * @buffer: target buffer or NULL
395  * @off: offset into the buffer
396  * @v: value to be stored
397  *
398  * Number format defined by gcc: numbers are recorded in the 32 bit
399  * unsigned binary form of the endianness of the machine generating the
400  * file. 64 bit numbers are stored as two 32 bit numbers, the low part
401  * first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store
402  * anything.
403  */
404 static size_t store_gcov_u64(void *buffer, size_t off, u64 v)
405 {
406 	u32 *data;
407 
408 	if (buffer) {
409 		data = buffer + off;
410 
411 		data[0] = (v & 0xffffffffUL);
412 		data[1] = (v >> 32);
413 	}
414 
415 	return sizeof(*data) * 2;
416 }
417 
418 /**
419  * convert_to_gcda - convert profiling data set to gcda file format
420  * @buffer: the buffer to store file data or %NULL if no data should be stored
421  * @info: profiling data set to be converted
422  *
423  * Returns the number of bytes that were/would have been stored into the buffer.
424  */
425 static size_t convert_to_gcda(char *buffer, struct gcov_info *info)
426 {
427 	struct gcov_fn_info *fi_ptr;
428 	struct gcov_ctr_info *ci_ptr;
429 	unsigned int fi_idx;
430 	unsigned int ct_idx;
431 	unsigned int cv_idx;
432 	size_t pos = 0;
433 
434 	/* File header. */
435 	pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
436 	pos += store_gcov_u32(buffer, pos, info->version);
437 	pos += store_gcov_u32(buffer, pos, info->stamp);
438 
439 	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
440 		fi_ptr = info->functions[fi_idx];
441 
442 		/* Function record. */
443 		pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
444 		pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH);
445 		pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
446 		pos += store_gcov_u32(buffer, pos, fi_ptr->lineno_checksum);
447 		pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum);
448 
449 		ci_ptr = fi_ptr->ctrs;
450 
451 		for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
452 			if (!counter_active(info, ct_idx))
453 				continue;
454 
455 			/* Counter record. */
456 			pos += store_gcov_u32(buffer, pos,
457 					      GCOV_TAG_FOR_COUNTER(ct_idx));
458 			pos += store_gcov_u32(buffer, pos, ci_ptr->num * 2);
459 
460 			for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
461 				pos += store_gcov_u64(buffer, pos,
462 						      ci_ptr->values[cv_idx]);
463 			}
464 
465 			ci_ptr++;
466 		}
467 	}
468 
469 	return pos;
470 }
471 
472 /**
473  * gcov_iter_new - allocate and initialize profiling data iterator
474  * @info: profiling data set to be iterated
475  *
476  * Return file iterator on success, %NULL otherwise.
477  */
478 struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
479 {
480 	struct gcov_iterator *iter;
481 
482 	iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL);
483 	if (!iter)
484 		goto err_free;
485 
486 	iter->info = info;
487 	/* Dry-run to get the actual buffer size. */
488 	iter->size = convert_to_gcda(NULL, info);
489 	iter->buffer = vmalloc(iter->size);
490 	if (!iter->buffer)
491 		goto err_free;
492 
493 	convert_to_gcda(iter->buffer, info);
494 
495 	return iter;
496 
497 err_free:
498 	kfree(iter);
499 	return NULL;
500 }
501 
502 
503 /**
504  * gcov_iter_get_info - return profiling data set for given file iterator
505  * @iter: file iterator
506  */
507 void gcov_iter_free(struct gcov_iterator *iter)
508 {
509 	vfree(iter->buffer);
510 	kfree(iter);
511 }
512 
513 /**
514  * gcov_iter_get_info - return profiling data set for given file iterator
515  * @iter: file iterator
516  */
517 struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
518 {
519 	return iter->info;
520 }
521 
522 /**
523  * gcov_iter_start - reset file iterator to starting position
524  * @iter: file iterator
525  */
526 void gcov_iter_start(struct gcov_iterator *iter)
527 {
528 	iter->pos = 0;
529 }
530 
531 /**
532  * gcov_iter_next - advance file iterator to next logical record
533  * @iter: file iterator
534  *
535  * Return zero if new position is valid, non-zero if iterator has reached end.
536  */
537 int gcov_iter_next(struct gcov_iterator *iter)
538 {
539 	if (iter->pos < iter->size)
540 		iter->pos += ITER_STRIDE;
541 
542 	if (iter->pos >= iter->size)
543 		return -EINVAL;
544 
545 	return 0;
546 }
547 
548 /**
549  * gcov_iter_write - write data for current pos to seq_file
550  * @iter: file iterator
551  * @seq: seq_file handle
552  *
553  * Return zero on success, non-zero otherwise.
554  */
555 int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
556 {
557 	size_t len;
558 
559 	if (iter->pos >= iter->size)
560 		return -EINVAL;
561 
562 	len = ITER_STRIDE;
563 	if (iter->pos + len > iter->size)
564 		len = iter->size - iter->pos;
565 
566 	seq_write(seq, iter->buffer + iter->pos, len);
567 
568 	return 0;
569 }
570