Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/gnu/gcov/gcc_4_7.c
39562 views
1
// SPDX-License-Identifier: GPL-2.0
2
// This program is free software; you can redistribute it and/or
3
// modify it under the terms of the GNU General Public License
4
// as published by the Free Software Foundation; version 2.
5
//
6
// This program is distributed in the hope that it will be useful,
7
// but WITHOUT ANY WARRANTY; without even the implied warranty of
8
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
// GNU General Public License for more details.
10
//
11
// You should have received a copy of the GNU General Public License
12
// along with this program; if not, write to the Free Software
13
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
14
// 02110-1301, USA.
15
/*
16
* This code provides functions to handle gcc's profiling data format
17
* introduced with gcc 4.7.
18
*
19
* This file is based heavily on gcc_3_4.c file.
20
*
21
* For a better understanding, refer to gcc source:
22
* gcc/gcov-io.h
23
* libgcc/libgcov.c
24
*
25
* Uses gcc-internal data definitions.
26
*/
27
28
#include <sys/param.h>
29
#include <sys/systm.h>
30
#include <sys/types.h>
31
#include <sys/systm.h>
32
#include <sys/sbuf.h>
33
#include <sys/malloc.h>
34
#include <sys/module.h>
35
#include <gnu/gcov/gcov.h>
36
37
38
#if (__GNUC__ >= 7)
39
#define GCOV_COUNTERS 9
40
#elif (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
41
#define GCOV_COUNTERS 10
42
#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9
43
#define GCOV_COUNTERS 9
44
#else
45
#define GCOV_COUNTERS 8
46
#endif
47
48
#define GCOV_TAG_FUNCTION_LENGTH 3
49
50
static struct gcov_info *gcov_info_head;
51
52
/**
53
* struct gcov_ctr_info - information about counters for a single function
54
* @num: number of counter values for this type
55
* @values: array of counter values for this type
56
*
57
* This data is generated by gcc during compilation and doesn't change
58
* at run-time with the exception of the values array.
59
*/
60
struct gcov_ctr_info {
61
unsigned int num;
62
gcov_type *values;
63
};
64
65
/**
66
* struct gcov_fn_info - profiling meta data per function
67
* @key: comdat key
68
* @ident: unique ident of function
69
* @lineno_checksum: function lineo_checksum
70
* @cfg_checksum: function cfg checksum
71
* @ctrs: instrumented counters
72
*
73
* This data is generated by gcc during compilation and doesn't change
74
* at run-time.
75
*
76
* Information about a single function. This uses the trailing array
77
* idiom. The number of counters is determined from the merge pointer
78
* array in gcov_info. The key is used to detect which of a set of
79
* comdat functions was selected -- it points to the gcov_info object
80
* of the object file containing the selected comdat function.
81
*/
82
struct gcov_fn_info {
83
const struct gcov_info *key;
84
unsigned int ident;
85
unsigned int lineno_checksum;
86
unsigned int cfg_checksum;
87
struct gcov_ctr_info ctrs[0];
88
};
89
90
/**
91
* struct gcov_info - profiling data per object file
92
* @version: gcov version magic indicating the gcc version used for compilation
93
* @next: list head for a singly-linked list
94
* @stamp: uniquifying time stamp
95
* @filename: name of the associated gcov data file
96
* @merge: merge functions (null for unused counter type)
97
* @n_functions: number of instrumented functions
98
* @functions: pointer to pointers to function information
99
*
100
* This data is generated by gcc during compilation and doesn't change
101
* at run-time with the exception of the next pointer.
102
*/
103
struct gcov_info {
104
unsigned int version;
105
struct gcov_info *next;
106
unsigned int stamp;
107
const char *filename;
108
void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
109
unsigned int n_functions;
110
struct gcov_fn_info **functions;
111
};
112
113
/**
114
* gcov_info_filename - return info filename
115
* @info: profiling data set
116
*/
117
const char *
118
gcov_info_filename(struct gcov_info *info)
119
{
120
return (info->filename);
121
}
122
123
/**
124
* gcov_info_version - return info version
125
* @info: profiling data set
126
*/
127
unsigned int
128
gcov_info_version(struct gcov_info *info)
129
{
130
return (info->version);
131
}
132
133
/**
134
* gcov_info_next - return next profiling data set
135
* @info: profiling data set
136
*
137
* Returns next gcov_info following @info or first gcov_info in the chain if
138
* @info is %NULL.
139
*/
140
struct gcov_info *
141
gcov_info_next(struct gcov_info *info)
142
{
143
if (!info)
144
return gcov_info_head;
145
146
return (info->next);
147
}
148
149
/**
150
* gcov_info_link - link/add profiling data set to the list
151
* @info: profiling data set
152
*/
153
void
154
gcov_info_link(struct gcov_info *info)
155
{
156
info->next = gcov_info_head;
157
gcov_info_head = info;
158
}
159
160
/**
161
* gcov_info_unlink - unlink/remove profiling data set from the list
162
* @prev: previous profiling data set
163
* @info: profiling data set
164
*/
165
void
166
gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
167
{
168
if (prev)
169
prev->next = info->next;
170
else
171
gcov_info_head = info->next;
172
}
173
174
/* Symbolic links to be created for each profiling data file. */
175
const struct gcov_link gcov_link[] = {
176
{ OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
177
{ 0, NULL},
178
};
179
180
/*
181
* Determine whether a counter is active. Doesn't change at run-time.
182
*/
183
static int
184
counter_active(struct gcov_info *info, unsigned int type)
185
{
186
return (info->merge[type] ? 1 : 0);
187
}
188
189
/* Determine number of active counters. Based on gcc magic. */
190
static unsigned int
191
num_counter_active(struct gcov_info *info)
192
{
193
unsigned int i;
194
unsigned int result = 0;
195
196
for (i = 0; i < GCOV_COUNTERS; i++) {
197
if (counter_active(info, i))
198
result++;
199
}
200
return (result);
201
}
202
203
/**
204
* gcov_info_reset - reset profiling data to zero
205
* @info: profiling data set
206
*/
207
void
208
gcov_info_reset(struct gcov_info *info)
209
{
210
struct gcov_ctr_info *ci_ptr;
211
unsigned int fi_idx;
212
unsigned int ct_idx;
213
214
for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
215
ci_ptr = info->functions[fi_idx]->ctrs;
216
217
for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
218
if (!counter_active(info, ct_idx))
219
continue;
220
221
memset(ci_ptr->values, 0,
222
sizeof(gcov_type) * ci_ptr->num);
223
ci_ptr++;
224
}
225
}
226
}
227
228
/**
229
* gcov_info_is_compatible - check if profiling data can be added
230
* @info1: first profiling data set
231
* @info2: second profiling data set
232
*
233
* Returns non-zero if profiling data can be added, zero otherwise.
234
*/
235
int
236
gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
237
{
238
return (info1->stamp == info2->stamp);
239
}
240
241
/**
242
* gcov_info_add - add up profiling data
243
* @dest: profiling data set to which data is added
244
* @source: profiling data set which is added
245
*
246
* Adds profiling counts of @source to @dest.
247
*/
248
void
249
gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
250
{
251
struct gcov_ctr_info *dci_ptr;
252
struct gcov_ctr_info *sci_ptr;
253
unsigned int fi_idx;
254
unsigned int ct_idx;
255
unsigned int val_idx;
256
257
for (fi_idx = 0; fi_idx < src->n_functions; fi_idx++) {
258
dci_ptr = dst->functions[fi_idx]->ctrs;
259
sci_ptr = src->functions[fi_idx]->ctrs;
260
261
for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
262
if (!counter_active(src, ct_idx))
263
continue;
264
265
for (val_idx = 0; val_idx < sci_ptr->num; val_idx++)
266
dci_ptr->values[val_idx] +=
267
sci_ptr->values[val_idx];
268
269
dci_ptr++;
270
sci_ptr++;
271
}
272
}
273
}
274
275
/**
276
* gcov_info_dup - duplicate profiling data set
277
* @info: profiling data set to duplicate
278
*
279
* Return newly allocated duplicate on success, %NULL on error.
280
*/
281
struct gcov_info *
282
gcov_info_dup(struct gcov_info *info)
283
{
284
struct gcov_info *dup;
285
struct gcov_ctr_info *dci_ptr; /* dst counter info */
286
struct gcov_ctr_info *sci_ptr; /* src counter info */
287
unsigned int active;
288
unsigned int fi_idx; /* function info idx */
289
unsigned int ct_idx; /* counter type idx */
290
size_t fi_size; /* function info size */
291
size_t cv_size; /* counter values size */
292
293
if ((dup = malloc(sizeof(*dup), M_GCOV, M_NOWAIT|M_ZERO)) == NULL)
294
return (NULL);
295
memcpy(dup, info, sizeof(*dup));
296
297
dup->next = NULL;
298
dup->filename = NULL;
299
dup->functions = NULL;
300
301
dup->filename = strdup_flags(info->filename, M_GCOV, M_NOWAIT);
302
if (dup->filename == NULL)
303
goto err_free;
304
305
dup->functions = malloc(info->n_functions * sizeof(struct gcov_fn_info *), M_GCOV, M_NOWAIT|M_ZERO);
306
if (dup->functions == NULL)
307
goto err_free;
308
active = num_counter_active(info);
309
fi_size = sizeof(struct gcov_fn_info);
310
fi_size += sizeof(struct gcov_ctr_info) * active;
311
312
for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
313
dup->functions[fi_idx] = malloc(fi_size, M_GCOV, M_NOWAIT|M_ZERO);
314
if (!dup->functions[fi_idx])
315
goto err_free;
316
317
*(dup->functions[fi_idx]) = *(info->functions[fi_idx]);
318
319
sci_ptr = info->functions[fi_idx]->ctrs;
320
dci_ptr = dup->functions[fi_idx]->ctrs;
321
322
for (ct_idx = 0; ct_idx < active; ct_idx++) {
323
324
cv_size = sizeof(gcov_type) * sci_ptr->num;
325
326
dci_ptr->values = malloc(cv_size, M_GCOV, M_NOWAIT);
327
328
if (!dci_ptr->values)
329
goto err_free;
330
331
dci_ptr->num = sci_ptr->num;
332
memcpy(dci_ptr->values, sci_ptr->values, cv_size);
333
334
sci_ptr++;
335
dci_ptr++;
336
}
337
}
338
339
return (dup);
340
err_free:
341
gcov_info_free(dup);
342
return (NULL);
343
}
344
345
/**
346
* gcov_info_free - release memory for profiling data set duplicate
347
* @info: profiling data set duplicate to free
348
*/
349
void
350
gcov_info_free(struct gcov_info *info)
351
{
352
unsigned int active;
353
unsigned int fi_idx;
354
unsigned int ct_idx;
355
struct gcov_ctr_info *ci_ptr;
356
357
if (!info->functions)
358
goto free_info;
359
360
active = num_counter_active(info);
361
362
for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
363
if (!info->functions[fi_idx])
364
continue;
365
366
ci_ptr = info->functions[fi_idx]->ctrs;
367
368
for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++)
369
free(ci_ptr->values, M_GCOV);
370
371
free(info->functions[fi_idx], M_GCOV);
372
}
373
374
free_info:
375
free(info->functions, M_GCOV);
376
free(__DECONST(char *, info->filename), M_GCOV);
377
free(info, M_GCOV);
378
}
379
380
#define ITER_STRIDE PAGE_SIZE
381
382
/**
383
* struct gcov_iterator - specifies current file position in logical records
384
* @info: associated profiling data
385
* @buffer: buffer containing file data
386
* @size: size of buffer
387
* @pos: current position in file
388
*/
389
struct gcov_iterator {
390
struct gcov_info *info;
391
caddr_t buffer;
392
size_t size;
393
off_t pos;
394
};
395
396
/**
397
* store_gcov_uint32 - store 32 bit number in gcov format to buffer
398
* @buffer: target buffer or NULL
399
* @off: offset into the buffer
400
* @v: value to be stored
401
*
402
* Number format defined by gcc: numbers are recorded in the 32 bit
403
* unsigned binary form of the endianness of the machine generating the
404
* file. Returns the number of bytes stored. If @buffer is %NULL, doesn't
405
* store anything.
406
*/
407
static size_t
408
store_gcov_uint32(void *buffer, size_t off, uint32_t v)
409
{
410
uint32_t *data;
411
412
if (buffer) {
413
data = (void*)((caddr_t)buffer + off);
414
*data = v;
415
}
416
417
return sizeof(*data);
418
}
419
420
/**
421
* store_gcov_uint64 - store 64 bit number in gcov format to buffer
422
* @buffer: target buffer or NULL
423
* @off: offset into the buffer
424
* @v: value to be stored
425
*
426
* Number format defined by gcc: numbers are recorded in the 32 bit
427
* unsigned binary form of the endianness of the machine generating the
428
* file. 64 bit numbers are stored as two 32 bit numbers, the low part
429
* first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store
430
* anything.
431
*/
432
433
static size_t
434
store_gcov_uint64(void *buffer, size_t off, uint64_t v)
435
{
436
uint32_t *data;
437
438
if (buffer) {
439
data = (void*)((caddr_t)buffer + off);
440
441
data[0] = (v & 0xffffffffUL);
442
data[1] = (v >> 32);
443
}
444
445
return sizeof(*data) * 2;
446
}
447
448
/**
449
* convert_to_gcda - convert profiling data set to gcda file format
450
* @buffer: the buffer to store file data or %NULL if no data should be stored
451
* @info: profiling data set to be converted
452
*
453
* Returns the number of bytes that were/would have been stored into the buffer.
454
*/
455
static size_t
456
convert_to_gcda(char *buffer, struct gcov_info *info)
457
{
458
struct gcov_fn_info *fi_ptr;
459
struct gcov_ctr_info *ci_ptr;
460
unsigned int fi_idx;
461
unsigned int ct_idx;
462
unsigned int cv_idx;
463
size_t pos = 0;
464
465
/* File header. */
466
pos += store_gcov_uint32(buffer, pos, GCOV_DATA_MAGIC);
467
pos += store_gcov_uint32(buffer, pos, info->version);
468
pos += store_gcov_uint32(buffer, pos, info->stamp);
469
470
for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
471
fi_ptr = info->functions[fi_idx];
472
473
/* Function record. */
474
pos += store_gcov_uint32(buffer, pos, GCOV_TAG_FUNCTION);
475
pos += store_gcov_uint32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH);
476
pos += store_gcov_uint32(buffer, pos, fi_ptr->ident);
477
pos += store_gcov_uint32(buffer, pos, fi_ptr->lineno_checksum);
478
pos += store_gcov_uint32(buffer, pos, fi_ptr->cfg_checksum);
479
480
ci_ptr = fi_ptr->ctrs;
481
482
for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
483
if (!counter_active(info, ct_idx))
484
continue;
485
486
/* Counter record. */
487
pos += store_gcov_uint32(buffer, pos,
488
GCOV_TAG_FOR_COUNTER(ct_idx));
489
pos += store_gcov_uint32(buffer, pos, ci_ptr->num * 2);
490
491
for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
492
pos += store_gcov_uint64(buffer, pos,
493
ci_ptr->values[cv_idx]);
494
}
495
496
ci_ptr++;
497
}
498
}
499
500
return (pos);
501
}
502
503
/**
504
* gcov_iter_new - allocate and initialize profiling data iterator
505
* @info: profiling data set to be iterated
506
*
507
* Return file iterator on success, %NULL otherwise.
508
*/
509
struct gcov_iterator *
510
gcov_iter_new(struct gcov_info *info)
511
{
512
struct gcov_iterator *iter;
513
514
iter = malloc(sizeof(struct gcov_iterator), M_GCOV, M_NOWAIT|M_ZERO);
515
if (iter == NULL)
516
goto err_free;
517
518
iter->info = info;
519
/* Dry-run to get the actual buffer size. */
520
iter->size = convert_to_gcda(NULL, info);
521
iter->buffer = malloc(iter->size, M_GCOV, M_NOWAIT);
522
if (!iter->buffer)
523
goto err_free;
524
525
convert_to_gcda(iter->buffer, info);
526
527
return iter;
528
529
err_free:
530
free(iter, M_GCOV);
531
return (NULL);
532
}
533
534
535
/**
536
* gcov_iter_get_info - return profiling data set for given file iterator
537
* @iter: file iterator
538
*/
539
void
540
gcov_iter_free(struct gcov_iterator *iter)
541
{
542
free(iter->buffer, M_GCOV);
543
free(iter, M_GCOV);
544
}
545
546
/**
547
* gcov_iter_get_info - return profiling data set for given file iterator
548
* @iter: file iterator
549
*/
550
struct gcov_info *
551
gcov_iter_get_info(struct gcov_iterator *iter)
552
{
553
return (iter->info);
554
}
555
556
/**
557
* gcov_iter_start - reset file iterator to starting position
558
* @iter: file iterator
559
*/
560
void
561
gcov_iter_start(struct gcov_iterator *iter)
562
{
563
iter->pos = 0;
564
}
565
566
/**
567
* gcov_iter_next - advance file iterator to next logical record
568
* @iter: file iterator
569
*
570
* Return zero if new position is valid, non-zero if iterator has reached end.
571
*/
572
int
573
gcov_iter_next(struct gcov_iterator *iter)
574
{
575
if (iter->pos < iter->size)
576
iter->pos += ITER_STRIDE;
577
578
if (iter->pos >= iter->size)
579
return (EINVAL);
580
581
return 0;
582
}
583
584
/**
585
* gcov_iter_write - write data for current pos to seq_file
586
* @iter: file iterator
587
* @seq: seq_file handle
588
*
589
* Return zero on success, non-zero otherwise.
590
*/
591
int
592
gcov_iter_write(struct gcov_iterator *iter, struct sbuf *sbuf)
593
{
594
size_t len;
595
596
if (iter->pos >= iter->size)
597
return (EINVAL);
598
599
len = ITER_STRIDE;
600
if (iter->pos + len > iter->size)
601
len = iter->size - iter->pos;
602
603
sbuf_bcat(sbuf, iter->buffer + iter->pos, len);
604
605
return (0);
606
}
607
608