Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/kernel/gcov/gcc_3_4.c
10820 views
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 <[email protected]>
13
*
14
* Uses gcc-internal data definitions.
15
*/
16
17
#include <linux/errno.h>
18
#include <linux/slab.h>
19
#include <linux/string.h>
20
#include <linux/seq_file.h>
21
#include <linux/vmalloc.h>
22
#include "gcov.h"
23
24
/* Symbolic links to be created for each profiling data file. */
25
const struct gcov_link gcov_link[] = {
26
{ OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
27
{ 0, NULL},
28
};
29
30
/*
31
* Determine whether a counter is active. Based on gcc magic. Doesn't change
32
* at run-time.
33
*/
34
static int counter_active(struct gcov_info *info, unsigned int type)
35
{
36
return (1 << type) & info->ctr_mask;
37
}
38
39
/* Determine number of active counters. Based on gcc magic. */
40
static unsigned int num_counter_active(struct gcov_info *info)
41
{
42
unsigned int i;
43
unsigned int result = 0;
44
45
for (i = 0; i < GCOV_COUNTERS; i++) {
46
if (counter_active(info, i))
47
result++;
48
}
49
return result;
50
}
51
52
/**
53
* gcov_info_reset - reset profiling data to zero
54
* @info: profiling data set
55
*/
56
void gcov_info_reset(struct gcov_info *info)
57
{
58
unsigned int active = num_counter_active(info);
59
unsigned int i;
60
61
for (i = 0; i < active; i++) {
62
memset(info->counts[i].values, 0,
63
info->counts[i].num * sizeof(gcov_type));
64
}
65
}
66
67
/**
68
* gcov_info_is_compatible - check if profiling data can be added
69
* @info1: first profiling data set
70
* @info2: second profiling data set
71
*
72
* Returns non-zero if profiling data can be added, zero otherwise.
73
*/
74
int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
75
{
76
return (info1->stamp == info2->stamp);
77
}
78
79
/**
80
* gcov_info_add - add up profiling data
81
* @dest: profiling data set to which data is added
82
* @source: profiling data set which is added
83
*
84
* Adds profiling counts of @source to @dest.
85
*/
86
void gcov_info_add(struct gcov_info *dest, struct gcov_info *source)
87
{
88
unsigned int i;
89
unsigned int j;
90
91
for (i = 0; i < num_counter_active(dest); i++) {
92
for (j = 0; j < dest->counts[i].num; j++) {
93
dest->counts[i].values[j] +=
94
source->counts[i].values[j];
95
}
96
}
97
}
98
99
/* Get size of function info entry. Based on gcc magic. */
100
static size_t get_fn_size(struct gcov_info *info)
101
{
102
size_t size;
103
104
size = sizeof(struct gcov_fn_info) + num_counter_active(info) *
105
sizeof(unsigned int);
106
if (__alignof__(struct gcov_fn_info) > sizeof(unsigned int))
107
size = ALIGN(size, __alignof__(struct gcov_fn_info));
108
return size;
109
}
110
111
/* Get address of function info entry. Based on gcc magic. */
112
static struct gcov_fn_info *get_fn_info(struct gcov_info *info, unsigned int fn)
113
{
114
return (struct gcov_fn_info *)
115
((char *) info->functions + fn * get_fn_size(info));
116
}
117
118
/**
119
* gcov_info_dup - duplicate profiling data set
120
* @info: profiling data set to duplicate
121
*
122
* Return newly allocated duplicate on success, %NULL on error.
123
*/
124
struct gcov_info *gcov_info_dup(struct gcov_info *info)
125
{
126
struct gcov_info *dup;
127
unsigned int i;
128
unsigned int active;
129
130
/* Duplicate gcov_info. */
131
active = num_counter_active(info);
132
dup = kzalloc(sizeof(struct gcov_info) +
133
sizeof(struct gcov_ctr_info) * active, GFP_KERNEL);
134
if (!dup)
135
return NULL;
136
dup->version = info->version;
137
dup->stamp = info->stamp;
138
dup->n_functions = info->n_functions;
139
dup->ctr_mask = info->ctr_mask;
140
/* Duplicate filename. */
141
dup->filename = kstrdup(info->filename, GFP_KERNEL);
142
if (!dup->filename)
143
goto err_free;
144
/* Duplicate table of functions. */
145
dup->functions = kmemdup(info->functions, info->n_functions *
146
get_fn_size(info), GFP_KERNEL);
147
if (!dup->functions)
148
goto err_free;
149
/* Duplicate counter arrays. */
150
for (i = 0; i < active ; i++) {
151
struct gcov_ctr_info *ctr = &info->counts[i];
152
size_t size = ctr->num * sizeof(gcov_type);
153
154
dup->counts[i].num = ctr->num;
155
dup->counts[i].merge = ctr->merge;
156
dup->counts[i].values = vmalloc(size);
157
if (!dup->counts[i].values)
158
goto err_free;
159
memcpy(dup->counts[i].values, ctr->values, size);
160
}
161
return dup;
162
163
err_free:
164
gcov_info_free(dup);
165
return NULL;
166
}
167
168
/**
169
* gcov_info_free - release memory for profiling data set duplicate
170
* @info: profiling data set duplicate to free
171
*/
172
void gcov_info_free(struct gcov_info *info)
173
{
174
unsigned int active = num_counter_active(info);
175
unsigned int i;
176
177
for (i = 0; i < active ; i++)
178
vfree(info->counts[i].values);
179
kfree(info->functions);
180
kfree(info->filename);
181
kfree(info);
182
}
183
184
/**
185
* struct type_info - iterator helper array
186
* @ctr_type: counter type
187
* @offset: index of the first value of the current function for this type
188
*
189
* This array is needed to convert the in-memory data format into the in-file
190
* data format:
191
*
192
* In-memory:
193
* for each counter type
194
* for each function
195
* values
196
*
197
* In-file:
198
* for each function
199
* for each counter type
200
* values
201
*
202
* See gcc source gcc/gcov-io.h for more information on data organization.
203
*/
204
struct type_info {
205
int ctr_type;
206
unsigned int offset;
207
};
208
209
/**
210
* struct gcov_iterator - specifies current file position in logical records
211
* @info: associated profiling data
212
* @record: record type
213
* @function: function number
214
* @type: counter type
215
* @count: index into values array
216
* @num_types: number of counter types
217
* @type_info: helper array to get values-array offset for current function
218
*/
219
struct gcov_iterator {
220
struct gcov_info *info;
221
222
int record;
223
unsigned int function;
224
unsigned int type;
225
unsigned int count;
226
227
int num_types;
228
struct type_info type_info[0];
229
};
230
231
static struct gcov_fn_info *get_func(struct gcov_iterator *iter)
232
{
233
return get_fn_info(iter->info, iter->function);
234
}
235
236
static struct type_info *get_type(struct gcov_iterator *iter)
237
{
238
return &iter->type_info[iter->type];
239
}
240
241
/**
242
* gcov_iter_new - allocate and initialize profiling data iterator
243
* @info: profiling data set to be iterated
244
*
245
* Return file iterator on success, %NULL otherwise.
246
*/
247
struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
248
{
249
struct gcov_iterator *iter;
250
251
iter = kzalloc(sizeof(struct gcov_iterator) +
252
num_counter_active(info) * sizeof(struct type_info),
253
GFP_KERNEL);
254
if (iter)
255
iter->info = info;
256
257
return iter;
258
}
259
260
/**
261
* gcov_iter_free - release memory for iterator
262
* @iter: file iterator to free
263
*/
264
void gcov_iter_free(struct gcov_iterator *iter)
265
{
266
kfree(iter);
267
}
268
269
/**
270
* gcov_iter_get_info - return profiling data set for given file iterator
271
* @iter: file iterator
272
*/
273
struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
274
{
275
return iter->info;
276
}
277
278
/**
279
* gcov_iter_start - reset file iterator to starting position
280
* @iter: file iterator
281
*/
282
void gcov_iter_start(struct gcov_iterator *iter)
283
{
284
int i;
285
286
iter->record = 0;
287
iter->function = 0;
288
iter->type = 0;
289
iter->count = 0;
290
iter->num_types = 0;
291
for (i = 0; i < GCOV_COUNTERS; i++) {
292
if (counter_active(iter->info, i)) {
293
iter->type_info[iter->num_types].ctr_type = i;
294
iter->type_info[iter->num_types++].offset = 0;
295
}
296
}
297
}
298
299
/* Mapping of logical record number to actual file content. */
300
#define RECORD_FILE_MAGIC 0
301
#define RECORD_GCOV_VERSION 1
302
#define RECORD_TIME_STAMP 2
303
#define RECORD_FUNCTION_TAG 3
304
#define RECORD_FUNCTON_TAG_LEN 4
305
#define RECORD_FUNCTION_IDENT 5
306
#define RECORD_FUNCTION_CHECK 6
307
#define RECORD_COUNT_TAG 7
308
#define RECORD_COUNT_LEN 8
309
#define RECORD_COUNT 9
310
311
/**
312
* gcov_iter_next - advance file iterator to next logical record
313
* @iter: file iterator
314
*
315
* Return zero if new position is valid, non-zero if iterator has reached end.
316
*/
317
int gcov_iter_next(struct gcov_iterator *iter)
318
{
319
switch (iter->record) {
320
case RECORD_FILE_MAGIC:
321
case RECORD_GCOV_VERSION:
322
case RECORD_FUNCTION_TAG:
323
case RECORD_FUNCTON_TAG_LEN:
324
case RECORD_FUNCTION_IDENT:
325
case RECORD_COUNT_TAG:
326
/* Advance to next record */
327
iter->record++;
328
break;
329
case RECORD_COUNT:
330
/* Advance to next count */
331
iter->count++;
332
/* fall through */
333
case RECORD_COUNT_LEN:
334
if (iter->count < get_func(iter)->n_ctrs[iter->type]) {
335
iter->record = 9;
336
break;
337
}
338
/* Advance to next counter type */
339
get_type(iter)->offset += iter->count;
340
iter->count = 0;
341
iter->type++;
342
/* fall through */
343
case RECORD_FUNCTION_CHECK:
344
if (iter->type < iter->num_types) {
345
iter->record = 7;
346
break;
347
}
348
/* Advance to next function */
349
iter->type = 0;
350
iter->function++;
351
/* fall through */
352
case RECORD_TIME_STAMP:
353
if (iter->function < iter->info->n_functions)
354
iter->record = 3;
355
else
356
iter->record = -1;
357
break;
358
}
359
/* Check for EOF. */
360
if (iter->record == -1)
361
return -EINVAL;
362
else
363
return 0;
364
}
365
366
/**
367
* seq_write_gcov_u32 - write 32 bit number in gcov format to seq_file
368
* @seq: seq_file handle
369
* @v: value to be stored
370
*
371
* Number format defined by gcc: numbers are recorded in the 32 bit
372
* unsigned binary form of the endianness of the machine generating the
373
* file.
374
*/
375
static int seq_write_gcov_u32(struct seq_file *seq, u32 v)
376
{
377
return seq_write(seq, &v, sizeof(v));
378
}
379
380
/**
381
* seq_write_gcov_u64 - write 64 bit number in gcov format to seq_file
382
* @seq: seq_file handle
383
* @v: value to be stored
384
*
385
* Number format defined by gcc: numbers are recorded in the 32 bit
386
* unsigned binary form of the endianness of the machine generating the
387
* file. 64 bit numbers are stored as two 32 bit numbers, the low part
388
* first.
389
*/
390
static int seq_write_gcov_u64(struct seq_file *seq, u64 v)
391
{
392
u32 data[2];
393
394
data[0] = (v & 0xffffffffUL);
395
data[1] = (v >> 32);
396
return seq_write(seq, data, sizeof(data));
397
}
398
399
/**
400
* gcov_iter_write - write data for current pos to seq_file
401
* @iter: file iterator
402
* @seq: seq_file handle
403
*
404
* Return zero on success, non-zero otherwise.
405
*/
406
int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
407
{
408
int rc = -EINVAL;
409
410
switch (iter->record) {
411
case RECORD_FILE_MAGIC:
412
rc = seq_write_gcov_u32(seq, GCOV_DATA_MAGIC);
413
break;
414
case RECORD_GCOV_VERSION:
415
rc = seq_write_gcov_u32(seq, iter->info->version);
416
break;
417
case RECORD_TIME_STAMP:
418
rc = seq_write_gcov_u32(seq, iter->info->stamp);
419
break;
420
case RECORD_FUNCTION_TAG:
421
rc = seq_write_gcov_u32(seq, GCOV_TAG_FUNCTION);
422
break;
423
case RECORD_FUNCTON_TAG_LEN:
424
rc = seq_write_gcov_u32(seq, 2);
425
break;
426
case RECORD_FUNCTION_IDENT:
427
rc = seq_write_gcov_u32(seq, get_func(iter)->ident);
428
break;
429
case RECORD_FUNCTION_CHECK:
430
rc = seq_write_gcov_u32(seq, get_func(iter)->checksum);
431
break;
432
case RECORD_COUNT_TAG:
433
rc = seq_write_gcov_u32(seq,
434
GCOV_TAG_FOR_COUNTER(get_type(iter)->ctr_type));
435
break;
436
case RECORD_COUNT_LEN:
437
rc = seq_write_gcov_u32(seq,
438
get_func(iter)->n_ctrs[iter->type] * 2);
439
break;
440
case RECORD_COUNT:
441
rc = seq_write_gcov_u64(seq,
442
iter->info->counts[iter->type].
443
values[iter->count + get_type(iter)->offset]);
444
break;
445
}
446
return rc;
447
}
448
449