1 /*
2  *  This code provides functions to handle gcc's profiling data format
3  *  introduced with gcc 3.4. Future versions of gcc may change the gcov
4  *  format (as happened before), so all format-specific information needs
5  *  to be kept modular and easily exchangeable.
6  *
7  *  This file is based on gcc-internal definitions. Functions and data
8  *  structures are defined to be compatible with gcc counterparts.
9  *  For a better understanding, refer to gcc source: gcc/gcov-io.h.
10  *
11  *    Copyright IBM Corp. 2009
12  *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
13  *
14  *    Uses gcc-internal data definitions.
15  *
16  *  Imported from Linux and modified for Xen by
17  *    Wei Liu <wei.liu2@citrix.com>
18  */
19 
20 
21 #include <xen/lib.h>
22 
23 #include "gcov.h"
24 
25 #if !(GCC_VERSION >= 30400 && GCC_VERSION < 40700)
26 #error "Wrong version of GCC used to compile gcov"
27 #endif
28 
29 #define GCOV_COUNTERS 5
30 
31 static struct gcov_info *gcov_info_head;
32 
33 /**
34  * struct gcov_fn_info - profiling meta data per function
35  * @ident: object file-unique function identifier
36  * @checksum: function checksum
37  * @n_ctrs: number of values per counter type belonging to this function
38  *
39  * This data is generated by gcc during compilation and doesn't change
40  * at run-time.
41  */
42 struct gcov_fn_info
43 {
44     unsigned int ident;
45     unsigned int checksum;
46     unsigned int n_ctrs[0];
47 };
48 
49 /**
50  * struct gcov_ctr_info - profiling data per counter type
51  * @num: number of counter values for this type
52  * @values: array of counter values for this type
53  * @merge: merge function for counter values of this type (unused)
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 {
60     unsigned int num;
61     gcov_type *values;
62     void (*merge)(gcov_type *, unsigned int);
63 };
64 
65 /**
66  * struct gcov_info - profiling data per object file
67  * @version: gcov version magic indicating the gcc version used for compilation
68  * @next: list head for a singly-linked list
69  * @stamp: time stamp
70  * @filename: name of the associated gcov data file
71  * @n_functions: number of instrumented functions
72  * @functions: function data
73  * @ctr_mask: mask specifying which counter types are active
74  * @counts: counter data per counter type
75  *
76  * This data is generated by gcc during compilation and doesn't change
77  * at run-time with the exception of the next pointer.
78  */
79 struct gcov_info
80 {
81     unsigned int              version;
82     struct gcov_info          *next;
83     unsigned int              stamp;
84     const char                *filename;
85     unsigned int              n_functions;
86     const struct gcov_fn_info *functions;
87     unsigned int              ctr_mask;
88     struct gcov_ctr_info      counts[0];
89 };
90 
91 /**
92  * struct type_info - iterator helper array
93  * @ctr_type: counter type
94  * @offset: index of the first value of the current function for this type
95  *
96  * This array is needed to convert the in-memory data format into the in-file
97  * data format:
98  *
99  * In-memory:
100  *   for each counter type
101  *     for each function
102  *       values
103  *
104  * In-file:
105  *   for each function
106  *     for each counter type
107  *       values
108  *
109  * See gcc source gcc/gcov-io.h for more information on data organization.
110  */
111 struct type_info {
112     int ctr_type;
113     unsigned int offset;
114 };
115 
116 /**
117  * struct gcov_iterator - specifies current file position in logical records
118  * @info: associated profiling data
119  * @record: record type
120  * @function: function number
121  * @type: counter type
122  * @count: index into values array
123  * @num_types: number of counter types
124  * @type_info: helper array to get values-array offset for current function
125  */
126 struct gcov_iterator {
127     const struct gcov_info *info;
128 
129     int record;
130     unsigned int function;
131     unsigned int type;
132     unsigned int count;
133 
134     int num_types;
135     struct type_info type_info[GCOV_COUNTERS];
136 };
137 
138 /* Mapping of logical record number to actual file content. */
139 #define RECORD_FILE_MAGIC       0
140 #define RECORD_GCOV_VERSION     1
141 #define RECORD_TIME_STAMP       2
142 #define RECORD_FUNCTION_TAG     3
143 #define RECORD_FUNCTON_TAG_LEN  4
144 #define RECORD_FUNCTION_IDENT   5
145 #define RECORD_FUNCTION_CHECK   6
146 #define RECORD_COUNT_TAG        7
147 #define RECORD_COUNT_LEN        8
148 #define RECORD_COUNT            9
149 
counter_active(const struct gcov_info * info,unsigned int type)150 static int counter_active(const struct gcov_info *info, unsigned int type)
151 {
152     return (1 << type) & info->ctr_mask;
153 }
154 
num_counter_active(const struct gcov_info * info)155 static unsigned int num_counter_active(const struct gcov_info *info)
156 {
157     unsigned int i;
158     unsigned int result = 0;
159 
160     for ( i = 0; i < GCOV_COUNTERS; i++ )
161         if ( counter_active(info, i) )
162             result++;
163 
164     return result;
165 }
166 
gcov_info_link(struct gcov_info * info)167 void gcov_info_link(struct gcov_info *info)
168 {
169     info->next = gcov_info_head;
170     gcov_info_head = info;
171 }
172 
gcov_info_next(const struct gcov_info * info)173 struct gcov_info *gcov_info_next(const struct gcov_info *info)
174 {
175     if ( !info )
176         return gcov_info_head;
177 
178     return info->next;
179 }
180 
gcov_info_filename(const struct gcov_info * info)181 const char *gcov_info_filename(const struct gcov_info *info)
182 {
183     return info->filename;
184 }
185 
gcov_info_reset(struct gcov_info * info)186 void gcov_info_reset(struct gcov_info *info)
187 {
188     unsigned int active = num_counter_active(info);
189     unsigned int i;
190 
191     for ( i = 0; i < active; i++ )
192         memset(info->counts[i].values, 0,
193                info->counts[i].num * sizeof(gcov_type));
194 }
195 
get_fn_size(const struct gcov_info * info)196 static size_t get_fn_size(const struct gcov_info *info)
197 {
198     size_t size;
199 
200     size = sizeof(struct gcov_fn_info) + num_counter_active(info) *
201         sizeof(unsigned int);
202     if ( __alignof__(struct gcov_fn_info) > sizeof(unsigned int) )
203         size = ROUNDUP(size, __alignof__(struct gcov_fn_info));
204     return size;
205 }
206 
get_fn_info(const struct gcov_info * info,unsigned int fn)207 static struct gcov_fn_info *get_fn_info(const struct gcov_info *info,
208                                         unsigned int fn)
209 {
210     return (struct gcov_fn_info *)
211         ((char *) info->functions + fn * get_fn_size(info));
212 }
213 
get_func(struct gcov_iterator * iter)214 static struct gcov_fn_info *get_func(struct gcov_iterator *iter)
215 {
216     return get_fn_info(iter->info, iter->function);
217 }
218 
get_type(struct gcov_iterator * iter)219 static struct type_info *get_type(struct gcov_iterator *iter)
220 {
221     return &iter->type_info[iter->type];
222 }
223 
224 /**
225  * gcov_iter_next - advance file iterator to next logical record
226  * @iter: file iterator
227  *
228  * Return zero if new position is valid, non-zero if iterator has reached end.
229  */
gcov_iter_next(struct gcov_iterator * iter)230 static int gcov_iter_next(struct gcov_iterator *iter)
231 {
232     switch ( iter->record )
233     {
234     case RECORD_FILE_MAGIC:
235     case RECORD_GCOV_VERSION:
236     case RECORD_FUNCTION_TAG:
237     case RECORD_FUNCTON_TAG_LEN:
238     case RECORD_FUNCTION_IDENT:
239     case RECORD_COUNT_TAG:
240         /* Advance to next record */
241         iter->record++;
242         break;
243     case RECORD_COUNT:
244         /* Advance to next count */
245         iter->count++;
246         /* fall through */
247     case RECORD_COUNT_LEN:
248         if ( iter->count < get_func(iter)->n_ctrs[iter->type] )
249         {
250             iter->record = 9;
251             break;
252         }
253         /* Advance to next counter type */
254         get_type(iter)->offset += iter->count;
255         iter->count = 0;
256         iter->type++;
257         /* fall through */
258     case RECORD_FUNCTION_CHECK:
259         if ( iter->type < iter->num_types )
260         {
261             iter->record = 7;
262             break;
263         }
264         /* Advance to next function */
265         iter->type = 0;
266         iter->function++;
267         /* fall through */
268     case RECORD_TIME_STAMP:
269         if ( iter->function < iter->info->n_functions )
270             iter->record = 3;
271         else
272             iter->record = -1;
273         break;
274     }
275     /* Check for EOF. */
276     if ( iter->record == -1 )
277         return -EINVAL;
278     else
279         return 0;
280 }
281 
282 /**
283  * gcov_iter_write - write data to buffer
284  * @iter: file iterator
285  * @buf: buffer to write to, if it is NULL, nothing is written
286  * @pos: position inside buffer to start writing
287  *
288  * Return number of bytes written into buffer.
289  */
gcov_iter_write(struct gcov_iterator * iter,char * buf,size_t pos)290 static size_t gcov_iter_write(struct gcov_iterator *iter, char *buf,
291                               size_t pos)
292 {
293     size_t ret = 0;
294 
295     switch ( iter->record )
296     {
297     case RECORD_FILE_MAGIC:
298         ret = gcov_store_uint32(buf, pos, GCOV_DATA_MAGIC);
299         break;
300     case RECORD_GCOV_VERSION:
301         ret = gcov_store_uint32(buf, pos, iter->info->version);
302         break;
303     case RECORD_TIME_STAMP:
304         ret = gcov_store_uint32(buf, pos, iter->info->stamp);
305         break;
306     case RECORD_FUNCTION_TAG:
307         ret = gcov_store_uint32(buf, pos, GCOV_TAG_FUNCTION);
308         break;
309     case RECORD_FUNCTON_TAG_LEN:
310         ret = gcov_store_uint32(buf, pos, 2);
311         break;
312     case RECORD_FUNCTION_IDENT:
313         ret = gcov_store_uint32(buf, pos, get_func(iter)->ident);
314         break;
315     case RECORD_FUNCTION_CHECK:
316         ret = gcov_store_uint32(buf, pos, get_func(iter)->checksum);
317         break;
318     case RECORD_COUNT_TAG:
319         ret = gcov_store_uint32(buf, pos,
320                                 GCOV_TAG_FOR_COUNTER(get_type(iter)->ctr_type));
321         break;
322     case RECORD_COUNT_LEN:
323         ret = gcov_store_uint32(buf, pos,
324                                 get_func(iter)->n_ctrs[iter->type] * 2);
325         break;
326     case RECORD_COUNT:
327         ret = gcov_store_uint64(buf, pos, iter->info->counts[iter->type].
328                                 values[iter->count + get_type(iter)->offset]);
329         break;
330     }
331 
332     return ret;
333 }
334 
335 /* If buffer is NULL, no data is written. */
gcov_info_to_gcda(char * buffer,const struct gcov_info * info)336 size_t gcov_info_to_gcda(char *buffer, const struct gcov_info *info)
337 {
338     struct gcov_iterator iter = { .info = info };
339     unsigned int i;
340     size_t pos = 0;
341 
342     for ( i = 0; i < GCOV_COUNTERS; i++ )
343     {
344         if ( counter_active(info, i) )
345         {
346             iter.type_info[iter.num_types].ctr_type = i;
347             iter.type_info[iter.num_types].offset = 0;
348             iter.num_types++;
349         }
350     }
351 
352     do {
353         pos += gcov_iter_write(&iter, buffer, pos);
354     } while ( gcov_iter_next(&iter) == 0 );
355 
356     return pos;
357 }
358 
359 /*
360  * Local variables:
361  * mode: C
362  * c-file-style: "BSD"
363  * c-basic-offset: 4
364  * tab-width: 4
365  * indent-tabs-mode: nil
366  * End:
367  */
368