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