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  *  Imported from Linux and modified for Xen by
14  *    Wei Liu <wei.liu2@citrix.com>
15  */
16 
17 #include <xen/string.h>
18 
19 #include "gcov.h"
20 
21 #if GCC_VERSION < 40700
22 #error "Wrong version of GCC used to compile gcov"
23 #elif GCC_VERSION < 40900
24 #define GCOV_COUNTERS 8
25 #elif GCC_VERSION < 50000
26 #define GCOV_COUNTERS 9
27 #elif GCC_VERSION < 70000
28 #define GCOV_COUNTERS 10
29 #elif GCC_VERSION < 100000
30 #define GCOV_COUNTERS 9
31 #elif GCC_VERSION < 140000
32 #define GCOV_COUNTERS 8
33 #elif GCC_VERSION < 150000
34 #define GCOV_COUNTERS 9
35 #else
36 #define GCOV_COUNTERS 10
37 #endif
38 
39 #define GCOV_TAG_FUNCTION_LENGTH        3
40 
41 #if GCC_VERSION < 120000
42 #define GCOV_UNIT_SIZE 1
43 #else
44 /* Since GCC 12, sizes are in BYTES and not in WORDS (4B). */
45 #define GCOV_UNIT_SIZE 4
46 #endif
47 
48 static struct gcov_info *gcov_info_head;
49 
50 /**
51  * struct gcov_ctr_info - information about counters for a single function
52  * @num: number of counter values for this type
53  * @values: array of counter values for this type
54  *
55  * This data is generated by gcc during compilation and doesn't change
56  * at run-time with the exception of the values array.
57  */
58 struct gcov_ctr_info {
59     unsigned int num;
60     gcov_type *values;
61 };
62 
63 /**
64  * struct gcov_fn_info - profiling meta data per function
65  * @key: comdat key
66  * @ident: unique ident of function
67  * @lineno_checksum: function lineo_checksum
68  * @cfg_checksum: function cfg checksum
69  * @ctrs: instrumented counters
70  *
71  * This data is generated by gcc during compilation and doesn't change
72  * at run-time.
73  *
74  * Information about a single function.  This uses the trailing array
75  * idiom. The number of counters is determined from the merge pointer
76  * array in gcov_info.  The key is used to detect which of a set of
77  * comdat functions was selected -- it points to the gcov_info object
78  * of the object file containing the selected comdat function.
79  */
80 struct gcov_fn_info {
81     const struct gcov_info *key;
82     unsigned int ident;
83     unsigned int lineno_checksum;
84     unsigned int cfg_checksum;
85     struct gcov_ctr_info ctrs[0];
86 };
87 
88 /**
89  * struct gcov_info - profiling data per object file
90  * @version: gcov version magic indicating the gcc version used for compilation
91  * @next: list head for a singly-linked list
92  * @stamp: uniquifying time stamp
93  * @filename: name of the associated gcov data file
94  * @merge: merge functions (null for unused counter type)
95  * @n_functions: number of instrumented functions
96  * @functions: pointer to pointers to function information
97  *
98  * This data is generated by gcc during compilation and doesn't change
99  * at run-time with the exception of the next pointer.
100  */
101 struct gcov_info {
102     unsigned int version;
103     struct gcov_info *next;
104     unsigned int stamp;
105 #if GCC_VERSION >= 120000
106     /*  GCC 12 introduced a checksum field */
107     unsigned int checksum;
108 #endif
109     const char *filename;
110     void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
111     unsigned int n_functions;
112     struct gcov_fn_info **functions;
113 };
114 
counter_active(const struct gcov_info * info,unsigned int type)115 static int counter_active(const struct gcov_info *info, unsigned int type)
116 {
117     return info->merge[type] ? 1 : 0;
118 }
119 
gcov_info_link(struct gcov_info * info)120 void gcov_info_link(struct gcov_info *info)
121 {
122     info->next = gcov_info_head;
123     gcov_info_head = info;
124 }
125 
gcov_info_next(const struct gcov_info * info)126 struct gcov_info *gcov_info_next(const struct gcov_info *info)
127 {
128     if ( !info )
129         return gcov_info_head;
130     return info->next;
131 }
132 
gcov_info_reset(struct gcov_info * info)133 void gcov_info_reset(struct gcov_info *info)
134 {
135     struct gcov_ctr_info *ci_ptr;
136     unsigned int fi_idx;
137     unsigned int ct_idx;
138 
139     for ( fi_idx = 0; fi_idx < info->n_functions; fi_idx++ )
140     {
141         ci_ptr = info->functions[fi_idx]->ctrs;
142 
143         for ( ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++ )
144         {
145             if ( !counter_active(info, ct_idx) )
146                 continue;
147 
148             memset(ci_ptr->values, 0, sizeof(gcov_type) * ci_ptr->num);
149             ci_ptr++;
150         }
151     }
152 }
153 
gcov_info_filename(const struct gcov_info * info)154 const char *gcov_info_filename(const struct gcov_info *info)
155 {
156     return info->filename;
157 }
158 
159 
160 /**
161  * gcov_info_to_gcda - convert profiling data set to gcda file format
162  * @buffer: the buffer to store file data or %NULL if no data should be stored
163  * @info: profiling data set to be converted
164  *
165  * Returns the number of bytes that were/would have been stored into the buffer.
166  */
gcov_info_to_gcda(char * buffer,const struct gcov_info * info)167 size_t gcov_info_to_gcda(char *buffer, const struct gcov_info *info)
168 {
169     struct gcov_fn_info *fi_ptr;
170     struct gcov_ctr_info *ci_ptr;
171     unsigned int fi_idx;
172     unsigned int ct_idx;
173     unsigned int cv_idx;
174     size_t pos = 0;
175 
176     /* File header. */
177     pos += gcov_store_uint32(buffer, pos, GCOV_DATA_MAGIC);
178     pos += gcov_store_uint32(buffer, pos, info->version);
179     pos += gcov_store_uint32(buffer, pos, info->stamp);
180 
181 #if GCC_VERSION >= 120000
182     /* Use zero as checksum of the compilation unit. */
183     pos += gcov_store_uint32(buffer, pos, 0);
184 #endif
185 
186     for ( fi_idx = 0; fi_idx < info->n_functions; fi_idx++ )
187     {
188         fi_ptr = info->functions[fi_idx];
189 
190         /* Function record. */
191         pos += gcov_store_uint32(buffer, pos, GCOV_TAG_FUNCTION);
192         pos += gcov_store_uint32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH * GCOV_UNIT_SIZE);
193         pos += gcov_store_uint32(buffer, pos, fi_ptr->ident);
194         pos += gcov_store_uint32(buffer, pos, fi_ptr->lineno_checksum);
195         pos += gcov_store_uint32(buffer, pos, fi_ptr->cfg_checksum);
196 
197         ci_ptr = fi_ptr->ctrs;
198 
199         for ( ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++ )
200         {
201             if (! counter_active(info, ct_idx) )
202                 continue;
203 
204             /* Counter record. */
205             pos += gcov_store_uint32(buffer, pos,
206                                      GCOV_TAG_FOR_COUNTER(ct_idx));
207             pos += gcov_store_uint32(buffer, pos, ci_ptr->num * 2 * GCOV_UNIT_SIZE);
208 
209             for ( cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++ )
210                 pos += gcov_store_uint64(buffer, pos, ci_ptr->values[cv_idx]);
211 
212             ci_ptr++;
213         }
214     }
215 
216     return pos;
217 }
218 
219 /*
220  * Local variables:
221  * mode: C
222  * c-file-style: "BSD"
223  * c-basic-offset: 4
224  * tab-width: 4
225  * indent-tabs-mode: nil
226  * End:
227  */
228