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