Unify code coverage and add Clang source-based coverage with MC/DC
Subsystems (44)
cover Unify code coverage and add Clang source-based coverage with MC/DC
The previous postings of Clang's source-based code coverage [1][2] drew objections [3][4]: rather than a third coverage implementation with its own interface next to gcov and KCOV, reviewers asked for a unified coverage interface, and for coverage instrumentation to honour noinstr. This series first restructures the existing coverage support into one framework, then adds source-based coverage and MC/DC as part of it.
Architecture
============
Today, each coverage implementation brings its own plumbing. gcov has its compiler flags in the top-level Makefile, the GCOV_PROFILE annotations, its options under "General architecture-dependent options" and /sys/kernel/debug/gcov/. KCOV has scripts/Makefile.kcov, KCOV_INSTRUMENT, its options under "Kernel hacking" and /sys/kernel/debug/kcov. Anything that depends on the kernel being built with coverage instrumentation spells it GCOV_KERNEL, and the earlier source-based coverage postings added a third copy of all of it.
This series turns that into one framework, kernel/coverage/:
kernel/coverage/
core.c, coverage.h debugfs root, backend registration, common reset
gcov/ gcov backend (moved from kernel/gcov/)
llvm/ Clang source-based coverage backend (new)
kcov.c KCOV (moved from kernel/kcov.c)A code coverage measurement implementation, or backend, only provides what is specific to its toolchain:
- its compiler flags, as CFLAGS_COVERAGE in scripts/Makefile.<backend>, including the ones for MC/DC;
- a struct coverage_backend registered with the core: its name, its enabled features and how to reset its data;
- its data, reachable under /sys/kernel/debug/coverage/;
- its capabilities: the architectures it supports, and whether it can instrument modules.
Everything else is shared by all backends:
- Configuration: one "Code coverage" menu with a choice of backend (none, gcov or Clang source-based coverage, as each data format is defined by its toolchain), CONFIG_COVERAGE_KERNEL for anything that depends on coverage instrumentation (LTO, KCFI, ...), CONFIG_COVERAGE_PROFILE_ALL, the per-subsystem CONFIG_COVERAGE_PROFILE_<subsystem> options and CONFIG_COVERAGE_MCDC. Existing configurations are migrated.
- Build control: COVERAGE_PROFILE[_obj.o] decides what gets instrumented, whichever backend is selected. Kbuild applies the backend's flags, leaves modules alone for backends that can't export their data, and sets up objtool accordingly.
- Runtime interface: /sys/kernel/debug/coverage/, with "backend" naming the backend and its features, "reset" resetting its data, and the backend's data files. The existing gcov and kcov entries stay where they are, for lcov and syzkaller, and are linked from there.
- noinstr: one mechanism for all backends, enforced by objtool, see below.
With that in place, adding source-based coverage (patch 14) only takes the backend specific parts.
KCOV is a per-task tracing interface for fuzzers with its own UAPI, so it is not a backend: it moves into kernel/coverage/, the same menu, the same debugfs root and the same documentation, and its noinstr handling is unified, but it keeps its UAPI and KCOV_INSTRUMENT, whose exclusions serve the fuzzing signal and the KCOV runtime rather than marking code that can't be instrumented.
noinstr
=======
gcov and source-based coverage instrument the code before inlining, so __always_inline helpers carried their counters into noinstr code: in an x86 build, 2833 relocations in .noinstr.text and .cpuidle.text referenced coverage counters. objtool didn't notice, as these are plain memory updates rather than calls. KCOV instruments after inlining and was not affected.
- Patch 4 stops instrumenting __always_inline functions in coverage builds, which costs about 1-2% of the coverage data.
- Patches 5-10 make the empty stubs that noinstr code calls __always_inline, and switch the GHCB accessors to the arch_ bitops.
- Patch 11 makes objtool report coverage counter updates in noinstr code.
- Patches 12 and 13 drop the coverage exclusions that only existed for
noinstr: arch/x86/coco/sev/noinstr.o, added by 9efb74f84ba8 ("x86/sev: Disable GCOV on noinstr object"), and the KCOV exclusion of the x86 entry code. The generic entry code stays excluded from KCOV for a different reason, which patch 13 documents.
Source-based coverage and MC/DC
===============================
Patches 14-20 add Clang's source-based code coverage for code built into the kernel image (raw profile formats of Clang 19-22 and 23+), enable it on x86_64, add MC/DC measurement for it and for gcov with GCC 14+ [5], including the gcov merge fix MC/DC needs, and document the result.
Testing
=======
- x86_64 with CONFIG_DEBUG_ENTRY, gcov with GCC 16 and Clang 22, and source-based coverage with Clang 19, 22 and 24, including MC/DC, on configurations toggling lockdep, context tracking, SEV and rseq:
objtool is clean and no coverage counter is left in noinstr code.
- KCOV with GCC 16 and Clang 22: the noinstr functions of the x86 entry code have no KCOV calls, and system call traces don't pick up interrupt code.
- Booted in a VM: gcov and source-based coverage data, reset, MC/DC reports from llvm-cov and gcov --conditions, gcov data persistence across module reloads, and KCOV.
- Every patch builds, kernels without coverage produce identical compiler invocations, and arm64, i386 and UML build with coverage.
[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
[3] https://lore.kernel.org/all/[email protected]/
[4] https://lore.kernel.org/all/[email protected]/
[5] https://lore.kernel.org/all/[email protected]/
Sasha Levin (20):
coverage: move gcov and kcov under kernel/coverage/
kbuild: unify static coverage instrumentation as COVERAGE_PROFILE
coverage: add a common debugfs interface
compiler_types.h: Don't instrument __always_inline functions for
coverage
context_tracking: Always inline the empty stubs called from noinstr
code
lockdep: Always inline the empty stubs called from noinstr code
perf: Always inline the empty perf_lopwr_cb() stub
rseq: Always inline the empty rseq_note_user_irq_entry() stub
x86: Always inline empty stubs called from noinstr code x86/sev: Use non-instrumented bitops in the GHCB accessors
objtool: Detect code coverage instrumentation in noinstr code x86/sev: Stop excluding noinstr.o from code coverage x86/entry: Stop excluding the entry code from KCOV
coverage: add Clang's source-based code coverage
x86: Enable Clang's source-based code coverage
coverage: add MC/DC measurement
gcov: fix gcov_info_add() merge semantics for IOR counters
kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection
gcov: add MC/DC condition coverage support
Documentation: dev-tools: document the common code coverage interface
Documentation/dev-tools/coverage.rst | 158 ++++++++++++
Documentation/dev-tools/gcov.rst | 46 +++-
Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/kcov.rst | 3 +
.../translations/zh_CN/dev-tools/gcov.rst | 16 +-
.../translations/zh_TW/dev-tools/gcov.rst | 16 +-
MAINTAINERS | 20 +-
Makefile | 23 +-
arch/Kconfig | 8 +-
arch/alpha/boot/compressed/Makefile | 2 +-
arch/parisc/kernel/vdso32/Makefile | 2 +-
arch/parisc/kernel/vdso64/Makefile | 2 +-
arch/powerpc/kernel/Makefile | 6 +-
arch/powerpc/kernel/trace/Makefile | 4 +-
arch/powerpc/kexec/Makefile | 2 +-
arch/powerpc/xmon/Makefile | 2 +-
arch/s390/boot/Makefile | 2 +-
arch/s390/kernel/Makefile | 2 +-
arch/sh/mm/Makefile | 2 +-
arch/x86/Kconfig | 2 +
arch/x86/coco/sev/Makefile | 2 -
arch/x86/entry/Makefile | 1 -
arch/x86/include/asm/processor.h | 2 +-
arch/x86/include/asm/sev.h | 6 +-
arch/x86/include/asm/svm.h | 8 +-
arch/x86/kernel/vmlinux.lds.S | 2 +
arch/x86/platform/efi/Makefile | 2 +-
arch/x86/um/vdso/Makefile | 4 +-
arch/xtensa/boot/lib/Makefile | 2 +-
drivers/gpu/drm/amd/amdgpu/Kconfig | 12 +-
drivers/gpu/drm/amd/amdgpu/Makefile | 4 +-
.../gpu/drm/amd/display/amdgpu_dm/Makefile | 2 +-
drivers/gpu/drm/i915/Makefile | 2 +-
drivers/gpu/drm/xe/Makefile | 2 +-
drivers/s390/char/Makefile | 2 +-
include/asm-generic/vmlinux.lds.h | 36 +++
include/linux/compiler_types.h | 13 +
include/linux/context_tracking.h | 8 +-
include/linux/irqflags.h | 6 +-
include/linux/lockdep.h | 4 +-
include/linux/perf_event.h | 2 +-
include/linux/rseq_entry.h | 2 +-
include/linux/sched.h | 2 +-
init/Kconfig | 9 +-
io_uring/Makefile | 4 +-
kernel/Makefile | 15 +-
kernel/coverage/Kconfig | 226 ++++++++++++++++++
kernel/coverage/Makefile | 18 ++
kernel/coverage/core.c | 71 ++++++
kernel/coverage/coverage.h | 22 ++
kernel/{ => coverage}/gcov/Makefile | 0
kernel/{ => coverage}/gcov/base.c | 0
kernel/{ => coverage}/gcov/clang.c | 2 +-
kernel/{ => coverage}/gcov/fs.c | 22 +-
kernel/{ => coverage}/gcov/gcc_4_7.c | 25 +-
kernel/{ => coverage}/gcov/gcc_base.c | 0
kernel/{ => coverage}/gcov/gcov.h | 0
kernel/{ => coverage}/kcov.c | 3 +
kernel/coverage/llvm/Makefile | 4 +
kernel/coverage/llvm/fs.c | 202 ++++++++++++++++
kernel/coverage/llvm/llvm-cov.h | 139 +++++++++++
kernel/entry/Makefile | 3 +
kernel/gcov/Kconfig | 55 -----
kernel/trace/Kconfig | 9 +-
kernel/trace/Makefile | 4 +-
lib/Kconfig.debug | 61 +----
lib/Makefile | 2 +-
lib/crypto/Makefile | 2 +-
net/bridge/Makefile | 8 +-
net/bridge/netfilter/Makefile | 4 +-
net/ipv4/Makefile | 4 +-
net/ipv4/netfilter/Makefile | 4 +-
net/ipv6/Makefile | 4 +-
net/ipv6/netfilter/Makefile | 4 +-
net/netfilter/Kconfig | 9 +-
net/netfilter/Makefile | 4 +-
net/netfilter/ipset/Kconfig | 9 +-
net/netfilter/ipset/Makefile | 4 +-
net/netfilter/ipvs/Kconfig | 9 +-
net/netfilter/ipvs/Makefile | 4 +-
net/rds/Kconfig | 9 +-
net/rds/Makefile | 4 +-
scripts/Kconfig.toolchain | 3 +
scripts/Makefile.gcov | 25 ++
scripts/Makefile.lib | 19 +-
scripts/Makefile.llvm-cov | 11 +
scripts/Makefile.modfinal | 2 +-
scripts/sbom/sbom/environment.py | 2 +-
tools/objtool/check.c | 53 ++++
tools/testing/selftests/net/rds/config.sh | 9 +-
tools/testing/selftests/net/rds/rds_run.sh | 8 +-
tools/unittests/test_cmatch.py | 2 +-
92 files changed, 1245 insertions(+), 312 deletions(-)
create mode 100644 Documentation/dev-tools/coverage.rst
create mode 100644 kernel/coverage/Kconfig
create mode 100644 kernel/coverage/Makefile
create mode 100644 kernel/coverage/core.c
create mode 100644 kernel/coverage/coverage.h
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 (97%)
rename kernel/{ => coverage}/gcov/gcc_4_7.c (94%)
rename kernel/{ => coverage}/gcov/gcc_base.c (100%)
rename kernel/{ => coverage}/gcov/gcov.h (100%)
rename kernel/{ => coverage}/kcov.c (99%)
create mode 100644 kernel/coverage/llvm/Makefile
create mode 100644 kernel/coverage/llvm/fs.c
create mode 100644 kernel/coverage/llvm/llvm-cov.h
delete mode 100644 kernel/gcov/Kconfig
create mode 100644 scripts/Makefile.gcov
create mode 100644 scripts/Makefile.llvm-covbase-commit: 83f32c773255720a661aa45d83e36278135f23f5
Signature · 1 line
2.53.0
RFC PATCH 1/20 coverage: move gcov and kcov under kernel/coverage/
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.
---
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%)MAINTAINERS +6 -2
@@ -11129,7 +11129,9 @@ GCOV BASED KERNEL PROFILING M: Peter Oberparleiter <[email protected]> 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 <[email protected]>
@@ -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
arch/Kconfig +0 -2
@@ -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
include/linux/sched.h +1 -1
@@ -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;
kernel/Makefile +1 -14
@@ -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/
kernel/coverage/Kconfig new file +65 -0
65 lines not on this page · raw message
kernel/coverage/Makefile new file +16 -0
@@ -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
kernel/coverage/gcov/Kconfig renamed from kernel/gcov/Kconfig +0 -0
kernel/coverage/gcov/Makefile renamed from kernel/gcov/Makefile +0 -0
kernel/coverage/gcov/base.c renamed from kernel/gcov/base.c +0 -0
kernel/coverage/gcov/clang.c renamed from kernel/gcov/clang.c +1 -1
@@ -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
kernel/coverage/gcov/fs.c renamed from kernel/gcov/fs.c +0 -0
kernel/coverage/gcov/gcc_4_7.c renamed from kernel/gcov/gcc_4_7.c +0 -0
kernel/coverage/gcov/gcc_base.c renamed from kernel/gcov/gcc_base.c +0 -0
kernel/coverage/gcov/gcov.h renamed from kernel/gcov/gcov.h +0 -0
kernel/coverage/kcov.c renamed from kernel/kcov.c +0 -0
lib/Kconfig.debug +1 -58
65 lines not on this page · raw message
tools/unittests/test_cmatch.py +1 -1
@@ -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 {
Signature · 1 line
2.53.0
RFC PATCH 2/20 kbuild: unify static coverage instrumentation as COVERAGE_PROFILE
In reply to Sasha LevinThe gcov plumbing is written for a single coverage implementation: the per-object and per-directory knobs are called GCOV_PROFILE, the flags are CFLAGS_GCOV, and every dependency on "coverage instrumentation is present" spells it as GCOV_KERNEL. Make this generic so that further coverage implementations can share it instead of growing parallel copies:
- The coverage implementation becomes a choice ("Code coverage measurement"), with gcov as its only member besides "None" for now. COVERAGE_KERNEL is set whenever one is selected. allyesconfig and allmodconfig keep selecting gcov through a COMPILE_TEST default.
- GCOV_PROFILE_ALL becomes COVERAGE_PROFILE_ALL. The old symbol is kept as a transitional one so that existing configurations keep profiling the entire kernel.
- GCOV_PROFILE[_obj.o] becomes COVERAGE_PROFILE[_obj.o] tree-wide, with unchanged precedence (object, then directory, then COVERAGE_PROFILE_ALL). Out-of-tree modules that use GCOV_PROFILE need the same rename.
- The compiler flags move from the top-level Makefile to scripts/Makefile.gcov, which sets CFLAGS_COVERAGE and is only included when gcov is enabled. This also stops running the -fprofile-update=prefer-atomic probe on every GCC build.
- The per-subsystem opt-ins GCOV_PROFILE_{NETFILTER,IPSET,IPVS,RDS, AMDGPU,URING,FTRACE} become COVERAGE_PROFILE_*, again with the old names kept as transitional symbols. The rds selftest is updated.
- Dependencies that are about instrumented code rather than about gcov (LTO, the KCFI integer normalization workaround, the longest symbol KUnit test, the per-subsystem opt-ins and objtool's --no-unreachable) now use COVERAGE_KERNEL.
--- Documentation/dev-tools/gcov.rst | 18 +-- .../translations/zh_CN/dev-tools/gcov.rst | 16 +-- .../translations/zh_TW/dev-tools/gcov.rst | 16 +-- MAINTAINERS | 1 + Makefile | 22 +--- arch/Kconfig | 6 +- arch/alpha/boot/compressed/Makefile | 2 +- arch/parisc/kernel/vdso32/Makefile | 2 +- arch/parisc/kernel/vdso64/Makefile | 2 +- arch/powerpc/kernel/Makefile | 6 +- arch/powerpc/kernel/trace/Makefile | 4 +- arch/powerpc/kexec/Makefile | 2 +- arch/powerpc/xmon/Makefile | 2 +- arch/s390/boot/Makefile | 2 +- arch/s390/kernel/Makefile | 2 +- arch/sh/mm/Makefile | 2 +- arch/x86/coco/sev/Makefile | 2 +- arch/x86/platform/efi/Makefile | 2 +- arch/xtensa/boot/lib/Makefile | 2 +- drivers/gpu/drm/amd/amdgpu/Kconfig | 12 +- drivers/gpu/drm/amd/amdgpu/Makefile | 4 +- .../gpu/drm/amd/display/amdgpu_dm/Makefile | 2 +- drivers/gpu/drm/i915/Makefile | 2 +- drivers/gpu/drm/xe/Makefile | 2 +- drivers/s390/char/Makefile | 2 +- init/Kconfig | 9 +- io_uring/Makefile | 4 +- kernel/coverage/Kconfig | 103 +++++++++++++++++- kernel/coverage/gcov/Kconfig | 55 ---------- kernel/trace/Kconfig | 9 +- kernel/trace/Makefile | 4 +- lib/Kconfig.debug | 2 +- lib/Makefile | 2 +- lib/crypto/Makefile | 2 +- net/bridge/Makefile | 8 +- net/bridge/netfilter/Makefile | 4 +- net/ipv4/Makefile | 4 +- net/ipv4/netfilter/Makefile | 4 +- net/ipv6/Makefile | 4 +- net/ipv6/netfilter/Makefile | 4 +- net/netfilter/Kconfig | 9 +- net/netfilter/Makefile | 4 +- net/netfilter/ipset/Kconfig | 9 +- net/netfilter/ipset/Makefile | 4 +- net/netfilter/ipvs/Kconfig | 9 +- net/netfilter/ipvs/Makefile | 4 +- net/rds/Kconfig | 9 +- net/rds/Makefile | 4 +- scripts/Makefile.gcov | 22 ++++ scripts/Makefile.lib | 14 +-- scripts/Makefile.modfinal | 2 +- scripts/sbom/sbom/environment.py | 2 +- tools/testing/selftests/net/rds/config.sh | 9 +- tools/testing/selftests/net/rds/rds_run.sh | 8 +- 54 files changed, 259 insertions(+), 203 deletions(-) delete mode 100644 kernel/coverage/gcov/Kconfig create mode 100644 scripts/Makefile.gcov
Documentation/dev-tools/gcov.rst +9 -9
@@ -36,10 +36,10 @@ Configure the kernel with:: and to get coverage data for the entire kernel:: - CONFIG_GCOV_PROFILE_ALL=y + CONFIG_COVERAGE_PROFILE_ALL=y Note that kernels compiled with profiling flags will be significantly -larger and run slower. Also CONFIG_GCOV_PROFILE_ALL may not be supported +larger and run slower. Also CONFIG_COVERAGE_PROFILE_ALL may not be supported on all architectures. Profiling data will only become accessible once debugfs has been
@@ -56,20 +56,20 @@ similar to the following to the respective kernel Makefile: - For a single file (e.g. main.o):: - GCOV_PROFILE_main.o := y + COVERAGE_PROFILE_main.o := y - For all files in one directory:: - GCOV_PROFILE := y + COVERAGE_PROFILE := y -To exclude files from being profiled even when CONFIG_GCOV_PROFILE_ALL +To exclude files from being profiled even when CONFIG_COVERAGE_PROFILE_ALL is specified, use:: - GCOV_PROFILE_main.o := n + COVERAGE_PROFILE_main.o := n and:: - GCOV_PROFILE := n + COVERAGE_PROFILE := n Only files which are linked to the main kernel image or are compiled as kernel modules are supported by this mechanism.
@@ -80,7 +80,7 @@ Module specific configs Gcov kernel configs for specific modules are described below: -CONFIG_GCOV_PROFILE_RDS: +CONFIG_COVERAGE_PROFILE_RDS: Enables GCOV profiling on RDS for checking which functions or lines are executed. This config is used by the rds selftest to generate coverage reports. If left unset the report is omitted.
@@ -207,7 +207,7 @@ Cause Solution Exclude affected source files from profiling by specifying - ``GCOV_PROFILE := n`` or ``GCOV_PROFILE_basename.o := n`` in the + ``COVERAGE_PROFILE := n`` or ``COVERAGE_PROFILE_basename.o := n`` in the corresponding Makefile. Problem
Documentation/translations/zh_CN/dev-tools/gcov.rst +8 -8
@@ -39,7 +39,7 @@ gcov分析核心支持在Linux内核中启用GCC的覆盖率测试工具 gcov_ 获取整个内核的覆盖率数据,还需要打开:: - CONFIG_GCOV_PROFILE_ALL=y + CONFIG_COVERAGE_PROFILE_ALL=y 需要注意的是,整个内核开启覆盖率统计会造成内核镜像文件尺寸的增大, 同时内核运行也会变慢一些。
@@ -58,20 +58,20 @@ gcov分析核心支持在Linux内核中启用GCC的覆盖率测试工具 gcov_ - 单独统计单个文件(例如main.o):: - GCOV_PROFILE_main.o := y + COVERAGE_PROFILE_main.o := y - 单独统计某一个路径:: - GCOV_PROFILE := y + COVERAGE_PROFILE := y -如果要在整个内核的覆盖率统计(开启CONFIG_GCOV_PROFILE_ALL)中单独排除 +如果要在整个内核的覆盖率统计(开启CONFIG_COVERAGE_PROFILE_ALL)中单独排除 某一个文件或者路径,可以使用如下的方法:: - GCOV_PROFILE_main.o := n + COVERAGE_PROFILE_main.o := n 和:: - GCOV_PROFILE := n + COVERAGE_PROFILE := n 此机制仅支持链接到内核镜像或编译为内核模块的文件。
@@ -186,8 +186,8 @@ kconfig会根据编译工具链的检查自动选择合适的gcov格式。 分析标志指定在了源文件但是没有链接到主内核,或者客制化了链接程序 解决方法 - 通过在相应的Makefile中使用 ``GCOV_PROFILE := n`` - 或者 ``GCOV_PROFILE_basename.o := n`` 来将链接报错的文件排除掉 + 通过在相应的Makefile中使用 ``COVERAGE_PROFILE := n`` + 或者 ``COVERAGE_PROFILE_basename.o := n`` 来将链接报错的文件排除掉 可能出现的问题2 从sysfs复制的文件显示为空或不完整
Documentation/translations/zh_TW/dev-tools/gcov.rst +8 -8
@@ -39,7 +39,7 @@ gcov分析核心支持在Linux內核中啓用GCC的覆蓋率測試工具 gcov_ 獲取整個內核的覆蓋率數據,還需要打開:: - CONFIG_GCOV_PROFILE_ALL=y + CONFIG_COVERAGE_PROFILE_ALL=y 需要注意的是,整個內核開啓覆蓋率統計會造成內核鏡像文件尺寸的增大, 同時內核運行也會變慢一些。
@@ -58,20 +58,20 @@ gcov分析核心支持在Linux內核中啓用GCC的覆蓋率測試工具 gcov_ - 單獨統計單個文件(例如main.o):: - GCOV_PROFILE_main.o := y + COVERAGE_PROFILE_main.o := y - 單獨統計某一個路徑:: - GCOV_PROFILE := y + COVERAGE_PROFILE := y -如果要在整個內核的覆蓋率統計(開啓CONFIG_GCOV_PROFILE_ALL)中單獨排除 +如果要在整個內核的覆蓋率統計(開啓CONFIG_COVERAGE_PROFILE_ALL)中單獨排除 某一個文件或者路徑,可以使用如下的方法:: - GCOV_PROFILE_main.o := n + COVERAGE_PROFILE_main.o := n 和:: - GCOV_PROFILE := n + COVERAGE_PROFILE := n 此機制僅支持鏈接到內核鏡像或編譯爲內核模塊的文件。
@@ -186,8 +186,8 @@ kconfig會根據編譯工具鏈的檢查自動選擇合適的gcov格式。 分析標誌指定在了源文件但是沒有鏈接到主內核,或者客製化了鏈接程序 解決方法 - 通過在相應的Makefile中使用 ``GCOV_PROFILE := n`` - 或者 ``GCOV_PROFILE_basename.o := n`` 來將鏈接報錯的文件排除掉 + 通過在相應的Makefile中使用 ``COVERAGE_PROFILE := n`` + 或者 ``COVERAGE_PROFILE_basename.o := n`` 來將鏈接報錯的文件排除掉 可能出現的問題2 從sysfs複製的文件顯示爲空或不完整
MAINTAINERS +1 -0
@@ -11132,6 +11132,7 @@ F: Documentation/dev-tools/gcov.rst F: kernel/coverage/Kconfig F: kernel/coverage/Makefile F: kernel/coverage/gcov/ +F: scripts/Makefile.gcov GDB KERNEL DEBUGGING HELPER SCRIPTS M: Jan Kiszka <[email protected]>
Makefile +1 -21
@@ -1183,27 +1183,6 @@ endif # Ensure compilers do not transform certain loops into calls to wcslen() KBUILD_CFLAGS += -fno-builtin-wcslen -CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -ifdef CONFIG_CC_IS_GCC -CFLAGS_GCOV += -fno-tree-loop-im -# Use atomic counter updates to avoid concurrent-access crashes in GCOV. -# Only enable if -fprofile-update=prefer-atomic does not introduce new -# undefined symbols (e.g. libatomic calls that the kernel cannot link). -CFLAGS_GCOV += $(call try-run,\ - echo 'long long x; void f(void){x++;}' | \ - $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ - -ftest-coverage -x c - -c -o "$$TMP.base" && \ - echo 'long long x; void f(void){x++;}' | \ - $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ - -ftest-coverage -fprofile-update=prefer-atomic \ - -x c - -c -o "$$TMP" && \ - $(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \ - $(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \ - cmp -s "$$TMP.ubase" "$$TMP.utest",\ - -fprofile-update=prefer-atomic) -endif -export CFLAGS_GCOV - # change __FILE__ to the relative path to the source directory ifdef building_out_of_srctree CFLAGS_PREFIX_MAP := -fmacro-prefix-map=$(srcroot)/=
@@ -1223,6 +1202,7 @@ include-$(CONFIG_KASAN) += scripts/Makefile.kasan 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_KCOV) += scripts/Makefile.kcov include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct include-$(CONFIG_KSTACK_ERASE) += scripts/Makefile.kstack_erase
arch/Kconfig +3 -3
@@ -819,7 +819,7 @@ config HAS_LTO_CLANG depends on $(success,$(AR) --help | head -n 1 | grep -qi llvm) depends on ARCH_SUPPORTS_LTO_CLANG depends on !FTRACE_MCOUNT_USE_RECORDMCOUNT - depends on !GCOV_KERNEL + depends on !COVERAGE_KERNEL help The compiler and Kconfig options support building with Clang's LTO.
@@ -985,7 +985,7 @@ config HAVE_CFI_ICALL_NORMALIZE_INTEGERS def_bool y depends on $(cc-option,-fsanitize=kcfi -fsanitize-cfi-icall-experimental-normalize-integers) # With GCOV/KASAN we need this fix: https://github.com/llvm/llvm-project/pull/104826 - depends on CLANG_VERSION >= 190103 || (!GCOV_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS) + depends on CLANG_VERSION >= 190103 || (!COVERAGE_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS) config HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC def_bool y
@@ -993,7 +993,7 @@ config HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC depends on ARM64 || X86_64 # With GCOV/KASAN we need this fix: https://github.com/rust-lang/rust/pull/129373 depends on RUSTC_LLVM_VERSION >= 190103 || \ - (!GCOV_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS) + (!COVERAGE_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS) config CFI_PERMISSIVE bool "Use CFI in permissive mode"
arch/alpha/boot/compressed/Makefile +1 -1
@@ -28,7 +28,7 @@ KBUILD_AFLAGS := -D__ASSEMBLY__ $(KBUILD_CFLAGS) # built with the kernel's instrumentation and -mcpu=, so build private # copies of the library routines the stub needs instead of linking it. UBSAN_SANITIZE := n -GCOV_PROFILE := n +COVERAGE_PROFILE := n $(obj)/memcpy.o: $(srctree)/arch/alpha/lib/memcpy.c FORCE $(call if_changed_dep,cc_o_c)
arch/parisc/kernel/vdso32/Makefile +1 -1
@@ -4,7 +4,7 @@ include $(srctree)/lib/vdso/Makefile.include KCOV_INSTRUMENT := n # Disable gcov profiling, ubsan and kasan for VDSO code -GCOV_PROFILE := n +COVERAGE_PROFILE := n UBSAN_SANITIZE := n KASAN_SANITIZE := n KCSAN_SANITIZE := n
arch/parisc/kernel/vdso64/Makefile +1 -1
@@ -4,7 +4,7 @@ include $(srctree)/lib/vdso/Makefile.include KCOV_INSTRUMENT := n # Disable gcov profiling, ubsan and kasan for VDSO code -GCOV_PROFILE := n +COVERAGE_PROFILE := n UBSAN_SANITIZE := n KASAN_SANITIZE := n KCSAN_SANITIZE := n
arch/powerpc/kernel/Makefile +3 -3
@@ -169,15 +169,15 @@ obj-$(CONFIG_PPC_SECURE_BOOT) += secure_boot.o ima_arch.o secvar-ops.o obj-$(CONFIG_PPC_SECVAR_SYSFS) += secvar-sysfs.o # Disable GCOV, KCOV & sanitizers in odd or sensitive code -GCOV_PROFILE_prom_init.o := n +COVERAGE_PROFILE_prom_init.o := n KCOV_INSTRUMENT_prom_init.o := n KCSAN_SANITIZE_prom_init.o := n UBSAN_SANITIZE_prom_init.o := n -GCOV_PROFILE_kprobes.o := n +COVERAGE_PROFILE_kprobes.o := n KCOV_INSTRUMENT_kprobes.o := n KCSAN_SANITIZE_kprobes.o := n UBSAN_SANITIZE_kprobes.o := n -GCOV_PROFILE_kprobes-ftrace.o := n +COVERAGE_PROFILE_kprobes-ftrace.o := n KCOV_INSTRUMENT_kprobes-ftrace.o := n KCSAN_SANITIZE_kprobes-ftrace.o := n UBSAN_SANITIZE_kprobes-ftrace.o := n
arch/powerpc/kernel/trace/Makefile +2 -2
@@ -24,11 +24,11 @@ obj-$(CONFIG_PPC64) += $(obj64-y) obj-$(CONFIG_PPC32) += $(obj32-y) # Disable GCOV, KCOV & sanitizers in odd or sensitive code -GCOV_PROFILE_ftrace.o := n +COVERAGE_PROFILE_ftrace.o := n KCOV_INSTRUMENT_ftrace.o := n KCSAN_SANITIZE_ftrace.o := n UBSAN_SANITIZE_ftrace.o := n -GCOV_PROFILE_ftrace_64_pg.o := n +COVERAGE_PROFILE_ftrace_64_pg.o := n KCOV_INSTRUMENT_ftrace_64_pg.o := n KCSAN_SANITIZE_ftrace_64_pg.o := n UBSAN_SANITIZE_ftrace_64_pg.o := n
arch/powerpc/kexec/Makefile +1 -1
@@ -12,7 +12,7 @@ obj-$(CONFIG_VMCORE_INFO) += vmcore_info.o obj-$(CONFIG_CRASH_DUMP) += crash.o # Disable GCOV, KCOV & sanitizers in odd or sensitive code -GCOV_PROFILE_core_$(BITS).o := n +COVERAGE_PROFILE_core_$(BITS).o := n KCOV_INSTRUMENT_core_$(BITS).o := n UBSAN_SANITIZE_core_$(BITS).o := n KASAN_SANITIZE_core.o := n
arch/powerpc/xmon/Makefile +1 -1
@@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Makefile for xmon -GCOV_PROFILE := n +COVERAGE_PROFILE := n KCOV_INSTRUMENT := n UBSAN_SANITIZE := n KASAN_SANITIZE := n
arch/s390/boot/Makefile +1 -1
@@ -5,7 +5,7 @@ # Tooling runtimes are unavailable and cannot be linked for early boot code KCOV_INSTRUMENT := n -GCOV_PROFILE := n +COVERAGE_PROFILE := n UBSAN_SANITIZE := n KASAN_SANITIZE := n KCSAN_SANITIZE := n
arch/s390/kernel/Makefile +1 -1
@@ -18,7 +18,7 @@ CFLAGS_REMOVE_unwind_bc.o = $(CC_FLAGS_FTRACE) endif -GCOV_PROFILE_early.o := n +COVERAGE_PROFILE_early.o := n KCOV_INSTRUMENT_early.o := n UBSAN_SANITIZE_early.o := n KASAN_SANITIZE_ipl.o := n
arch/sh/mm/Makefile +1 -1
@@ -42,4 +42,4 @@ obj-$(CONFIG_IOREMAP_FIXED) += ioremap_fixed.o obj-$(CONFIG_UNCACHED_MAPPING) += uncached.o obj-$(CONFIG_HAVE_SRAM_POOL) += sram.o -GCOV_PROFILE_pmb.o := n +COVERAGE_PROFILE_pmb.o := n
arch/x86/coco/sev/Makefile +1 -1
@@ -9,4 +9,4 @@ UBSAN_SANITIZE_noinstr.o := n KASAN_SANITIZE_noinstr.o := n KCSAN_SANITIZE_noinstr.o := n -GCOV_PROFILE_noinstr.o := n +COVERAGE_PROFILE_noinstr.o := n
arch/x86/platform/efi/Makefile +1 -1
@@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 KASAN_SANITIZE := n -GCOV_PROFILE := n +COVERAGE_PROFILE := n obj-$(CONFIG_EFI) += memmap.o quirks.o efi.o efi_$(BITS).o \ efi_stub_$(BITS).o
arch/xtensa/boot/lib/Makefile +1 -1
@@ -18,7 +18,7 @@ endif KASAN_SANITIZE := n KCSAN_SANITIZE := n KCOV_INSTRUMENT := n -GCOV_PROFILE := n +COVERAGE_PROFILE := n CFLAGS_REMOVE_inflate.o += -fstack-protector -fstack-protector-strong CFLAGS_REMOVE_zmem.o += -fstack-protector -fstack-protector-strong
drivers/gpu/drm/amd/amdgpu/Kconfig +6 -6
@@ -103,16 +103,16 @@ config DRM_AMDGPU_WERROR Only enable this if you are warning code for amdgpu.ko. -config GCOV_PROFILE_AMDGPU - bool "Enable GCOV profiling on amdgpu" +config COVERAGE_PROFILE_AMDGPU + bool "Enable code coverage profiling on amdgpu" depends on DRM_AMDGPU - depends on GCOV_KERNEL - default n + depends on COVERAGE_KERNEL + default GCOV_PROFILE_AMDGPU help - Enable GCOV profiling on the amdgpu driver for checking which + Enable code coverage profiling on the amdgpu driver for checking which functions/lines are executed during testing. This adds compiler instrumentation flags to all amdgpu source files, producing - .gcda/.gcno coverage data accessible via debugfs. + coverage data accessible via debugfs. This increases the amdgpu module size by ~50% and adds ~2-5% runtime overhead on GPU submission paths.
drivers/gpu/drm/amd/amdgpu/Makefile +2 -2
@@ -27,8 +27,8 @@ FULL_AMD_PATH=$(src)/.. DISPLAY_FOLDER_NAME=display FULL_AMD_DISPLAY_PATH = $(FULL_AMD_PATH)/$(DISPLAY_FOLDER_NAME) -ifdef CONFIG_GCOV_PROFILE_AMDGPU -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_AMDGPU +COVERAGE_PROFILE := y endif ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
drivers/gpu/drm/amd/display/amdgpu_dm/Makefile +1 -1
@@ -71,5 +71,5 @@ ifneq ($(CONFIG_DRM_AMD_DC_KUNIT_TEST),) obj-y += $(AMDDALPATH)/amdgpu_dm/tests/ # Enable gcov for amdgpu_dm KUnit builds -GCOV_PROFILE := y +COVERAGE_PROFILE := y endif
drivers/gpu/drm/i915/Makefile +1 -1
@@ -456,7 +456,7 @@ always-$(CONFIG_DRM_I915_WERROR) += \ $(shell cd $(src) && find * -name '*.h'))) quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@) - cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_GCOV), $(c_flags)) -S -o /dev/null -x c /dev/null -include $<; \ + cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_COVERAGE), $(c_flags)) -S -o /dev/null -x c /dev/null -include $<; \ $(KERNELDOC) -none -Werror $<; touch $@ $(obj)/%.hdrtest: $(src)/%.h FORCE
drivers/gpu/drm/xe/Makefile +1 -1
@@ -392,7 +392,7 @@ always-$(CONFIG_DRM_XE_WERROR) += \ $(patsubst %.h,%.hdrtest, $(shell cd $(src) && find * -name '*.h' $(hdrtest_find_args))) quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@) - cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_GCOV), $(c_flags)) -S -o /dev/null -x c /dev/null -include $< -include $<; \ + cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_COVERAGE), $(c_flags)) -S -o /dev/null -x c /dev/null -include $< -include $<; \ $(srctree)/scripts/kernel-doc -none -Werror $<; touch $@ $(obj)/%.hdrtest: $(src)/%.h FORCE
drivers/s390/char/Makefile +1 -1
@@ -10,7 +10,7 @@ ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_sclp_early_core.o = $(CC_FLAGS_FTRACE) endif -GCOV_PROFILE_sclp_early_core.o := n +COVERAGE_PROFILE_sclp_early_core.o := n KCOV_INSTRUMENT_sclp_early_core.o := n UBSAN_SANITIZE_sclp_early_core.o := n KASAN_SANITIZE_sclp_early_core.o := n
init/Kconfig +5 -4
@@ -1780,11 +1780,12 @@ config IO_URING applications to submit and complete IO through submission and completion rings that are shared between the kernel and application. -config GCOV_PROFILE_URING - bool "Enable GCOV profiling on the io_uring subsystem" - depends on IO_URING && GCOV_KERNEL +config COVERAGE_PROFILE_URING + bool "Enable code coverage profiling on the io_uring subsystem" + depends on IO_URING && COVERAGE_KERNEL + default GCOV_PROFILE_URING help - Enable GCOV profiling on the io_uring subsystem, to facilitate + Enable code coverage profiling on the io_uring subsystem, to facilitate code coverage testing. If unsure, say N.
io_uring/Makefile +2 -2
@@ -2,8 +2,8 @@ # # Makefile for io_uring -ifdef CONFIG_GCOV_PROFILE_URING -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_URING +COVERAGE_PROFILE := y endif obj-$(CONFIG_IO_URING) += io_uring.o opdef.o kbuf.o rsrc.o notif.o \
kernel/coverage/Kconfig +102 -1
109 lines not on this page · raw message
kernel/coverage/gcov/Kconfig deleted +0 -55
55 lines not on this page · raw message
kernel/trace/Kconfig +5 -4
@@ -1110,11 +1110,12 @@ config RING_BUFFER_RECORD_RECURSION This will add more overhead to cases that have recursion. -config GCOV_PROFILE_FTRACE - bool "Enable GCOV profiling on ftrace subsystem" - depends on GCOV_KERNEL +config COVERAGE_PROFILE_FTRACE + bool "Enable code coverage profiling on ftrace subsystem" + depends on COVERAGE_KERNEL + default GCOV_PROFILE_FTRACE help - Enable GCOV profiling on ftrace subsystem for checking + Enable code coverage profiling on ftrace subsystem for checking which functions/lines are tested. If unsure, say N.
kernel/trace/Makefile +2 -2
@@ -44,8 +44,8 @@ KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING endif # for GCOV coverage profiling -ifdef CONFIG_GCOV_PROFILE_FTRACE -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_FTRACE +COVERAGE_PROFILE := y endif # Functions in these files can run from IRQ entry before hardirq context
lib/Kconfig.debug +1 -1
@@ -3048,7 +3048,7 @@ config FORTIFY_KUNIT_TEST config LONGEST_SYM_KUNIT_TEST tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS depends on KUNIT && KPROBES - depends on !CALL_PADDING && !CFI && !GCOV_KERNEL + depends on !CALL_PADDING && !CFI && !COVERAGE_KERNEL default KUNIT_ALL_TESTS help Tests the longest symbol possible
lib/Makefile +1 -1
@@ -88,7 +88,7 @@ obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_KASAN),yy) # FIXME: Clang breaks test_bitmap_const_eval when KASAN and GCOV are enabled -GCOV_PROFILE_test_bitmap.o := n +COVERAGE_PROFILE_test_bitmap.o := n endif obj-$(CONFIG_TEST_XARRAY) += test_xarray.o
lib/crypto/Makefile +1 -1
@@ -143,7 +143,7 @@ obj-$(CONFIG_CRYPTO_LIB_CURVE25519) += libcurve25519.o libcurve25519-y := curve25519.o # Disable GCOV in odd or sensitive code -GCOV_PROFILE_curve25519.o := n +COVERAGE_PROFILE_curve25519.o := n ifeq ($(CONFIG_ARCH_SUPPORTS_INT128),y) libcurve25519-$(CONFIG_CRYPTO_LIB_CURVE25519_GENERIC) += curve25519-hacl64.o
net/bridge/Makefile +4 -4
@@ -30,8 +30,8 @@ bridge-$(CONFIG_BRIDGE_MRP) += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o bridge-$(CONFIG_BRIDGE_CFM) += br_cfm.o br_cfm_netlink.o -ifdef CONFIG_GCOV_PROFILE_NETFILTER -GCOV_PROFILE_br_nf_core.o := y -GCOV_PROFILE_br_netfilter_hooks.o := y -GCOV_PROFILE_br_netfilter_ipv6.o := y +ifdef CONFIG_COVERAGE_PROFILE_NETFILTER +COVERAGE_PROFILE_br_nf_core.o := y +COVERAGE_PROFILE_br_netfilter_hooks.o := y +COVERAGE_PROFILE_br_netfilter_ipv6.o := y endif
net/bridge/netfilter/Makefile +2 -2
@@ -39,6 +39,6 @@ obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o obj-$(CONFIG_BRIDGE_EBT_NFLOG) += ebt_nflog.o -ifdef CONFIG_GCOV_PROFILE_NETFILTER -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_NETFILTER +COVERAGE_PROFILE := y endif
net/ipv4/Makefile +2 -2
@@ -74,6 +74,6 @@ obj-$(CONFIG_BPF_SYSCALL) += bpf_tcp_ca.o obj-$(CONFIG_CGROUP_BPF) += bpf_tcp_ops.o endif -ifdef CONFIG_GCOV_PROFILE_NETFILTER -GCOV_PROFILE_netfilter.o := y +ifdef CONFIG_COVERAGE_PROFILE_NETFILTER +COVERAGE_PROFILE_netfilter.o := y endif
net/ipv4/netfilter/Makefile +2 -2
@@ -52,6 +52,6 @@ obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o obj-$(CONFIG_NF_DUP_IPV4) += nf_dup_ipv4.o -ifdef CONFIG_GCOV_PROFILE_NETFILTER -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_NETFILTER +COVERAGE_PROFILE := y endif
net/ipv6/Makefile +2 -2
@@ -55,6 +55,6 @@ obj-y += mcast_snoop.o ndisc_snoop.o obj-$(CONFIG_TCP_AO) += tcp_ao.o endif -ifdef CONFIG_GCOV_PROFILE_NETFILTER -GCOV_PROFILE_netfilter.o := y +ifdef CONFIG_COVERAGE_PROFILE_NETFILTER +COVERAGE_PROFILE_netfilter.o := y endif
net/ipv6/netfilter/Makefile +2 -2
@@ -44,6 +44,6 @@ obj-$(CONFIG_IP6_NF_TARGET_NPT) += ip6t_NPT.o obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o obj-$(CONFIG_IP6_NF_TARGET_SYNPROXY) += ip6t_SYNPROXY.o -ifdef CONFIG_GCOV_PROFILE_NETFILTER -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_NETFILTER +COVERAGE_PROFILE := y endif
net/netfilter/Kconfig +5 -4
@@ -1649,11 +1649,12 @@ config NETFILTER_XT_MATCH_U32 endif # NETFILTER_XTABLES -config GCOV_PROFILE_NETFILTER - bool "Enable GCOV profiling for netfilter" - depends on GCOV_KERNEL +config COVERAGE_PROFILE_NETFILTER + bool "Enable code coverage profiling for netfilter" + depends on COVERAGE_KERNEL + default GCOV_PROFILE_NETFILTER help - Enable GCOV profiling for netfilter to check which functions/lines + Enable code coverage profiling for netfilter to check which functions/lines are executed. If unsure, say N.
net/netfilter/Makefile +2 -2
@@ -241,6 +241,6 @@ obj-$(CONFIG_IP_VS) += ipvs/ # lwtunnel obj-$(CONFIG_LWTUNNEL) += nf_hooks_lwtunnel.o -ifdef CONFIG_GCOV_PROFILE_NETFILTER -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_NETFILTER +COVERAGE_PROFILE := y endif
net/netfilter/ipset/Kconfig +5 -4
@@ -175,11 +175,12 @@ config IP_SET_LIST_SET To compile it as a module, choose M here. If unsure, say N. -config GCOV_PROFILE_IPSET - bool "Enable GCOV profiling for ipset" - depends on GCOV_KERNEL +config COVERAGE_PROFILE_IPSET + bool "Enable code coverage profiling for ipset" + depends on COVERAGE_KERNEL + default GCOV_PROFILE_IPSET help - Enable GCOV profiling for ipset to check which functions/lines + Enable code coverage profiling for ipset to check which functions/lines are executed. If unsure, say N.
net/netfilter/ipset/Makefile +2 -2
@@ -29,6 +29,6 @@ obj-$(CONFIG_IP_SET_HASH_NETPORTNET) += ip_set_hash_netportnet.o # list types obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o -ifdef CONFIG_GCOV_PROFILE_IPSET -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_IPSET +COVERAGE_PROFILE := y endif
net/netfilter/ipvs/Kconfig +5 -4
@@ -349,11 +349,12 @@ config IP_VS_PE_SIP help Allow persistence based on the SIP Call-ID -config GCOV_PROFILE_IPVS - bool "Enable GCOV profiling for IPVS" - depends on GCOV_KERNEL +config COVERAGE_PROFILE_IPVS + bool "Enable code coverage profiling for IPVS" + depends on COVERAGE_KERNEL + default GCOV_PROFILE_IPVS help - Enable GCOV profiling for IPVS to check which functions/lines + Enable code coverage profiling for IPVS to check which functions/lines are executed. If unsure, say N.
net/netfilter/ipvs/Makefile +2 -2
@@ -43,6 +43,6 @@ obj-$(CONFIG_IP_VS_FTP) += ip_vs_ftp.o # IPVS connection template retrievers obj-$(CONFIG_IP_VS_PE_SIP) += ip_vs_pe_sip.o -ifdef CONFIG_GCOV_PROFILE_IPVS -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_IPVS +COVERAGE_PROFILE := y endif
net/rds/Kconfig +5 -4
@@ -27,11 +27,12 @@ config RDS_DEBUG depends on RDS default n -config GCOV_PROFILE_RDS - bool "Enable GCOV profiling on RDS" - depends on GCOV_KERNEL +config COVERAGE_PROFILE_RDS + bool "Enable code coverage profiling on RDS" + depends on COVERAGE_KERNEL + default GCOV_PROFILE_RDS help - Enable GCOV profiling on RDS for checking which functions/lines + Enable code coverage profiling on RDS for checking which functions/lines are executed. If unsure, say N.
net/rds/Makefile +2 -2
@@ -17,6 +17,6 @@ rds_tcp-y := tcp.o tcp_connect.o tcp_listen.o tcp_recv.o \ ccflags-$(CONFIG_RDS_DEBUG) := -DRDS_DEBUG # for GCOV coverage profiling -ifdef CONFIG_GCOV_PROFILE_RDS -GCOV_PROFILE := y +ifdef CONFIG_COVERAGE_PROFILE_RDS +COVERAGE_PROFILE := y endif
scripts/Makefile.gcov new file +22 -0
@@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 + +CFLAGS_COVERAGE := -fprofile-arcs -ftest-coverage +ifdef CONFIG_CC_IS_GCC +CFLAGS_COVERAGE += -fno-tree-loop-im +# Use atomic counter updates to avoid concurrent-access crashes in GCOV. +# Only enable if -fprofile-update=prefer-atomic does not introduce new +# undefined symbols (e.g. libatomic calls that the kernel cannot link). +CFLAGS_COVERAGE += $(call try-run,\ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -x c - -c -o "$$TMP.base" && \ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -fprofile-update=prefer-atomic \ + -x c - -c -o "$$TMP" && \ + $(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \ + $(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \ + cmp -s "$$TMP.ubase" "$$TMP.utest",\ + -fprofile-update=prefer-atomic) +endif +export CFLAGS_COVERAGE
scripts/Makefile.lib +7 -7
@@ -38,14 +38,14 @@ _a_flags = $(filter-out $(AFLAGS_REMOVE_$(target-stem).o), \ _cpp_flags = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(target-stem).lds) # -# Enable gcov profiling flags for a file, directory or for all files depending -# on variables GCOV_PROFILE_obj.o, GCOV_PROFILE and CONFIG_GCOV_PROFILE_ALL -# (in this order) +# Enable code coverage instrumentation for a file, directory or for all files +# depending on variables COVERAGE_PROFILE_obj.o, COVERAGE_PROFILE and +# CONFIG_COVERAGE_PROFILE_ALL (in this order) # -ifeq ($(CONFIG_GCOV_KERNEL),y) +ifeq ($(CONFIG_COVERAGE_KERNEL),y) _c_flags += $(if $(patsubst n%,, \ - $(GCOV_PROFILE_$(target-stem).o)$(GCOV_PROFILE)$(if $(is-kernel-object),$(CONFIG_GCOV_PROFILE_ALL))), \ - $(CFLAGS_GCOV)) + $(COVERAGE_PROFILE_$(target-stem).o)$(COVERAGE_PROFILE)$(if $(is-kernel-object),$(CONFIG_COVERAGE_PROFILE_ALL))), \ + $(CFLAGS_COVERAGE)) endif #
@@ -206,7 +206,7 @@ objtool-args-$(CONFIG_MITIGATION_SLS) += --sls objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess -objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable +objtool-args-$(or $(CONFIG_COVERAGE_KERNEL),$(CONFIG_KCOV)) += --no-unreachable objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror objtool-args = $(objtool-args-y) \
scripts/Makefile.modfinal +1 -1
@@ -19,7 +19,7 @@ __modfinal: $(modules:%.o=%.ko) # modname and part-of-module are set to make c_flags define proper module flags modname = $(notdir $(@:.mod.o=)) part-of-module = y -GCOV_PROFILE := n +COVERAGE_PROFILE := n ccflags-remove-y := $(CC_FLAGS_CFI) %.mod.o: %.mod.c FORCE
scripts/sbom/sbom/environment.py +1 -1
@@ -18,7 +18,7 @@ KERNEL_BUILD_VARIABLES_ALLOWLIST = [ "CC", "CC_FLAGS_FPU", "CC_FLAGS_NO_FPU", - "CFLAGS_GCOV", + "CFLAGS_COVERAGE", "CFLAGS_KERNEL", "CFLAGS_MODULE", "CHECK",
tools/testing/selftests/net/rds/config.sh +5 -4
@@ -43,13 +43,14 @@ scripts/config "${FLAGS[@]}" --enable CONFIG_RDS_TCP if [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then # instrument RDS and only RDS + scripts/config "${FLAGS[@]}" --disable CONFIG_COVERAGE_NONE scripts/config "${FLAGS[@]}" --enable CONFIG_GCOV_KERNEL - scripts/config "${FLAGS[@]}" --disable GCOV_PROFILE_ALL - scripts/config "${FLAGS[@]}" --enable GCOV_PROFILE_RDS + scripts/config "${FLAGS[@]}" --disable COVERAGE_PROFILE_ALL + scripts/config "${FLAGS[@]}" --enable COVERAGE_PROFILE_RDS else scripts/config "${FLAGS[@]}" --disable CONFIG_GCOV_KERNEL - scripts/config "${FLAGS[@]}" --disable GCOV_PROFILE_ALL - scripts/config "${FLAGS[@]}" --disable GCOV_PROFILE_RDS + scripts/config "${FLAGS[@]}" --disable COVERAGE_PROFILE_ALL + scripts/config "${FLAGS[@]}" --disable COVERAGE_PROFILE_RDS fi # need network namespaces to run tests with veth network interfaces
tools/testing/selftests/net/rds/rds_run.sh +4 -4
@@ -70,16 +70,16 @@ check_gcov_env() # Check to see if the kconfig has the required configs to generate a coverage report check_gcov_conf() { - if ! grep -x "CONFIG_GCOV_PROFILE_RDS=y" "$kconfig" > /dev/null 2>&1; then - echo "# INFO: CONFIG_GCOV_PROFILE_RDS should be enabled for coverage reports" + if ! grep -x "CONFIG_COVERAGE_PROFILE_RDS=y" "$kconfig" > /dev/null 2>&1; then + echo "# INFO: CONFIG_COVERAGE_PROFILE_RDS should be enabled for coverage reports" GENERATE_GCOV_REPORT=0 fi if ! grep -x "CONFIG_GCOV_KERNEL=y" "$kconfig" > /dev/null 2>&1; then echo "# INFO: CONFIG_GCOV_KERNEL should be enabled for coverage reports" GENERATE_GCOV_REPORT=0 fi - if grep -x "CONFIG_GCOV_PROFILE_ALL=y" "$kconfig" > /dev/null 2>&1; then - echo "# INFO: CONFIG_GCOV_PROFILE_ALL should be disabled for coverage reports" + if grep -x "CONFIG_COVERAGE_PROFILE_ALL=y" "$kconfig" > /dev/null 2>&1; then + echo "# INFO: CONFIG_COVERAGE_PROFILE_ALL should be disabled for coverage reports" GENERATE_GCOV_REPORT=0 fi
Signature · 1 line
2.53.0
RFC PATCH 3/20 coverage: add a common debugfs interface
In reply to Sasha LevinEach coverage implementation creates its own top-level debugfs entry with its own conventions: /sys/kernel/debug/gcov/ and /sys/kernel/debug/kcov. Give them a common root, /sys/kernel/debug/coverage/, which contains:
backend name of the selected code coverage measurement implementation reset write to reset all coverage data of that implementation gcov symlink to ../gcov when gcov is enabled kcov symlink to ../kcov when KCOV is enabled
backend and reset are only present when a code coverage measurement implementation is selected, so a kernel with only KCOV just has the kcov symlink. The data formats are defined by the respective toolchains and stay implementation specific. The existing paths are kept as they are, since lcov, distribution tooling and syzkaller rely on them; the gcov reset file now shares its implementation with the common one.
The common reset never touches KCOV, whose coverage buffers are owned by the tasks that set them up.
--- MAINTAINERS | 8 +++++ kernel/coverage/Makefile | 1 + kernel/coverage/core.c | 71 ++++++++++++++++++++++++++++++++++++++ kernel/coverage/coverage.h | 22 ++++++++++++ kernel/coverage/gcov/fs.c | 21 ++++++++--- kernel/coverage/kcov.c | 3 ++ 6 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 kernel/coverage/core.c create mode 100644 kernel/coverage/coverage.h
MAINTAINERS +8 -0
@@ -6589,6 +6589,14 @@ S: Maintained F: Documentation/devicetree/bindings/media/coda.yaml F: drivers/media/platform/chips-media/coda +CODE COVERAGE +M: Sasha Levin <[email protected]> +S: Maintained +F: kernel/coverage/Kconfig +F: kernel/coverage/Makefile +F: kernel/coverage/core.c +F: kernel/coverage/coverage.h + CODE OF CONDUCT M: Greg Kroah-Hartman <[email protected]> S: Supported
kernel/coverage/Makefile +1 -0
@@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(or $(CONFIG_COVERAGE_KERNEL),$(CONFIG_KCOV)) += core.o obj-$(CONFIG_GCOV_KERNEL) += gcov/ obj-$(CONFIG_KCOV) += kcov.o
kernel/coverage/core.c new file +71 -0
71 lines not on this page · raw message
kernel/coverage/coverage.h new file +22 -0
@@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _KERNEL_COVERAGE_H +#define _KERNEL_COVERAGE_H + +struct dentry ; + +/** +* struct coverage_backend - code coverage measurement implementation +* @name: name reported by /sys/kernel/debug/coverage/backend +* @features: optional space separated list of enabled features +* @reset: reset all coverage data collected so far +*/ +struct coverage_backend { +const char *name; +const char *features; +void (*reset)(void ); +}; + +struct dentry *coverage_debugfs_root (void ); +void coverage_register_backend (const struct coverage_backend *backend); + +#endif /* _KERNEL_COVERAGE_H */
kernel/coverage/gcov/fs.c +17 -4
@@ -28,6 +28,7 @@#include <linux/seq_file.h> #include <linux/mm.h> #include "gcov.h" +#include "../coverage.h" /** * struct gcov_node - represents a debugfs entry
@@ -628,11 +629,10 @@ static struct gcov_node *get_child_by_name(struct gcov_node *parent, }/* -* write() implementation for reset file. Reset all profiling data to zero -* and remove nodes for which all associated object files are unloaded. +* Reset all profiling data to zero and remove nodes for which all associated +* object files are unloaded. */ -static ssize_t reset_write (struct file *file,const char __user *addr, -size_t len,loff_t *pos) +static void gcov_reset_all (void ) {struct gcov_node *node;
@@ -648,7 +648,13 @@ static ssize_t reset_write(struct file *file, const char __user *addr, } }mutex_unlock (&node_lock); +} +/* write() implementation for reset file. */ +static ssize_t reset_write (struct file *file,const char __user *addr, +size_t len,loff_t *pos) +{ +gcov_reset_all ();return len; }
@@ -859,6 +865,11 @@ void gcov_event(enum gcov_action action, struct gcov_info *info)mutex_unlock (&node_lock); } +static const struct coverage_backend gcov_backend = { + .name ="gcov" , + .reset = gcov_reset_all, +}; +/* Create debugfs entries. */ static __init int gcov_fs_init (void ) {
@@ -874,6 +885,8 @@ static __init int gcov_fs_init(void)*/ debugfs_create_file ("reset" ,0600 , root_node.dentry,NULL , &gcov_reset_fops); +debugfs_create_symlink ("gcov" ,coverage_debugfs_root (),"../gcov" ); +coverage_register_backend (&gcov_backend);/* Replay previous events to get our fs hierarchy up-to-date. */ gcov_enable_events ();return 0 ;
kernel/coverage/kcov.c +3 -0
@@ -27,6 +27,8 @@#include <linux/log2.h> #include <asm/setup.h> +#include "coverage.h" +#define kcov_debug (fmt, ...) pr_debug("%s: " fmt, __func__, ##__VA_ARGS__)/* Number of 64-bit words written per one comparison: */
@@ -1149,6 +1151,7 @@ static int __init kcov_init(void)* use of debugfs_create_file_unsafe() is actually safe here. */ debugfs_create_file_unsafe ("kcov" ,0600 ,NULL ,NULL , &kcov_fops); +debugfs_create_symlink ("kcov" ,coverage_debugfs_root (),"../kcov" );#ifdef CONFIG_KCOV_SELFTEST selftest ();
Signature · 1 line
2.53.0
RFC PATCH 4/20 compiler_types.h: Don't instrument __always_inline functions for coverage
In reply to Sasha Levinnoinstr 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.
--- include/linux/compiler_types.h | 13 +++++++++++++ 1 file changed, 13 insertions(+)
include/linux/compiler_types.h +13 -0
@@ -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
Signature · 1 line
2.53.0
Peter Zijlstra
maintainerRe: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
Quoted 14 lines · Sasha Levin
noinstr code may only call other noinstr functions and
__always_inlinefunctions. gcov and Clang's source-based code coverage insert their instrumentation before inlining, so the instrumentation of an__always_inlinefunction 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.
Quoted 8 lines
Make
__always_inlineimply__no_profilewhen a code coverage measurement implementation is enabled, so that no noinstr code carries coverage instrumentation. The cost is that__always_inlinefunctions don't show up in the coverage data at all, including at their callers outside of noinstr code. For anx86_64configuration 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.
Quoted 3 lines
Leave unoptimized objects alone: GCC refuses to inline an
__always_inlinefunction across ano_profile_instrument_functionmismatch 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.
Quoted 32 lines
Signed-off-by Sasha Levin <[email protected]>--- include/linux/compiler_types.h | 13 +++++++++++++ 1 file changed, 13 insertions(+)include/linux/compiler_types.h +13
-0@@ -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 Signature · 1 line
2.53.0
Peter Zijlstra
maintainerRe: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
Quoted 34 lines · Peter Zijlstra
Quoted 14 lines · Sasha Levin
noinstr code may only call other noinstr functions and
__always_inlinefunctions. gcov and Clang's source-based code coverage insert their instrumentation before inlining, so the instrumentation of an__always_inlinefunction 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.
Quoted 8 lines
Make
__always_inlineimply__no_profilewhen a code coverage measurement implementation is enabled, so that no noinstr code carries coverage instrumentation. The cost is that__always_inlinefunctions don't show up in the coverage data at all, including at their callers outside of noinstr code. For anx86_64configuration 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_inlinefunction.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.
Quoted 4 lines
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.
Re: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
Quoted 4 lines · Peter Zijlstra
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_inlinefunction.
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?
Quoted 4 lines
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.
Quoted 2 lines · Peter Zijlstra
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.
Quoted 2 lines · Peter Zijlstra
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/[email protected]/
Signature · 2 lines
Thanks, Sasha
Re: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
Quoted 6 lines · Sasha Levin
Quoted 4 lines · Peter Zijlstra
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_inlinefunction.
Quoted 8 lines
Yes, the
__always_inlinehelpers 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.
Quoted 9 lines
Quoted 4 lines
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_inlinewin 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..
RFC PATCH 5/20 context_tracking: Always inline the empty stubs called from noinstr code
In reply to Sasha LevinThe !CONFIG_CONTEXT_TRACKING_USER versions of user_enter_irqoff() and user_exit_irqoff(), and the !CONFIG_CONTEXT_TRACKING_IDLE versions of ct_idle_enter() and ct_idle_exit(), are plain static inline functions, while their real counterparts are __always_inline (user_*_irqoff()) or noinstr (ct_idle_*()) functions. They are called from noinstr code, so when they aren't inlined that's a call leaving the noinstr section. This happens with code coverage instrumentation, which makes the compiler refuse to inline an instrumented function into a noinstr one:
vmlinux.o: warning: objtool: do_syscall_64+0x81: call to user_exit_irqoff() leaves .noinstr.text section
Make the stubs __always_inline as well.
--- include/linux/context_tracking.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
include/linux/context_tracking.h +4 -4
@@ -93,8 +93,8 @@ static __always_inline bool context_tracking_guest_exit(void)#else static inline void user_enter (void ) { }static inline void user_exit (void ) { } -static inline void user_enter_irqoff (void ) { } -static inline void user_exit_irqoff (void ) { } +static __always_inline void user_enter_irqoff(void) { } +static __always_inline void user_exit_irqoff(void) { }static inline int exception_enter (void ) {return 0 ; }static inline void exception_exit (enum ctx_state prev_ctx) { }static inline int ct_state (void ) {return -1 ; }
@@ -162,8 +162,8 @@ static __always_inline void warn_rcu_exit(bool rcu) }#else -static inline void ct_idle_enter (void ) { } -static inline void ct_idle_exit (void ) { } +static __always_inline void ct_idle_enter(void) { } +static __always_inline void ct_idle_exit(void) { } static __always_inline bool warn_rcu_enter(void) { return false; } static __always_inline void warn_rcu_exit(bool rcu) { }
Signature · 1 line
2.53.0
Peter Zijlstra
maintainerRe: [RFC PATCH 05/20] context_tracking: Always inline the empty stubs called from noinstr code
Quoted 9 lines · Sasha Levin
The !CONFIG_CONTEXT_TRACKING_USER versions of
user_enter_irqoff()anduser_exit_irqoff(), and the !CONFIG_CONTEXT_TRACKING_IDLE versions ofct_idle_enter()andct_idle_exit(), are plain static inline functions, while their real counterparts are__always_inline(user_*_irqoff()) or noinstr (ct_idle_*()) functions. They are called from noinstr code, so when they aren't inlined that's a call leaving the noinstr section. This happens with code coverage instrumentation, which makes the compiler refuse to inline an instrumented function into a noinstr one:
Quoted 3 lines
vmlinux.o: warning: objtool:
do_syscall_64+0x81: call touser_exit_irqoff()leaves .noinstr.text sectionMake the stubs
__always_inlineas well.
Ah yes, nothing beats adding instrumentation to empty functions :-)
RFC PATCH 6/20 lockdep: Always inline the empty stubs called from noinstr code
In reply to Sasha LevinWith !CONFIG_PROVE_LOCKING, lockdep_hardirqs_on() and lockdep_hardirqs_off() are empty static inline functions, and so are lockdep_off() and lockdep_on() with !CONFIG_LOCKDEP, while the real versions are noinstr functions and macros respectively. They are called from noinstr code, which the compiler won't inline them into when building with code coverage instrumentation:
vmlinux.o: warning: objtool: do_syscall_64+0x7b: call to lockdep_hardirqs_off() leaves .noinstr.text section vmlinux.o: warning: objtool: irqentry_nmi_enter+0x4: call to lockdep_off() leaves .noinstr.text section
Make the stubs __always_inline, along with the neighbouring lockdep_hardirqs_on_prepare() stub for consistency.
--- include/linux/irqflags.h | 6 +++--- include/linux/lockdep.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-)
include/linux/irqflags.h +3 -3
@@ -32,9 +32,9 @@ struct task_struct;#else static inline void lockdep_softirqs_on (unsigned long ip) { }static inline void lockdep_softirqs_off (unsigned long ip) { } -static inline void lockdep_hardirqs_on_prepare (void ) { } -static inline void lockdep_hardirqs_on (unsigned long ip) { } -static inline void lockdep_hardirqs_off (unsigned long ip) { } + static __always_inline void lockdep_hardirqs_on_prepare(void) { } + static __always_inline void lockdep_hardirqs_on(unsigned long ip) { } + static __always_inline void lockdep_hardirqs_off(unsigned long ip) { }static inline void lockdep_cleanup_dead_cpu (unsigned int cpu,struct task_struct *idle) {}#endif
include/linux/lockdep.h +2 -2
@@ -324,11 +324,11 @@ static inline void lockdep_init_task(struct task_struct *task) { } -static inline void lockdep_off (void ) +static __always_inline void lockdep_off(void) { } -static inline void lockdep_on (void ) +static __always_inline void lockdep_on(void) { }
Signature · 1 line
2.53.0
RFC PATCH 7/20 perf: Always inline the empty perf_lopwr_cb() stub
In reply to Sasha Levinperf_lopwr_cb() is called from the noinstr acpi_idle_do_entry(). The x86 implementation is __always_inline, but the empty fallback is a plain static inline function, which the compiler won't inline into noinstr code when building with code coverage instrumentation:
vmlinux.o: warning: objtool: acpi_idle_do_entry+0x4: call to perf_lopwr_cb() leaves .noinstr.text section
Make the stub __always_inline as well.
--- include/linux/perf_event.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/perf_event.h +1 -1
@@ -2137,7 +2137,7 @@ typedef int (perf_snapshot_branch_stack_t)(struct perf_branch_entry *entries,DECLARE_STATIC_CALL (perf_snapshot_branch_stack, perf_snapshot_branch_stack_t);#ifndef PERF_NEEDS_LOPWR_CB -static inline void perf_lopwr_cb (bool mode) +static __always_inline void perf_lopwr_cb(bool mode) { }#endif
Signature · 1 line
2.53.0
[email protected]
AI reviewRe: [RFC PATCH 07/20] perf: Always inline the empty perf_lopwr_cb() stub
Sashiko has reviewed this patch and found no issues.
Quoted 10 lines
perf_lopwr_cb()is called from the noinstracpi_idle_do_entry(). The x86 implementation is__always_inline, but the empty fallback is a plain static inline function, which the compiler won't inline into noinstr code when building with code coverage instrumentation:vmlinux.o: warning: objtool:
acpi_idle_do_entry+0x4: call toperf_lopwr_cb()leaves .noinstr.text sectionMake the stub
__always_inlineas well.Signed-off-by Sasha Levin <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
RFC PATCH 8/20 rseq: Always inline the empty rseq_note_user_irq_entry() stub
In reply to Sasha Levinrseq_note_user_irq_entry() is called from noinstr entry code. The CONFIG_RSEQ version is __always_inline, but the !CONFIG_RSEQ stub is a plain static inline function, which the compiler won't inline into noinstr code when building with code coverage instrumentation.
Make the stub __always_inline as well.
--- include/linux/rseq_entry.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/rseq_entry.h +1 -1
@@ -759,7 +759,7 @@ static __always_inline void rseq_debug_syscall_return(struct pt_regs *regs)__rseq_debug_syscall_return (regs); }#else /* CONFIG_RSEQ */ -static inline void rseq_note_user_irq_entry (void ) { } +static __always_inline void rseq_note_user_irq_entry(void) { }static inline bool rseq_exit_to_user_mode_restart (struct pt_regs *regs,unsigned long ti_work) {return false;
Signature · 1 line
2.53.0
RFC PATCH 9/20 x86: Always inline empty stubs called from noinstr code
In reply to Sasha LevinThe !CONFIG_AMD_MEM_ENCRYPT stubs of sev_es_ist_enter(), sev_es_ist_exit() and sev_es_nmi_complete(), and the !CONFIG_CPU_SUP_AMD stub of amd_clear_divider(), are plain static inline functions, while the real implementations are __always_inline. They are called from noinstr code, which the compiler won't inline them into when building with code coverage instrumentation:
vmlinux.o: warning: objtool: exc_nmi+0x7: call to sev_es_nmi_complete() leaves .noinstr.text section
Make the stubs __always_inline as well.
--- arch/x86/include/asm/processor.h | 2 +- arch/x86/include/asm/sev.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-)
arch/x86/include/asm/processor.h +1 -1
@@ -735,7 +735,7 @@ static __always_inline void amd_clear_divider(void)extern void amd_check_microcode (void );#else -static inline void amd_clear_divider (void ) { } +static __always_inline void amd_clear_divider(void) { }static inline void amd_check_microcode (void ) { }#endif
arch/x86/include/asm/sev.h +3 -3
@@ -601,10 +601,10 @@ static inline void sev_evict_cache(void *va, int npages)#else /* !CONFIG_AMD_MEM_ENCRYPT */ #define snp_vmpl 0 -static inline void sev_es_ist_enter (struct pt_regs *regs) { } -static inline void sev_es_ist_exit (void ) { } +static __always_inline void sev_es_ist_enter(struct pt_regs *regs) { } +static __always_inline void sev_es_ist_exit(void) { }static inline int sev_es_setup_ap_jump_table (struct real_mode_header *rmh) {return 0 ; } -static inline void sev_es_nmi_complete (void ) { } +static __always_inline void sev_es_nmi_complete(void) { }static inline int sev_es_efi_map_ghcbs_cas (pgd_t *pgd) {return 0 ; }static inline void sev_enable (struct boot_params *bp) { }static inline int pvalidate (unsigned long vaddr,bool rmp_psize,bool validate) {return 0 ; }
Signature · 1 line
2.53.0
RFC PATCH 10/20 x86/sev: Use non-instrumented bitops in the GHCB accessors
In reply to Sasha LevinThe GHCB accessors are used from noinstr code, such as __sev_es_nmi_complete(), but use test_bit() and __set_bit(), which call the KASAN and KCSAN checks. arch/x86/coco/sev/Makefile disables KASAN and KCSAN for noinstr.o, which turns those checks into empty stubs, but they still have to be inlined. Instrumentation such as gcov can prevent that and leave calls to kasan_check_write() behind in noinstr code.
Use the arch_ bitops, which aren't instrumented, as is done elsewhere for noinstr code.
--- arch/x86/include/asm/svm.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/x86/include/asm/svm.h +4 -4
@@ -674,8 +674,8 @@ static inline void __unused_size_checks(void)#define DEFINE_GHCB_ACCESSORS (field) \ static __always_inline bool ghcb_##field##_is_valid(const struct ghcb *ghcb) \ { \ - return test_bit(GHCB_BITMAP_IDX(field), \ - (unsigned long *)&ghcb->save.valid_bitmap); \ + return arch_test_bit(GHCB_BITMAP_IDX(field), \ + (unsigned long *)&ghcb->save.valid_bitmap); \ } \ \ static __always_inline u64 ghcb_get_##field(struct ghcb *ghcb) \
@@ -690,8 +690,8 @@ static inline void __unused_size_checks(void) \ static __always_inline void ghcb_set_##field(struct ghcb *ghcb, u64 value) \ { \ - __set_bit(GHCB_BITMAP_IDX(field), \ - (unsigned long *)&ghcb->save.valid_bitmap); \ + arch___set_bit(GHCB_BITMAP_IDX(field), \ + (unsigned long *)&ghcb->save.valid_bitmap); \ ghcb->save.field = value; \ }
Signature · 1 line
2.53.0
RFC PATCH 11/20 objtool: Detect code coverage instrumentation in noinstr code
In reply to Sasha Levingcov 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.
--- tools/objtool/check.c | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
tools/objtool/check.c +53 -0
@@ -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;
Signature · 1 line
2.53.0
Peter Zijlstra
maintainerRe: [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code
Quoted 10 lines · Sasha Levin
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()andinstrumentation_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_cntsand__llvm_prf_bitssections of Clang's source-based code coverage.
Nice!
Quoted 56 lines
Signed-off-by Sasha Levin <[email protected]>--- tools/objtool/check.c | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)tools/objtool/check.c +36
-0@@ -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));
tools/objtool/check.c static inline bool noinstr_call_dest(struct objtool_file *file, · line 3555
Quoted 10 lines
tools/objtool/check.c +10
-0+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.
tools/objtool/check.c static int validate_insn(struct objtool_file *file, struct symbol *func, · line 3734
Quoted 13 lines
tools/objtool/check.c +6
-0+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?
Re: [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code
Quoted 3 lines · Peter Zijlstra
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::coverageor 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.
Quoted 1 line
Perhaps merge with the previous conditional?
Will do.
Signature · 2 lines
Thanks, Sasha
RFC PATCH 12/20 x86/sev: Stop excluding noinstr.o from code coverage
In reply to Sasha Levinnoinstr.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.
--- arch/x86/coco/sev/Makefile | 2 -- 1 file changed, 2 deletions(-)
arch/x86/coco/sev/Makefile +0 -2
@@ -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
Signature · 1 line
2.53.0
RFC PATCH 13/20 x86/entry: Stop excluding the entry code from KCOV
In reply to Sasha LevinThe x86 entry code has been excluded from KCOV since commit 20355e5f73a7 ("x86/entry: Exclude low level entry code from sanitizing"), when noinstr couldn't keep KCOV out of noinstr functions.
Since commit 540540d06e9d ("kcov: add __no_sanitize_coverage to fix noinstr for all architectures"), noinstr implies __no_sanitize_coverage, and on x86 objtool removes any KCOV call left in noinstr code. KCOV instruments the code after inlining, so __always_inline helpers don't carry it into noinstr functions either. What remains instrumentable in arch/x86/entry/ is the system call dispatch and the ptrace system call hooks, which run in task context.
Drop the exclusion, so that noinstr alone keeps KCOV out of the x86 entry code, like it does for the other coverage implementations. A traced read(-1) now reports 18 instead of 12 PCs, the additional ones being the system call dispatch.
The generic entry code has to stay excluded, but for a different reason than its Makefile gives: parts of the interrupt exit code, such as irqentry_exit_cond_resched(), run after leaving hardirq context, where KCOV adds their coverage to the trace of the interrupted task. Without the exclusion, they show up in the traces of system calls interrupted by the timer. Document that instead.
--- arch/x86/entry/Makefile | 1 - kernel/entry/Makefile | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-)
arch/x86/entry/Makefile +0 -1
@@ -5,7 +5,6 @@ KASAN_SANITIZE := n UBSAN_SANITIZE := n -KCOV_INSTRUMENT := n CFLAGS_REMOVE_syscall_32.o = $(CC_FLAGS_FTRACE) CFLAGS_REMOVE_syscall_64.o = $(CC_FLAGS_FTRACE)
kernel/entry/Makefile +3 -0
@@ -4,6 +4,9 @@ # as long as these things cannot be disabled per function. KASAN_SANITIZE := n UBSAN_SANITIZE := n + +# Parts of the interrupt exit code run after leaving hardirq context, where KCOV +# would add their coverage to the trace of the interrupted task. KCOV_INSTRUMENT := n # Branch profiling isn't noinstr-safe
Signature · 1 line
2.53.0
RFC PATCH 14/20 coverage: add Clang's source-based code coverage
In reply to Sasha LevinAdd 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 5Clang'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.
--- 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
MAINTAINERS +2 -0
@@ -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 <[email protected]>
Makefile +1 -0
@@ -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
include/asm-generic/vmlinux.lds.h +36 -0
@@ -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 = .; \
kernel/coverage/Kconfig +32 -1
@@ -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.
kernel/coverage/Makefile +1 -0
@@ -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.
kernel/coverage/llvm/Makefile new file +4 -0
@@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +COVERAGE_PROFILE := n + +obj-y := fs.o
kernel/coverage/llvm/fs.c new file +201 -0
201 lines not on this page · raw message
kernel/coverage/llvm/llvm-cov.h new file +139 -0
139 lines not on this page · raw message
scripts/Makefile.lib +5 -2
@@ -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 #
scripts/Makefile.llvm-cov new file +6 -0
@@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +CFLAGS_COVERAGE := -fprofile-instr-generate -fcoverage-mapping \ + -fprofile-update=atomic + +export CFLAGS_COVERAGE
Signature · 1 line
2.53.0
RFC PATCH 15/20 x86: Enable Clang's source-based code coverage
In reply to Sasha LevinPlace 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.
--- arch/x86/Kconfig | 2 ++ arch/x86/kernel/vmlinux.lds.S | 2 ++ 2 files changed, 4 insertions(+)
arch/x86/Kconfig +2 -0
@@ -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
arch/x86/kernel/vmlinux.lds.S +2 -0
@@ -212,6 +212,8 @@ SECTIONS BUG_TABLE + LLVM_COV_DATA + ORC_UNWIND_TABLE /* Init code and data - will be freed after init */
Signature · 1 line
2.53.0
RFC PATCH 16/20 coverage: add MC/DC measurement
In reply to Sasha LevinAdd 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.
--- kernel/coverage/Kconfig | 26 ++++++++++++++++++++++++++ kernel/coverage/llvm/fs.c | 5 +++-- scripts/Makefile.llvm-cov | 5 +++++ 3 files changed, 34 insertions(+), 2 deletions(-)
kernel/coverage/Kconfig +26 -0
@@ -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
kernel/coverage/llvm/fs.c +3 -2
@@ -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 )
scripts/Makefile.llvm-cov +5 -0
@@ -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
Signature · 1 line
2.53.0
Nathan Chancellor
maintainerRe: [RFC PATCH 16/20] coverage: add MC/DC measurement
Quoted 3 lines · Sasha Levin
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.
Signature · 2 lines
Cheers, Nathan
Re: [RFC PATCH 16/20] coverage: add MC/DC measurement
Quoted 10 lines · Nathan Chancellor
Quoted 3 lines · Sasha Levin
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.
Signature · 2 lines
Thanks, Sasha
RFC PATCH 17/20 gcov: fix gcov_info_add() merge semantics for IOR counters
In reply to Sasha Levingcov_info_add() unconditionally uses += to merge all counter types. This is wrong for counters that use IOR merge semantics (bitwise OR), such as GCOV_COUNTER_IOR and GCC 14's GCOV_COUNTER_CONDS (MC/DC condition coverage). These counters store bitsets that must be merged with |=, not accumulated with +=.
Detect IOR merge semantics by comparing the merge function pointer against __gcov_merge_ior, matching how GCC's own libgcov identifies merge semantics. This fixes the pre-existing bug for GCOV_COUNTER_IOR and also enables correct merging for MC/DC condition coverage data.
--- kernel/coverage/gcov/gcc_4_7.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
kernel/coverage/gcov/gcc_4_7.c +22 -3
@@ -18,6 +18,8 @@#include <linux/mm.h> #include "gcov.h" +extern void __gcov_merge_ior (gcov_type *,unsigned int ); +#if (__GNUC__ >=15 )#define GCOV_COUNTERS 10#elif (__GNUC__ >=14 )
@@ -187,6 +189,15 @@ static int counter_active(struct gcov_info *info, unsigned int type)return info->merge[type] ?1 :0 ; } +/* +* Determine whether a counter uses IOR merge semantics (bitwise OR of +* bitsets). Used for condition coverage (MC/DC) and other IOR-based counters. +*/ +static bool counter_is_ior (struct gcov_info *info,unsigned int type) +{ +return info->merge[type] == __gcov_merge_ior; +} +/* Determine number of active counters. Based on gcc magic. */ static unsigned int num_counter_active (struct gcov_info *info) {
@@ -259,9 +270,17 @@ void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)if (!counter_active (src, ct_idx))continue ; -for (val_idx =0 ; val_idx < sci_ptr->num; val_idx++) - dci_ptr->values[val_idx] += - sci_ptr->values[val_idx]; +if (counter_is_ior (src, ct_idx)) { +for (val_idx =0 ; val_idx < sci_ptr->num; + val_idx++) + dci_ptr->values[val_idx] |= + sci_ptr->values[val_idx]; + }else { +for (val_idx =0 ; val_idx < sci_ptr->num; + val_idx++) + dci_ptr->values[val_idx] += + sci_ptr->values[val_idx]; + } dci_ptr++; sci_ptr++;
Signature · 1 line
2.53.0
RFC PATCH 18/20 kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection
In reply to Sasha LevinAdd a Kconfig symbol to detect compiler support for -fcondition-coverage, which enables MC/DC (Modified Condition/Decision Coverage) instrumentation. This flag is available since GCC 14.
--- scripts/Kconfig.toolchain | 3 +++ 1 file changed, 3 insertions(+)
scripts/Kconfig.toolchain +3 -0
@@ -144,6 +144,9 @@ config CC_HAS_ASSUME config CC_HAS_NO_PROFILE_FN_ATTR def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror) +config CC_HAS_CONDITION_COVERAGE + def_bool $(cc-option,-fcondition-coverage) + config CC_OPT_ZERO_INIT_PADDING_BITS def_string "$(cc-option-str,-fzero-init-padding-bits=all)"
Signature · 1 line
2.53.0
RFC PATCH 19/20 gcov: add MC/DC condition coverage support
In reply to Sasha LevinSupport 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.
--- 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(-)
arch/x86/um/vdso/Makefile +2 -2
@@ -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.
kernel/coverage/Kconfig +5 -2
@@ -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
kernel/coverage/gcov/fs.c +3 -2
@@ -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. */
scripts/Makefile.gcov +3 -0
@@ -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
Signature · 1 line
2.53.0
RFC PATCH 20/20 Documentation: dev-tools: document the common code coverage interface
In reply to Sasha LevinDescribe 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.
--- 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
Documentation/dev-tools/coverage.rst new file +158 -0
@@ -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.
Documentation/dev-tools/gcov.rst +28 -0
@@ -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 -----------------
Documentation/dev-tools/index.rst +1 -0
@@ -23,6 +23,7 @@ Documentation/process/debugging/index.rst coccinelle context-analysis sparse + coverage kcov gcov kasan
Documentation/dev-tools/kcov.rst +3 -0
@@ -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.
MAINTAINERS +1 -0
@@ -6592,6 +6592,7 @@ F: drivers/media/platform/chips-media/coda CODE COVERAGE M: Sasha Levin <[email protected]> S: Maintained +F: Documentation/dev-tools/coverage.rst F: kernel/coverage/Kconfig F: kernel/coverage/Makefile F: kernel/coverage/core.c
Signature · 1 line
2.53.0