3

we are using Linux kernel 5.4 with gcc 9.1.0 on different architectures (arm, arm64, x86_64). I am in charge of creating code coverage for kernel modules. I am not selecting the Linux kernel version nor the compiler version.

I am able to create code coverage for arm/arm64 and see the results in /sys/kernel/debugfs/gcov/... as usual. Also, loaded modules are visible in the gcov subdirectory.

However, for x86_64, I on only see /sys/kernel/debubfs/gcov/reset and nothing else, even if I set CONFIG_GCOV_COVERAGE_ALL=y.

The __gcov_init in kernel/gcov/base.c normally creates a

pr_info("version magic=...")

which is visible on arm/arm64 at boot time but not for x86_64. __gcov_init should be called for every source file as a gcc constructor and the first call creates the version magic message.

It seems to me that in my combination the code coverage function __gcov_init is never called in x86_64.

In the config, I use

CONFIG_GCOV_PROFILE=y
CONFIG_GCOV_FORMAT_4_7=y

and sometimes only for x86 CONFIG_GCOV_PROFILE_ALL=y. Nothing besides the "reset" file is visible /sys/kernel/debug/gcov/... for x86.

Any suggestions are welcome.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • You say "I am able to create code coverage for arm/arm64 and see the results in /sys/kernel/debugfs/gcov/... as usual." How do you do that? What is the compiler command that you're using? – stanm Jun 10 '21 at 20:21
  • To get Linux kernel code coverage, you have to add the CONFIG_GCOV_COVERAGE=y and recompile the kernel. You can then add GCOV_COVERAGE := y to selected subdirectories in their Kbuild/Makefile. After rebooting the new kernel, you will see coverage data in debugfs. You have to mount it and then you see a /sys/kernel/debug/gcov subdirectory containg *.gcda and *.gcno files. This is working for ARM and ARM64, but unfortunately not for X86_64. I can then use the gcov utility to visualize the coverage data. – Reiner Huober Jun 11 '21 at 11:15
  • Thanks for the clarification! The only documentation I could find is here: https://01.org/linuxgraphics/gfx-docs/drm/dev-tools/gcov.html . I cannot find any signs why it shouldn't work for x86. When you say it doesn't work, you mean the "/sys/kernel/debug/gcov" directory is not there, even after you mount debugfs? – stanm Jun 11 '21 at 13:56
  • No, it is there and you see the directory /sys/kernel/debug/gcov and the control file /sys/kernel/debug/gcov/reset (when you write to it, the coverage counters are reset to zero). However, normally if you compile under /home/user/linux/..., you will find files under /sys/kernel/debug/gcov/home/user/linux/... . This is the case for ARM/ARM64 – Reiner Huober Jun 14 '21 at 06:09

2 Answers2

1

Code coverage constructors are now written in a ".ctors.65435" section (the number is possibly a constructor priority), but kernel 5.4 only knows ".ctors".

For the kernel internal code coverage, the following change (already in later kernels, e.g. 5.12) brings back code coverage

--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -631,6 +631,7 @@
 #ifdef CONFIG_CONSTRUCTORS
 #define KERNEL_CTORS() . = ALIGN(8);                      \
                        __ctors_start = .;                 \
+                       KEEP(*(SORT(.ctors.*)))            \
                        KEEP(*(.ctors))                    \
                        KEEP(*(SORT(.init_array.*)))       \
                        KEEP(*(.init_array))               \

However, kernel/module.c does not know ".ctors.65435" either, and the modules compiled with gcc9 or gcc10 do not have a ".ctors" ELF sections. So I just changed as a HACK (!)

--- a/kernel/module.c
+++ b/kernel/module.c
@@ -3294,7 +3294,7 @@ static int find_module_sections(struct module *mod, struct load_info *info)
        mod->unused_gpl_crcs = section_addr(info, "__kcrctab_unused_gpl");
 #endif
 #ifdef CONFIG_CONSTRUCTORS
-       mod->ctors = section_objs(info, ".ctors",
+       mod->ctors = section_objs(info, ".ctors.65435",
                                  sizeof(*mod->ctors), &mod->num_ctors);
        if (!mod->ctors)
                mod->ctors = section_objs(info, ".init_array",

This brings back module code coverage, but

.ctors.* for modules is not known in the newest 5.x kernel, and so I think this should be fixed in the Linux Kernel module handling

1

Instead of hacking and use ".ctors.65435" in kernel/module.c instead of ".ctors", the proper solution is to change the module linker script to combine all ".ctors.*" section with the ."ctors" section:

diff --git a/scripts/module-common.lds b/scripts/module-common.lds
index d61b9e8678e8..0cd6aabaa460 100644
--- a/scripts/module-common.lds
+++ b/scripts/module-common.lds
@@ -20,6 +20,7 @@ SECTIONS {
        __kcrctab_unused_gpl    0 : { *(SORT(___kcrctab_unused_gpl+*)) }
        __kcrctab_gpl_future    0 : { *(SORT(___kcrctab_gpl_future+*)) }

+       .ctors                  0 : ALIGN(8) { *(SORT(.ctors.*)) *(.ctors) }
        .init_array             0 : ALIGN(8) { *(SORT(.init_array.*)) *(.init_array) }

        __jump_table            0 : ALIGN(8) { KEEP(*(__jump_table)) }
--
2.29.2

This brings back the code coverage for modules.