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