xref: /openbmc/linux/kernel/gcov/gcc_4_7.c (revision 57904291176fa16a981cefca5cbe1a0b50196792)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
25f41ea03SFrantisek Hrbata /*
35f41ea03SFrantisek Hrbata  *  This code provides functions to handle gcc's profiling data format
45f41ea03SFrantisek Hrbata  *  introduced with gcc 4.7.
55f41ea03SFrantisek Hrbata  *
65f41ea03SFrantisek Hrbata  *  This file is based heavily on gcc_3_4.c file.
75f41ea03SFrantisek Hrbata  *
85f41ea03SFrantisek Hrbata  *  For a better understanding, refer to gcc source:
95f41ea03SFrantisek Hrbata  *  gcc/gcov-io.h
105f41ea03SFrantisek Hrbata  *  libgcc/libgcov.c
115f41ea03SFrantisek Hrbata  *
125f41ea03SFrantisek Hrbata  *  Uses gcc-internal data definitions.
135f41ea03SFrantisek Hrbata  */
145f41ea03SFrantisek Hrbata 
155f41ea03SFrantisek Hrbata #include <linux/errno.h>
165f41ea03SFrantisek Hrbata #include <linux/slab.h>
175f41ea03SFrantisek Hrbata #include <linux/string.h>
181391efa9SJohannes Berg #include <linux/mm.h>
195f41ea03SFrantisek Hrbata #include "gcov.h"
205f41ea03SFrantisek Hrbata 
21*ae30200eSPeter Oberparleiter #if (__GNUC__ >= 14)
22*ae30200eSPeter Oberparleiter #define GCOV_COUNTERS			9
23*ae30200eSPeter Oberparleiter #elif (__GNUC__ >= 10)
2440249c69SPeter Oberparleiter #define GCOV_COUNTERS			8
2540249c69SPeter Oberparleiter #elif (__GNUC__ >= 7)
2605384213SMartin Liska #define GCOV_COUNTERS			9
2705384213SMartin Liska #elif (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
283e44c471SLorenzo Stoakes #define GCOV_COUNTERS			10
29a992bf83SYuan Pengfei #else
3099b75eb7SNick Desaulniers #define GCOV_COUNTERS			9
31a992bf83SYuan Pengfei #endif
32a992bf83SYuan Pengfei 
335f41ea03SFrantisek Hrbata #define GCOV_TAG_FUNCTION_LENGTH	3
345f41ea03SFrantisek Hrbata 
35977ef30aSMartin Liska /* Since GCC 12.1 sizes are in BYTES and not in WORDS (4B). */
36977ef30aSMartin Liska #if (__GNUC__ >= 12)
37977ef30aSMartin Liska #define GCOV_UNIT_SIZE				4
38977ef30aSMartin Liska #else
39977ef30aSMartin Liska #define GCOV_UNIT_SIZE				1
40977ef30aSMartin Liska #endif
41977ef30aSMartin Liska 
425f41ea03SFrantisek Hrbata static struct gcov_info *gcov_info_head;
435f41ea03SFrantisek Hrbata 
445f41ea03SFrantisek Hrbata /**
455f41ea03SFrantisek Hrbata  * struct gcov_ctr_info - information about counters for a single function
465f41ea03SFrantisek Hrbata  * @num: number of counter values for this type
475f41ea03SFrantisek Hrbata  * @values: array of counter values for this type
485f41ea03SFrantisek Hrbata  *
495f41ea03SFrantisek Hrbata  * This data is generated by gcc during compilation and doesn't change
505f41ea03SFrantisek Hrbata  * at run-time with the exception of the values array.
515f41ea03SFrantisek Hrbata  */
525f41ea03SFrantisek Hrbata struct gcov_ctr_info {
535f41ea03SFrantisek Hrbata 	unsigned int num;
545f41ea03SFrantisek Hrbata 	gcov_type *values;
555f41ea03SFrantisek Hrbata };
565f41ea03SFrantisek Hrbata 
575f41ea03SFrantisek Hrbata /**
585f41ea03SFrantisek Hrbata  * struct gcov_fn_info - profiling meta data per function
595f41ea03SFrantisek Hrbata  * @key: comdat key
605f41ea03SFrantisek Hrbata  * @ident: unique ident of function
615f41ea03SFrantisek Hrbata  * @lineno_checksum: function lineo_checksum
625f41ea03SFrantisek Hrbata  * @cfg_checksum: function cfg checksum
635f41ea03SFrantisek Hrbata  * @ctrs: instrumented counters
645f41ea03SFrantisek Hrbata  *
655f41ea03SFrantisek Hrbata  * This data is generated by gcc during compilation and doesn't change
665f41ea03SFrantisek Hrbata  * at run-time.
675f41ea03SFrantisek Hrbata  *
685f41ea03SFrantisek Hrbata  * Information about a single function.  This uses the trailing array
695f41ea03SFrantisek Hrbata  * idiom. The number of counters is determined from the merge pointer
705f41ea03SFrantisek Hrbata  * array in gcov_info.  The key is used to detect which of a set of
715f41ea03SFrantisek Hrbata  * comdat functions was selected -- it points to the gcov_info object
725f41ea03SFrantisek Hrbata  * of the object file containing the selected comdat function.
735f41ea03SFrantisek Hrbata  */
745f41ea03SFrantisek Hrbata struct gcov_fn_info {
755f41ea03SFrantisek Hrbata 	const struct gcov_info *key;
765f41ea03SFrantisek Hrbata 	unsigned int ident;
775f41ea03SFrantisek Hrbata 	unsigned int lineno_checksum;
785f41ea03SFrantisek Hrbata 	unsigned int cfg_checksum;
79fba4168eSGustavo A. R. Silva 	struct gcov_ctr_info ctrs[];
805f41ea03SFrantisek Hrbata };
815f41ea03SFrantisek Hrbata 
825f41ea03SFrantisek Hrbata /**
835f41ea03SFrantisek Hrbata  * struct gcov_info - profiling data per object file
845f41ea03SFrantisek Hrbata  * @version: gcov version magic indicating the gcc version used for compilation
855f41ea03SFrantisek Hrbata  * @next: list head for a singly-linked list
865f41ea03SFrantisek Hrbata  * @stamp: uniquifying time stamp
87e96b95c2SRickard x Andersson  * @checksum: unique object checksum
885f41ea03SFrantisek Hrbata  * @filename: name of the associated gcov data file
895f41ea03SFrantisek Hrbata  * @merge: merge functions (null for unused counter type)
905f41ea03SFrantisek Hrbata  * @n_functions: number of instrumented functions
915f41ea03SFrantisek Hrbata  * @functions: pointer to pointers to function information
925f41ea03SFrantisek Hrbata  *
935f41ea03SFrantisek Hrbata  * This data is generated by gcc during compilation and doesn't change
945f41ea03SFrantisek Hrbata  * at run-time with the exception of the next pointer.
955f41ea03SFrantisek Hrbata  */
965f41ea03SFrantisek Hrbata struct gcov_info {
975f41ea03SFrantisek Hrbata 	unsigned int version;
985f41ea03SFrantisek Hrbata 	struct gcov_info *next;
995f41ea03SFrantisek Hrbata 	unsigned int stamp;
100e96b95c2SRickard x Andersson  /* Since GCC 12.1 a checksum field is added. */
101e96b95c2SRickard x Andersson #if (__GNUC__ >= 12)
102e96b95c2SRickard x Andersson 	unsigned int checksum;
103e96b95c2SRickard x Andersson #endif
1045f41ea03SFrantisek Hrbata 	const char *filename;
1055f41ea03SFrantisek Hrbata 	void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
1065f41ea03SFrantisek Hrbata 	unsigned int n_functions;
1075f41ea03SFrantisek Hrbata 	struct gcov_fn_info **functions;
1085f41ea03SFrantisek Hrbata };
1095f41ea03SFrantisek Hrbata 
1105f41ea03SFrantisek Hrbata /**
1115f41ea03SFrantisek Hrbata  * gcov_info_filename - return info filename
1125f41ea03SFrantisek Hrbata  * @info: profiling data set
1135f41ea03SFrantisek Hrbata  */
gcov_info_filename(struct gcov_info * info)1145f41ea03SFrantisek Hrbata const char *gcov_info_filename(struct gcov_info *info)
1155f41ea03SFrantisek Hrbata {
1165f41ea03SFrantisek Hrbata 	return info->filename;
1175f41ea03SFrantisek Hrbata }
1185f41ea03SFrantisek Hrbata 
1195f41ea03SFrantisek Hrbata /**
1205f41ea03SFrantisek Hrbata  * gcov_info_version - return info version
1215f41ea03SFrantisek Hrbata  * @info: profiling data set
1225f41ea03SFrantisek Hrbata  */
gcov_info_version(struct gcov_info * info)1235f41ea03SFrantisek Hrbata unsigned int gcov_info_version(struct gcov_info *info)
1245f41ea03SFrantisek Hrbata {
1255f41ea03SFrantisek Hrbata 	return info->version;
1265f41ea03SFrantisek Hrbata }
1275f41ea03SFrantisek Hrbata 
1285f41ea03SFrantisek Hrbata /**
1295f41ea03SFrantisek Hrbata  * gcov_info_next - return next profiling data set
1305f41ea03SFrantisek Hrbata  * @info: profiling data set
1315f41ea03SFrantisek Hrbata  *
1325f41ea03SFrantisek Hrbata  * Returns next gcov_info following @info or first gcov_info in the chain if
1335f41ea03SFrantisek Hrbata  * @info is %NULL.
1345f41ea03SFrantisek Hrbata  */
gcov_info_next(struct gcov_info * info)1355f41ea03SFrantisek Hrbata struct gcov_info *gcov_info_next(struct gcov_info *info)
1365f41ea03SFrantisek Hrbata {
1375f41ea03SFrantisek Hrbata 	if (!info)
1385f41ea03SFrantisek Hrbata 		return gcov_info_head;
1395f41ea03SFrantisek Hrbata 
1405f41ea03SFrantisek Hrbata 	return info->next;
1415f41ea03SFrantisek Hrbata }
1425f41ea03SFrantisek Hrbata 
1435f41ea03SFrantisek Hrbata /**
1445f41ea03SFrantisek Hrbata  * gcov_info_link - link/add profiling data set to the list
1455f41ea03SFrantisek Hrbata  * @info: profiling data set
1465f41ea03SFrantisek Hrbata  */
gcov_info_link(struct gcov_info * info)1475f41ea03SFrantisek Hrbata void gcov_info_link(struct gcov_info *info)
1485f41ea03SFrantisek Hrbata {
1495f41ea03SFrantisek Hrbata 	info->next = gcov_info_head;
1505f41ea03SFrantisek Hrbata 	gcov_info_head = info;
1515f41ea03SFrantisek Hrbata }
1525f41ea03SFrantisek Hrbata 
1535f41ea03SFrantisek Hrbata /**
1545f41ea03SFrantisek Hrbata  * gcov_info_unlink - unlink/remove profiling data set from the list
1555f41ea03SFrantisek Hrbata  * @prev: previous profiling data set
1565f41ea03SFrantisek Hrbata  * @info: profiling data set
1575f41ea03SFrantisek Hrbata  */
gcov_info_unlink(struct gcov_info * prev,struct gcov_info * info)1585f41ea03SFrantisek Hrbata void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
1595f41ea03SFrantisek Hrbata {
1605f41ea03SFrantisek Hrbata 	if (prev)
1615f41ea03SFrantisek Hrbata 		prev->next = info->next;
1625f41ea03SFrantisek Hrbata 	else
1635f41ea03SFrantisek Hrbata 		gcov_info_head = info->next;
1645f41ea03SFrantisek Hrbata }
1655f41ea03SFrantisek Hrbata 
166e178a5beSGreg Hackmann /**
167e178a5beSGreg Hackmann  * gcov_info_within_module - check if a profiling data set belongs to a module
168e178a5beSGreg Hackmann  * @info: profiling data set
169e178a5beSGreg Hackmann  * @mod: module
170e178a5beSGreg Hackmann  *
171e178a5beSGreg Hackmann  * Returns true if profiling data belongs module, false otherwise.
172e178a5beSGreg Hackmann  */
gcov_info_within_module(struct gcov_info * info,struct module * mod)173e178a5beSGreg Hackmann bool gcov_info_within_module(struct gcov_info *info, struct module *mod)
174e178a5beSGreg Hackmann {
175e178a5beSGreg Hackmann 	return within_module((unsigned long)info, mod);
176e178a5beSGreg Hackmann }
177e178a5beSGreg Hackmann 
1785f41ea03SFrantisek Hrbata /* Symbolic links to be created for each profiling data file. */
1795f41ea03SFrantisek Hrbata const struct gcov_link gcov_link[] = {
1805f41ea03SFrantisek Hrbata 	{ OBJ_TREE, "gcno" },	/* Link to .gcno file in $(objtree). */
1815f41ea03SFrantisek Hrbata 	{ 0, NULL},
1825f41ea03SFrantisek Hrbata };
1835f41ea03SFrantisek Hrbata 
1845f41ea03SFrantisek Hrbata /*
1855f41ea03SFrantisek Hrbata  * Determine whether a counter is active. Doesn't change at run-time.
1865f41ea03SFrantisek Hrbata  */
counter_active(struct gcov_info * info,unsigned int type)1875f41ea03SFrantisek Hrbata static int counter_active(struct gcov_info *info, unsigned int type)
1885f41ea03SFrantisek Hrbata {
1895f41ea03SFrantisek Hrbata 	return info->merge[type] ? 1 : 0;
1905f41ea03SFrantisek Hrbata }
1915f41ea03SFrantisek Hrbata 
1925f41ea03SFrantisek Hrbata /* Determine number of active counters. Based on gcc magic. */
num_counter_active(struct gcov_info * info)1935f41ea03SFrantisek Hrbata static unsigned int num_counter_active(struct gcov_info *info)
1945f41ea03SFrantisek Hrbata {
1955f41ea03SFrantisek Hrbata 	unsigned int i;
1965f41ea03SFrantisek Hrbata 	unsigned int result = 0;
1975f41ea03SFrantisek Hrbata 
1985f41ea03SFrantisek Hrbata 	for (i = 0; i < GCOV_COUNTERS; i++) {
1995f41ea03SFrantisek Hrbata 		if (counter_active(info, i))
2005f41ea03SFrantisek Hrbata 			result++;
2015f41ea03SFrantisek Hrbata 	}
2025f41ea03SFrantisek Hrbata 	return result;
2035f41ea03SFrantisek Hrbata }
2045f41ea03SFrantisek Hrbata 
2055f41ea03SFrantisek Hrbata /**
2065f41ea03SFrantisek Hrbata  * gcov_info_reset - reset profiling data to zero
2075f41ea03SFrantisek Hrbata  * @info: profiling data set
2085f41ea03SFrantisek Hrbata  */
gcov_info_reset(struct gcov_info * info)2095f41ea03SFrantisek Hrbata void gcov_info_reset(struct gcov_info *info)
2105f41ea03SFrantisek Hrbata {
2115f41ea03SFrantisek Hrbata 	struct gcov_ctr_info *ci_ptr;
2125f41ea03SFrantisek Hrbata 	unsigned int fi_idx;
2135f41ea03SFrantisek Hrbata 	unsigned int ct_idx;
2145f41ea03SFrantisek Hrbata 
2155f41ea03SFrantisek Hrbata 	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
2165f41ea03SFrantisek Hrbata 		ci_ptr = info->functions[fi_idx]->ctrs;
2175f41ea03SFrantisek Hrbata 
2185f41ea03SFrantisek Hrbata 		for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
2195f41ea03SFrantisek Hrbata 			if (!counter_active(info, ct_idx))
2205f41ea03SFrantisek Hrbata 				continue;
2215f41ea03SFrantisek Hrbata 
2225f41ea03SFrantisek Hrbata 			memset(ci_ptr->values, 0,
2235f41ea03SFrantisek Hrbata 					sizeof(gcov_type) * ci_ptr->num);
2245f41ea03SFrantisek Hrbata 			ci_ptr++;
2255f41ea03SFrantisek Hrbata 		}
2265f41ea03SFrantisek Hrbata 	}
2275f41ea03SFrantisek Hrbata }
2285f41ea03SFrantisek Hrbata 
2295f41ea03SFrantisek Hrbata /**
2305f41ea03SFrantisek Hrbata  * gcov_info_is_compatible - check if profiling data can be added
2315f41ea03SFrantisek Hrbata  * @info1: first profiling data set
2325f41ea03SFrantisek Hrbata  * @info2: second profiling data set
2335f41ea03SFrantisek Hrbata  *
2345f41ea03SFrantisek Hrbata  * Returns non-zero if profiling data can be added, zero otherwise.
2355f41ea03SFrantisek Hrbata  */
gcov_info_is_compatible(struct gcov_info * info1,struct gcov_info * info2)2365f41ea03SFrantisek Hrbata int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
2375f41ea03SFrantisek Hrbata {
2385f41ea03SFrantisek Hrbata 	return (info1->stamp == info2->stamp);
2395f41ea03SFrantisek Hrbata }
2405f41ea03SFrantisek Hrbata 
2415f41ea03SFrantisek Hrbata /**
2425f41ea03SFrantisek Hrbata  * gcov_info_add - add up profiling data
24326ecea08SAlex Shi  * @dst: profiling data set to which data is added
24426ecea08SAlex Shi  * @src: profiling data set which is added
2455f41ea03SFrantisek Hrbata  *
24626ecea08SAlex Shi  * Adds profiling counts of @src to @dst.
2475f41ea03SFrantisek Hrbata  */
gcov_info_add(struct gcov_info * dst,struct gcov_info * src)2485f41ea03SFrantisek Hrbata void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
2495f41ea03SFrantisek Hrbata {
2505f41ea03SFrantisek Hrbata 	struct gcov_ctr_info *dci_ptr;
2515f41ea03SFrantisek Hrbata 	struct gcov_ctr_info *sci_ptr;
2525f41ea03SFrantisek Hrbata 	unsigned int fi_idx;
2535f41ea03SFrantisek Hrbata 	unsigned int ct_idx;
2545f41ea03SFrantisek Hrbata 	unsigned int val_idx;
2555f41ea03SFrantisek Hrbata 
2565f41ea03SFrantisek Hrbata 	for (fi_idx = 0; fi_idx < src->n_functions; fi_idx++) {
2575f41ea03SFrantisek Hrbata 		dci_ptr = dst->functions[fi_idx]->ctrs;
2585f41ea03SFrantisek Hrbata 		sci_ptr = src->functions[fi_idx]->ctrs;
2595f41ea03SFrantisek Hrbata 
2605f41ea03SFrantisek Hrbata 		for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
2615f41ea03SFrantisek Hrbata 			if (!counter_active(src, ct_idx))
2625f41ea03SFrantisek Hrbata 				continue;
2635f41ea03SFrantisek Hrbata 
2645f41ea03SFrantisek Hrbata 			for (val_idx = 0; val_idx < sci_ptr->num; val_idx++)
2655f41ea03SFrantisek Hrbata 				dci_ptr->values[val_idx] +=
2665f41ea03SFrantisek Hrbata 					sci_ptr->values[val_idx];
2675f41ea03SFrantisek Hrbata 
2685f41ea03SFrantisek Hrbata 			dci_ptr++;
2695f41ea03SFrantisek Hrbata 			sci_ptr++;
2705f41ea03SFrantisek Hrbata 		}
2715f41ea03SFrantisek Hrbata 	}
2725f41ea03SFrantisek Hrbata }
2735f41ea03SFrantisek Hrbata 
2745f41ea03SFrantisek Hrbata /**
2755f41ea03SFrantisek Hrbata  * gcov_info_dup - duplicate profiling data set
2765f41ea03SFrantisek Hrbata  * @info: profiling data set to duplicate
2775f41ea03SFrantisek Hrbata  *
2785f41ea03SFrantisek Hrbata  * Return newly allocated duplicate on success, %NULL on error.
2795f41ea03SFrantisek Hrbata  */
gcov_info_dup(struct gcov_info * info)2805f41ea03SFrantisek Hrbata struct gcov_info *gcov_info_dup(struct gcov_info *info)
2815f41ea03SFrantisek Hrbata {
2825f41ea03SFrantisek Hrbata 	struct gcov_info *dup;
2835f41ea03SFrantisek Hrbata 	struct gcov_ctr_info *dci_ptr; /* dst counter info */
2845f41ea03SFrantisek Hrbata 	struct gcov_ctr_info *sci_ptr; /* src counter info */
2855f41ea03SFrantisek Hrbata 	unsigned int active;
2865f41ea03SFrantisek Hrbata 	unsigned int fi_idx; /* function info idx */
2875f41ea03SFrantisek Hrbata 	unsigned int ct_idx; /* counter type idx */
2885f41ea03SFrantisek Hrbata 	size_t fi_size; /* function info size */
2895f41ea03SFrantisek Hrbata 	size_t cv_size; /* counter values size */
2905f41ea03SFrantisek Hrbata 
2915f41ea03SFrantisek Hrbata 	dup = kmemdup(info, sizeof(*dup), GFP_KERNEL);
2925f41ea03SFrantisek Hrbata 	if (!dup)
2935f41ea03SFrantisek Hrbata 		return NULL;
2945f41ea03SFrantisek Hrbata 
2955f41ea03SFrantisek Hrbata 	dup->next = NULL;
2965f41ea03SFrantisek Hrbata 	dup->filename = NULL;
2975f41ea03SFrantisek Hrbata 	dup->functions = NULL;
2985f41ea03SFrantisek Hrbata 
2995f41ea03SFrantisek Hrbata 	dup->filename = kstrdup(info->filename, GFP_KERNEL);
3005f41ea03SFrantisek Hrbata 	if (!dup->filename)
3015f41ea03SFrantisek Hrbata 		goto err_free;
3025f41ea03SFrantisek Hrbata 
3035f41ea03SFrantisek Hrbata 	dup->functions = kcalloc(info->n_functions,
3045f41ea03SFrantisek Hrbata 				 sizeof(struct gcov_fn_info *), GFP_KERNEL);
3055f41ea03SFrantisek Hrbata 	if (!dup->functions)
3065f41ea03SFrantisek Hrbata 		goto err_free;
3075f41ea03SFrantisek Hrbata 
3085f41ea03SFrantisek Hrbata 	active = num_counter_active(info);
3095f41ea03SFrantisek Hrbata 	fi_size = sizeof(struct gcov_fn_info);
3105f41ea03SFrantisek Hrbata 	fi_size += sizeof(struct gcov_ctr_info) * active;
3115f41ea03SFrantisek Hrbata 
3125f41ea03SFrantisek Hrbata 	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
3135f41ea03SFrantisek Hrbata 		dup->functions[fi_idx] = kzalloc(fi_size, GFP_KERNEL);
3145f41ea03SFrantisek Hrbata 		if (!dup->functions[fi_idx])
3155f41ea03SFrantisek Hrbata 			goto err_free;
3165f41ea03SFrantisek Hrbata 
3175f41ea03SFrantisek Hrbata 		*(dup->functions[fi_idx]) = *(info->functions[fi_idx]);
3185f41ea03SFrantisek Hrbata 
3195f41ea03SFrantisek Hrbata 		sci_ptr = info->functions[fi_idx]->ctrs;
3205f41ea03SFrantisek Hrbata 		dci_ptr = dup->functions[fi_idx]->ctrs;
3215f41ea03SFrantisek Hrbata 
3225f41ea03SFrantisek Hrbata 		for (ct_idx = 0; ct_idx < active; ct_idx++) {
3235f41ea03SFrantisek Hrbata 
3245f41ea03SFrantisek Hrbata 			cv_size = sizeof(gcov_type) * sci_ptr->num;
3255f41ea03SFrantisek Hrbata 
3261391efa9SJohannes Berg 			dci_ptr->values = kvmalloc(cv_size, GFP_KERNEL);
3275f41ea03SFrantisek Hrbata 
3285f41ea03SFrantisek Hrbata 			if (!dci_ptr->values)
3295f41ea03SFrantisek Hrbata 				goto err_free;
3305f41ea03SFrantisek Hrbata 
3315f41ea03SFrantisek Hrbata 			dci_ptr->num = sci_ptr->num;
3325f41ea03SFrantisek Hrbata 			memcpy(dci_ptr->values, sci_ptr->values, cv_size);
3335f41ea03SFrantisek Hrbata 
3345f41ea03SFrantisek Hrbata 			sci_ptr++;
3355f41ea03SFrantisek Hrbata 			dci_ptr++;
3365f41ea03SFrantisek Hrbata 		}
3375f41ea03SFrantisek Hrbata 	}
3385f41ea03SFrantisek Hrbata 
3395f41ea03SFrantisek Hrbata 	return dup;
3405f41ea03SFrantisek Hrbata err_free:
3415f41ea03SFrantisek Hrbata 	gcov_info_free(dup);
3425f41ea03SFrantisek Hrbata 	return NULL;
3435f41ea03SFrantisek Hrbata }
3445f41ea03SFrantisek Hrbata 
3455f41ea03SFrantisek Hrbata /**
3465f41ea03SFrantisek Hrbata  * gcov_info_free - release memory for profiling data set duplicate
3475f41ea03SFrantisek Hrbata  * @info: profiling data set duplicate to free
3485f41ea03SFrantisek Hrbata  */
gcov_info_free(struct gcov_info * info)3495f41ea03SFrantisek Hrbata void gcov_info_free(struct gcov_info *info)
3505f41ea03SFrantisek Hrbata {
3515f41ea03SFrantisek Hrbata 	unsigned int active;
3525f41ea03SFrantisek Hrbata 	unsigned int fi_idx;
3535f41ea03SFrantisek Hrbata 	unsigned int ct_idx;
3545f41ea03SFrantisek Hrbata 	struct gcov_ctr_info *ci_ptr;
3555f41ea03SFrantisek Hrbata 
3565f41ea03SFrantisek Hrbata 	if (!info->functions)
3575f41ea03SFrantisek Hrbata 		goto free_info;
3585f41ea03SFrantisek Hrbata 
3595f41ea03SFrantisek Hrbata 	active = num_counter_active(info);
3605f41ea03SFrantisek Hrbata 
3615f41ea03SFrantisek Hrbata 	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
3625f41ea03SFrantisek Hrbata 		if (!info->functions[fi_idx])
3635f41ea03SFrantisek Hrbata 			continue;
3645f41ea03SFrantisek Hrbata 
3655f41ea03SFrantisek Hrbata 		ci_ptr = info->functions[fi_idx]->ctrs;
3665f41ea03SFrantisek Hrbata 
3675f41ea03SFrantisek Hrbata 		for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++)
3681391efa9SJohannes Berg 			kvfree(ci_ptr->values);
3695f41ea03SFrantisek Hrbata 
3705f41ea03SFrantisek Hrbata 		kfree(info->functions[fi_idx]);
3715f41ea03SFrantisek Hrbata 	}
3725f41ea03SFrantisek Hrbata 
3735f41ea03SFrantisek Hrbata free_info:
3745f41ea03SFrantisek Hrbata 	kfree(info->functions);
3755f41ea03SFrantisek Hrbata 	kfree(info->filename);
3765f41ea03SFrantisek Hrbata 	kfree(info);
3775f41ea03SFrantisek Hrbata }
3785f41ea03SFrantisek Hrbata 
3795f41ea03SFrantisek Hrbata /**
3805f41ea03SFrantisek Hrbata  * convert_to_gcda - convert profiling data set to gcda file format
3815f41ea03SFrantisek Hrbata  * @buffer: the buffer to store file data or %NULL if no data should be stored
3825f41ea03SFrantisek Hrbata  * @info: profiling data set to be converted
3835f41ea03SFrantisek Hrbata  *
3845f41ea03SFrantisek Hrbata  * Returns the number of bytes that were/would have been stored into the buffer.
3855f41ea03SFrantisek Hrbata  */
convert_to_gcda(char * buffer,struct gcov_info * info)3867a1d55b9SJohannes Berg size_t convert_to_gcda(char *buffer, struct gcov_info *info)
3875f41ea03SFrantisek Hrbata {
3885f41ea03SFrantisek Hrbata 	struct gcov_fn_info *fi_ptr;
3895f41ea03SFrantisek Hrbata 	struct gcov_ctr_info *ci_ptr;
3905f41ea03SFrantisek Hrbata 	unsigned int fi_idx;
3915f41ea03SFrantisek Hrbata 	unsigned int ct_idx;
3925f41ea03SFrantisek Hrbata 	unsigned int cv_idx;
3935f41ea03SFrantisek Hrbata 	size_t pos = 0;
3945f41ea03SFrantisek Hrbata 
3955f41ea03SFrantisek Hrbata 	/* File header. */
3965f41ea03SFrantisek Hrbata 	pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
3975f41ea03SFrantisek Hrbata 	pos += store_gcov_u32(buffer, pos, info->version);
3985f41ea03SFrantisek Hrbata 	pos += store_gcov_u32(buffer, pos, info->stamp);
3995f41ea03SFrantisek Hrbata 
400977ef30aSMartin Liska #if (__GNUC__ >= 12)
401977ef30aSMartin Liska 	/* Use zero as checksum of the compilation unit. */
402977ef30aSMartin Liska 	pos += store_gcov_u32(buffer, pos, 0);
403977ef30aSMartin Liska #endif
404977ef30aSMartin Liska 
4055f41ea03SFrantisek Hrbata 	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
4065f41ea03SFrantisek Hrbata 		fi_ptr = info->functions[fi_idx];
4075f41ea03SFrantisek Hrbata 
4085f41ea03SFrantisek Hrbata 		/* Function record. */
4095f41ea03SFrantisek Hrbata 		pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
410977ef30aSMartin Liska 		pos += store_gcov_u32(buffer, pos,
411977ef30aSMartin Liska 			GCOV_TAG_FUNCTION_LENGTH * GCOV_UNIT_SIZE);
4125f41ea03SFrantisek Hrbata 		pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
4135f41ea03SFrantisek Hrbata 		pos += store_gcov_u32(buffer, pos, fi_ptr->lineno_checksum);
4145f41ea03SFrantisek Hrbata 		pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum);
4155f41ea03SFrantisek Hrbata 
4165f41ea03SFrantisek Hrbata 		ci_ptr = fi_ptr->ctrs;
4175f41ea03SFrantisek Hrbata 
4185f41ea03SFrantisek Hrbata 		for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
4195f41ea03SFrantisek Hrbata 			if (!counter_active(info, ct_idx))
4205f41ea03SFrantisek Hrbata 				continue;
4215f41ea03SFrantisek Hrbata 
4225f41ea03SFrantisek Hrbata 			/* Counter record. */
4235f41ea03SFrantisek Hrbata 			pos += store_gcov_u32(buffer, pos,
4245f41ea03SFrantisek Hrbata 					      GCOV_TAG_FOR_COUNTER(ct_idx));
425977ef30aSMartin Liska 			pos += store_gcov_u32(buffer, pos,
426977ef30aSMartin Liska 				ci_ptr->num * 2 * GCOV_UNIT_SIZE);
4275f41ea03SFrantisek Hrbata 
4285f41ea03SFrantisek Hrbata 			for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
4295f41ea03SFrantisek Hrbata 				pos += store_gcov_u64(buffer, pos,
4305f41ea03SFrantisek Hrbata 						      ci_ptr->values[cv_idx]);
4315f41ea03SFrantisek Hrbata 			}
4325f41ea03SFrantisek Hrbata 
4335f41ea03SFrantisek Hrbata 			ci_ptr++;
4345f41ea03SFrantisek Hrbata 		}
4355f41ea03SFrantisek Hrbata 	}
4365f41ea03SFrantisek Hrbata 
4375f41ea03SFrantisek Hrbata 	return pos;
4385f41ea03SFrantisek Hrbata }
439