Skip to content

Commit

Permalink
Merge pull request #14 from chuckwolber/v1.0-patch-cleanup
Browse files Browse the repository at this point in the history
V1.0 patch cleanup
  • Loading branch information
whentojump authored Aug 22, 2024
2 parents 31c2ada + 7492a69 commit a94bbc2
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 104 deletions.
147 changes: 88 additions & 59 deletions patches/v1.0/0000-cover-letter.patch
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@ From: Wentao Zhang <[email protected]>
Date: Sun, 18 Aug 2024 18:50:22 -0500
Subject: [RFC PATCH 0/3] Enable measuring the kernel's Source-based Code Coverage and MC/DC with Clang

This patch series adds support for building x86-64 kernels with Clang's Source-
based Code Coverage and measuring modified condition/decision coverage (MC/DC).
This series adds support for building x86-64 kernels with Clang's Source-
based Code Coverage[1] in order to facilitate Modified Condition/Decision
Coverage (MC/DC)[2] that provably correlates to source code for all levels
of compiler optimization.

The newly added kernel/llvm-cov/ directory complements the existing gcov
implementation. gcov works at a lower level and may better reflect the actual
execution of object code. However, it lacks the necessary information to connect
coverage measurement back to source code locations and gcov reports are
sometimes confusing. With a nonzero optimization level (which is the default
when building the kernel), it's even harder to rebuild the mapping between
coverage reports and source code. In the following example from
drivers/firmware/dmi_scan.c, an expression with four leaf conditions are
reported to have six branch outcomes, which is not ideally informative in many
use cases.
implementation. Gcov works at the object code level which may better
reflect actual execution. However, Gcov lacks the necessary information to
correlate coverage measurement with source code location when compiler
optimization level is non-zero (which is the default when building the
kernel). In addition, gcov reports are occasionally ambiguous when
attempting to compare with source code level developer intent.

In the following gcov example from drivers/firmware/dmi_scan.c, an
expression with four conditions is reported to have six branch outcomes,
which is not ideally informative in many safety related use cases, such as
automotive, medical, and aerospace.

5: 1068: if (s == e || *e != '/' || !month || month > 12) {
branch 0 taken 5 (fallthrough)
Expand All @@ -25,12 +29,12 @@ branch 3 taken 0
branch 4 taken 0 (fallthrough)
branch 5 taken 5

On the other hand, Clang's Source-based Code Coverage [1] does instrumentation
at the compiler frontend and maintains accurate mapping from coverage measure to
source code locations. The generated reports can reflect exactly how the code is
written regardless of optimization and can present advanced metrics like branch
coverage and MC/DC in a more human-friendly way. Coverage report for the same
snippet by llvm-cov would look like the below:
On the other hand, Clang's Source-based Code Coverage instruments at the
compiler frontend which maintains an accurate mapping from coverage
measurement to source code location. Coverage reports reflect exactly how
the code is written regardless of optimization and can present advanced
metrics like branch coverage and MC/DC in a clearer way. Coverage report
for the same snippet by llvm-cov would look as follows:

1068| 5| if (s == e || *e != '/' || !month || month > 12) {
------------------
Expand All @@ -40,11 +44,12 @@ snippet by llvm-cov would look like the below:
| Branch (1068:39): [True: 0, False: 5]
------------------

Clang has also added MC/DC support since its release 18.1.0. MC/DC is a fine-
Clang has added MC/DC support as of its 18.1.0 release. MC/DC is a fine-
grained coverage metric required by many automotive and aviation industrial
standards for certifying mission-critical software [2]. In the following example
from arch/x86/events/probe.c, llvm-cov gives the MC/DC measure for the decision
at line 43.
standards for certifying mission-critical software [3].

In the following example from arch/x86/events/probe.c, 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))
------------------
Expand All @@ -67,49 +72,73 @@ at line 43.
------------------
44| 5| continue;

As the results suggest, during the given span of measurement, only condition C2
(!msr[bit].test(bit, data)) is covered. That means C2 was evaluated to both true
and false and in those test vectors C2 affected the decision outcome
independently. Therefore MC/DC for the shown decision is 1 out of 2 (50.00%).
As the results suggest, during the span of measurement, only condition C2
(!msr[bit].test(bit, data)) is covered. That means C2 was evaluated to both
true and false, and in those test vectors C2 affected the decision outcome
independently. Therefore MC/DC for this decision is 1 out of 2 (50.00%).

To do a full kernel measurement, instrument the kernel with LLVM_COV_KERNEL_MCDC
enabled, run the testsuites, and collect the raw profile data under
To do a full kernel measurement, instrument the kernel with
LLVM_COV_KERNEL_MCDC enabled, and optionally set a
LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS value (the default is six). Run the
testsuites, and collect the raw profile data under
/sys/kernel/debug/llvm-cov/profraw. Such raw profile data can be merged and
indexed, and used for generating coverage reports in various formats.

$ cp /sys/kernel/debug/llvm-cov/profraw vmlinux.profraw
$ llvm-profdata merge vmlinux.profraw -o vmlinux.profdata
$ llvm-cov show --show-mcdc --show-mcdc-summary \
--format=text --use-color=false -output-dir=coverage_reports \
-instr-profile vmlinux.profdata vmlinux

The first patch in the series enables Clang's Source-based Code Coverage for the
kernel. The second patch disables instrumenting the same set of files that were
skipped by kernel/gcov/ as well. The third patch enables MC/DC instrumentation
which is built on top of Source-based Code Coverage.

This work reuses a portion of code from a previous effort by Sami Tolvanen et
al. [3], but we aim for source-based *code coverage* required for high assurance
(MC/DC) while [3] focused more on performance optimization.

This initial submission is restricted to x86-64. Support for other architectures
would need a bit more Makefile & linker script modification. Informally we've
confirmed that arm64 works and more are being tested.

Note that Source-based Code Coverage is Clang-specific and isn't compatible with
Clang's gcov support in kernel/gcov/. Currently, kernel/gcov/ is not able to
measure MC/DC without modifying CFLAGS_GCOV and it would face the same issues in
terms of source correlation as gcov in general does.

Some demo and results can be found in [4]. We will talk about this patch series
in the Refereed Track of LPC 2024 [5] and would really like to hear the
$ llvm-cov show --show-mcdc --show-mcdc-summary \
--format=text --use-color=false -output-dir=coverage_reports \
-instr-profile vmlinux.profdata vmlinux

The first patch in this series enables Clang's Source-based Code Coverage
in the kernel. The second patch disables instrumentation in the same areas
that were disabled for kernel/gcov/. The third patch adds kernel
configuration directives to enable MC/DC instrumentation.

This work reuses code from a previous effort by Sami Tolvanen et al. [4].
Our aim is for source-based *code coverage* required for high assurance
(MC/DC) while [4] focused more on performance optimization.

This initial submission is restricted to x86-64. Support for other
architectures would need a bit more Makefile & linker script modification.
Informally we've confirmed that arm64 works and more are being tested.

Note that Source-based Code Coverage is Clang-specific and isn't compatible
with Clang's gcov support in kernel/gcov/. Currently, kernel/gcov/ is not
able to measure MC/DC without modifying CFLAGS_GCOV and it would face the
same issues in terms of source correlation as gcov in general does.

Some demo and results can be found in [5]. We will talk about this patch
series in the Refereed Track at LPC 2024 [6] and would appreciate the
community's feedback.

Known Limitations:

Kernel code with logical expressions exceeding
LVM_COV_KERNEL_MCDC_MAX_CONDITIONS will produce a compiler warning.
Expressions with up to 45 conditions are found in the Linux kernel source
tree, but 44 seems to be the max value before the build fails due to kernel
size. As of LLVM 19 the max number of conditions possible is 32767.

As of LLVM 19, certain expressions are still not covered, and will produce
build warnings when they are encountered:

"[...] if a boolean expression is embedded in the nest of another boolean
expression but separated by a non-logical operator, this is also not
supported. For example, in x = (a && b && c && func(d && f)), the d && f
case starts a new boolean expression that is separated from the other
conditions by the operator func(). When this is encountered, a warning
will be generated and the boolean expression will not be
instrumented." [7]


[1] https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
[2] https://digital-library.theiet.org/content/journals/10.1049/sej.1994.0025
[3] https://lore.kernel.org/lkml/[email protected]/
[4] https://github.com/xlab-uiuc/linux-mcdc
[5] https://lpc.events/event/18/contributions/1718/
[2] https://en.wikipedia.org/wiki/Modified_condition%2Fdecision_coverage
[3] https://digital-library.theiet.org/content/journals/10.1049/sej.1994.0025
[4] https://lore.kernel.org/lkml/[email protected]/
[5] https://github.com/xlab-uiuc/linux-mcdc
[6] https://lpc.events/event/18/contributions/1718/
[7] https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#mc-dc-instrumentation

Wentao Zhang (3):
[RFC PATCH 1/3] llvm-cov: add Clang's Source-based Code Coverage
Expand All @@ -132,20 +161,20 @@ Wentao Zhang (3):
drivers/firmware/efi/libstub/Makefile | 2 +
include/asm-generic/vmlinux.lds.h | 38 ++++
kernel/Makefile | 1 +
kernel/llvm-cov/Kconfig | 51 ++++++
kernel/llvm-cov/Kconfig | 65 +++++++
kernel/llvm-cov/Makefile | 5 +
kernel/llvm-cov/fs.c | 253 ++++++++++++++++++++++++++
kernel/llvm-cov/llvm-cov.h | 156 ++++++++++++++++
kernel/trace/Makefile | 1 +
scripts/Makefile.lib | 21 +++
scripts/Makefile.modfinal | 1 +
scripts/mod/modpost.c | 2 +
22 files changed, 551 insertions(+)
22 files changed, 565 insertions(+)
create mode 100644 kernel/llvm-cov/Kconfig
create mode 100644 kernel/llvm-cov/Makefile
create mode 100644 kernel/llvm-cov/fs.c
create mode 100644 kernel/llvm-cov/llvm-cov.h

--
2.34.1
--
2.45.2

Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ Date: Wed, 14 Aug 2024 15:42:58 -0500
Subject: [RFC PATCH 1/3] llvm-cov: add Clang's Source-based Code Coverage
support

This patch implements the debugfs entries for serializing profiles and
resetting counters/bitmaps and adds Source-based Code Coverage flags and
kconfig options.
Implement debugfs entries for serializing profiles and resetting
counters/bitmaps. Also adds Source-based Code Coverage flags and kconfig
options.

This work reuses a portion of code from a previous effort by Sami
Tolvanen et al. [1], specifically its debugfs interface and the
Expand All @@ -27,20 +27,20 @@ Signed-off-by: Chuck Wolber <[email protected]>
arch/x86/kernel/vmlinux.lds.S | 2 +
include/asm-generic/vmlinux.lds.h | 38 +++++
kernel/Makefile | 1 +
kernel/llvm-cov/Kconfig | 30 ++++
kernel/llvm-cov/Kconfig | 29 ++++
kernel/llvm-cov/Makefile | 5 +
kernel/llvm-cov/fs.c | 253 ++++++++++++++++++++++++++++++
kernel/llvm-cov/llvm-cov.h | 156 ++++++++++++++++++
scripts/Makefile.lib | 10 ++
scripts/mod/modpost.c | 2 +
12 files changed, 502 insertions(+)
12 files changed, 501 insertions(+)
create mode 100644 kernel/llvm-cov/Kconfig
create mode 100644 kernel/llvm-cov/Makefile
create mode 100644 kernel/llvm-cov/fs.c
create mode 100644 kernel/llvm-cov/llvm-cov.h

diff --git a/Makefile b/Makefile
index 68ebd6d6b..1750a2b7d 100644
index 68ebd6d6b444..1750a2b7dfe8 100644
--- a/Makefile
+++ b/Makefile
@@ -737,6 +737,9 @@ endif # KBUILD_EXTMOD
Expand All @@ -54,7 +54,7 @@ index 68ebd6d6b..1750a2b7d 100644
ifdef CONFIG_CC_IS_GCC
CFLAGS_GCOV += -fno-tree-loop-im
diff --git a/arch/Kconfig b/arch/Kconfig
index 975dd22a2..0727265f6 100644
index 975dd22a2dbd..0727265f677f 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -1601,6 +1601,7 @@ config ARCH_HAS_KERNEL_FPU_SUPPORT
Expand All @@ -66,7 +66,7 @@ index 975dd22a2..0727265f6 100644
source "scripts/gcc-plugins/Kconfig"

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 007bab9f2..87a793b82 100644
index 007bab9f2a0e..87a793b82bab 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -85,6 +85,7 @@ config X86
Expand All @@ -78,7 +78,7 @@ index 007bab9f2..87a793b82 100644
select ARCH_HAS_MEM_ENCRYPT
select ARCH_HAS_MEMBARRIER_SYNC_CORE
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 6e73403e8..904337722 100644
index 6e73403e874f..90433772240a 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -191,6 +191,8 @@ SECTIONS
Expand All @@ -91,7 +91,7 @@ index 6e73403e8..904337722 100644

. = ALIGN(PAGE_SIZE);
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 1ae447931..770ff8469 100644
index 1ae44793132a..770ff8469dcd 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -334,6 +334,44 @@
Expand Down Expand Up @@ -140,7 +140,7 @@ index 1ae447931..770ff8469 100644
STRUCT_ALIGN(); \
__dtb_start = .; \
diff --git a/kernel/Makefile b/kernel/Makefile
index 3c13240df..773e6a9ee 100644
index 3c13240dfc9f..773e6a9ee00a 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -117,6 +117,7 @@ obj-$(CONFIG_HAVE_STATIC_CALL) += static_call.o
Expand All @@ -153,10 +153,10 @@ index 3c13240df..773e6a9ee 100644

diff --git a/kernel/llvm-cov/Kconfig b/kernel/llvm-cov/Kconfig
new file mode 100644
index 000000000..b28090ba9
index 000000000000..505eba5bd23c
--- /dev/null
+++ b/kernel/llvm-cov/Kconfig
@@ -0,0 +1,30 @@
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Clang's source-based kernel coverage measurement (EXPERIMENTAL)"
+
Expand Down Expand Up @@ -186,10 +186,9 @@ index 000000000..b28090ba9
+ profile.
+
+endmenu
+
diff --git a/kernel/llvm-cov/Makefile b/kernel/llvm-cov/Makefile
new file mode 100644
index 000000000..a0f45e05f
index 000000000000..a0f45e05f8fb
--- /dev/null
+++ b/kernel/llvm-cov/Makefile
@@ -0,0 +1,5 @@
Expand All @@ -201,7 +200,7 @@ index 000000000..a0f45e05f
\ No newline at end of file
diff --git a/kernel/llvm-cov/fs.c b/kernel/llvm-cov/fs.c
new file mode 100644
index 000000000..917ca50d0
index 000000000000..c56f660a174e
--- /dev/null
+++ b/kernel/llvm-cov/fs.c
@@ -0,0 +1,253 @@
Expand Down Expand Up @@ -458,10 +457,9 @@ index 000000000..917ca50d0
+
+module_init(llvm_cov_init);
+module_exit(llvm_cov_exit);
\ No newline at end of file
diff --git a/kernel/llvm-cov/llvm-cov.h b/kernel/llvm-cov/llvm-cov.h
new file mode 100644
index 000000000..4a91b3c8e
index 000000000000..d9551a685756
--- /dev/null
+++ b/kernel/llvm-cov/llvm-cov.h
@@ -0,0 +1,156 @@
Expand Down Expand Up @@ -621,9 +619,8 @@ index 000000000..4a91b3c8e
+}
+
+#endif /* _LLVM_COV_H */
\ No newline at end of file
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index fe3668dc4..b9ceaee34 100644
index fe3668dc4954..b9ceaee34b28 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -158,6 +158,16 @@ _c_flags += $(if $(patsubst n%,, \
Expand All @@ -644,7 +641,7 @@ index fe3668dc4..b9ceaee34 100644
# Enable address sanitizer flags for kernel except some files or directories
# we don't want to check (depends on variables KASAN_SANITIZE_obj.o, KASAN_SANITIZE)
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index d16d0ace2..836c2289b 100644
index d16d0ace2775..836c2289b8d8 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -743,6 +743,8 @@ static const char *const section_white_list[] =
Expand All @@ -657,5 +654,5 @@ index d16d0ace2..836c2289b 100644
};

--
2.34.1
2.45.2

Loading

0 comments on commit a94bbc2

Please sign in to comment.