1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/errno.h>
16#include <linux/slab.h>
17#include <linux/string.h>
18#include <linux/seq_file.h>
19#include <linux/vmalloc.h>
20#include "gcov.h"
21
22#if (__GNUC__ >= 7)
23#define GCOV_COUNTERS 9
24#elif (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
25#define GCOV_COUNTERS 10
26#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9
27#define GCOV_COUNTERS 9
28#else
29#define GCOV_COUNTERS 8
30#endif
31
32#define GCOV_TAG_FUNCTION_LENGTH 3
33
34static struct gcov_info *gcov_info_head;
35
36
37
38
39
40
41
42
43
44struct gcov_ctr_info {
45 unsigned int num;
46 gcov_type *values;
47};
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66struct gcov_fn_info {
67 const struct gcov_info *key;
68 unsigned int ident;
69 unsigned int lineno_checksum;
70 unsigned int cfg_checksum;
71 struct gcov_ctr_info ctrs[0];
72};
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87struct gcov_info {
88 unsigned int version;
89 struct gcov_info *next;
90 unsigned int stamp;
91 const char *filename;
92 void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
93 unsigned int n_functions;
94 struct gcov_fn_info **functions;
95};
96
97
98
99
100
101const char *gcov_info_filename(struct gcov_info *info)
102{
103 return info->filename;
104}
105
106
107
108
109
110unsigned int gcov_info_version(struct gcov_info *info)
111{
112 return info->version;
113}
114
115
116
117
118
119
120
121
122struct gcov_info *gcov_info_next(struct gcov_info *info)
123{
124 if (!info)
125 return gcov_info_head;
126
127 return info->next;
128}
129
130
131
132
133
134void gcov_info_link(struct gcov_info *info)
135{
136 info->next = gcov_info_head;
137 gcov_info_head = info;
138}
139
140
141
142
143
144
145void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
146{
147 if (prev)
148 prev->next = info->next;
149 else
150 gcov_info_head = info->next;
151}
152
153
154
155
156
157
158
159
160bool gcov_info_within_module(struct gcov_info *info, struct module *mod)
161{
162 return within_module((unsigned long)info, mod);
163}
164
165
166const struct gcov_link gcov_link[] = {
167 { OBJ_TREE, "gcno" },
168 { 0, NULL},
169};
170
171
172
173
174static int counter_active(struct gcov_info *info, unsigned int type)
175{
176 return info->merge[type] ? 1 : 0;
177}
178
179
180static unsigned int num_counter_active(struct gcov_info *info)
181{
182 unsigned int i;
183 unsigned int result = 0;
184
185 for (i = 0; i < GCOV_COUNTERS; i++) {
186 if (counter_active(info, i))
187 result++;
188 }
189 return result;
190}
191
192
193
194
195
196void gcov_info_reset(struct gcov_info *info)
197{
198 struct gcov_ctr_info *ci_ptr;
199 unsigned int fi_idx;
200 unsigned int ct_idx;
201
202 for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
203 ci_ptr = info->functions[fi_idx]->ctrs;
204
205 for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
206 if (!counter_active(info, ct_idx))
207 continue;
208
209 memset(ci_ptr->values, 0,
210 sizeof(gcov_type) * ci_ptr->num);
211 ci_ptr++;
212 }
213 }
214}
215
216
217
218
219
220
221
222
223int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
224{
225 return (info1->stamp == info2->stamp);
226}
227
228
229
230
231
232
233
234
235void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
236{
237 struct gcov_ctr_info *dci_ptr;
238 struct gcov_ctr_info *sci_ptr;
239 unsigned int fi_idx;
240 unsigned int ct_idx;
241 unsigned int val_idx;
242
243 for (fi_idx = 0; fi_idx < src->n_functions; fi_idx++) {
244 dci_ptr = dst->functions[fi_idx]->ctrs;
245 sci_ptr = src->functions[fi_idx]->ctrs;
246
247 for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
248 if (!counter_active(src, ct_idx))
249 continue;
250
251 for (val_idx = 0; val_idx < sci_ptr->num; val_idx++)
252 dci_ptr->values[val_idx] +=
253 sci_ptr->values[val_idx];
254
255 dci_ptr++;
256 sci_ptr++;
257 }
258 }
259}
260
261
262
263
264
265
266
267struct gcov_info *gcov_info_dup(struct gcov_info *info)
268{
269 struct gcov_info *dup;
270 struct gcov_ctr_info *dci_ptr;
271 struct gcov_ctr_info *sci_ptr;
272 unsigned int active;
273 unsigned int fi_idx;
274 unsigned int ct_idx;
275 size_t fi_size;
276 size_t cv_size;
277
278 dup = kmemdup(info, sizeof(*dup), GFP_KERNEL);
279 if (!dup)
280 return NULL;
281
282 dup->next = NULL;
283 dup->filename = NULL;
284 dup->functions = NULL;
285
286 dup->filename = kstrdup(info->filename, GFP_KERNEL);
287 if (!dup->filename)
288 goto err_free;
289
290 dup->functions = kcalloc(info->n_functions,
291 sizeof(struct gcov_fn_info *), GFP_KERNEL);
292 if (!dup->functions)
293 goto err_free;
294
295 active = num_counter_active(info);
296 fi_size = sizeof(struct gcov_fn_info);
297 fi_size += sizeof(struct gcov_ctr_info) * active;
298
299 for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
300 dup->functions[fi_idx] = kzalloc(fi_size, GFP_KERNEL);
301 if (!dup->functions[fi_idx])
302 goto err_free;
303
304 *(dup->functions[fi_idx]) = *(info->functions[fi_idx]);
305
306 sci_ptr = info->functions[fi_idx]->ctrs;
307 dci_ptr = dup->functions[fi_idx]->ctrs;
308
309 for (ct_idx = 0; ct_idx < active; ct_idx++) {
310
311 cv_size = sizeof(gcov_type) * sci_ptr->num;
312
313 dci_ptr->values = vmalloc(cv_size);
314
315 if (!dci_ptr->values)
316 goto err_free;
317
318 dci_ptr->num = sci_ptr->num;
319 memcpy(dci_ptr->values, sci_ptr->values, cv_size);
320
321 sci_ptr++;
322 dci_ptr++;
323 }
324 }
325
326 return dup;
327err_free:
328 gcov_info_free(dup);
329 return NULL;
330}
331
332
333
334
335
336void gcov_info_free(struct gcov_info *info)
337{
338 unsigned int active;
339 unsigned int fi_idx;
340 unsigned int ct_idx;
341 struct gcov_ctr_info *ci_ptr;
342
343 if (!info->functions)
344 goto free_info;
345
346 active = num_counter_active(info);
347
348 for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
349 if (!info->functions[fi_idx])
350 continue;
351
352 ci_ptr = info->functions[fi_idx]->ctrs;
353
354 for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++)
355 vfree(ci_ptr->values);
356
357 kfree(info->functions[fi_idx]);
358 }
359
360free_info:
361 kfree(info->functions);
362 kfree(info->filename);
363 kfree(info);
364}
365
366#define ITER_STRIDE PAGE_SIZE
367
368
369
370
371
372
373
374
375struct gcov_iterator {
376 struct gcov_info *info;
377 void *buffer;
378 size_t size;
379 loff_t pos;
380};
381
382
383
384
385
386
387
388
389
390
391
392
393static size_t store_gcov_u32(void *buffer, size_t off, u32 v)
394{
395 u32 *data;
396
397 if (buffer) {
398 data = buffer + off;
399 *data = v;
400 }
401
402 return sizeof(*data);
403}
404
405
406
407
408
409
410
411
412
413
414
415
416
417static size_t store_gcov_u64(void *buffer, size_t off, u64 v)
418{
419 u32 *data;
420
421 if (buffer) {
422 data = buffer + off;
423
424 data[0] = (v & 0xffffffffUL);
425 data[1] = (v >> 32);
426 }
427
428 return sizeof(*data) * 2;
429}
430
431
432
433
434
435
436
437
438static size_t convert_to_gcda(char *buffer, struct gcov_info *info)
439{
440 struct gcov_fn_info *fi_ptr;
441 struct gcov_ctr_info *ci_ptr;
442 unsigned int fi_idx;
443 unsigned int ct_idx;
444 unsigned int cv_idx;
445 size_t pos = 0;
446
447
448 pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
449 pos += store_gcov_u32(buffer, pos, info->version);
450 pos += store_gcov_u32(buffer, pos, info->stamp);
451
452 for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
453 fi_ptr = info->functions[fi_idx];
454
455
456 pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
457 pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH);
458 pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
459 pos += store_gcov_u32(buffer, pos, fi_ptr->lineno_checksum);
460 pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum);
461
462 ci_ptr = fi_ptr->ctrs;
463
464 for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
465 if (!counter_active(info, ct_idx))
466 continue;
467
468
469 pos += store_gcov_u32(buffer, pos,
470 GCOV_TAG_FOR_COUNTER(ct_idx));
471 pos += store_gcov_u32(buffer, pos, ci_ptr->num * 2);
472
473 for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
474 pos += store_gcov_u64(buffer, pos,
475 ci_ptr->values[cv_idx]);
476 }
477
478 ci_ptr++;
479 }
480 }
481
482 return pos;
483}
484
485
486
487
488
489
490
491struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
492{
493 struct gcov_iterator *iter;
494
495 iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL);
496 if (!iter)
497 goto err_free;
498
499 iter->info = info;
500
501 iter->size = convert_to_gcda(NULL, info);
502 iter->buffer = vmalloc(iter->size);
503 if (!iter->buffer)
504 goto err_free;
505
506 convert_to_gcda(iter->buffer, info);
507
508 return iter;
509
510err_free:
511 kfree(iter);
512 return NULL;
513}
514
515
516
517
518
519
520void gcov_iter_free(struct gcov_iterator *iter)
521{
522 vfree(iter->buffer);
523 kfree(iter);
524}
525
526
527
528
529
530struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
531{
532 return iter->info;
533}
534
535
536
537
538
539void gcov_iter_start(struct gcov_iterator *iter)
540{
541 iter->pos = 0;
542}
543
544
545
546
547
548
549
550int gcov_iter_next(struct gcov_iterator *iter)
551{
552 if (iter->pos < iter->size)
553 iter->pos += ITER_STRIDE;
554
555 if (iter->pos >= iter->size)
556 return -EINVAL;
557
558 return 0;
559}
560
561
562
563
564
565
566
567
568int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
569{
570 size_t len;
571
572 if (iter->pos >= iter->size)
573 return -EINVAL;
574
575 len = ITER_STRIDE;
576 if (iter->pos + len > iter->size)
577 len = iter->size - iter->pos;
578
579 seq_write(seq, iter->buffer + iter->pos, len);
580
581 return 0;
582}
583