Commit e178a5be authored by Greg Hackmann's avatar Greg Hackmann Committed by Linus Torvalds

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.comSigned-off-by: default avatarGreg Hackmann <ghackmann@android.com>
Signed-off-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Signed-off-by: default avatarTri Vo <trong@android.com>
Co-developed-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Co-developed-by: default avatarTri Vo <trong@android.com>
Tested-by: default avatarTrilok Soni <tsoni@quicinc.com>
Tested-by: default avatarPrasad Sodagudi <psodagud@quicinc.com>
Tested-by: default avatarTri Vo <trong@android.com>
Tested-by: default avatarDaniel Mentz <danielmentz@google.com>
Tested-by: default avatarPetri Gynther <pgynther@google.com>
Reviewed-by: default avatarPeter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent aa069a23
...@@ -53,6 +53,7 @@ config GCOV_PROFILE_ALL ...@@ -53,6 +53,7 @@ config GCOV_PROFILE_ALL
choice choice
prompt "Specify GCOV format" prompt "Specify GCOV format"
depends on GCOV_KERNEL depends on GCOV_KERNEL
depends on CC_IS_GCC
---help--- ---help---
The gcov format is usually determined by the GCC version, and the The gcov format is usually determined by the GCC version, and the
default is chosen according to your GCC version. However, there are default is chosen according to your GCC version. However, there are
...@@ -62,7 +63,7 @@ choice ...@@ -62,7 +63,7 @@ choice
config GCOV_FORMAT_3_4 config GCOV_FORMAT_3_4
bool "GCC 3.4 format" bool "GCC 3.4 format"
depends on CC_IS_GCC && GCC_VERSION < 40700 depends on GCC_VERSION < 40700
---help--- ---help---
Select this option to use the format defined by GCC 3.4. Select this option to use the format defined by GCC 3.4.
......
...@@ -4,3 +4,4 @@ ccflags-y := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"' ...@@ -4,3 +4,4 @@ ccflags-y := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"'
obj-y := base.o fs.o obj-y := base.o fs.o
obj-$(CONFIG_GCOV_FORMAT_3_4) += gcc_base.o gcc_3_4.o obj-$(CONFIG_GCOV_FORMAT_3_4) += gcc_base.o gcc_3_4.o
obj-$(CONFIG_GCOV_FORMAT_4_7) += gcc_base.o gcc_4_7.o obj-$(CONFIG_GCOV_FORMAT_4_7) += gcc_base.o gcc_4_7.o
obj-$(CONFIG_CC_IS_CLANG) += clang.o
...@@ -64,7 +64,7 @@ static int gcov_module_notifier(struct notifier_block *nb, unsigned long event, ...@@ -64,7 +64,7 @@ static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
/* Remove entries located in module from linked list. */ /* Remove entries located in module from linked list. */
while ((info = gcov_info_next(info))) { while ((info = gcov_info_next(info))) {
if (within_module((unsigned long)info, mod)) { if (gcov_info_within_module(info, mod)) {
gcov_info_unlink(prev, info); gcov_info_unlink(prev, info);
if (gcov_events_enabled) if (gcov_events_enabled)
gcov_event(GCOV_REMOVE, info); gcov_event(GCOV_REMOVE, info);
......
This diff is collapsed.
...@@ -137,6 +137,18 @@ void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info) ...@@ -137,6 +137,18 @@ void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
gcov_info_head = info->next; gcov_info_head = info->next;
} }
/**
* gcov_info_within_module - check if a profiling data set belongs to a module
* @info: profiling data set
* @mod: module
*
* Returns true if profiling data belongs module, false otherwise.
*/
bool gcov_info_within_module(struct gcov_info *info, struct module *mod)
{
return within_module((unsigned long)info, mod);
}
/* Symbolic links to be created for each profiling data file. */ /* Symbolic links to be created for each profiling data file. */
const struct gcov_link gcov_link[] = { const struct gcov_link gcov_link[] = {
{ OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */ { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
......
...@@ -150,6 +150,18 @@ void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info) ...@@ -150,6 +150,18 @@ void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
gcov_info_head = info->next; gcov_info_head = info->next;
} }
/**
* gcov_info_within_module - check if a profiling data set belongs to a module
* @info: profiling data set
* @mod: module
*
* Returns true if profiling data belongs module, false otherwise.
*/
bool gcov_info_within_module(struct gcov_info *info, struct module *mod)
{
return within_module((unsigned long)info, mod);
}
/* Symbolic links to be created for each profiling data file. */ /* Symbolic links to be created for each profiling data file. */
const struct gcov_link gcov_link[] = { const struct gcov_link gcov_link[] = {
{ OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */ { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#ifndef GCOV_H #ifndef GCOV_H
#define GCOV_H GCOV_H #define GCOV_H GCOV_H
#include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
/* /*
...@@ -46,6 +47,7 @@ unsigned int gcov_info_version(struct gcov_info *info); ...@@ -46,6 +47,7 @@ unsigned int gcov_info_version(struct gcov_info *info);
struct gcov_info *gcov_info_next(struct gcov_info *info); struct gcov_info *gcov_info_next(struct gcov_info *info);
void gcov_info_link(struct gcov_info *info); void gcov_info_link(struct gcov_info *info);
void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info); void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info);
bool gcov_info_within_module(struct gcov_info *info, struct module *mod);
/* Base interface. */ /* Base interface. */
enum gcov_action { enum gcov_action {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment