* [RFC PATCH 01/20] coverage: move gcov and kcov under kernel/coverage/
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage Sasha Levin
` (7 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
To: linux-kernel
Cc: Alexander Potapenko, Andrew Morton, Andrey Konovalov, Ben Segall,
Bill Wendling, Chuck Wolber, Dietmar Eggemann, Dmitry Vyukov,
Ingo Molnar, Juri Lelli, Justin Stitt, kasan-dev, K Prateek Nayak,
linux-doc, llvm, Marco Elver, Masami Hiramatsu,
Mauro Carvalho Chehab, Mel Gorman, Nathan Chancellor,
Nick Desaulniers, Peter Oberparleiter, Peter Zijlstra,
Steven Rostedt, Thomas Gleixner, Valentin Schneider,
Vincent Guittot
The kernel carries more than one code coverage implementation, each
with its own directory, Kconfig menu, build plumbing and debugfs
interface. Start consolidating them by moving the existing
implementations next to each other:
kernel/gcov/ -> kernel/coverage/gcov/
kernel/kcov.c -> kernel/coverage/kcov.c
Both are configured from a new "Code coverage" menu in
kernel/coverage/Kconfig, which is sourced from "Kernel Testing and
Coverage" where the KCOV options used to live. The gcov options move
there from "General architecture-dependent options".
No functional change: the Kconfig symbols and their dependencies are
unchanged, and so are the per-object compiler flags of kcov.o, which
move to kernel/coverage/Makefile.
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
MAINTAINERS | 8 +++-
arch/Kconfig | 2 -
include/linux/sched.h | 2 +-
kernel/Makefile | 15 +------
kernel/coverage/Kconfig | 65 +++++++++++++++++++++++++++
kernel/coverage/Makefile | 16 +++++++
kernel/{ => coverage}/gcov/Kconfig | 0
kernel/{ => coverage}/gcov/Makefile | 0
kernel/{ => coverage}/gcov/base.c | 0
kernel/{ => coverage}/gcov/clang.c | 2 +-
kernel/{ => coverage}/gcov/fs.c | 0
kernel/{ => coverage}/gcov/gcc_4_7.c | 0
kernel/{ => coverage}/gcov/gcc_base.c | 0
kernel/{ => coverage}/gcov/gcov.h | 0
kernel/{ => coverage}/kcov.c | 0
lib/Kconfig.debug | 59 +-----------------------
tools/unittests/test_cmatch.py | 2 +-
17 files changed, 92 insertions(+), 79 deletions(-)
create mode 100644 kernel/coverage/Kconfig
create mode 100644 kernel/coverage/Makefile
rename kernel/{ => coverage}/gcov/Kconfig (100%)
rename kernel/{ => coverage}/gcov/Makefile (100%)
rename kernel/{ => coverage}/gcov/base.c (100%)
rename kernel/{ => coverage}/gcov/clang.c (99%)
rename kernel/{ => coverage}/gcov/fs.c (100%)
rename kernel/{ => coverage}/gcov/gcc_4_7.c (100%)
rename kernel/{ => coverage}/gcov/gcc_base.c (100%)
rename kernel/{ => coverage}/gcov/gcov.h (100%)
rename kernel/{ => coverage}/kcov.c (100%)
diff --git a/MAINTAINERS b/MAINTAINERS
index 1ed6d81ed2f6b..2e0a98ceed3e7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11129,7 +11129,9 @@ GCOV BASED KERNEL PROFILING
M: Peter Oberparleiter <oberpar@linux.ibm.com>
S: Maintained
F: Documentation/dev-tools/gcov.rst
-F: kernel/gcov/
+F: kernel/coverage/Kconfig
+F: kernel/coverage/Makefile
+F: kernel/coverage/gcov/
GDB KERNEL DEBUGGING HELPER SCRIPTS
M: Jan Kiszka <jan.kiszka@siemens.com>
@@ -14350,7 +14352,9 @@ B: https://bugzilla.kernel.org/buglist.cgi?component=Sanitizers&product=Memory%2
F: Documentation/dev-tools/kcov.rst
F: include/linux/kcov.h
F: include/uapi/linux/kcov.h
-F: kernel/kcov.c
+F: kernel/coverage/Kconfig
+F: kernel/coverage/Makefile
+F: kernel/coverage/kcov.c
F: scripts/Makefile.kcov
KCSAN
diff --git a/arch/Kconfig b/arch/Kconfig
index 50601c0301e84..f4b24649f039c 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -1777,8 +1777,6 @@ config ARCH_VMLINUX_NEEDS_RELOCS
config HAVE_GENERIC_TIF_BITS
bool
-source "kernel/gcov/Kconfig"
-
source "scripts/gcc-plugins/Kconfig"
config FUNCTION_ALIGNMENT_4B
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 1c93da75226e3..aa71fe45a6ae1 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1536,7 +1536,7 @@ struct task_struct {
#endif /* CONFIG_TRACING */
#ifdef CONFIG_KCOV
- /* See kernel/kcov.c for more details. */
+ /* See kernel/coverage/kcov.c for more details. */
/* Coverage collection mode enabled for this task (0 if disabled): */
unsigned int kcov_mode;
diff --git a/kernel/Makefile b/kernel/Makefile
index 4ff92e24680c5..50bf37a26bae3 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -35,18 +35,6 @@ KCSAN_SANITIZE_softirq.o = n
# and produce insane amounts of uninteresting coverage.
KCOV_INSTRUMENT_extable.o := n
KCOV_INSTRUMENT_stacktrace.o := n
-# Don't self-instrument.
-KCOV_INSTRUMENT_kcov.o := n
-# If sanitizers detect any issues in kcov, it may lead to recursion
-# via printk, etc.
-KASAN_SANITIZE_kcov.o := n
-KCSAN_SANITIZE_kcov.o := n
-UBSAN_SANITIZE_kcov.o := n
-KMSAN_SANITIZE_kcov.o := n
-
-CONTEXT_ANALYSIS_kcov.o := y
-CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack) -fno-stack-protector
-
obj-y += sched/
obj-y += locking/
obj-y += power/
@@ -96,8 +84,7 @@ obj-$(CONFIG_IKHEADERS) += kheaders.o
obj-$(CONFIG_SMP) += stop_machine.o
obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o audit_watch.o audit_fsnotify.o audit_tree.o
-obj-$(CONFIG_GCOV_KERNEL) += gcov/
-obj-$(CONFIG_KCOV) += kcov.o
+obj-y += coverage/
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_FAIL_FUNCTION) += fail_function.o
obj-$(CONFIG_KGDB) += debug/
diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
new file mode 100644
index 0000000000000..dd90b6a61ae65
--- /dev/null
+++ b/kernel/coverage/Kconfig
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Code coverage"
+
+source "kernel/coverage/gcov/Kconfig"
+
+config ARCH_HAS_KCOV
+ bool
+ help
+ An architecture should select this when it can successfully
+ build and run with CONFIG_KCOV. This typically requires
+ disabling instrumentation for some early boot code.
+
+config KCOV
+ bool "Code coverage for fuzzing"
+ depends on ARCH_HAS_KCOV
+ depends on !ARCH_WANTS_NO_INSTR || HAVE_NOINSTR_HACK || \
+ GCC_VERSION >= 120000 || CC_IS_CLANG
+ select DEBUG_FS
+ select OBJTOOL if HAVE_NOINSTR_HACK
+ help
+ KCOV exposes kernel code coverage information in a form suitable
+ for coverage-guided fuzzing (randomized testing).
+
+ For more details, see Documentation/dev-tools/kcov.rst.
+
+config KCOV_ENABLE_COMPARISONS
+ bool "Enable comparison operands collection by KCOV"
+ depends on KCOV
+ depends on $(cc-option,-fsanitize-coverage=trace-cmp)
+ help
+ KCOV also exposes operands of every comparison in the instrumented
+ code along with operand sizes and PCs of the comparison instructions.
+ These operands can be used by fuzzing engines to improve the quality
+ of fuzzing coverage.
+
+config KCOV_INSTRUMENT_ALL
+ bool "Instrument all code by default"
+ depends on KCOV
+ default y
+ help
+ If you are doing generic system call fuzzing (like e.g. syzkaller),
+ then you will want to instrument the whole kernel and you should
+ say y here. If you are doing more targeted fuzzing (like e.g.
+ filesystem fuzzing with AFL) then you will want to enable coverage
+ for more specific subsets of files, and should say n here.
+
+config KCOV_IRQ_AREA_SIZE
+ hex "Size of interrupt coverage collection area in words"
+ depends on KCOV
+ range 0x80 0x1000000
+ default 0x40000
+ help
+ KCOV uses preallocated areas to collect coverage from soft
+ interrupts. This specifies the size of those areas in the
+ number of unsigned long words.
+
+config KCOV_SELFTEST
+ bool "Perform short selftests on boot"
+ depends on KCOV
+ help
+ Run short KCOV coverage collection selftests on boot.
+ On test failure, causes the kernel to panic. Recommended to be
+ enabled, ensuring critical functionality works as intended.
+
+endmenu
diff --git a/kernel/coverage/Makefile b/kernel/coverage/Makefile
new file mode 100644
index 0000000000000..a4cd8cc18d9bc
--- /dev/null
+++ b/kernel/coverage/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_GCOV_KERNEL) += gcov/
+obj-$(CONFIG_KCOV) += kcov.o
+
+# Don't self-instrument.
+KCOV_INSTRUMENT_kcov.o := n
+# If sanitizers detect any issues in kcov, it may lead to recursion
+# via printk, etc.
+KASAN_SANITIZE_kcov.o := n
+KCSAN_SANITIZE_kcov.o := n
+UBSAN_SANITIZE_kcov.o := n
+KMSAN_SANITIZE_kcov.o := n
+
+CONTEXT_ANALYSIS_kcov.o := y
+CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack) -fno-stack-protector
diff --git a/kernel/gcov/Kconfig b/kernel/coverage/gcov/Kconfig
similarity index 100%
rename from kernel/gcov/Kconfig
rename to kernel/coverage/gcov/Kconfig
diff --git a/kernel/gcov/Makefile b/kernel/coverage/gcov/Makefile
similarity index 100%
rename from kernel/gcov/Makefile
rename to kernel/coverage/gcov/Makefile
diff --git a/kernel/gcov/base.c b/kernel/coverage/gcov/base.c
similarity index 100%
rename from kernel/gcov/base.c
rename to kernel/coverage/gcov/base.c
diff --git a/kernel/gcov/clang.c b/kernel/coverage/gcov/clang.c
similarity index 99%
rename from kernel/gcov/clang.c
rename to kernel/coverage/gcov/clang.c
index fd98ced0e51d2..e979e8983dce8 100644
--- a/kernel/gcov/clang.c
+++ b/kernel/coverage/gcov/clang.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Google, Inc.
- * modified from kernel/gcov/gcc_4_7.c
+ * modified from kernel/coverage/gcov/gcc_4_7.c
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
diff --git a/kernel/gcov/fs.c b/kernel/coverage/gcov/fs.c
similarity index 100%
rename from kernel/gcov/fs.c
rename to kernel/coverage/gcov/fs.c
diff --git a/kernel/gcov/gcc_4_7.c b/kernel/coverage/gcov/gcc_4_7.c
similarity index 100%
rename from kernel/gcov/gcc_4_7.c
rename to kernel/coverage/gcov/gcc_4_7.c
diff --git a/kernel/gcov/gcc_base.c b/kernel/coverage/gcov/gcc_base.c
similarity index 100%
rename from kernel/gcov/gcc_base.c
rename to kernel/coverage/gcov/gcc_base.c
diff --git a/kernel/gcov/gcov.h b/kernel/coverage/gcov/gcov.h
similarity index 100%
rename from kernel/gcov/gcov.h
rename to kernel/coverage/gcov/gcov.h
diff --git a/kernel/kcov.c b/kernel/coverage/kcov.c
similarity index 100%
rename from kernel/kcov.c
rename to kernel/coverage/kcov.c
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 6759087154edd..41a5925e4568b 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2192,64 +2192,7 @@ config FAULT_INJECTION_STACKTRACE_FILTER
help
Provide stacktrace filter for fault-injection capabilities
-config ARCH_HAS_KCOV
- bool
- help
- An architecture should select this when it can successfully
- build and run with CONFIG_KCOV. This typically requires
- disabling instrumentation for some early boot code.
-
-config KCOV
- bool "Code coverage for fuzzing"
- depends on ARCH_HAS_KCOV
- depends on !ARCH_WANTS_NO_INSTR || HAVE_NOINSTR_HACK || \
- GCC_VERSION >= 120000 || CC_IS_CLANG
- select DEBUG_FS
- select OBJTOOL if HAVE_NOINSTR_HACK
- help
- KCOV exposes kernel code coverage information in a form suitable
- for coverage-guided fuzzing (randomized testing).
-
- For more details, see Documentation/dev-tools/kcov.rst.
-
-config KCOV_ENABLE_COMPARISONS
- bool "Enable comparison operands collection by KCOV"
- depends on KCOV
- depends on $(cc-option,-fsanitize-coverage=trace-cmp)
- help
- KCOV also exposes operands of every comparison in the instrumented
- code along with operand sizes and PCs of the comparison instructions.
- These operands can be used by fuzzing engines to improve the quality
- of fuzzing coverage.
-
-config KCOV_INSTRUMENT_ALL
- bool "Instrument all code by default"
- depends on KCOV
- default y
- help
- If you are doing generic system call fuzzing (like e.g. syzkaller),
- then you will want to instrument the whole kernel and you should
- say y here. If you are doing more targeted fuzzing (like e.g.
- filesystem fuzzing with AFL) then you will want to enable coverage
- for more specific subsets of files, and should say n here.
-
-config KCOV_IRQ_AREA_SIZE
- hex "Size of interrupt coverage collection area in words"
- depends on KCOV
- range 0x80 0x1000000
- default 0x40000
- help
- KCOV uses preallocated areas to collect coverage from soft
- interrupts. This specifies the size of those areas in the
- number of unsigned long words.
-
-config KCOV_SELFTEST
- bool "Perform short selftests on boot"
- depends on KCOV
- help
- Run short KCOV coverage collection selftests on boot.
- On test failure, causes the kernel to panic. Recommended to be
- enabled, ensuring critical functionality works as intended.
+source "kernel/coverage/Kconfig"
menuconfig RUNTIME_TESTING_MENU
bool "Runtime Testing"
diff --git a/tools/unittests/test_cmatch.py b/tools/unittests/test_cmatch.py
index 109141cd2ab8d..06f9c61572e2e 100755
--- a/tools/unittests/test_cmatch.py
+++ b/tools/unittests/test_cmatch.py
@@ -706,7 +706,7 @@ class TestSubWithLocalXforms(TestCaseDiff):
def test_struct_kcov(self):
"""
- Test a struct from kernel/kcov.c.
+ Test a struct from kernel/coverage/kcov.c.
"""
line = """
struct kcov {
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 01/20] coverage: move gcov and kcov under kernel/coverage/ Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
2026-10-28 13:51 ` Peter Zijlstra
2026-10-28 11:59 ` [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code Sasha Levin
` (6 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
To: linux-kernel
Cc: Bill Wendling, Chuck Wolber, Justin Stitt, llvm, Marco Elver,
Masami Hiramatsu, Miguel Ojeda, Nathan Chancellor,
Nick Desaulniers, Peter Zijlstra, Thomas Gleixner
noinstr code may only call other noinstr functions and __always_inline
functions. gcov and Clang's source-based code coverage insert their
instrumentation before inlining, so the instrumentation of an
__always_inline function ends up in every noinstr function it gets
inlined into. With Clang's source-based coverage on x86, 2500 of the
3449 relocations in .noinstr.text and 333 of the 451 in .cpuidle.text
of vmlinux.o point to coverage counters or MC/DC bitmaps. GCC's gcov
does the same. Since these are plain memory updates rather than calls,
objtool's noinstr validation doesn't notice them.
KCOV doesn't have this problem, as it instruments the code after
inlining and checks the attributes of the function that the code ends
up in.
Make __always_inline imply __no_profile when a code coverage
measurement implementation is enabled, so that no noinstr code carries
coverage instrumentation. The cost is that __always_inline functions
don't show up in the coverage data at all, including at their callers
outside of noinstr code. For an x86_64 configuration built with Clang 22
and MC/DC, that removes 556 of 60788 functions (0.9%), 29261 of 1598305
regions (1.8%), 11067 of 591108 branches (1.9%) and 445 of 36561 MC/DC
conditions (1.2%) from the coverage data.
Leave unoptimized objects alone: GCC refuses to inline an
__always_inline function across a no_profile_instrument_function
mismatch at -O0, and those objects don't contain noinstr code.
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
include/linux/compiler_types.h | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index c5921f1390079..c3298d29a9425 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -246,6 +246,19 @@ struct ftrace_likely_data {
*/
#define __inline__ inline
+/*
+ * noinstr code may only call noinstr and __always_inline functions. Code
+ * coverage instrumentation is added before inlining, so the counters of an
+ * __always_inline function would end up in the noinstr code it gets inlined
+ * into: don't instrument __always_inline functions. GCC refuses to inline
+ * across a no_profile_instrument_function mismatch without optimization, so
+ * leave the rare unoptimized objects alone; they don't contain noinstr code.
+ */
+#if defined(CONFIG_COVERAGE_KERNEL) && defined(__OPTIMIZE__)
+#undef __always_inline
+#define __always_inline inline __attribute__((__always_inline__)) __no_profile
+#endif
+
/*
* GCC does not warn about unused static inline functions for -Wunused-function.
* Suppress the warning in clang as well by using __maybe_unused, but enable it
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
2026-10-28 11:59 ` [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage Sasha Levin
@ 2026-10-28 13:51 ` Peter Zijlstra
2026-10-28 14:01 ` Peter Zijlstra
0 siblings, 1 reply; 18+ messages in thread
From: Peter Zijlstra @ 2026-10-28 13:51 UTC (permalink / raw)
To: Sasha Levin
Cc: linux-kernel, Bill Wendling, Chuck Wolber, Justin Stitt, llvm,
Marco Elver, Masami Hiramatsu, Miguel Ojeda, Nathan Chancellor,
Nick Desaulniers, Thomas Gleixner, wentaoz5, chuck.wolber,
steven.h.vanderleest
On Wed, Oct 28, 2026 at 07:59:28AM -0400, Sasha Levin wrote:
> noinstr code may only call other noinstr functions and __always_inline
> functions. gcov and Clang's source-based code coverage insert their
> instrumentation before inlining, so the instrumentation of an
> __always_inline function ends up in every noinstr function it gets
> inlined into. With Clang's source-based coverage on x86, 2500 of the
> 3449 relocations in .noinstr.text and 333 of the 451 in .cpuidle.text
> of vmlinux.o point to coverage counters or MC/DC bitmaps. GCC's gcov
> does the same. Since these are plain memory updates rather than calls,
> objtool's noinstr validation doesn't notice them.
>
> KCOV doesn't have this problem, as it instruments the code after
> inlining and checks the attributes of the function that the code ends
> up in.
>
> Make __always_inline imply __no_profile when a code coverage
> measurement implementation is enabled, so that no noinstr code carries
> coverage instrumentation. The cost is that __always_inline functions
> don't show up in the coverage data at all, including at their callers
> outside of noinstr code. For an x86_64 configuration built with Clang 22
> and MC/DC, that removes 556 of 60788 functions (0.9%), 29261 of 1598305
> regions (1.8%), 11067 of 591108 branches (1.9%) and 445 of 36561 MC/DC
> conditions (1.2%) from the coverage data.
This is pretty vile. But also, since people were trying to use MC/DC for
validation efforts and the like, how does this impact them? If I
understand correctly their previous emails on the topic, this means they
have to go manually audit every single __always_inline function.
Now; I understand this is a tool limitation and there really must not be
instrumentation in noinstr code, since that can run before things like
page-tables are complete (consider the PTI case). So this is the best
option.
Still, I'm left wondering if these tools should be fixed. For instance,
while the front-end emits this instrumentation, the backend could easily
remove it again when it finds itself in the context of a noinstr
function after inlining.
> Leave unoptimized objects alone: GCC refuses to inline an
> __always_inline function across a no_profile_instrument_function
> mismatch at -O0, and those objects don't contain noinstr code.
We have -O0 code? I thought we hard relied on building with at least
-O1, we often get 'fixes' to enable code to build with -O0.
> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---
> include/linux/compiler_types.h | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
> index c5921f1390079..c3298d29a9425 100644
> --- a/include/linux/compiler_types.h
> +++ b/include/linux/compiler_types.h
> @@ -246,6 +246,19 @@ struct ftrace_likely_data {
> */
> #define __inline__ inline
>
> +/*
> + * noinstr code may only call noinstr and __always_inline functions. Code
> + * coverage instrumentation is added before inlining, so the counters of an
> + * __always_inline function would end up in the noinstr code it gets inlined
> + * into: don't instrument __always_inline functions. GCC refuses to inline
> + * across a no_profile_instrument_function mismatch without optimization, so
> + * leave the rare unoptimized objects alone; they don't contain noinstr code.
> + */
> +#if defined(CONFIG_COVERAGE_KERNEL) && defined(__OPTIMIZE__)
> +#undef __always_inline
> +#define __always_inline inline __attribute__((__always_inline__)) __no_profile
> +#endif
> +
> /*
> * GCC does not warn about unused static inline functions for -Wunused-function.
> * Suppress the warning in clang as well by using __maybe_unused, but enable it
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
2026-10-28 13:51 ` Peter Zijlstra
@ 2026-10-28 14:01 ` Peter Zijlstra
2026-10-28 16:45 ` Sasha Levin
0 siblings, 1 reply; 18+ messages in thread
From: Peter Zijlstra @ 2026-10-28 14:01 UTC (permalink / raw)
To: Sasha Levin
Cc: linux-kernel, Bill Wendling, Chuck Wolber, Justin Stitt, llvm,
Marco Elver, Masami Hiramatsu, Miguel Ojeda, Nathan Chancellor,
Nick Desaulniers, Thomas Gleixner, wentaoz5, chuck.wolber,
steven.h.vanderleest, Josh Poimboeuf
On Wed, Oct 28, 2026 at 02:51:40PM +0100, Peter Zijlstra wrote:
> On Wed, Oct 28, 2026 at 07:59:28AM -0400, Sasha Levin wrote:
> > noinstr code may only call other noinstr functions and __always_inline
> > functions. gcov and Clang's source-based code coverage insert their
> > instrumentation before inlining, so the instrumentation of an
> > __always_inline function ends up in every noinstr function it gets
> > inlined into. With Clang's source-based coverage on x86, 2500 of the
> > 3449 relocations in .noinstr.text and 333 of the 451 in .cpuidle.text
> > of vmlinux.o point to coverage counters or MC/DC bitmaps. GCC's gcov
> > does the same. Since these are plain memory updates rather than calls,
> > objtool's noinstr validation doesn't notice them.
> >
> > KCOV doesn't have this problem, as it instruments the code after
> > inlining and checks the attributes of the function that the code ends
> > up in.
> >
> > Make __always_inline imply __no_profile when a code coverage
> > measurement implementation is enabled, so that no noinstr code carries
> > coverage instrumentation. The cost is that __always_inline functions
> > don't show up in the coverage data at all, including at their callers
> > outside of noinstr code. For an x86_64 configuration built with Clang 22
> > and MC/DC, that removes 556 of 60788 functions (0.9%), 29261 of 1598305
> > regions (1.8%), 11067 of 591108 branches (1.9%) and 445 of 36561 MC/DC
> > conditions (1.2%) from the coverage data.
>
> This is pretty vile. But also, since people were trying to use MC/DC for
> validation efforts and the like, how does this impact them? If I
> understand correctly their previous emails on the topic, this means they
> have to go manually audit every single __always_inline function.
>
> Now; I understand this is a tool limitation and there really must not be
> instrumentation in noinstr code, since that can run before things like
> page-tables are complete (consider the PTI case). So this is the best
> option.
>
> Still, I'm left wondering if these tools should be fixed. For instance,
> while the front-end emits this instrumentation, the backend could easily
> remove it again when it finds itself in the context of a noinstr
> function after inlining.
Ah, also, we have objtool's hack_noinstr :/ But sssh, don't tell Josh
about that.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
2026-10-28 14:01 ` Peter Zijlstra
@ 2026-10-28 16:45 ` Sasha Levin
2026-10-29 5:49 ` Chuck Wolber
0 siblings, 1 reply; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 16:45 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Sasha Levin, linux-kernel, Bill Wendling, Chuck Wolber,
Justin Stitt, llvm, Marco Elver, Masami Hiramatsu, Miguel Ojeda,
Nathan Chancellor, Nick Desaulniers, Thomas Gleixner, wentaoz5,
chuck.wolber, steven.h.vanderleest, Josh Poimboeuf
On Wed, Oct 28, 2026 at 02:51:40PM +0100, Peter Zijlstra wrote:
>This is pretty vile. But also, since people were trying to use MC/DC for
>validation efforts and the like, how does this impact them? If I
>understand correctly their previous emails on the topic, this means they
>have to go manually audit every single __always_inline function.
Yes, the __always_inline helpers join noinstr code as something that
has to be justified separately rather than covered by tests. Steve's
answer to your noinstr question in 2024 [1] was that this is workable
as long as the number of distinct categories stays small, and this
adds one (556 functions and 445 MC/DC conditions for the config in the
changelog).
Chuck, Steve, does that match what you'd expect on your side?
>Still, I'm left wondering if these tools should be fixed. For instance,
>while the front-end emits this instrumentation, the backend could easily
>remove it again when it finds itself in the context of a noinstr
>function after inlining.
Yup, we should do that. Both GCC and Clang add the counters before
inlining and let always_inline win over the attribute mismatch, so
neither can do it today. We can look at improving them, but it's probably
outside of what this series can do.
On Wed, Oct 28, 2026 at 03:01:18PM +0100, Peter Zijlstra wrote:
>Ah, also, we have objtool's hack_noinstr :/ But sssh, don't tell Josh
>about that.
I looked at that. NOPing the gcov counter updates would be easy, as
they're all single locked instructions, but Clang's MC/DC bitmap
updates go through registers and would need dataflow analysis. It
would also only help x86, while gcov and noinstr are on arm64, riscv
and s390 too. So I'd rather keep this as the guarantee, with objtool
(patch 11) checking it.
On Wed, Oct 28, 2026 at 02:51:40PM +0100, Peter Zijlstra wrote:
>We have -O0 code? I thought we hard relied on building with at least
>-O1, we often get 'fixes' to enable code to build with -O0.
Only crypto/jitterentropy.c, and lpfc when built with GCOV=y. The check
makes no difference for either (lpfc doesn't build at -O0 regardless),
so I'll drop it in v2.
[1] https://lore.kernel.org/all/20240905180758.1396234-1-wentaoz5@illinois.edu/
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
2026-10-28 16:45 ` Sasha Levin
@ 2026-10-29 5:49 ` Chuck Wolber
0 siblings, 0 replies; 18+ messages in thread
From: Chuck Wolber @ 2026-10-29 5:49 UTC (permalink / raw)
To: Sasha Levin, Peter Zijlstra
Cc: linux-kernel, Bill Wendling, Justin Stitt, llvm, Marco Elver,
Masami Hiramatsu, Miguel Ojeda, Nathan Chancellor,
Nick Desaulniers, Thomas Gleixner, wentaoz5, chuck.wolber,
Josh Poimboeuf
On Wed, Oct 28, 2026, at 4:45 PM, Sasha Levin wrote:
> On Wed, Oct 28, 2026 at 02:51:40PM +0100, Peter Zijlstra wrote:
>>This is pretty vile. But also, since people were trying to use MC/DC
>>for validation efforts and the like, how does this impact them? If I
>>understand correctly their previous emails on the topic, this means
>>they have to go manually audit every single __always_inline function.
>
> Yes, the __always_inline helpers join noinstr code as something that
> has to be justified separately rather than covered by tests. Steve's
> answer to your noinstr question in 2024 [1] was that this is workable
> as long as the number of distinct categories stays small, and this
> adds one (556 functions and 445 MC/DC conditions for the config in the
> changelog).
>
> Chuck, Steve, does that match what you'd expect on your side?
Yes, this is correct.
And "small" is a relative term that should not be a factor that holds
back this patchset. Perfect is the enemy of good and we can always
improve on the level of coverage over time.
>>Still, I'm left wondering if these tools should be fixed. For
>>instance, while the front-end emits this instrumentation, the backend
>>could easily remove it again when it finds itself in the context of a
>>noinstr function after inlining.
>
> Yup, we should do that. Both GCC and Clang add the counters before
> inlining and let always_inline win over the attribute mismatch, so
> neither can do it today. We can look at improving them, but it's
> probably outside of what this series can do.
Agreed. This is a good example of how the level of coverage can be
improved.
..Ch:W..
^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 01/20] coverage: move gcov and kcov under kernel/coverage/ Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
2026-10-28 14:11 ` Peter Zijlstra
2026-10-28 11:59 ` [RFC PATCH 12/20] x86/sev: Stop excluding noinstr.o from code coverage Sasha Levin
` (5 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
To: linux-kernel
Cc: Bill Wendling, Chuck Wolber, Josh Poimboeuf, Justin Stitt, llvm,
Marco Elver, Masami Hiramatsu, Nathan Chancellor,
Nick Desaulniers, Peter Zijlstra, Thomas Gleixner
gcov and Clang's source-based code coverage update their counters
inline, rather than by calling into a runtime like KCOV does, so
coverage instrumentation in noinstr code goes unnoticed by the noinstr
validation of call destinations.
Flag instructions in noinstr code, outside of instrumentation_begin()
and instrumentation_end() sections, that reference coverage counters:
GCC's __gcov<N>.* counters (including the MC/DC counters), Clang's gcov
__llvm_gcov_ctr* counters, and the __llvm_prf_cnts and __llvm_prf_bits
sections of Clang's source-based code coverage.
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
tools/objtool/check.c | 53 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 464f6c9d9ff0b..6382d2a44ef2b 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4,6 +4,7 @@
*/
#define _GNU_SOURCE /* memmem() */
+#include <ctype.h>
#include <fnmatch.h>
#include <string.h>
#include <stdlib.h>
@@ -3507,6 +3508,52 @@ static inline bool noinstr_call_dest(struct objtool_file *file,
return false;
}
+/*
+ * Code coverage instrumentation (gcov, Clang's source-based coverage) updates
+ * its counters inline rather than through calls, so it can't be caught by
+ * checking the call destinations of noinstr code.
+ */
+static bool is_coverage_counter(struct symbol *sym)
+{
+ if (!strcmp(sym->sec->name, "__llvm_prf_cnts") ||
+ !strcmp(sym->sec->name, "__llvm_prf_bits"))
+ return true;
+
+ return (strstarts(sym->name, "__gcov") && isdigit((unsigned char)sym->name[6])) ||
+ strstarts(sym->name, "__llvm_gcov_ctr");
+}
+
+static bool insn_updates_coverage(struct objtool_file *file,
+ struct instruction *insn)
+{
+ struct symbol *sym;
+ struct reloc *reloc;
+
+ for (reloc = insn_reloc(file, insn);
+ reloc;
+ reloc = find_reloc_by_dest_range(file->elf, insn->sec,
+ reloc_offset(reloc) + 1,
+ insn->offset + insn->len -
+ (reloc_offset(reloc) + 1))) {
+
+ sym = reloc->sym;
+ if (!sym->sec)
+ continue;
+
+ if (is_sec_sym(sym)) {
+ sym = find_symbol_containing(sym->sec,
+ arch_insn_adjusted_addend(insn, reloc));
+ if (!sym)
+ sym = reloc->sym;
+ }
+
+ if (is_coverage_counter(sym))
+ return true;
+ }
+
+ return false;
+}
+
static int validate_call(struct objtool_file *file,
struct instruction *insn,
struct insn_state *state)
@@ -3680,6 +3727,12 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
if (statep->noinstr)
statep->instr += insn->instr;
+ if (statep->noinstr && statep->instr <= 0 &&
+ insn_updates_coverage(file, insn)) {
+ WARN_INSN(insn, "coverage counter update in .noinstr.text section");
+ return 1;
+ }
+
if (insn->hint) {
if (insn->restore) {
struct instruction *save_insn, *i;
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code
2026-10-28 11:59 ` [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code Sasha Levin
@ 2026-10-28 14:11 ` Peter Zijlstra
2026-10-28 16:45 ` Sasha Levin
0 siblings, 1 reply; 18+ messages in thread
From: Peter Zijlstra @ 2026-10-28 14:11 UTC (permalink / raw)
To: Sasha Levin
Cc: linux-kernel, Bill Wendling, Chuck Wolber, Josh Poimboeuf,
Justin Stitt, llvm, Marco Elver, Masami Hiramatsu,
Nathan Chancellor, Nick Desaulniers, Thomas Gleixner
On Wed, Oct 28, 2026 at 07:59:35AM -0400, Sasha Levin wrote:
> gcov and Clang's source-based code coverage update their counters
> inline, rather than by calling into a runtime like KCOV does, so
> coverage instrumentation in noinstr code goes unnoticed by the noinstr
> validation of call destinations.
>
> Flag instructions in noinstr code, outside of instrumentation_begin()
> and instrumentation_end() sections, that reference coverage counters:
> GCC's __gcov<N>.* counters (including the MC/DC counters), Clang's gcov
> __llvm_gcov_ctr* counters, and the __llvm_prf_cnts and __llvm_prf_bits
> sections of Clang's source-based code coverage.
Nice!
> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---
> tools/objtool/check.c | 53 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 53 insertions(+)
>
> diff --git a/tools/objtool/check.c b/tools/objtool/check.c
> index 464f6c9d9ff0b..6382d2a44ef2b 100644
> --- a/tools/objtool/check.c
> +++ b/tools/objtool/check.c
> @@ -4,6 +4,7 @@
> */
>
> #define _GNU_SOURCE /* memmem() */
> +#include <ctype.h>
> #include <fnmatch.h>
> #include <string.h>
> #include <stdlib.h>
> @@ -3507,6 +3508,52 @@ static inline bool noinstr_call_dest(struct objtool_file *file,
> return false;
> }
>
> +/*
> + * Code coverage instrumentation (gcov, Clang's source-based coverage) updates
> + * its counters inline rather than through calls, so it can't be caught by
> + * checking the call destinations of noinstr code.
> + */
> +static bool is_coverage_counter(struct symbol *sym)
> +{
> + if (!strcmp(sym->sec->name, "__llvm_prf_cnts") ||
> + !strcmp(sym->sec->name, "__llvm_prf_bits"))
> + return true;
> +
> + return (strstarts(sym->name, "__gcov") && isdigit((unsigned char)sym->name[6])) ||
> + strstarts(sym->name, "__llvm_gcov_ctr");
> +}
> +
> +static bool insn_updates_coverage(struct objtool_file *file,
> + struct instruction *insn)
> +{
> + struct symbol *sym;
> + struct reloc *reloc;
> +
> + for (reloc = insn_reloc(file, insn);
> + reloc;
> + reloc = find_reloc_by_dest_range(file->elf, insn->sec,
> + reloc_offset(reloc) + 1,
> + insn->offset + insn->len -
> + (reloc_offset(reloc) + 1))) {
> +
> + sym = reloc->sym;
> + if (!sym->sec)
> + continue;
> +
> + if (is_sec_sym(sym)) {
> + sym = find_symbol_containing(sym->sec,
> + arch_insn_adjusted_addend(insn, reloc));
> + if (!sym)
> + sym = reloc->sym;
> + }
> +
> + if (is_coverage_counter(sym))
> + return true;
> + }
> +
> + return false;
> +}
Since you can have many relocations to the same symbol; does it make
sense to cache the results of those many strcmp()s in symbol::coverage
or something? We have plenty bits left in struct symbol.
> +
> static int validate_call(struct objtool_file *file,
> struct instruction *insn,
> struct insn_state *state)
> @@ -3680,6 +3727,12 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
> if (statep->noinstr)
> statep->instr += insn->instr;
>
> + if (statep->noinstr && statep->instr <= 0 &&
> + insn_updates_coverage(file, insn)) {
> + WARN_INSN(insn, "coverage counter update in .noinstr.text section");
> + return 1;
> + }
Perhaps merge with the previous conditional?
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code
2026-10-28 14:11 ` Peter Zijlstra
@ 2026-10-28 16:45 ` Sasha Levin
0 siblings, 0 replies; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 16:45 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Sasha Levin, linux-kernel, Bill Wendling, Chuck Wolber,
Josh Poimboeuf, Justin Stitt, llvm, Marco Elver, Masami Hiramatsu,
Nathan Chancellor, Nick Desaulniers, Thomas Gleixner
On Wed, Oct 28, 2026 at 03:11:48PM +0100, Peter Zijlstra wrote:
>Since you can have many relocations to the same symbol; does it make
>sense to cache the results of those many strcmp()s in symbol::coverage
>or something? We have plenty bits left in struct symbol.
Sure, I'll throw in a symbol::coverage_counter bit in classify_symbols(). It
has to happen before the is_global_sym() check there, since the counters, and
the section symbols that relocations often point at, are local.
>Perhaps merge with the previous conditional?
Will do.
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC PATCH 12/20] x86/sev: Stop excluding noinstr.o from code coverage
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
` (2 preceding siblings ...)
2026-10-28 11:59 ` [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 14/20] coverage: add Clang's source-based " Sasha Levin
` (4 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
To: linux-kernel
Cc: Bill Wendling, Borislav Petkov, Chuck Wolber, Dave Hansen,
H. Peter Anvin, Ingo Molnar, Justin Stitt, llvm, Marco Elver,
Masami Hiramatsu, Nathan Chancellor, Nick Desaulniers,
Peter Zijlstra, Thomas Gleixner, x86
noinstr.o was excluded from gcov after Clang's gcov instrumentation
left calls to kasan_check_write() in __sev_es_nmi_complete(), and
because any coverage instrumentation would violate noinstr.
The GHCB accessors no longer call the KASAN and KCSAN checks, and the
noinstr attribute is now sufficient to keep coverage instrumentation
out of noinstr code, which objtool verifies. Drop the now redundant
exclusion and rely on the attributes, like the rest of the kernel's
noinstr code does.
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/coco/sev/Makefile | 2 --
1 file changed, 2 deletions(-)
diff --git a/arch/x86/coco/sev/Makefile b/arch/x86/coco/sev/Makefile
index be4785d766ec1..fb8ffedfc8b0c 100644
--- a/arch/x86/coco/sev/Makefile
+++ b/arch/x86/coco/sev/Makefile
@@ -8,5 +8,3 @@ UBSAN_SANITIZE_noinstr.o := n
# GCC may fail to respect __no_sanitize_address or __no_kcsan when inlining
KASAN_SANITIZE_noinstr.o := n
KCSAN_SANITIZE_noinstr.o := n
-
-COVERAGE_PROFILE_noinstr.o := n
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [RFC PATCH 14/20] coverage: add Clang's source-based code coverage
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
` (3 preceding siblings ...)
2026-10-28 11:59 ` [RFC PATCH 12/20] x86/sev: Stop excluding noinstr.o from code coverage Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 15/20] x86: Enable " Sasha Levin
` (3 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
To: linux-kernel
Cc: Wentao Zhang, Alexander Potapenko, Andrey Konovalov,
Arnd Bergmann, Bill Wendling, Chuck Wolber, Dmitry Vyukov,
Justin Stitt, kasan-dev, linux-arch, linux-kbuild, llvm,
Marco Elver, Masami Hiramatsu, Nathan Chancellor,
Nick Desaulniers, Nicolas Schier, Peter Oberparleiter,
Peter Zijlstra, Thomas Gleixner
Add Clang's Source-based Code Coverage [1] as a second code coverage
measurement implementation, next to gcov.
gcov works at the object code level, which may better reflect actual
execution, but its measurements can lose source level precision when
compiler optimizations are enabled, as they always are for the kernel.
In the following example from drivers/firmware/dmi_scan.c, measured on
v6.11, gcov reports six branch outcomes for an expression with four
conditions:
5: 1068: if (s == e || *e != '/' || !month || month > 12) {
branch 0 taken 5 (fallthrough)
branch 1 taken 0
branch 2 taken 5 (fallthrough)
branch 3 taken 0
branch 4 taken 0 (fallthrough)
branch 5 taken 5
Clang's Source-based Code Coverage instruments the code in the compiler
front end, so the coverage data maps back to the source code exactly,
regardless of optimizations, and reports each condition:
1068| 5| if (s == e || *e != '/' || !month || month > 12) {
------------------
| Branch (1068:6): [True: 0, False: 5]
| Branch (1068:16): [True: 0, False: 5]
| Branch (1068:29): [True: 0, False: 5]
| Branch (1068:39): [True: 0, False: 5]
------------------
It is selected with CONFIG_LLVM_COV_KERNEL in the "Code coverage
measurement" choice, uses the common COVERAGE_PROFILE annotations,
noinstr handling and reset, and exports the raw profile of the kernel
image in /sys/kernel/debug/coverage/llvm/profraw. The raw profile
formats of Clang 19 to 22 (version 10) and of Clang 23 and later
(version 11) are supported. Counters are updated atomically, like gcov
does when the compiler allows it.
The data is exported from sections that the architecture's linker script
places with LLVM_COV_DATA, so only code built into the kernel image is
instrumented; modules are left alone.
This reuses the debugfs interface and raw profile serialization of
earlier work by Sami Tolvanen et al. [2], without the parts specific to
profile guided optimization.
Link: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html [1]
Link: https://lore.kernel.org/lkml/20210407211704.367039-1-morbo@google.com/ [2]
Originally-by: Wentao Zhang <wentaoz5@illinois.edu>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
MAINTAINERS | 2 +
Makefile | 1 +
include/asm-generic/vmlinux.lds.h | 36 ++++++
kernel/coverage/Kconfig | 33 ++++-
kernel/coverage/Makefile | 1 +
kernel/coverage/llvm/Makefile | 4 +
kernel/coverage/llvm/fs.c | 201 ++++++++++++++++++++++++++++++
kernel/coverage/llvm/llvm-cov.h | 139 +++++++++++++++++++++
scripts/Makefile.lib | 7 +-
scripts/Makefile.llvm-cov | 6 +
10 files changed, 427 insertions(+), 3 deletions(-)
create mode 100644 kernel/coverage/llvm/Makefile
create mode 100644 kernel/coverage/llvm/fs.c
create mode 100644 kernel/coverage/llvm/llvm-cov.h
create mode 100644 scripts/Makefile.llvm-cov
diff --git a/MAINTAINERS b/MAINTAINERS
index 573ac65304f7c..506f25cfc3e85 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6596,6 +6596,8 @@ F: kernel/coverage/Kconfig
F: kernel/coverage/Makefile
F: kernel/coverage/core.c
F: kernel/coverage/coverage.h
+F: kernel/coverage/llvm/
+F: scripts/Makefile.llvm-cov
CODE OF CONDUCT
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Makefile b/Makefile
index cb34eb7e1b790..8f60f7b99b67a 100644
--- a/Makefile
+++ b/Makefile
@@ -1203,6 +1203,7 @@ include-$(CONFIG_KCSAN) += scripts/Makefile.kcsan
include-$(CONFIG_KMSAN) += scripts/Makefile.kmsan
include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan
include-$(CONFIG_GCOV_KERNEL) += scripts/Makefile.gcov
+include-$(CONFIG_LLVM_COV_KERNEL) += scripts/Makefile.llvm-cov
include-$(CONFIG_KCOV) += scripts/Makefile.kcov
include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct
include-$(CONFIG_KSTACK_ERASE) += scripts/Makefile.kstack_erase
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 3eb732942d699..840899b604336 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -359,6 +359,42 @@
#define THERMAL_TABLE(name)
#endif
+#ifdef CONFIG_LLVM_COV_KERNEL
+#define LLVM_COV_DATA \
+ __llvm_prf_data : AT(ADDR(__llvm_prf_data) - LOAD_OFFSET) { \
+ BOUNDED_SECTION_POST_LABEL(__llvm_prf_data, \
+ __llvm_prf_data, \
+ _start, _end) \
+ } \
+ __llvm_prf_cnts : AT(ADDR(__llvm_prf_cnts) - LOAD_OFFSET) { \
+ BOUNDED_SECTION_POST_LABEL(__llvm_prf_cnts, \
+ __llvm_prf_cnts, \
+ _start, _end) \
+ } \
+ __llvm_prf_names : AT(ADDR(__llvm_prf_names) - LOAD_OFFSET) { \
+ BOUNDED_SECTION_POST_LABEL(__llvm_prf_names, \
+ __llvm_prf_names, \
+ _start, _end) \
+ } \
+ __llvm_prf_bits : AT(ADDR(__llvm_prf_bits) - LOAD_OFFSET) { \
+ BOUNDED_SECTION_POST_LABEL(__llvm_prf_bits, \
+ __llvm_prf_bits, \
+ _start, _end) \
+ } \
+ __llvm_covfun : AT(ADDR(__llvm_covfun) - LOAD_OFFSET) { \
+ BOUNDED_SECTION_POST_LABEL(__llvm_covfun, \
+ __llvm_covfun, \
+ _start, _end) \
+ } \
+ __llvm_covmap : AT(ADDR(__llvm_covmap) - LOAD_OFFSET) { \
+ BOUNDED_SECTION_POST_LABEL(__llvm_covmap, \
+ __llvm_covmap, \
+ _start, _end) \
+ }
+#else
+#define LLVM_COV_DATA
+#endif
+
#define KERNEL_DTB() \
STRUCT_ALIGN(); \
__dtb_start = .; \
diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
index 62a6fa94bd308..acfcce23e4c97 100644
--- a/kernel/coverage/Kconfig
+++ b/kernel/coverage/Kconfig
@@ -4,6 +4,15 @@ menu "Code coverage"
config ARCH_HAS_GCOV_PROFILE_ALL
def_bool n
+config ARCH_HAS_LLVM_COV
+ bool
+ help
+ An architecture should select this when its linker script places
+ the sections of Clang's source-based code coverage (LLVM_COV_DATA).
+
+config ARCH_HAS_LLVM_COV_PROFILE_ALL
+ bool
+
choice
prompt "Code coverage measurement"
default GCOV_KERNEL if COMPILE_TEST
@@ -49,9 +58,30 @@ config GCOV_KERNEL
and its modules is exported in gcov's format in /sys/kernel/debug/gcov,
see Documentation/dev-tools/gcov.rst.
+config LLVM_COV_KERNEL
+ bool "Clang's source-based code coverage"
+ depends on CC_IS_CLANG && CLANG_VERSION >= 190000
+ depends on DEBUG_FS
+ depends on ARCH_HAS_LLVM_COV
+ depends on !ARCH_WANTS_NO_INSTR || CC_HAS_NO_PROFILE_FN_ATTR
+ help
+ This option enables Clang's Source-based Code Coverage. It
+ instruments the code in the compiler's front end, so that coverage
+ data maps back to the source code exactly, regardless of the
+ optimizations applied.
+
+ The raw profile is exported in /sys/kernel/debug/coverage/llvm/profraw.
+ Convert it with "llvm-profdata merge" and analyze it with "llvm-cov"
+ against vmlinux, using the LLVM version that built the kernel.
+
+ Only code built into the kernel image is instrumented.
+
endchoice
config COVERAGE_KERNEL
+ def_bool GCOV_KERNEL || LLVM_COV_KERNEL
+
+config COVERAGE_MODULES
def_bool GCOV_KERNEL
config GCOV_PROFILE_ALL
@@ -64,7 +94,8 @@ config GCOV_PROFILE_ALL
config COVERAGE_PROFILE_ALL
bool "Profile entire kernel"
depends on !COMPILE_TEST
- depends on GCOV_KERNEL && ARCH_HAS_GCOV_PROFILE_ALL
+ depends on (GCOV_KERNEL && ARCH_HAS_GCOV_PROFILE_ALL) || \
+ (LLVM_COV_KERNEL && ARCH_HAS_LLVM_COV_PROFILE_ALL)
default GCOV_PROFILE_ALL
help
This option activates coverage instrumentation for the entire kernel.
diff --git a/kernel/coverage/Makefile b/kernel/coverage/Makefile
index 881fada442bd1..d783272d9974d 100644
--- a/kernel/coverage/Makefile
+++ b/kernel/coverage/Makefile
@@ -2,6 +2,7 @@
obj-$(or $(CONFIG_COVERAGE_KERNEL),$(CONFIG_KCOV)) += core.o
obj-$(CONFIG_GCOV_KERNEL) += gcov/
+obj-$(CONFIG_LLVM_COV_KERNEL) += llvm/
obj-$(CONFIG_KCOV) += kcov.o
# Don't self-instrument.
diff --git a/kernel/coverage/llvm/Makefile b/kernel/coverage/llvm/Makefile
new file mode 100644
index 0000000000000..7090171b070eb
--- /dev/null
+++ b/kernel/coverage/llvm/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+COVERAGE_PROFILE := n
+
+obj-y := fs.o
diff --git a/kernel/coverage/llvm/fs.c b/kernel/coverage/llvm/fs.c
new file mode 100644
index 0000000000000..e90c325daeba3
--- /dev/null
+++ b/kernel/coverage/llvm/fs.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Sami Tolvanen <samitolvanen@google.com>, Google, Inc.
+ * Copyright (C) 2024 Jinghao Jia <jinghao7@illinois.edu>, UIUC
+ * Copyright (C) 2024 Wentao Zhang <wentaoz5@illinois.edu>, UIUC
+ */
+
+#define pr_fmt(fmt) "llvm-cov: " fmt
+
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+
+#include "../coverage.h"
+#include "llvm-cov.h"
+
+/*
+ * Serializes taking a snapshot of the profile data against resetting it. The
+ * instrumented code updates the counters and bitmaps without taking it, so a
+ * snapshot is not atomic with respect to code running on other CPUs.
+ */
+static DEFINE_MUTEX(llvm_cov_mutex);
+
+struct llvm_cov_private_data {
+ char *buffer;
+ unsigned long size;
+};
+
+/*
+ * Raw profile data format:
+ * https://llvm.org/docs/InstrProfileFormat.html#raw-profile-format. Only the
+ * information relevant to Source-based Code Coverage is populated. Binary IDs,
+ * continuous mode, single-byte mode, value profiling, type profiling and
+ * uniform counters are not implemented.
+ */
+
+static void llvm_cov_fill_raw_profile_header(void **buffer)
+{
+ struct __llvm_profile_header *header = *buffer;
+
+ header->magic = INSTR_PROF_RAW_MAGIC_64;
+ header->version = INSTR_PROF_RAW_VERSION;
+ header->binary_ids_size = 0;
+ header->num_data = __llvm_prf_data_count();
+ header->padding_bytes_before_counters = 0;
+ header->num_counters = __llvm_prf_cnts_count();
+ header->padding_bytes_after_counters =
+ __llvm_prf_get_padding(__llvm_prf_cnts_size());
+ header->num_bitmap_bytes = __llvm_prf_bits_size();
+ header->padding_bytes_after_bitmap_bytes =
+ __llvm_prf_get_padding(__llvm_prf_bits_size());
+#if CONFIG_CLANG_VERSION >= 230000
+ header->num_uniform_counters = 0;
+ header->padding_bytes_after_uniform_counters = 0;
+ header->uniform_counters_delta = 0;
+#endif
+ header->names_size = __llvm_prf_names_size();
+ header->counters_delta = (u64)__llvm_prf_cnts_start -
+ (u64)__llvm_prf_data_start;
+ header->bitmap_delta = (u64)__llvm_prf_bits_start -
+ (u64)__llvm_prf_data_start;
+ header->names_delta = (u64)__llvm_prf_names_start;
+ header->num_v_tables = 0;
+ header->v_names_size = 0;
+ header->value_kind_last = IPVK_LAST;
+
+ *buffer += sizeof(*header);
+}
+
+/*
+ * Copy the source into the buffer, incrementing the pointer into buffer in the
+ * process.
+ */
+static void llvm_cov_copy_section_to_buffer(void **buffer, void *src,
+ unsigned long size)
+{
+ memcpy(*buffer, src, size);
+ *buffer += size;
+}
+
+static unsigned long llvm_cov_get_raw_profile_size(void)
+{
+ return sizeof(struct __llvm_profile_header) +
+ __llvm_prf_data_size() +
+ __llvm_prf_cnts_size() +
+ __llvm_prf_get_padding(__llvm_prf_cnts_size()) +
+ __llvm_prf_bits_size() +
+ __llvm_prf_get_padding(__llvm_prf_bits_size()) +
+ __llvm_prf_names_size() +
+ __llvm_prf_get_padding(__llvm_prf_names_size());
+}
+
+/*
+ * Serialize in-memory data into a format LLVM tools can understand
+ * (https://llvm.org/docs/InstrProfileFormat.html#raw-profile-format)
+ */
+static int llvm_cov_serialize_raw_profile(struct llvm_cov_private_data *p)
+{
+ void *buffer;
+
+ p->size = llvm_cov_get_raw_profile_size();
+ p->buffer = vzalloc(p->size);
+ if (!p->buffer)
+ return -ENOMEM;
+
+ buffer = p->buffer;
+
+ llvm_cov_fill_raw_profile_header(&buffer);
+ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_data_start,
+ __llvm_prf_data_size());
+ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_cnts_start,
+ __llvm_prf_cnts_size());
+ buffer += __llvm_prf_get_padding(__llvm_prf_cnts_size());
+ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_bits_start,
+ __llvm_prf_bits_size());
+ buffer += __llvm_prf_get_padding(__llvm_prf_bits_size());
+ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_names_start,
+ __llvm_prf_names_size());
+
+ return 0;
+}
+
+/* open() implementation for llvm-cov data file. */
+static int llvm_cov_open(struct inode *inode, struct file *file)
+{
+ struct llvm_cov_private_data *data;
+ int err;
+
+ data = kzalloc_obj(*data);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_lock(&llvm_cov_mutex);
+ err = llvm_cov_serialize_raw_profile(data);
+ mutex_unlock(&llvm_cov_mutex);
+
+ if (err) {
+ kfree(data);
+ return err;
+ }
+
+ file->private_data = data;
+ return 0;
+}
+
+/* read() implementation for llvm-cov data file. */
+static ssize_t llvm_cov_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct llvm_cov_private_data *data = file->private_data;
+
+ return simple_read_from_buffer(buf, count, ppos, data->buffer,
+ data->size);
+}
+
+/* release() implementation for llvm-cov data file. */
+static int llvm_cov_release(struct inode *inode, struct file *file)
+{
+ struct llvm_cov_private_data *data = file->private_data;
+
+ vfree(data->buffer);
+ kfree(data);
+ return 0;
+}
+
+static const struct file_operations llvm_cov_data_fops = {
+ .owner = THIS_MODULE,
+ .open = llvm_cov_open,
+ .read = llvm_cov_read,
+ .llseek = default_llseek,
+ .release = llvm_cov_release,
+};
+
+static void llvm_cov_reset(void)
+{
+ mutex_lock(&llvm_cov_mutex);
+ memset(__llvm_prf_cnts_start, 0, __llvm_prf_cnts_size());
+ memset(__llvm_prf_bits_start, 0, __llvm_prf_bits_size());
+ mutex_unlock(&llvm_cov_mutex);
+}
+
+static const struct coverage_backend llvm_cov_backend = {
+ .name = "llvm",
+ .reset = llvm_cov_reset,
+};
+
+static int __init llvm_cov_init(void)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("llvm", coverage_debugfs_root());
+ debugfs_create_file("profraw", 0400, dir, NULL, &llvm_cov_data_fops);
+ coverage_register_backend(&llvm_cov_backend);
+
+ return 0;
+}
+device_initcall(llvm_cov_init);
diff --git a/kernel/coverage/llvm/llvm-cov.h b/kernel/coverage/llvm/llvm-cov.h
new file mode 100644
index 0000000000000..61c76e803f88b
--- /dev/null
+++ b/kernel/coverage/llvm/llvm-cov.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Sami Tolvanen <samitolvanen@google.com>, Google, Inc.
+ * Copyright (C) 2024 Jinghao Jia <jinghao7@illinois.edu>, UIUC
+ * Copyright (C) 2024 Wentao Zhang <wentaoz5@illinois.edu>, UIUC
+ */
+
+#ifndef _LLVM_COV_H
+#define _LLVM_COV_H
+
+#include <linux/types.h>
+
+/*
+ * These internal LLVM definitions must match the compiler version. See
+ * llvm/include/llvm/ProfileData/InstrProfData.inc in LLVM's source code.
+ */
+
+#define INSTR_PROF_RAW_MAGIC_64 \
+ ((u64)255 << 56 | \
+ (u64)'l' << 48 | \
+ (u64)'p' << 40 | \
+ (u64)'r' << 32 | \
+ (u64)'o' << 24 | \
+ (u64)'f' << 16 | \
+ (u64)'r' << 8 | \
+ (u64)129)
+
+#if CONFIG_CLANG_VERSION >= 230000
+#define INSTR_PROF_RAW_VERSION 11
+#else
+#define INSTR_PROF_RAW_VERSION 10
+#endif
+#define INSTR_PROF_DATA_ALIGNMENT 8
+#define IPVK_LAST 2
+
+/**
+ * struct __llvm_profile_header - represents the raw profile header data
+ * structure. Description of each member can be found here:
+ * https://llvm.org/docs/InstrProfileFormat.html#header.
+ */
+struct __llvm_profile_header {
+ u64 magic;
+ u64 version;
+ u64 binary_ids_size;
+ u64 num_data;
+ u64 padding_bytes_before_counters;
+ u64 num_counters;
+ u64 padding_bytes_after_counters;
+ u64 num_bitmap_bytes;
+ u64 padding_bytes_after_bitmap_bytes;
+#if CONFIG_CLANG_VERSION >= 230000
+ u64 num_uniform_counters;
+ u64 padding_bytes_after_uniform_counters;
+ u64 uniform_counters_delta;
+#endif
+ u64 names_size;
+ u64 counters_delta;
+ u64 bitmap_delta;
+ u64 names_delta;
+ u64 num_v_tables;
+ u64 v_names_size;
+ u64 value_kind_last;
+};
+
+/**
+ * struct __llvm_profile_data - represents the per-function control structure.
+ * Description of each member can be found here:
+ * https://llvm.org/docs/InstrProfileFormat.html#profile-metadata. To measure
+ * Source-based Code Coverage, the internals of this struct don't matter at run
+ * time. The only purpose of the definition below is to run sizeof() against it
+ * so that we can calculate the "num_data" field in header.
+ */
+struct __llvm_profile_data {
+ const u64 name_ref;
+ const u64 func_hash;
+ const void *counter_ptr;
+#if CONFIG_CLANG_VERSION >= 230000
+ const void *uniform_counter_ptr;
+#endif
+ const void *bitmap_ptr;
+ const void *function_pointer;
+ void *values;
+ const u32 num_counters;
+ const u16 num_value_sites[IPVK_LAST + 1];
+#if CONFIG_CLANG_VERSION >= 230000
+ const u16 offload_device_wave_size;
+#endif
+ const u32 num_bitmap_bytes;
+} __aligned(INSTR_PROF_DATA_ALIGNMENT);
+
+/* Payload sections */
+
+extern struct __llvm_profile_data __llvm_prf_data_start[];
+extern struct __llvm_profile_data __llvm_prf_data_end[];
+
+extern u64 __llvm_prf_cnts_start[];
+extern u64 __llvm_prf_cnts_end[];
+
+extern char __llvm_prf_names_start[];
+extern char __llvm_prf_names_end[];
+
+extern char __llvm_prf_bits_start[];
+extern char __llvm_prf_bits_end[];
+
+#define __DEFINE_SECTION_SIZE(s) \
+ static inline unsigned long __llvm_prf_ ## s ## _size(void) \
+ { \
+ unsigned long start = \
+ (unsigned long)__llvm_prf_ ## s ## _start; \
+ unsigned long end = \
+ (unsigned long)__llvm_prf_ ## s ## _end; \
+ return end - start; \
+ }
+#define __DEFINE_SECTION_COUNT(s) \
+ static inline unsigned long __llvm_prf_ ## s ## _count(void) \
+ { \
+ return __llvm_prf_ ## s ## _size() / \
+ sizeof(__llvm_prf_ ## s ## _start[0]); \
+ }
+
+__DEFINE_SECTION_SIZE(data)
+__DEFINE_SECTION_SIZE(cnts)
+__DEFINE_SECTION_SIZE(names)
+__DEFINE_SECTION_SIZE(bits)
+
+__DEFINE_SECTION_COUNT(data)
+__DEFINE_SECTION_COUNT(cnts)
+__DEFINE_SECTION_COUNT(names)
+__DEFINE_SECTION_COUNT(bits)
+
+#undef __DEFINE_SECTION_SIZE
+#undef __DEFINE_SECTION_COUNT
+
+static inline unsigned long __llvm_prf_get_padding(unsigned long size)
+{
+ return 7 & (sizeof(u64) - size % sizeof(u64));
+}
+
+#endif /* _LLVM_COV_H */
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index e0dc21e009522..77d1ab47125be 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -42,10 +42,13 @@ _cpp_flags = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(target-stem).lds)
# depending on variables COVERAGE_PROFILE_obj.o, COVERAGE_PROFILE and
# CONFIG_COVERAGE_PROFILE_ALL (in this order)
#
+# Modules are only instrumented if the coverage implementation supports them.
+#
ifeq ($(CONFIG_COVERAGE_KERNEL),y)
-_c_flags += $(if $(patsubst n%,, \
+_c_flags += $(if $(if $(part-of-module),$(CONFIG_COVERAGE_MODULES),y), \
+ $(if $(patsubst n%,, \
$(COVERAGE_PROFILE_$(target-stem).o)$(COVERAGE_PROFILE)$(if $(is-kernel-object),$(CONFIG_COVERAGE_PROFILE_ALL))), \
- $(CFLAGS_COVERAGE))
+ $(CFLAGS_COVERAGE)))
endif
#
diff --git a/scripts/Makefile.llvm-cov b/scripts/Makefile.llvm-cov
new file mode 100644
index 0000000000000..5babb09b6d3e6
--- /dev/null
+++ b/scripts/Makefile.llvm-cov
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+CFLAGS_COVERAGE := -fprofile-instr-generate -fcoverage-mapping \
+ -fprofile-update=atomic
+
+export CFLAGS_COVERAGE
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [RFC PATCH 15/20] x86: Enable Clang's source-based code coverage
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
` (4 preceding siblings ...)
2026-10-28 11:59 ` [RFC PATCH 14/20] coverage: add Clang's source-based " Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 16/20] coverage: add MC/DC measurement Sasha Levin
` (2 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
To: linux-kernel
Cc: Wentao Zhang, Bill Wendling, Borislav Petkov, Chuck Wolber,
Dave Hansen, H. Peter Anvin, Ingo Molnar, Justin Stitt, llvm,
Marco Elver, Masami Hiramatsu, Nathan Chancellor,
Nick Desaulniers, Peter Zijlstra, Thomas Gleixner, x86
Place the sections of Clang's source-based code coverage in the x86
linker script and allow it to be used, including for the entire
kernel, on x86_64.
Originally-by: Wentao Zhang <wentaoz5@illinois.edu>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/Kconfig | 2 ++
arch/x86/kernel/vmlinux.lds.S | 2 ++
2 files changed, 4 insertions(+)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index a5e29ffd5ddc3..0c26d61350553 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -89,6 +89,8 @@ config X86
select ARCH_HAS_FORTIFY_SOURCE
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_HAS_KCOV if X86_64
+ select ARCH_HAS_LLVM_COV if X86_64
+ select ARCH_HAS_LLVM_COV_PROFILE_ALL if X86_64
select ARCH_HAS_KERNEL_FPU_SUPPORT
select ARCH_HAS_MEM_ENCRYPT
select ARCH_HAS_MEMBARRIER_SYNC_CORE
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 2438b89a4620a..98f41b943fe63 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -212,6 +212,8 @@ SECTIONS
BUG_TABLE
+ LLVM_COV_DATA
+
ORC_UNWIND_TABLE
/* Init code and data - will be freed after init */
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [RFC PATCH 16/20] coverage: add MC/DC measurement
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
` (5 preceding siblings ...)
2026-10-28 11:59 ` [RFC PATCH 15/20] x86: Enable " Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
2026-10-28 19:14 ` Nathan Chancellor
2026-10-28 11:59 ` [RFC PATCH 19/20] gcov: add MC/DC condition coverage support Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 20/20] Documentation: dev-tools: document the common code coverage interface Sasha Levin
8 siblings, 1 reply; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
To: linux-kernel
Cc: Wentao Zhang, Alexander Potapenko, Andrey Konovalov,
Bill Wendling, Chuck Wolber, Dmitry Vyukov, Justin Stitt,
kasan-dev, linux-kbuild, llvm, Marco Elver, Masami Hiramatsu,
Nathan Chancellor, Nick Desaulniers, Nicolas Schier,
Peter Oberparleiter, Peter Zijlstra, Thomas Gleixner
Add CONFIG_COVERAGE_MCDC to measure Modified Condition/Decision
Coverage (MC/DC) [1], which is required by several automotive and
aviation standards for certifying safety critical software [2]. Start
with Clang's source-based code coverage, which supports it since Clang
18 [3].
In the following example from arch/x86/events/probe.c, measured on
v6.11, llvm-cov gives the MC/DC measurement for the compound logic
decision at line 43:
43| 12| if (msr[bit].test && !msr[bit].test(bit, data))
------------------
|---> MC/DC Decision Region (43:8) to (43:50)
|
| Number of Conditions: 2
| Condition C1 --> (43:8)
| Condition C2 --> (43:25)
|
| Executed MC/DC Test Vectors:
|
| C1, C2 Result
| 1 { T, F = F }
| 2 { T, T = T }
|
| C1-Pair: not covered
| C2-Pair: covered: (1,2)
| MC/DC Coverage for Decision: 50.00%
|
------------------
44| 5| continue;
Only condition C2 was shown to independently affect the outcome of the
decision, so the MC/DC of this decision is 1 out of 2.
MC/DC uses bitmaps to track the executed test vectors, which grow
quickly with the number of conditions. CONFIG_LLVM_COV_MCDC_MAX_CONDITIONS
sets the largest decision to instrument, through -fmcdc-max-conditions.
Larger decisions, and decisions the compiler can't instrument, produce
warnings. Starting with Clang 22 they are kept from becoming errors
with CONFIG_WERROR; older versions can't be used together with it.
Link: https://en.wikipedia.org/wiki/Modified_condition%2Fdecision_coverage [1]
Link: https://digital-library.theiet.org/content/journals/10.1049/sej.1994.0025 [2]
Link: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#mc-dc-instrumentation [3]
Originally-by: Wentao Zhang <wentaoz5@illinois.edu>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
kernel/coverage/Kconfig | 26 ++++++++++++++++++++++++++
kernel/coverage/llvm/fs.c | 5 +++--
scripts/Makefile.llvm-cov | 5 +++++
3 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
index acfcce23e4c97..fab3da51305bb 100644
--- a/kernel/coverage/Kconfig
+++ b/kernel/coverage/Kconfig
@@ -106,6 +106,32 @@ config COVERAGE_PROFILE_ALL
larger and run slower. Also be sure to exclude files from profiling
which are not linked to the kernel image to prevent linker errors.
+config COVERAGE_MCDC
+ bool "Measure modified condition/decision coverage (MC/DC)"
+ depends on LLVM_COV_KERNEL
+ depends on CLANG_VERSION >= 220000 || !WERROR
+ help
+ MC/DC verifies that each condition of a boolean decision has been
+ shown to independently affect the outcome of the decision. It is
+ required for certification by safety standards such as DO-178C
+ (avionics) and ISO 26262 (automotive).
+
+ Decisions that the compiler can't instrument produce build warnings
+ and are not measured.
+
+ If unsure, say N.
+
+config LLVM_COV_MCDC_MAX_CONDITIONS
+ int "Maximum number of conditions in a decision to instrument"
+ range 6 32767
+ depends on COVERAGE_MCDC && LLVM_COV_KERNEL
+ default 6
+ help
+ This value is passed to the "-fmcdc-max-conditions" option of Clang.
+ Decisions with more conditions produce warnings and are not
+ instrumented. MC/DC uses bitmaps to track the executed test vectors,
+ which grow quickly with the number of conditions.
+
# The per-subsystem COVERAGE_PROFILE_* options used to be GCOV_PROFILE_*.
config GCOV_PROFILE_AMDGPU
bool
diff --git a/kernel/coverage/llvm/fs.c b/kernel/coverage/llvm/fs.c
index e90c325daeba3..16ada19650d07 100644
--- a/kernel/coverage/llvm/fs.c
+++ b/kernel/coverage/llvm/fs.c
@@ -184,8 +184,9 @@ static void llvm_cov_reset(void)
}
static const struct coverage_backend llvm_cov_backend = {
- .name = "llvm",
- .reset = llvm_cov_reset,
+ .name = "llvm",
+ .features = IS_ENABLED(CONFIG_COVERAGE_MCDC) ? "mcdc" : NULL,
+ .reset = llvm_cov_reset,
};
static int __init llvm_cov_init(void)
diff --git a/scripts/Makefile.llvm-cov b/scripts/Makefile.llvm-cov
index 5babb09b6d3e6..771a033a92669 100644
--- a/scripts/Makefile.llvm-cov
+++ b/scripts/Makefile.llvm-cov
@@ -3,4 +3,9 @@
CFLAGS_COVERAGE := -fprofile-instr-generate -fcoverage-mapping \
-fprofile-update=atomic
+ifdef CONFIG_COVERAGE_MCDC
+CFLAGS_COVERAGE += -fcoverage-mcdc $(call cc-option,-Wno-error=pgo-coverage) \
+ -Xclang -fmcdc-max-conditions=$(CONFIG_LLVM_COV_MCDC_MAX_CONDITIONS)
+endif
+
export CFLAGS_COVERAGE
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [RFC PATCH 16/20] coverage: add MC/DC measurement
2026-10-28 11:59 ` [RFC PATCH 16/20] coverage: add MC/DC measurement Sasha Levin
@ 2026-10-28 19:14 ` Nathan Chancellor
2026-10-28 20:39 ` Sasha Levin
0 siblings, 1 reply; 18+ messages in thread
From: Nathan Chancellor @ 2026-10-28 19:14 UTC (permalink / raw)
To: Sasha Levin
Cc: linux-kernel, Wentao Zhang, Alexander Potapenko, Andrey Konovalov,
Bill Wendling, Chuck Wolber, Dmitry Vyukov, Justin Stitt,
kasan-dev, linux-kbuild, llvm, Marco Elver, Masami Hiramatsu,
Nick Desaulniers, Nicolas Schier, Peter Oberparleiter,
Peter Zijlstra, Thomas Gleixner
On Wed, Oct 28, 2026 at 07:59:40AM -0400, Sasha Levin wrote:
> Larger decisions, and decisions the compiler can't instrument, produce
> warnings. Starting with Clang 22 they are kept from becoming errors
> with CONFIG_WERROR; older versions can't be used together with it.
What is the reason for keeping these warnings in the build as opposed to
just disabling them altogether? Are developers expected to address them
and if so, how? It seems odd to keep them in the build but prevent them
from breaking the build unless they are not intended to be acted on, in
which case they should just be disabled outright.
--
Cheers,
Nathan
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC PATCH 16/20] coverage: add MC/DC measurement
2026-10-28 19:14 ` Nathan Chancellor
@ 2026-10-28 20:39 ` Sasha Levin
0 siblings, 0 replies; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 20:39 UTC (permalink / raw)
To: Nathan Chancellor
Cc: linux-kernel, Wentao Zhang, Alexander Potapenko, Andrey Konovalov,
Bill Wendling, Chuck Wolber, Dmitry Vyukov, Justin Stitt,
kasan-dev, linux-kbuild, llvm, Marco Elver, Masami Hiramatsu,
Nick Desaulniers, Nicolas Schier, Peter Oberparleiter,
Peter Zijlstra, Thomas Gleixner
On Wed, Oct 28, 2026 at 12:14:32PM -0700, Nathan Chancellor wrote:
>On Wed, Oct 28, 2026 at 07:59:40AM -0400, Sasha Levin wrote:
>> Larger decisions, and decisions the compiler can't instrument, produce
>> warnings. Starting with Clang 22 they are kept from becoming errors
>> with CONFIG_WERROR; older versions can't be used together with it.
>
>What is the reason for keeping these warnings in the build as opposed to
>just disabling them altogether? Are developers expected to address them
>and if so, how? It seems odd to keep them in the build but prevent them
>from breaking the build unless they are not intended to be acted on, in
>which case they should just be disabled outright.
They're not meant to be acted on. Clang only put them in a warning group
(-Wpgo-coverage) in 22, so before that there was no way to turn them
off, which is why I just left them on. I'll disable them in v2 where
Clang allows it.
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC PATCH 19/20] gcov: add MC/DC condition coverage support
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
` (6 preceding siblings ...)
2026-10-28 11:59 ` [RFC PATCH 16/20] coverage: add MC/DC measurement Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 20/20] Documentation: dev-tools: document the common code coverage interface Sasha Levin
8 siblings, 0 replies; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
To: linux-kernel
Cc: Alexander Potapenko, Andrey Konovalov, Anton Ivanov,
Bill Wendling, Borislav Petkov, Chuck Wolber, Dave Hansen,
Dmitry Vyukov, H. Peter Anvin, Ingo Molnar, Johannes Berg,
Justin Stitt, kasan-dev, linux-kbuild, linux-um, llvm,
Marco Elver, Masami Hiramatsu, Nathan Chancellor,
Nick Desaulniers, Nicolas Schier, Peter Oberparleiter,
Peter Zijlstra, Richard Weinberger, Thomas Gleixner, x86
Support CONFIG_COVERAGE_MCDC with gcov, using GCC's MC/DC condition
coverage instrumentation (-fcondition-coverage), which is available
since GCC 14. The data is shown by "gcov --conditions".
Add -fcondition-coverage and -Wno-error=coverage-too-many-conditions to
the gcov compiler flags when enabled. Both flags are gated on the config
option to avoid Clang warnings about unknown options.
Also add -fcondition-coverage to CFLAGS_REMOVE in the x86 UML vDSO
Makefile to prevent instrumentation of userspace vDSO code.
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/x86/um/vdso/Makefile | 4 ++--
kernel/coverage/Kconfig | 7 +++++--
kernel/coverage/gcov/fs.c | 5 +++--
scripts/Makefile.gcov | 3 +++
4 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/arch/x86/um/vdso/Makefile b/arch/x86/um/vdso/Makefile
index 7664cbedbe30f..adfc57c1a726d 100644
--- a/arch/x86/um/vdso/Makefile
+++ b/arch/x86/um/vdso/Makefile
@@ -42,8 +42,8 @@ $(vobjs): KBUILD_CFLAGS += $(CFL)
#
# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
#
-CFLAGS_REMOVE_vdso-note.o = -pg -fprofile-arcs -ftest-coverage
-CFLAGS_REMOVE_um_vdso.o = -pg -fprofile-arcs -ftest-coverage
+CFLAGS_REMOVE_vdso-note.o = -pg -fprofile-arcs -ftest-coverage -fcondition-coverage
+CFLAGS_REMOVE_um_vdso.o = -pg -fprofile-arcs -ftest-coverage -fcondition-coverage
#
# The DSO images are built using a special linker script.
diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
index fab3da51305bb..5990e366db994 100644
--- a/kernel/coverage/Kconfig
+++ b/kernel/coverage/Kconfig
@@ -108,8 +108,8 @@ config COVERAGE_PROFILE_ALL
config COVERAGE_MCDC
bool "Measure modified condition/decision coverage (MC/DC)"
- depends on LLVM_COV_KERNEL
- depends on CLANG_VERSION >= 220000 || !WERROR
+ depends on LLVM_COV_KERNEL || (GCOV_KERNEL && CC_HAS_CONDITION_COVERAGE)
+ depends on !LLVM_COV_KERNEL || CLANG_VERSION >= 220000 || !WERROR
help
MC/DC verifies that each condition of a boolean decision has been
shown to independently affect the outcome of the decision. It is
@@ -119,6 +119,9 @@ config COVERAGE_MCDC
Decisions that the compiler can't instrument produce build warnings
and are not measured.
+ With gcov this requires GCC 14 or later, and the data is shown by
+ "gcov --conditions".
+
If unsure, say N.
config LLVM_COV_MCDC_MAX_CONDITIONS
diff --git a/kernel/coverage/gcov/fs.c b/kernel/coverage/gcov/fs.c
index 23d366dcfc2f7..dfaccc01e06f3 100644
--- a/kernel/coverage/gcov/fs.c
+++ b/kernel/coverage/gcov/fs.c
@@ -866,8 +866,9 @@ void gcov_event(enum gcov_action action, struct gcov_info *info)
}
static const struct coverage_backend gcov_backend = {
- .name = "gcov",
- .reset = gcov_reset_all,
+ .name = "gcov",
+ .features = IS_ENABLED(CONFIG_COVERAGE_MCDC) ? "mcdc" : NULL,
+ .reset = gcov_reset_all,
};
/* Create debugfs entries. */
diff --git a/scripts/Makefile.gcov b/scripts/Makefile.gcov
index 1620b81398eaa..b00dd40f2167d 100644
--- a/scripts/Makefile.gcov
+++ b/scripts/Makefile.gcov
@@ -19,4 +19,7 @@ CFLAGS_COVERAGE += $(call try-run,\
cmp -s "$$TMP.ubase" "$$TMP.utest",\
-fprofile-update=prefer-atomic)
endif
+ifdef CONFIG_COVERAGE_MCDC
+CFLAGS_COVERAGE += -fcondition-coverage -Wno-error=coverage-too-many-conditions
+endif
export CFLAGS_COVERAGE
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [RFC PATCH 20/20] Documentation: dev-tools: document the common code coverage interface
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
` (7 preceding siblings ...)
2026-10-28 11:59 ` [RFC PATCH 19/20] gcov: add MC/DC condition coverage support Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
8 siblings, 0 replies; 18+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
To: linux-kernel
Cc: Alexander Potapenko, Andrey Konovalov, Bill Wendling,
Chuck Wolber, Dmitry Vyukov, Jonathan Corbet, Justin Stitt,
kasan-dev, linux-doc, llvm, Marco Elver, Masami Hiramatsu,
Nathan Chancellor, Nick Desaulniers, Peter Oberparleiter,
Peter Zijlstra, Randy Dunlap, Shuah Khan, Thomas Gleixner,
workflows
Describe the code coverage implementations and what they share: the
Kconfig options, the COVERAGE_PROFILE annotations, the
/sys/kernel/debug/coverage/ interface, MC/DC measurement, and how they
deal with noinstr code. Point to it from the gcov and KCOV
documentation, and document gcov's MC/DC support.
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
Documentation/dev-tools/coverage.rst | 158 +++++++++++++++++++++++++++
Documentation/dev-tools/gcov.rst | 28 +++++
Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/kcov.rst | 3 +
MAINTAINERS | 1 +
5 files changed, 191 insertions(+)
create mode 100644 Documentation/dev-tools/coverage.rst
diff --git a/Documentation/dev-tools/coverage.rst b/Documentation/dev-tools/coverage.rst
new file mode 100644
index 0000000000000..547863b2afd6d
--- /dev/null
+++ b/Documentation/dev-tools/coverage.rst
@@ -0,0 +1,158 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============
+Code coverage
+=============
+
+The kernel can be instrumented to find out which parts of it get executed.
+This serves two different purposes:
+
+* Measuring code coverage, e.g. to assess and improve a test suite, or to
+ show the structural coverage required by safety standards. This is done by
+ one of the code coverage measurement implementations described below.
+
+* Guiding fuzzers. This is done by KCOV, which collects coverage per task;
+ see Documentation/dev-tools/kcov.rst.
+
+All of them are configured in the "Kernel hacking -> Kernel Testing and
+Coverage -> Code coverage" menu, live in ``kernel/coverage/``, and share the
+``/sys/kernel/debug/coverage/`` debugfs directory.
+
+
+Code coverage measurement
+=========================
+
+The implementation is selected by the "Code coverage measurement" choice:
+
+============================ ============== ================================
+Option Compilers Data
+============================ ============== ================================
+``CONFIG_GCOV_KERNEL`` GCC, Clang gcov ``.gcda`` files, see
+ Documentation/dev-tools/gcov.rst
+``CONFIG_LLVM_COV_KERNEL`` Clang 19 and LLVM raw profile of the kernel
+ later image (``.profraw``)
+============================ ============== ================================
+
+The instrumentation and the format of the data are defined by the toolchain,
+so only one implementation can be used at a time. Both only instrument C
+code.
+
+Clang's source-based code coverage (``CONFIG_LLVM_COV_KERNEL``) instruments
+the code in the compiler front end. Its data therefore maps back to the source
+code exactly, regardless of the optimizations applied, and it can show the
+coverage of every condition of a decision. It only instruments code built into
+the kernel image. gcov also supports modules.
+
+
+Selecting the code to instrument
+--------------------------------
+
+``CONFIG_COVERAGE_PROFILE_ALL`` instruments the entire kernel. On architectures
+that don't support it, or to instrument only parts of the kernel, add a line
+like the following to the respective Makefile.
+
+For a single file (e.g. main.o)::
+
+ COVERAGE_PROFILE_main.o := y
+
+For all files in one directory::
+
+ COVERAGE_PROFILE := y
+
+To exclude files from being instrumented even when
+``CONFIG_COVERAGE_PROFILE_ALL`` is enabled, use::
+
+ COVERAGE_PROFILE_main.o := n
+
+and::
+
+ COVERAGE_PROFILE := n
+
+The per-file setting takes precedence over the per-directory one, which takes
+precedence over ``CONFIG_COVERAGE_PROFILE_ALL``.
+
+Only exclude code that can't work when instrumented, for example because it
+runs before the kernel is relocated or is copied elsewhere. Code that must not
+be instrumented for correctness reasons, such as the entry code, is annotated
+``noinstr`` instead; see `noinstr`_.
+
+
+Interface
+---------
+
+``/sys/kernel/debug/coverage/`` contains:
+
+``backend``
+ The name of the selected implementation, ``gcov`` or ``llvm``, followed by
+ ``mcdc`` when MC/DC is measured.
+
+``reset``
+ Writing to this file resets all coverage data of the selected
+ implementation. It does not affect KCOV.
+
+``gcov``
+ A symlink to ``/sys/kernel/debug/gcov``, where gcov keeps its data (see
+ Documentation/dev-tools/gcov.rst).
+
+``llvm/profraw``
+ The raw profile of the kernel image, for Clang's source-based code
+ coverage.
+
+``kcov``
+ A symlink to ``/sys/kernel/debug/kcov`` (see
+ Documentation/dev-tools/kcov.rst).
+
+The instrumented code updates the coverage data without synchronizing with
+readers, so reading the data while other CPUs execute instrumented code
+doesn't give an atomic snapshot, and the same applies to a reset.
+
+With Clang's source-based code coverage, convert the raw profile and create a
+report with the ``llvm-profdata`` and ``llvm-cov`` tools of the LLVM version
+that built the kernel, since the raw profile format changes between releases::
+
+ # cp /sys/kernel/debug/coverage/llvm/profraw vmlinux.profraw
+ $ llvm-profdata merge vmlinux.profraw -o vmlinux.profdata
+ $ llvm-cov report -instr-profile=vmlinux.profdata vmlinux
+ $ llvm-cov show -instr-profile=vmlinux.profdata vmlinux kernel/fork.c
+
+
+MC/DC
+-----
+
+Modified condition/decision coverage (MC/DC) verifies that each condition of a
+boolean decision has been shown to independently affect the outcome of the
+decision. It is required by safety standards such as DO-178C (avionics) and
+ISO 26262 (automotive). Enable it with ``CONFIG_COVERAGE_MCDC``:
+
+* With Clang's source-based code coverage, decisions with more than
+ ``CONFIG_LLVM_COV_MCDC_MAX_CONDITIONS`` conditions are not measured. Show
+ the results with ``llvm-cov show -show-mcdc`` and
+ ``llvm-cov report -show-mcdc-summary``.
+
+* With gcov, GCC 14 or later is required, and decisions with more than 64
+ conditions are not measured. Show the results with ``gcov --conditions``.
+
+The compiler warns about every decision it doesn't instrument.
+
+
+noinstr
+=======
+
+Code annotated ``noinstr`` must not be instrumented; this includes the code
+running on kernel entry and exit, and in NMI context. Outside of
+``instrumentation_begin()``/``instrumentation_end()`` sections, ``noinstr``
+functions may only call other ``noinstr`` functions and ``__always_inline``
+functions.
+
+The code coverage measurement implementations insert their instrumentation
+before functions get inlined, which would carry the instrumentation of an
+``__always_inline`` function into the ``noinstr`` code it is inlined into. To
+avoid that, ``__always_inline`` functions are not instrumented when one of them
+is enabled, and as a consequence they don't show up in the coverage data.
+
+With ``CONFIG_NOINSTR_VALIDATION``, objtool reports coverage counters that get
+updated by ``noinstr`` code outside of such sections.
+
+KCOV instruments the code after inlining and honours ``noinstr`` through the
+``no_sanitize_coverage`` attribute with Clang and GCC 12 or later; with older
+GCC, objtool removes its calls from ``noinstr`` code.
diff --git a/Documentation/dev-tools/gcov.rst b/Documentation/dev-tools/gcov.rst
index b6910c3fc00dd..d2950070f3ca4 100644
--- a/Documentation/dev-tools/gcov.rst
+++ b/Documentation/dev-tools/gcov.rst
@@ -22,6 +22,9 @@ Possible uses:
* minimizing kernel configurations (do I need this option if the
associated code is never run?)
+See Documentation/dev-tools/coverage.rst for the options and interfaces shared
+with the other code coverage implementations.
+
.. _gcov: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html
.. _lcov: https://github.com/linux-test-project/lcov
@@ -180,6 +183,31 @@ b) gcov is run on the BUILD machine
[user@build] gcov -o /tmp/coverage/tmp/out/init main.c
+MC/DC Coverage
+--------------
+
+When using GCC 14 or later with ``CONFIG_COVERAGE_MCDC=y``, Modified
+Condition/Decision Coverage (MC/DC) data is collected alongside standard
+branch coverage. MC/DC verifies that each condition in a decision
+independently affects the decision's outcome.
+
+To view MC/DC data, use::
+
+ gcov --conditions -o /sys/kernel/debug/gcov/path/to/dir file.c
+
+MC/DC coverage is required by safety standards such as DO-178C
+(avionics) and ISO 26262 (automotive).
+
+GCC's condition coverage implementation has a limit of 64 conditions per
+boolean expression, due to the use of 64-bit bitmasks internally.
+Expressions exceeding this limit cannot be instrumented and will produce
+a compiler warning. This does not affect the correctness of the kernel;
+those expressions simply will not have MC/DC coverage data.
+
+Note that MC/DC instrumentation increases binary size and execution
+overhead compared to standard gcov profiling.
+
+
Note on compilers
-----------------
diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst
index 59cbb77b33ff4..d670c1398e495 100644
--- a/Documentation/dev-tools/index.rst
+++ b/Documentation/dev-tools/index.rst
@@ -23,6 +23,7 @@ Documentation/process/debugging/index.rst
coccinelle
context-analysis
sparse
+ coverage
kcov
gcov
kasan
diff --git a/Documentation/dev-tools/kcov.rst b/Documentation/dev-tools/kcov.rst
index 1a739290c8ecc..98a30a1a3f88a 100644
--- a/Documentation/dev-tools/kcov.rst
+++ b/Documentation/dev-tools/kcov.rst
@@ -12,6 +12,9 @@ To achieve this goal, it does not collect coverage in soft/hard interrupts
(unless remove coverage collection is enabled, see below) and from some
inherently non-deterministic parts of the kernel (e.g. scheduler, locking).
+KCOV is configured next to the other code coverage implementations, see
+Documentation/dev-tools/coverage.rst.
+
Besides collecting code coverage, KCOV can also collect comparison operands.
See the "Comparison operands collection" section for details.
diff --git a/MAINTAINERS b/MAINTAINERS
index 506f25cfc3e85..ff9b355c2fc0d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6592,6 +6592,7 @@ F: drivers/media/platform/chips-media/coda
CODE COVERAGE
M: Sasha Levin <sashal@kernel.org>
S: Maintained
+F: Documentation/dev-tools/coverage.rst
F: kernel/coverage/Kconfig
F: kernel/coverage/Makefile
F: kernel/coverage/core.c
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread