aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/gcov/clang.c
diff options
context:
space:
mode:
authorGreg Hackmann <ghackmann@android.com>2019-05-14 18:45:31 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-05-14 22:52:51 -0400
commite178a5beb36960902379040ee0b667fb0a8eee93 (patch)
tree03b2302179b767410a313fdf9003e9e5dce702e8 /kernel/gcov/clang.c
parentaa069a23a220e9ab00d6837b76917952d0fb587e (diff)
gcov: clang support
LLVM uses profiling data that's deliberately similar to GCC, but has a very different way of exporting that data. LLVM calls llvm_gcov_init() once per module, and provides a couple of callbacks that we can use to ask for more data. We care about the "writeout" callback, which in turn calls back into compiler-rt/this module to dump all the gathered coverage data to disk: llvm_gcda_start_file() llvm_gcda_emit_function() llvm_gcda_emit_arcs() llvm_gcda_emit_function() llvm_gcda_emit_arcs() [... repeats for each function ...] llvm_gcda_summary_info() llvm_gcda_end_file() This design is much more stateless and unstructured than gcc's, and is intended to run at process exit. This forces us to keep some local state about which module we're dealing with at the moment. On the other hand, it also means we don't depend as much on how LLVM represents profiling data internally. See LLVM's lib/Transforms/Instrumentation/GCOVProfiling.cpp for more details on how this works, particularly GCOVProfiler::emitProfileArcs(), GCOVProfiler::insertCounterWriteout(), and GCOVProfiler::insertFlush(). [akpm@linux-foundation.org: coding-style fixes] Link: http://lkml.kernel.org/r/20190417225328.208129-1-trong@android.com Signed-off-by: Greg Hackmann <ghackmann@android.com> Signed-off-by: Nick Desaulniers <ndesaulniers@google.com> Signed-off-by: Tri Vo <trong@android.com> Co-developed-by: Nick Desaulniers <ndesaulniers@google.com> Co-developed-by: Tri Vo <trong@android.com> Tested-by: Trilok Soni <tsoni@quicinc.com> Tested-by: Prasad Sodagudi <psodagud@quicinc.com> Tested-by: Tri Vo <trong@android.com> Tested-by: Daniel Mentz <danielmentz@google.com> Tested-by: Petri Gynther <pgynther@google.com> Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/gcov/clang.c')
-rw-r--r--kernel/gcov/clang.c581
1 files changed, 581 insertions, 0 deletions
diff --git a/kernel/gcov/clang.c b/kernel/gcov/clang.c
new file mode 100644
index 000000000000..c94b820a1b62
--- /dev/null
+++ b/kernel/gcov/clang.c
@@ -0,0 +1,581 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2019 Google, Inc.
4 * modified from kernel/gcov/gcc_4_7.c
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 *
16 * LLVM uses profiling data that's deliberately similar to GCC, but has a
17 * very different way of exporting that data. LLVM calls llvm_gcov_init() once
18 * per module, and provides a couple of callbacks that we can use to ask for
19 * more data.
20 *
21 * We care about the "writeout" callback, which in turn calls back into
22 * compiler-rt/this module to dump all the gathered coverage data to disk:
23 *
24 * llvm_gcda_start_file()
25 * llvm_gcda_emit_function()
26 * llvm_gcda_emit_arcs()
27 * llvm_gcda_emit_function()
28 * llvm_gcda_emit_arcs()
29 * [... repeats for each function ...]
30 * llvm_gcda_summary_info()
31 * llvm_gcda_end_file()
32 *
33 * This design is much more stateless and unstructured than gcc's, and is
34 * intended to run at process exit. This forces us to keep some local state
35 * about which module we're dealing with at the moment. On the other hand, it
36 * also means we don't depend as much on how LLVM represents profiling data
37 * internally.
38 *
39 * See LLVM's lib/Transforms/Instrumentation/GCOVProfiling.cpp for more
40 * details on how this works, particularly GCOVProfiler::emitProfileArcs(),
41 * GCOVProfiler::insertCounterWriteout(), and
42 * GCOVProfiler::insertFlush().
43 */
44
45#define pr_fmt(fmt) "gcov: " fmt
46
47#include <linux/kernel.h>
48#include <linux/list.h>
49#include <linux/printk.h>
50#include <linux/ratelimit.h>
51#include <linux/seq_file.h>
52#include <linux/slab.h>
53#include <linux/vmalloc.h>
54#include "gcov.h"
55
56typedef void (*llvm_gcov_callback)(void);
57
58struct gcov_info {
59 struct list_head head;
60
61 const char *filename;
62 unsigned int version;
63 u32 checksum;
64
65 struct list_head functions;
66};
67
68struct gcov_fn_info {
69 struct list_head head;
70
71 u32 ident;
72 u32 checksum;
73 u8 use_extra_checksum;
74 u32 cfg_checksum;
75
76 u32 num_counters;
77 u64 *counters;
78 const char *function_name;
79};
80
81static struct gcov_info *current_info;
82
83static LIST_HEAD(clang_gcov_list);
84
85void llvm_gcov_init(llvm_gcov_callback writeout, llvm_gcov_callback flush)
86{
87 struct gcov_info *info = kzalloc(sizeof(*info), GFP_KERNEL);
88
89 if (!info)
90 return;
91
92 INIT_LIST_HEAD(&info->head);
93 INIT_LIST_HEAD(&info->functions);
94
95 mutex_lock(&gcov_lock);
96
97 list_add_tail(&info->head, &clang_gcov_list);
98 current_info = info;
99 writeout();
100 current_info = NULL;
101 if (gcov_events_enabled)
102 gcov_event(GCOV_ADD, info);
103
104 mutex_unlock(&gcov_lock);
105}
106EXPORT_SYMBOL(llvm_gcov_init);
107
108void llvm_gcda_start_file(const char *orig_filename, const char version[4],
109 u32 checksum)
110{
111 current_info->filename = orig_filename;
112 memcpy(&current_info->version, version, sizeof(current_info->version));
113 current_info->checksum = checksum;
114}
115EXPORT_SYMBOL(llvm_gcda_start_file);
116
117void llvm_gcda_emit_function(u32 ident, const char *function_name,
118 u32 func_checksum, u8 use_extra_checksum, u32 cfg_checksum)
119{
120 struct gcov_fn_info *info = kzalloc(sizeof(*info), GFP_KERNEL);
121
122 if (!info)
123 return;
124
125 INIT_LIST_HEAD(&info->head);
126 info->ident = ident;
127 info->checksum = func_checksum;
128 info->use_extra_checksum = use_extra_checksum;
129 info->cfg_checksum = cfg_checksum;
130 if (function_name)
131 info->function_name = kstrdup(function_name, GFP_KERNEL);
132
133 list_add_tail(&info->head, &current_info->functions);
134}
135EXPORT_SYMBOL(llvm_gcda_emit_function);
136
137void llvm_gcda_emit_arcs(u32 num_counters, u64 *counters)
138{
139 struct gcov_fn_info *info = list_last_entry(&current_info->functions,
140 struct gcov_fn_info, head);
141
142 info->num_counters = num_counters;
143 info->counters = counters;
144}
145EXPORT_SYMBOL(llvm_gcda_emit_arcs);
146
147void llvm_gcda_summary_info(void)
148{
149}
150EXPORT_SYMBOL(llvm_gcda_summary_info);
151
152void llvm_gcda_end_file(void)
153{
154}
155EXPORT_SYMBOL(llvm_gcda_end_file);
156
157/**
158 * gcov_info_filename - return info filename
159 * @info: profiling data set
160 */
161const char *gcov_info_filename(struct gcov_info *info)
162{
163 return info->filename;
164}
165
166/**
167 * gcov_info_version - return info version
168 * @info: profiling data set
169 */
170unsigned int gcov_info_version(struct gcov_info *info)
171{
172 return info->version;
173}
174
175/**
176 * gcov_info_next - return next profiling data set
177 * @info: profiling data set
178 *
179 * Returns next gcov_info following @info or first gcov_info in the chain if
180 * @info is %NULL.
181 */
182struct gcov_info *gcov_info_next(struct gcov_info *info)
183{
184 if (!info)
185 return list_first_entry_or_null(&clang_gcov_list,
186 struct gcov_info, head);
187 if (list_is_last(&info->head, &clang_gcov_list))
188 return NULL;
189 return list_next_entry(info, head);
190}
191
192/**
193 * gcov_info_link - link/add profiling data set to the list
194 * @info: profiling data set
195 */
196void gcov_info_link(struct gcov_info *info)
197{
198 list_add_tail(&info->head, &clang_gcov_list);
199}
200
201/**
202 * gcov_info_unlink - unlink/remove profiling data set from the list
203 * @prev: previous profiling data set
204 * @info: profiling data set
205 */
206void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
207{
208 /* Generic code unlinks while iterating. */
209 __list_del_entry(&info->head);
210}
211
212/**
213 * gcov_info_within_module - check if a profiling data set belongs to a module
214 * @info: profiling data set
215 * @mod: module
216 *
217 * Returns true if profiling data belongs module, false otherwise.
218 */
219bool gcov_info_within_module(struct gcov_info *info, struct module *mod)
220{
221 return within_module((unsigned long)info->filename, mod);
222}
223
224/* Symbolic links to be created for each profiling data file. */
225const struct gcov_link gcov_link[] = {
226 { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
227 { 0, NULL},
228};
229
230/**
231 * gcov_info_reset - reset profiling data to zero
232 * @info: profiling data set
233 */
234void gcov_info_reset(struct gcov_info *info)
235{
236 struct gcov_fn_info *fn;
237
238 list_for_each_entry(fn, &info->functions, head)
239 memset(fn->counters, 0,
240 sizeof(fn->counters[0]) * fn->num_counters);
241}
242
243/**
244 * gcov_info_is_compatible - check if profiling data can be added
245 * @info1: first profiling data set
246 * @info2: second profiling data set
247 *
248 * Returns non-zero if profiling data can be added, zero otherwise.
249 */
250int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
251{
252 struct gcov_fn_info *fn_ptr1 = list_first_entry_or_null(
253 &info1->functions, struct gcov_fn_info, head);
254 struct gcov_fn_info *fn_ptr2 = list_first_entry_or_null(
255 &info2->functions, struct gcov_fn_info, head);
256
257 if (info1->checksum != info2->checksum)
258 return false;
259 if (!fn_ptr1)
260 return fn_ptr1 == fn_ptr2;
261 while (!list_is_last(&fn_ptr1->head, &info1->functions) &&
262 !list_is_last(&fn_ptr2->head, &info2->functions)) {
263 if (fn_ptr1->checksum != fn_ptr2->checksum)
264 return false;
265 if (fn_ptr1->use_extra_checksum != fn_ptr2->use_extra_checksum)
266 return false;
267 if (fn_ptr1->use_extra_checksum &&
268 fn_ptr1->cfg_checksum != fn_ptr2->cfg_checksum)
269 return false;
270 fn_ptr1 = list_next_entry(fn_ptr1, head);
271 fn_ptr2 = list_next_entry(fn_ptr2, head);
272 }
273 return list_is_last(&fn_ptr1->head, &info1->functions) &&
274 list_is_last(&fn_ptr2->head, &info2->functions);
275}
276
277/**
278 * gcov_info_add - add up profiling data
279 * @dest: profiling data set to which data is added
280 * @source: profiling data set which is added
281 *
282 * Adds profiling counts of @source to @dest.
283 */
284void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
285{
286 struct gcov_fn_info *dfn_ptr;
287 struct gcov_fn_info *sfn_ptr = list_first_entry_or_null(&src->functions,
288 struct gcov_fn_info, head);
289
290 list_for_each_entry(dfn_ptr, &dst->functions, head) {
291 u32 i;
292
293 for (i = 0; i < sfn_ptr->num_counters; i++)
294 dfn_ptr->counters[i] += sfn_ptr->counters[i];
295 }
296}
297
298static struct gcov_fn_info *gcov_fn_info_dup(struct gcov_fn_info *fn)
299{
300 size_t cv_size; /* counter values size */
301 struct gcov_fn_info *fn_dup = kmemdup(fn, sizeof(*fn),
302 GFP_KERNEL);
303 if (!fn_dup)
304 return NULL;
305 INIT_LIST_HEAD(&fn_dup->head);
306
307 fn_dup->function_name = kstrdup(fn->function_name, GFP_KERNEL);
308 if (!fn_dup->function_name)
309 goto err_name;
310
311 cv_size = fn->num_counters * sizeof(fn->counters[0]);
312 fn_dup->counters = vmalloc(cv_size);
313 if (!fn_dup->counters)
314 goto err_counters;
315 memcpy(fn_dup->counters, fn->counters, cv_size);
316
317 return fn_dup;
318
319err_counters:
320 kfree(fn_dup->function_name);
321err_name:
322 kfree(fn_dup);
323 return NULL;
324}
325
326/**
327 * gcov_info_dup - duplicate profiling data set
328 * @info: profiling data set to duplicate
329 *
330 * Return newly allocated duplicate on success, %NULL on error.
331 */
332struct gcov_info *gcov_info_dup(struct gcov_info *info)
333{
334 struct gcov_info *dup;
335 struct gcov_fn_info *fn;
336
337 dup = kmemdup(info, sizeof(*dup), GFP_KERNEL);
338 if (!dup)
339 return NULL;
340 INIT_LIST_HEAD(&dup->head);
341 INIT_LIST_HEAD(&dup->functions);
342 dup->filename = kstrdup(info->filename, GFP_KERNEL);
343 if (!dup->filename)
344 goto err;
345
346 list_for_each_entry(fn, &info->functions, head) {
347 struct gcov_fn_info *fn_dup = gcov_fn_info_dup(fn);
348
349 if (!fn_dup)
350 goto err;
351 list_add_tail(&fn_dup->head, &dup->functions);
352 }
353
354 return dup;
355
356err:
357 gcov_info_free(dup);
358 return NULL;
359}
360
361/**
362 * gcov_info_free - release memory for profiling data set duplicate
363 * @info: profiling data set duplicate to free
364 */
365void gcov_info_free(struct gcov_info *info)
366{
367 struct gcov_fn_info *fn, *tmp;
368
369 list_for_each_entry_safe(fn, tmp, &info->functions, head) {
370 kfree(fn->function_name);
371 vfree(fn->counters);
372 list_del(&fn->head);
373 kfree(fn);
374 }
375 kfree(info->filename);
376 kfree(info);
377}
378
379#define ITER_STRIDE PAGE_SIZE
380
381/**
382 * struct gcov_iterator - specifies current file position in logical records
383 * @info: associated profiling data
384 * @buffer: buffer containing file data
385 * @size: size of buffer
386 * @pos: current position in file
387 */
388struct gcov_iterator {
389 struct gcov_info *info;
390 void *buffer;
391 size_t size;
392 loff_t pos;
393};
394
395/**
396 * store_gcov_u32 - store 32 bit number in gcov format to buffer
397 * @buffer: target buffer or NULL
398 * @off: offset into the buffer
399 * @v: value to be stored
400 *
401 * Number format defined by gcc: numbers are recorded in the 32 bit
402 * unsigned binary form of the endianness of the machine generating the
403 * file. Returns the number of bytes stored. If @buffer is %NULL, doesn't
404 * store anything.
405 */
406static size_t store_gcov_u32(void *buffer, size_t off, u32 v)
407{
408 u32 *data;
409
410 if (buffer) {
411 data = buffer + off;
412 *data = v;
413 }
414
415 return sizeof(*data);
416}
417
418/**
419 * store_gcov_u64 - store 64 bit number in gcov format to buffer
420 * @buffer: target buffer or NULL
421 * @off: offset into the buffer
422 * @v: value to be stored
423 *
424 * Number format defined by gcc: numbers are recorded in the 32 bit
425 * unsigned binary form of the endianness of the machine generating the
426 * file. 64 bit numbers are stored as two 32 bit numbers, the low part
427 * first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store
428 * anything.
429 */
430static size_t store_gcov_u64(void *buffer, size_t off, u64 v)
431{
432 u32 *data;
433
434 if (buffer) {
435 data = buffer + off;
436
437 data[0] = (v & 0xffffffffUL);
438 data[1] = (v >> 32);
439 }
440
441 return sizeof(*data) * 2;
442}
443
444/**
445 * convert_to_gcda - convert profiling data set to gcda file format
446 * @buffer: the buffer to store file data or %NULL if no data should be stored
447 * @info: profiling data set to be converted
448 *
449 * Returns the number of bytes that were/would have been stored into the buffer.
450 */
451static size_t convert_to_gcda(char *buffer, struct gcov_info *info)
452{
453 struct gcov_fn_info *fi_ptr;
454 size_t pos = 0;
455
456 /* File header. */
457 pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
458 pos += store_gcov_u32(buffer, pos, info->version);
459 pos += store_gcov_u32(buffer, pos, info->checksum);
460
461 list_for_each_entry(fi_ptr, &info->functions, head) {
462 u32 i;
463 u32 len = 2;
464
465 if (fi_ptr->use_extra_checksum)
466 len++;
467
468 pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
469 pos += store_gcov_u32(buffer, pos, len);
470 pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
471 pos += store_gcov_u32(buffer, pos, fi_ptr->checksum);
472 if (fi_ptr->use_extra_checksum)
473 pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum);
474
475 pos += store_gcov_u32(buffer, pos, GCOV_TAG_COUNTER_BASE);
476 pos += store_gcov_u32(buffer, pos, fi_ptr->num_counters * 2);
477 for (i = 0; i < fi_ptr->num_counters; i++)
478 pos += store_gcov_u64(buffer, pos, fi_ptr->counters[i]);
479 }
480
481 return pos;
482}
483
484/**
485 * gcov_iter_new - allocate and initialize profiling data iterator
486 * @info: profiling data set to be iterated
487 *
488 * Return file iterator on success, %NULL otherwise.
489 */
490struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
491{
492 struct gcov_iterator *iter;
493
494 iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL);
495 if (!iter)
496 goto err_free;
497
498 iter->info = info;
499 /* Dry-run to get the actual buffer size. */
500 iter->size = convert_to_gcda(NULL, info);
501 iter->buffer = vmalloc(iter->size);
502 if (!iter->buffer)
503 goto err_free;
504
505 convert_to_gcda(iter->buffer, info);
506
507 return iter;
508
509err_free:
510 kfree(iter);
511 return NULL;
512}
513
514
515/**
516 * gcov_iter_get_info - return profiling data set for given file iterator
517 * @iter: file iterator
518 */
519void gcov_iter_free(struct gcov_iterator *iter)
520{
521 vfree(iter->buffer);
522 kfree(iter);
523}
524
525/**
526 * gcov_iter_get_info - return profiling data set for given file iterator
527 * @iter: file iterator
528 */
529struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
530{
531 return iter->info;
532}
533
534/**
535 * gcov_iter_start - reset file iterator to starting position
536 * @iter: file iterator
537 */
538void gcov_iter_start(struct gcov_iterator *iter)
539{
540 iter->pos = 0;
541}
542
543/**
544 * gcov_iter_next - advance file iterator to next logical record
545 * @iter: file iterator
546 *
547 * Return zero if new position is valid, non-zero if iterator has reached end.
548 */
549int gcov_iter_next(struct gcov_iterator *iter)
550{
551 if (iter->pos < iter->size)
552 iter->pos += ITER_STRIDE;
553
554 if (iter->pos >= iter->size)
555 return -EINVAL;
556
557 return 0;
558}
559
560/**
561 * gcov_iter_write - write data for current pos to seq_file
562 * @iter: file iterator
563 * @seq: seq_file handle
564 *
565 * Return zero on success, non-zero otherwise.
566 */
567int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
568{
569 size_t len;
570
571 if (iter->pos >= iter->size)
572 return -EINVAL;
573
574 len = ITER_STRIDE;
575 if (iter->pos + len > iter->size)
576 len = iter->size - iter->pos;
577
578 seq_write(seq, iter->buffer + iter->pos, len);
579
580 return 0;
581}