summaryrefslogtreecommitdiff
path: root/xen/common/coverage/gcc_3_4.c
blob: 3631f4bc2535194567c66128a4a2ac7304d72162 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
/*
 *  This code provides functions to handle gcc's profiling data format
 *  introduced with gcc 3.4. Future versions of gcc may change the gcov
 *  format (as happened before), so all format-specific information needs
 *  to be kept modular and easily exchangeable.
 *
 *  This file is based on gcc-internal definitions. Functions and data
 *  structures are defined to be compatible with gcc counterparts.
 *  For a better understanding, refer to gcc source: gcc/gcov-io.h.
 *
 *    Copyright IBM Corp. 2009
 *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
 *
 *    Uses gcc-internal data definitions.
 *
 *  Imported from Linux and modified for Xen by
 *    Wei Liu <wei.liu2@citrix.com>
 */


#include <xen/lib.h>

#include "gcov.h"

#if !(GCC_VERSION >= 30400 && GCC_VERSION < 40700)
#error "Wrong version of GCC used to compile gcov"
#endif

#define GCOV_COUNTERS 5

static struct gcov_info *gcov_info_head;

/**
 * struct gcov_fn_info - profiling meta data per function
 * @ident: object file-unique function identifier
 * @checksum: function checksum
 * @n_ctrs: number of values per counter type belonging to this function
 *
 * This data is generated by gcc during compilation and doesn't change
 * at run-time.
 */
struct gcov_fn_info
{
    unsigned int ident;
    unsigned int checksum;
    unsigned int n_ctrs[0];
};

/**
 * struct gcov_ctr_info - profiling data per counter type
 * @num: number of counter values for this type
 * @values: array of counter values for this type
 * @merge: merge function for counter values of this type (unused)
 *
 * This data is generated by gcc during compilation and doesn't change
 * at run-time with the exception of the values array.
 */
struct gcov_ctr_info
{
    unsigned int num;
    gcov_type *values;
    void (*merge)(gcov_type *, unsigned int);
};

/**
 * struct gcov_info - profiling data per object file
 * @version: gcov version magic indicating the gcc version used for compilation
 * @next: list head for a singly-linked list
 * @stamp: time stamp
 * @filename: name of the associated gcov data file
 * @n_functions: number of instrumented functions
 * @functions: function data
 * @ctr_mask: mask specifying which counter types are active
 * @counts: counter data per counter type
 *
 * This data is generated by gcc during compilation and doesn't change
 * at run-time with the exception of the next pointer.
 */
struct gcov_info
{
    unsigned int              version;
    struct gcov_info          *next;
    unsigned int              stamp;
    const char                *filename;
    unsigned int              n_functions;
    const struct gcov_fn_info *functions;
    unsigned int              ctr_mask;
    struct gcov_ctr_info      counts[0];
};

/**
 * struct type_info - iterator helper array
 * @ctr_type: counter type
 * @offset: index of the first value of the current function for this type
 *
 * This array is needed to convert the in-memory data format into the in-file
 * data format:
 *
 * In-memory:
 *   for each counter type
 *     for each function
 *       values
 *
 * In-file:
 *   for each function
 *     for each counter type
 *       values
 *
 * See gcc source gcc/gcov-io.h for more information on data organization.
 */
struct type_info {
    int ctr_type;
    unsigned int offset;
};

/**
 * struct gcov_iterator - specifies current file position in logical records
 * @info: associated profiling data
 * @record: record type
 * @function: function number
 * @type: counter type
 * @count: index into values array
 * @num_types: number of counter types
 * @type_info: helper array to get values-array offset for current function
 */
struct gcov_iterator {
    const struct gcov_info *info;

    int record;
    unsigned int function;
    unsigned int type;
    unsigned int count;

    int num_types;
    struct type_info type_info[GCOV_COUNTERS];
};

/* Mapping of logical record number to actual file content. */
#define RECORD_FILE_MAGIC       0
#define RECORD_GCOV_VERSION     1
#define RECORD_TIME_STAMP       2
#define RECORD_FUNCTION_TAG     3
#define RECORD_FUNCTON_TAG_LEN  4
#define RECORD_FUNCTION_IDENT   5
#define RECORD_FUNCTION_CHECK   6
#define RECORD_COUNT_TAG        7
#define RECORD_COUNT_LEN        8
#define RECORD_COUNT            9

static int counter_active(const struct gcov_info *info, unsigned int type)
{
    return (1 << type) & info->ctr_mask;
}

static unsigned int num_counter_active(const struct gcov_info *info)
{
    unsigned int i;
    unsigned int result = 0;

    for ( i = 0; i < GCOV_COUNTERS; i++ )
        if ( counter_active(info, i) )
            result++;

    return result;
}

void gcov_info_link(struct gcov_info *info)
{
    info->next = gcov_info_head;
    gcov_info_head = info;
}

struct gcov_info *gcov_info_next(const struct gcov_info *info)
{
    if ( !info )
        return gcov_info_head;

    return info->next;
}

const char *gcov_info_filename(const struct gcov_info *info)
{
    return info->filename;
}

void gcov_info_reset(struct gcov_info *info)
{
    unsigned int active = num_counter_active(info);
    unsigned int i;

    for ( i = 0; i < active; i++ )
        memset(info->counts[i].values, 0,
               info->counts[i].num * sizeof(gcov_type));
}

static size_t get_fn_size(const struct gcov_info *info)
{
    size_t size;

    size = sizeof(struct gcov_fn_info) + num_counter_active(info) *
        sizeof(unsigned int);
    if ( __alignof__(struct gcov_fn_info) > sizeof(unsigned int) )
        size = ROUNDUP(size, __alignof__(struct gcov_fn_info));
    return size;
}

static struct gcov_fn_info *get_fn_info(const struct gcov_info *info,
                                        unsigned int fn)
{
    return (struct gcov_fn_info *)
        ((char *) info->functions + fn * get_fn_size(info));
}

static struct gcov_fn_info *get_func(struct gcov_iterator *iter)
{
    return get_fn_info(iter->info, iter->function);
}

static struct type_info *get_type(struct gcov_iterator *iter)
{
    return &iter->type_info[iter->type];
}

/**
 * gcov_iter_next - advance file iterator to next logical record
 * @iter: file iterator
 *
 * Return zero if new position is valid, non-zero if iterator has reached end.
 */
static int gcov_iter_next(struct gcov_iterator *iter)
{
    switch ( iter->record )
    {
    case RECORD_FILE_MAGIC:
    case RECORD_GCOV_VERSION:
    case RECORD_FUNCTION_TAG:
    case RECORD_FUNCTON_TAG_LEN:
    case RECORD_FUNCTION_IDENT:
    case RECORD_COUNT_TAG:
        /* Advance to next record */
        iter->record++;
        break;
    case RECORD_COUNT:
        /* Advance to next count */
        iter->count++;
        /* fall through */
    case RECORD_COUNT_LEN:
        if ( iter->count < get_func(iter)->n_ctrs[iter->type] )
        {
            iter->record = 9;
            break;
        }
        /* Advance to next counter type */
        get_type(iter)->offset += iter->count;
        iter->count = 0;
        iter->type++;
        /* fall through */
    case RECORD_FUNCTION_CHECK:
        if ( iter->type < iter->num_types )
        {
            iter->record = 7;
            break;
        }
        /* Advance to next function */
        iter->type = 0;
        iter->function++;
        /* fall through */
    case RECORD_TIME_STAMP:
        if ( iter->function < iter->info->n_functions )
            iter->record = 3;
        else
            iter->record = -1;
        break;
    }
    /* Check for EOF. */
    if ( iter->record == -1 )
        return -EINVAL;
    else
        return 0;
}

/**
 * gcov_iter_write - write data to buffer
 * @iter: file iterator
 * @buf: buffer to write to, if it is NULL, nothing is written
 * @pos: position inside buffer to start writing
 *
 * Return number of bytes written into buffer.
 */
static size_t gcov_iter_write(struct gcov_iterator *iter, char *buf,
                              size_t pos)
{
    size_t ret = 0;

    switch ( iter->record )
    {
    case RECORD_FILE_MAGIC:
        ret = gcov_store_uint32(buf, pos, GCOV_DATA_MAGIC);
        break;
    case RECORD_GCOV_VERSION:
        ret = gcov_store_uint32(buf, pos, iter->info->version);
        break;
    case RECORD_TIME_STAMP:
        ret = gcov_store_uint32(buf, pos, iter->info->stamp);
        break;
    case RECORD_FUNCTION_TAG:
        ret = gcov_store_uint32(buf, pos, GCOV_TAG_FUNCTION);
        break;
    case RECORD_FUNCTON_TAG_LEN:
        ret = gcov_store_uint32(buf, pos, 2);
        break;
    case RECORD_FUNCTION_IDENT:
        ret = gcov_store_uint32(buf, pos, get_func(iter)->ident);
        break;
    case RECORD_FUNCTION_CHECK:
        ret = gcov_store_uint32(buf, pos, get_func(iter)->checksum);
        break;
    case RECORD_COUNT_TAG:
        ret = gcov_store_uint32(buf, pos,
                                GCOV_TAG_FOR_COUNTER(get_type(iter)->ctr_type));
        break;
    case RECORD_COUNT_LEN:
        ret = gcov_store_uint32(buf, pos,
                                get_func(iter)->n_ctrs[iter->type] * 2);
        break;
    case RECORD_COUNT:
        ret = gcov_store_uint64(buf, pos, iter->info->counts[iter->type].
                                values[iter->count + get_type(iter)->offset]);
        break;
    }

    return ret;
}

/* If buffer is NULL, no data is written. */
size_t gcov_info_to_gcda(char *buffer, const struct gcov_info *info)
{
    struct gcov_iterator iter = { .info = info };
    unsigned int i;
    size_t pos = 0;

    for ( i = 0; i < GCOV_COUNTERS; i++ )
    {
        if ( counter_active(info, i) )
        {
            iter.type_info[iter.num_types].ctr_type = i;
            iter.type_info[iter.num_types].offset = 0;
            iter.num_types++;
        }
    }

    do {
        pos += gcov_iter_write(&iter, buffer, pos);
    } while ( gcov_iter_next(&iter) == 0 );

    return pos;
}

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */