diff --git a/.github/workflows/llvm-trunk-linux-mainline.yml b/.github/workflows/llvm-trunk-linux-mainline.yml index 6fdd942..78f3ef6 100644 --- a/.github/workflows/llvm-trunk-linux-mainline.yml +++ b/.github/workflows/llvm-trunk-linux-mainline.yml @@ -8,7 +8,7 @@ on: paths: - '.github/workflows/llvm-trunk-linux-mainline.yml' - 'ci/linux-mainline/**' - - 'patches/v2.0/**' + - 'patches/v3.0/**' - 'scripts/**' workflow_dispatch: schedule: diff --git a/README.md b/README.md index db9605d..5f8482c 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,9 @@ [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/xlab-uiuc/linux-mcdc/llvm-trunk-linux-v5.15.153.yml?label=LLVM%20trunk%2BLinux%20v5.15.153)](https://github.com/xlab-uiuc/linux-mcdc/actions/workflows/llvm-trunk-linux-v5.15.153.yml) This repository demonstrates [KUnit](https://docs.kernel.org/dev-tools/kunit/index.html)'s -modified condition/decision coverage (MC/DC) of 5.15.y Linux kernel using -[Clang source-based code coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html) -and [`llvm-cov`](https://llvm.org/docs/CommandGuide/llvm-cov.html). A patch set -for mainline kernel is also being prepared. +modified condition/decision coverage (MC/DC) of 5.15.y and mainline Linux kernel +using [Clang source-based code coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html) +and [`llvm-cov`](https://llvm.org/docs/CommandGuide/llvm-cov.html). +**Patches can be found under [`patches/`](patches/), or join LKML discussions: +[link](https://lore.kernel.org/lkml/20240824230641.385839-1-wentaoz5@illinois.edu/)** + Follow the instructions [here](docs/measure-kernel-mcdc.md) to get started. Example text coverage report: [link](https://github.com/xlab-uiuc/linux-mcdc/actions/runs/10013137034/job/27681036852#step:8:7) (login with any GitHub account required) @@ -24,34 +26,17 @@ Example HTML coverage report: -Tentative repository structure: - -```text -linux-mcdc -| -├── ci/{linux-v5.15.153} -│   ├── 1_install_deps.sh -│   ├── 2_pull_source.sh -│   ├── 3_get_llvm.sh -│   ├── 4_build_kernel.sh -│   └── 5_boot_kernel_and_collect_coverage.sh -| -├── docs -│   ├── elisa-slides.pdf -│   └── measure-kernel-mcdc.md -| -├── patches -│   ├── README.md -│   └── {v0.4,v0.5,v0.6} -| -├── README.md -| -├── screenshot.png -| -└── scripts - ├── build-llvm.sh - └── q -``` +We gave three talks in [LPC 2024](https://lpc.events/event/18/page/224-lpc-2024-overview): + +- [Making Linux Fly: Towards a Certified Linux Kernel](https://lpc.events/event/18/contributions/1718/) (Refereed Track) + [[recording](https://www.youtube.com/live/1KWkfHxTqYY?feature=shared&t=3957)] + [[slides](https://lpc.events/event/18/contributions/1718/attachments/1584/3477/LPC'24%20Fly%20(no%20animation).pdf)] +- [Measuring and Understanding Linux Kernel Tests](https://lpc.events/event/18/contributions/1793/) (Kernel Testing & Dependability MC) + [[recording](https://www.youtube.com/live/kcr8NXEbzcg?feature=shared&t=9380)] + [[slides](https://lpc.events/event/18/contributions/1793/attachments/1624/3447/LPC'24%20Linux%20Testing.pdf)] +- [Source-based code coverage of Linux kernel](https://lpc.events/event/18/contributions/1895/) (Safe Systems with Linux MC) + [[recording](https://www.youtube.com/live/kcr8NXEbzcg?feature=shared&t=23820)] + [[slides](https://lpc.events/event/18/contributions/1895/attachments/1643/3462/LPC'24%20Source%20based%20(short).pdf)] We gave an [ELISA](https://elisa.tech/) seminar titled "Making Linux Fly: Towards Certified Linux Kernel". diff --git a/patches/latest b/patches/latest index 9c2f1ee..26425a3 120000 --- a/patches/latest +++ b/patches/latest @@ -1 +1 @@ -v2.0/ \ No newline at end of file +v3.0/ \ No newline at end of file diff --git a/patches/v2.0/0000-cover-letter.patch b/patches/v2.0/0000-cover-letter.patch index 45d9d82..1ae138d 100644 --- a/patches/v2.0/0000-cover-letter.patch +++ b/patches/v2.0/0000-cover-letter.patch @@ -1,7 +1,7 @@ From 7c60851daae39815fa696fc2265e838b1913f7ac Mon Sep 17 00:00:00 2001 From: Wentao Zhang Date: Wed, 4 Sep 2024 18:13:07 -0500 -Subject: [RFC PATCH 0/4] Enable measuring the kernel's Source-based Code Coverage and MC/DC with Clang +Subject: [PATCH v2 0/4] Enable measuring the kernel's Source-based Code Coverage and MC/DC with Clang This series adds support for building x86-64 kernels with Clang's Source- based Code Coverage[1] in order to facilitate Modified Condition/Decision diff --git a/patches/v2.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch b/patches/v2.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch index 9c8fa51..0436a11 100644 --- a/patches/v2.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch +++ b/patches/v2.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch @@ -1,7 +1,7 @@ From 13c86fc7877aec4892832554845968d7415f2960 Mon Sep 17 00:00:00 2001 From: Wentao Zhang Date: Fri, 30 Aug 2024 14:48:03 -0500 -Subject: [RFC PATCH 1/4] llvm-cov: add Clang's Source-based Code Coverage +Subject: [PATCH v2 1/4] llvm-cov: add Clang's Source-based Code Coverage support Add infrastructure to support Clang's source-based code coverage [1]. This diff --git a/patches/v2.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch b/patches/v2.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch index cf31b6b..e224983 100644 --- a/patches/v2.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch +++ b/patches/v2.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch @@ -1,7 +1,7 @@ From e7d38a2115b9c1041dcda404f102888c9894da10 Mon Sep 17 00:00:00 2001 From: Wentao Zhang Date: Fri, 30 Aug 2024 14:49:24 -0500 -Subject: [RFC PATCH 2/4] llvm-cov: add Clang's MC/DC support +Subject: [PATCH v2 2/4] llvm-cov: add Clang's MC/DC support Add infrastructure to enable Clang's Modified Condition/Decision Coverage (MC/DC) [1]. diff --git a/patches/v2.0/0003-x86-disable-llvm-cov-instrumentation.patch b/patches/v2.0/0003-x86-disable-llvm-cov-instrumentation.patch index 771df8d..54851d9 100644 --- a/patches/v2.0/0003-x86-disable-llvm-cov-instrumentation.patch +++ b/patches/v2.0/0003-x86-disable-llvm-cov-instrumentation.patch @@ -1,7 +1,7 @@ From f4cc26b9d49b7c34671c04a8b62485a311530298 Mon Sep 17 00:00:00 2001 From: Wentao Zhang Date: Fri, 30 Aug 2024 14:48:53 -0500 -Subject: [RFC PATCH 3/4] x86: disable llvm-cov instrumentation +Subject: [PATCH v2 3/4] x86: disable llvm-cov instrumentation Disable instrumentation for arch/x86/crypto/curve25519-x86_64.c. Otherwise compilation would fail with "error: inline assembly requires more registers diff --git a/patches/v2.0/0004-x86-enable-llvm-cov-support.patch b/patches/v2.0/0004-x86-enable-llvm-cov-support.patch index 166d584..1e756b8 100644 --- a/patches/v2.0/0004-x86-enable-llvm-cov-support.patch +++ b/patches/v2.0/0004-x86-enable-llvm-cov-support.patch @@ -1,7 +1,7 @@ From 7c60851daae39815fa696fc2265e838b1913f7ac Mon Sep 17 00:00:00 2001 From: Wentao Zhang Date: Fri, 30 Aug 2024 14:49:06 -0500 -Subject: [RFC PATCH 4/4] x86: enable llvm-cov support +Subject: [PATCH v2 4/4] x86: enable llvm-cov support Set ARCH_HAS_* options to "y" in kconfig and include section description in linker script. diff --git a/patches/v3.0/0000-cover-letter.patch b/patches/v3.0/0000-cover-letter.patch new file mode 100644 index 0000000..0dc400c --- /dev/null +++ b/patches/v3.0/0000-cover-letter.patch @@ -0,0 +1,194 @@ +From fba32f3502243c3f1bcb967667f6618897b8bf80 Mon Sep 17 00:00:00 2001 +From: Wentao Zhang +Date: Tue, 8 Oct 2024 16:51:11 -0500 +Subject: [PATCH v3 0/5] Enable measuring the kernel's Source-based Code Coverage and MC/DC with Clang + +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 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) +branch 1 taken 0 +branch 2 taken 5 (fallthrough) +branch 3 taken 0 +branch 4 taken 0 (fallthrough) +branch 5 taken 5 + +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) { + ------------------ + | 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] + ------------------ + +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 [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)) + ------------------ + |---> 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; + +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, 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 two patches enable the llvm-cov infrastructure, where the first +enables source based code coverage and the second adds MC/DC support. The +next patch disables instrumentation for curve25519-x86_64.c for the same +reason as gcov. The final patch enables coverage for x86-64. + +The choice to use a new Makefile variable LLVM_COV_PROFILE, instead of +reusing GCOV_PROFILE, was deliberate. More work needs to be done to +determine if it is appropriate to reuse the same flag. In addition, given +the fundamentally different approaches to instrumentation and the resulting +variation in coverage reports, there is a strong likelihood that coverage +type will need to be managed separately. + +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]. + +Known Limitations: + +Kernel code with logical expressions exceeding +LVM_COV_KERNEL_MCDC_MAX_CONDITIONS will produce a compiler warning. +Expressions with up to 47 conditions are found in the Linux kernel source +tree (as of v6.11), but 46 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://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/20210407211704.367039-1-morbo@google.com/ +[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 + +--- +v2 -> v3: + +* Rebased onto v6.12-rc2 from v6.11-rc6. +* llvm-cov: + * Improve documentation regarding LLVM raw profile version. + * Remove license boilerplate text. +* Kconfig: + * Make LLVM_COV_KERNEL and GCOV_KERNEL exclusive. + * Improve documentation for ARCH_HAS_LLVM_COV, + ARCH_HAS_LLVM_COV_PROFILE_ALL and LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS. + * Remove "default n". + +--- + +Wentao Zhang (5): + llvm-cov: add Clang's Source-based Code Coverage support + llvm-cov: add Clang's MC/DC support + x86: disable llvm-cov instrumentation + x86: enable llvm-cov support + MAINTAINERS: add "LLVM-COV BASED KERNEL PROFILING" entry + + MAINTAINERS | 6 + + Makefile | 9 ++ + arch/Kconfig | 1 + + arch/x86/Kconfig | 2 + + arch/x86/crypto/Makefile | 3 +- + arch/x86/kernel/vmlinux.lds.S | 2 + + include/asm-generic/vmlinux.lds.h | 36 +++++ + kernel/Makefile | 1 + + kernel/llvm-cov/Kconfig | 118 +++++++++++++++ + kernel/llvm-cov/Makefile | 4 + + kernel/llvm-cov/fs.c | 243 ++++++++++++++++++++++++++++++ + kernel/llvm-cov/llvm-cov.h | 159 +++++++++++++++++++ + scripts/Makefile.lib | 23 +++ + scripts/mod/modpost.c | 2 + + 14 files changed, 608 insertions(+), 1 deletion(-) + 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.45.2 + diff --git a/patches/v3.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch b/patches/v3.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch new file mode 100644 index 0000000..e32919a --- /dev/null +++ b/patches/v3.0/0001-llvm-cov-add-Clang-s-Source-based-Code-Coverage-supp.patch @@ -0,0 +1,704 @@ +From c2d7749054577e985347caabfbb21bf36af93551 Mon Sep 17 00:00:00 2001 +From: Wentao Zhang +Date: Fri, 30 Aug 2024 14:48:03 -0500 +Subject: [PATCH v3 1/5] llvm-cov: add Clang's Source-based Code Coverage + support + +Add infrastructure to support Clang's source-based code coverage [1]. This +includes debugfs entries for serializing profiles and resetting +counters/bitmaps. Also adds coverage flags and kconfig options. + +The newly added kernel/llvm-cov/ directory complements the existing gcov +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) +branch 1 taken 0 +branch 2 taken 5 (fallthrough) +branch 3 taken 0 +branch 4 taken 0 (fallthrough) +branch 5 taken 5 + +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) { + ------------------ + | 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] + ------------------ + +This work reuses a portion of code from a previous effort by Sami Tolvanen +et al. [2], specifically its debugfs interface and the underlying profile +processing, but discards all its PGO-specific parts, notably value +profiling. To our end (code coverage required for high assurance), we +require instrumentation at the compiler front end, instead of IR; we care +about counters and bitmaps, but not value profiling. + +Link: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html [1] +Link: https://lore.kernel.org/lkml/20210407211704.367039-1-morbo@google.com/ [2] +Signed-off-by: Wentao Zhang +Reviewed-by: Chuck Wolber +Tested-by: Chuck Wolber +Reviewed-by: Nathan Chancellor +--- + Makefile | 3 + + arch/Kconfig | 1 + + include/asm-generic/vmlinux.lds.h | 36 +++++ + kernel/Makefile | 1 + + kernel/llvm-cov/Kconfig | 75 +++++++++ + kernel/llvm-cov/Makefile | 4 + + kernel/llvm-cov/fs.c | 243 ++++++++++++++++++++++++++++++ + kernel/llvm-cov/llvm-cov.h | 159 +++++++++++++++++++ + scripts/Makefile.lib | 11 ++ + scripts/mod/modpost.c | 2 + + 10 files changed, 535 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 c5493c0c0..1823f04fb 100644 +--- a/Makefile ++++ b/Makefile +@@ -736,6 +736,9 @@ endif # KBUILD_EXTMOD + # Defaults to vmlinux, but the arch makefile usually adds further targets + all: vmlinux + ++CFLAGS_LLVM_COV := -fprofile-instr-generate -fcoverage-mapping ++export CFLAGS_LLVM_COV ++ + CFLAGS_GCOV := -fprofile-arcs -ftest-coverage + ifdef CONFIG_CC_IS_GCC + CFLAGS_GCOV += -fno-tree-loop-im +diff --git a/arch/Kconfig b/arch/Kconfig +index 8af374ea1..6abf9dc2a 100644 +--- a/arch/Kconfig ++++ b/arch/Kconfig +@@ -1642,6 +1642,7 @@ config ARCH_HAS_KERNEL_FPU_SUPPORT + the kernel, as described in Documentation/core-api/floating-point.rst. + + source "kernel/gcov/Kconfig" ++source "kernel/llvm-cov/Kconfig" + + source "scripts/gcc-plugins/Kconfig" + +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index eeadbaecc..c8a333157 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -335,6 +335,42 @@ + #define THERMAL_TABLE(name) + #endif + ++#ifdef CONFIG_LLVM_COV_KERNEL ++#define LLVM_COV_DATA \ ++ __llvm_prf_data : AT(ADDR(__llvm_prf_data) - LOAD_OFFSET) { \ ++ BOUNDED_SECTION_POST_LABEL(__llvm_prf_data, \ ++ __llvm_prf_data, \ ++ _start, _end) \ ++ } \ ++ __llvm_prf_cnts : AT(ADDR(__llvm_prf_cnts) - LOAD_OFFSET) { \ ++ BOUNDED_SECTION_POST_LABEL(__llvm_prf_cnts, \ ++ __llvm_prf_cnts, \ ++ _start, _end) \ ++ } \ ++ __llvm_prf_names : AT(ADDR(__llvm_prf_names) - LOAD_OFFSET) { \ ++ BOUNDED_SECTION_POST_LABEL(__llvm_prf_names, \ ++ __llvm_prf_names, \ ++ _start, _end) \ ++ } \ ++ __llvm_prf_bits : AT(ADDR(__llvm_prf_bits) - LOAD_OFFSET) { \ ++ BOUNDED_SECTION_POST_LABEL(__llvm_prf_bits, \ ++ __llvm_prf_bits, \ ++ _start, _end) \ ++ } \ ++ __llvm_covfun : AT(ADDR(__llvm_covfun) - LOAD_OFFSET) { \ ++ BOUNDED_SECTION_POST_LABEL(__llvm_covfun, \ ++ __llvm_covfun, \ ++ _start, _end) \ ++ } \ ++ __llvm_covmap : AT(ADDR(__llvm_covmap) - LOAD_OFFSET) { \ ++ BOUNDED_SECTION_POST_LABEL(__llvm_covmap, \ ++ __llvm_covmap, \ ++ _start, _end) \ ++ } ++#else ++#define LLVM_COV_DATA ++#endif ++ + #define KERNEL_DTB() \ + STRUCT_ALIGN(); \ + __dtb_start = .; \ +diff --git a/kernel/Makefile b/kernel/Makefile +index 87866b037..ea63bec02 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -116,6 +116,7 @@ obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o + obj-$(CONFIG_HAVE_STATIC_CALL) += static_call.o + obj-$(CONFIG_HAVE_STATIC_CALL_INLINE) += static_call_inline.o + obj-$(CONFIG_CFI_CLANG) += cfi.o ++obj-$(CONFIG_LLVM_COV_KERNEL) += llvm-cov/ + + obj-$(CONFIG_PERF_EVENTS) += events/ + +diff --git a/kernel/llvm-cov/Kconfig b/kernel/llvm-cov/Kconfig +new file mode 100644 +index 000000000..e0533be92 +--- /dev/null ++++ b/kernel/llvm-cov/Kconfig +@@ -0,0 +1,75 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++menu "Clang's source-based kernel coverage measurement (EXPERIMENTAL)" ++ ++config ARCH_HAS_LLVM_COV ++ bool ++ help ++ An architecture can select this when it can successfully build and run ++ with CONFIG_LLVM_COV_KERNEL. This typically requires (1) disabling ++ instrumentation for object files that otherwise result in build ++ failures and (2) including LLVM_COV_DATA in the linker script so that ++ relevant sections are placed correctly into vmlinux. ++ ++config ARCH_HAS_LLVM_COV_PROFILE_ALL ++ bool ++ help ++ An architecture can select this when it can successfully build and run ++ with CONFIG_LLVM_COV_PROFILE_ALL. This typically requires ++ (1) disabling instrumentation for object files that otherwise result ++ in build failures and (2) including LLVM_COV_DATA in the linker script ++ so that relevant sections are placed correctly into vmlinux. ++ ++config LLVM_COV_KERNEL ++ bool "Enable Clang's source-based kernel coverage measurement" ++ depends on DEBUG_FS ++ depends on ARCH_HAS_LLVM_COV ++ depends on CC_IS_CLANG && CLANG_VERSION >= 180000 ++ depends on !GCOV_KERNEL ++ help ++ This option enables Clang's Source-based Code Coverage. ++ ++ If unsure, say N. ++ ++ On a kernel compiled with this option, run your test suites, and ++ download the raw profile from /sys/kernel/debug/llvm-cov/profraw. ++ This file can then be converted into the indexed format with ++ llvm-profdata and used to generate coverage reports with llvm-cov. ++ ++ Additionally specify CONFIG_LLVM_COV_PROFILE_ALL=y to get profiling ++ data for the entire kernel. To enable profiling for specific files or ++ directories, add a line similar to the following to the respective ++ Makefile: ++ ++ For a single file (e.g. main.o): ++ LLVM_COV_PROFILE_main.o := y ++ ++ For all files in one directory: ++ LLVM_COV_PROFILE := y ++ ++ To exclude files from being profiled even when ++ CONFIG_LLVM_COV_PROFILE_ALL is specified, use: ++ ++ LLVM_COV_PROFILE_main.o := n ++ and: ++ LLVM_COV_PROFILE := n ++ ++ Note that a kernel compiled with coverage flags will be significantly ++ larger and run slower. ++ ++ Note that the debugfs filesystem has to be mounted to access the raw ++ profile. ++ ++config LLVM_COV_PROFILE_ALL ++ bool "Profile entire Kernel" ++ depends on !COMPILE_TEST ++ depends on LLVM_COV_KERNEL ++ depends on ARCH_HAS_LLVM_COV_PROFILE_ALL ++ help ++ This options activates profiling for the entire kernel. ++ ++ If unsure, say N. ++ ++ Note that a kernel compiled with profiling flags will be significantly ++ larger and run slower. ++ ++endmenu +diff --git a/kernel/llvm-cov/Makefile b/kernel/llvm-cov/Makefile +new file mode 100644 +index 000000000..1a03a88c7 +--- /dev/null ++++ b/kernel/llvm-cov/Makefile +@@ -0,0 +1,4 @@ ++# SPDX-License-Identifier: GPL-2.0 ++LLVM_COV_PROFILE := n ++ ++obj-y += fs.o +diff --git a/kernel/llvm-cov/fs.c b/kernel/llvm-cov/fs.c +new file mode 100644 +index 000000000..363064bf9 +--- /dev/null ++++ b/kernel/llvm-cov/fs.c +@@ -0,0 +1,243 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2019 Sami Tolvanen , Google, Inc. ++ * Copyright (C) 2024 Jinghao Jia , UIUC ++ * Copyright (C) 2024 Wentao Zhang , UIUC ++ */ ++ ++#define pr_fmt(fmt) "llvm-cov: " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "llvm-cov.h" ++ ++/* ++ * This lock guards both counter/bitmap reset and serialization of the ++ * raw profile data. Keeping both of these activities separate via locking ++ * ensures that we don't try to serialize data that's being reset. ++ */ ++DEFINE_SPINLOCK(llvm_cov_lock); ++ ++static struct dentry *directory; ++ ++struct llvm_cov_private_data { ++ char *buffer; ++ unsigned long size; ++}; ++ ++/* ++ * Raw profile data format: ++ * https://llvm.org/docs/InstrProfileFormat.html#raw-profile-format. We will ++ * only populate information that's relevant to basic Source-based Code Coverage ++ * before serialization. Other features like binary IDs, continuous mode, ++ * single-byte mode, value profiling, type profiling etc are not implemented. ++ */ ++ ++static void llvm_cov_fill_raw_profile_header(void **buffer) ++{ ++ struct __llvm_profile_header *header = *(struct __llvm_profile_header **)buffer; ++ ++ header->magic = INSTR_PROF_RAW_MAGIC_64; ++ header->version = INSTR_PROF_RAW_VERSION; ++ header->binary_ids_size = 0; ++ header->num_data = __llvm_prf_data_count(); ++ header->padding_bytes_before_counters = 0; ++ header->num_counters = __llvm_prf_cnts_count(); ++ header->padding_bytes_after_counters = ++ __llvm_prf_get_padding(__llvm_prf_cnts_size()); ++ header->num_bitmap_bytes = __llvm_prf_bits_size(); ++ header->padding_bytes_after_bitmap_bytes = ++ __llvm_prf_get_padding(__llvm_prf_bits_size()); ++ header->names_size = __llvm_prf_names_size(); ++ header->counters_delta = (u64)__llvm_prf_cnts_start - ++ (u64)__llvm_prf_data_start; ++ header->bitmap_delta = (u64)__llvm_prf_bits_start - ++ (u64)__llvm_prf_data_start; ++ header->names_delta = (u64)__llvm_prf_names_start; ++#if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 190000 ++ header->num_v_tables = 0; ++ header->v_names_size = 0; ++#endif ++ header->value_kind_last = IPVK_LAST; ++ ++ *buffer += sizeof(*header); ++} ++ ++/* ++ * Copy the source into the buffer, incrementing the pointer into buffer in the ++ * process. ++ */ ++static void llvm_cov_copy_section_to_buffer(void **buffer, void *src, ++ unsigned long size) ++{ ++ memcpy(*buffer, src, size); ++ *buffer += size; ++} ++ ++static unsigned long llvm_cov_get_raw_profile_size(void) ++{ ++ return sizeof(struct __llvm_profile_header) + ++ __llvm_prf_data_size() + ++ __llvm_prf_cnts_size() + ++ __llvm_prf_get_padding(__llvm_prf_cnts_size()) + ++ __llvm_prf_bits_size() + ++ __llvm_prf_get_padding(__llvm_prf_bits_size()) + ++ __llvm_prf_names_size() + ++ __llvm_prf_get_padding(__llvm_prf_names_size()); ++} ++ ++/* ++ * Serialize in-memory data into a format LLVM tools can understand ++ * (https://llvm.org/docs/InstrProfileFormat.html#raw-profile-format) ++ */ ++static int llvm_cov_serialize_raw_profile(struct llvm_cov_private_data *p) ++{ ++ int err = 0; ++ void *buffer; ++ ++ p->size = llvm_cov_get_raw_profile_size(); ++ p->buffer = vzalloc(p->size); ++ ++ if (!p->buffer) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ buffer = p->buffer; ++ ++ llvm_cov_fill_raw_profile_header(&buffer); ++ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_data_start, ++ __llvm_prf_data_size()); ++ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_cnts_start, ++ __llvm_prf_cnts_size()); ++ buffer += __llvm_prf_get_padding(__llvm_prf_cnts_size()); ++ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_bits_start, ++ __llvm_prf_bits_size()); ++ buffer += __llvm_prf_get_padding(__llvm_prf_bits_size()); ++ llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_names_start, ++ __llvm_prf_names_size()); ++ buffer += __llvm_prf_get_padding(__llvm_prf_names_size()); ++ ++out: ++ return err; ++} ++ ++/* open() implementation for llvm-cov data file. */ ++static int llvm_cov_open(struct inode *inode, struct file *file) ++{ ++ struct llvm_cov_private_data *data; ++ unsigned long flags; ++ int err; ++ ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if (!data) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ flags = llvm_cov_claim_lock(); ++ ++ err = llvm_cov_serialize_raw_profile(data); ++ if (unlikely(err)) { ++ kfree(data); ++ goto out_unlock; ++ } ++ ++ file->private_data = data; ++ ++out_unlock: ++ llvm_cov_release_lock(flags); ++out: ++ return err; ++} ++ ++/* read() implementation for llvm-cov data file. */ ++static ssize_t llvm_cov_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ struct llvm_cov_private_data *data = file->private_data; ++ ++ if (!data) ++ return -EBADF; ++ ++ return simple_read_from_buffer(buf, count, ppos, data->buffer, ++ data->size); ++} ++ ++/* release() implementation for llvm-cov data file. */ ++static int llvm_cov_release(struct inode *inode, struct file *file) ++{ ++ struct llvm_cov_private_data *data = file->private_data; ++ ++ if (data) { ++ vfree(data->buffer); ++ kfree(data); ++ } ++ ++ return 0; ++} ++ ++static const struct file_operations llvm_cov_data_fops = { ++ .owner = THIS_MODULE, ++ .open = llvm_cov_open, ++ .read = llvm_cov_read, ++ .llseek = default_llseek, ++ .release = llvm_cov_release ++}; ++ ++/* write() implementation for llvm-cov reset file */ ++static ssize_t reset_write(struct file *file, const char __user *addr, ++ size_t len, loff_t *pos) ++{ ++ unsigned long flags; ++ ++ flags = llvm_cov_claim_lock(); ++ memset(__llvm_prf_cnts_start, 0, __llvm_prf_cnts_size()); ++ memset(__llvm_prf_bits_start, 0, __llvm_prf_bits_size()); ++ llvm_cov_release_lock(flags); ++ ++ return len; ++} ++ ++static const struct file_operations llvm_cov_reset_fops = { ++ .owner = THIS_MODULE, ++ .write = reset_write, ++ .llseek = noop_llseek, ++}; ++ ++/* Create debugfs entries. */ ++static int __init llvm_cov_init(void) ++{ ++ directory = debugfs_create_dir("llvm-cov", NULL); ++ if (!directory) ++ goto err_remove; ++ ++ if (!debugfs_create_file("profraw", 0400, directory, NULL, ++ &llvm_cov_data_fops)) ++ goto err_remove; ++ ++ if (!debugfs_create_file("reset", 0200, directory, NULL, ++ &llvm_cov_reset_fops)) ++ goto err_remove; ++ ++ return 0; ++ ++err_remove: ++ debugfs_remove_recursive(directory); ++ pr_err("initialization failed\n"); ++ return -EIO; ++} ++ ++/* Remove debugfs entries. */ ++static void __exit llvm_cov_exit(void) ++{ ++ debugfs_remove_recursive(directory); ++} ++ ++module_init(llvm_cov_init); ++module_exit(llvm_cov_exit); +diff --git a/kernel/llvm-cov/llvm-cov.h b/kernel/llvm-cov/llvm-cov.h +new file mode 100644 +index 000000000..d807f1b57 +--- /dev/null ++++ b/kernel/llvm-cov/llvm-cov.h +@@ -0,0 +1,159 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2019 Sami Tolvanen , Google, Inc. ++ * Copyright (C) 2024 Jinghao Jia , UIUC ++ * Copyright (C) 2024 Wentao Zhang , UIUC ++ */ ++ ++#ifndef _LLVM_COV_H ++#define _LLVM_COV_H ++ ++extern spinlock_t llvm_cov_lock; ++ ++static __always_inline unsigned long llvm_cov_claim_lock(void) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&llvm_cov_lock, flags); ++ ++ return flags; ++} ++ ++static __always_inline void llvm_cov_release_lock(unsigned long flags) ++{ ++ spin_unlock_irqrestore(&llvm_cov_lock, flags); ++} ++ ++/* ++ * Note: These internal LLVM definitions must match the compiler version. ++ * See llvm/include/llvm/ProfileData/InstrProfData.inc in LLVM's source code. ++ */ ++ ++#define INSTR_PROF_RAW_MAGIC_64 \ ++ ((u64)255 << 56 | \ ++ (u64)'l' << 48 | \ ++ (u64)'p' << 40 | \ ++ (u64)'r' << 32 | \ ++ (u64)'o' << 24 | \ ++ (u64)'f' << 16 | \ ++ (u64)'r' << 8 | \ ++ (u64)129) ++ ++/* ++ * Version bump in LLVM commit db7e9e68411d, in which code coverage ++ * functionality (llvm-cov) is unaffected. ++ */ ++#if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 190000 ++#define INSTR_PROF_RAW_VERSION 10 ++#define INSTR_PROF_DATA_ALIGNMENT 8 ++#define IPVK_LAST 2 ++/* ++ * Version bump in LLVM commit a50486fd736a, which adds MC/DC support. ++ * Version 9 is the minimal version of raw profile supported in kernel. ++ */ ++#elif defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 180000 ++#define INSTR_PROF_RAW_VERSION 9 ++#define INSTR_PROF_DATA_ALIGNMENT 8 ++#define IPVK_LAST 1 ++#endif ++ ++/* ++ * struct __llvm_profile_header - represents the raw profile header data ++ * structure. Description of each member can be found here: ++ * https://llvm.org/docs/InstrProfileFormat.html#header. ++ */ ++struct __llvm_profile_header { ++ u64 magic; ++ u64 version; ++ u64 binary_ids_size; ++ u64 num_data; ++ u64 padding_bytes_before_counters; ++ u64 num_counters; ++ u64 padding_bytes_after_counters; ++ u64 num_bitmap_bytes; ++ u64 padding_bytes_after_bitmap_bytes; ++ u64 names_size; ++ u64 counters_delta; ++ u64 bitmap_delta; ++ u64 names_delta; ++/* ++ * Version bump in LLVM commit db7e9e68411d, in which code coverage ++ * functionality (llvm-cov) is unaffected. These newly introduced ++ * fields will be populated with their default values. ++ */ ++#if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION >= 190000 ++ u64 num_v_tables; ++ u64 v_names_size; ++#endif ++ u64 value_kind_last; ++}; ++ ++/* ++ * struct __llvm_profile_data - represents the per-function control structure. ++ * Description of each member can be found here: ++ * https://llvm.org/docs/InstrProfileFormat.html#profile-metadata. To measure ++ * Source-based Code Coverage, the internals of this struct don't matter at run ++ * time. The only purpose of the definition below is to run sizeof() against it ++ * so that we can calculate the "num_data" field in header. ++ */ ++struct __llvm_profile_data { ++ const u64 name_ref; ++ const u64 func_hash; ++ const void *counter_ptr; ++ const void *bitmap_ptr; ++ const void *function_pointer; ++ void *values; ++ const u32 num_counters; ++ const u16 num_value_sites[IPVK_LAST + 1]; ++ const u32 num_bitmap_bytes; ++} __aligned(INSTR_PROF_DATA_ALIGNMENT); ++ ++/* Payload sections */ ++ ++extern struct __llvm_profile_data __llvm_prf_data_start[]; ++extern struct __llvm_profile_data __llvm_prf_data_end[]; ++ ++extern u64 __llvm_prf_cnts_start[]; ++extern u64 __llvm_prf_cnts_end[]; ++ ++extern char __llvm_prf_names_start[]; ++extern char __llvm_prf_names_end[]; ++ ++extern char __llvm_prf_bits_start[]; ++extern char __llvm_prf_bits_end[]; ++ ++#define __DEFINE_SECTION_SIZE(s) \ ++ static inline unsigned long __llvm_prf_ ## s ## _size(void) \ ++ { \ ++ unsigned long start = \ ++ (unsigned long)__llvm_prf_ ## s ## _start; \ ++ unsigned long end = \ ++ (unsigned long)__llvm_prf_ ## s ## _end; \ ++ return end - start; \ ++ } ++#define __DEFINE_SECTION_COUNT(s) \ ++ static inline unsigned long __llvm_prf_ ## s ## _count(void) \ ++ { \ ++ return __llvm_prf_ ## s ## _size() / \ ++ sizeof(__llvm_prf_ ## s ## _start[0]); \ ++ } ++ ++__DEFINE_SECTION_SIZE(data) ++__DEFINE_SECTION_SIZE(cnts) ++__DEFINE_SECTION_SIZE(names) ++__DEFINE_SECTION_SIZE(bits) ++ ++__DEFINE_SECTION_COUNT(data) ++__DEFINE_SECTION_COUNT(cnts) ++__DEFINE_SECTION_COUNT(names) ++__DEFINE_SECTION_COUNT(bits) ++ ++#undef __DEFINE_SECTION_SIZE ++#undef __DEFINE_SECTION_COUNT ++ ++static inline unsigned long __llvm_prf_get_padding(unsigned long size) ++{ ++ return 7 & (sizeof(u64) - size % sizeof(u64)); ++} ++ ++#endif /* _LLVM_COV_H */ +diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib +index 01a9f567d..5bf907575 100644 +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -137,6 +137,17 @@ _c_flags += $(if $(patsubst n%,, \ + $(CFLAGS_GCOV)) + endif + ++# ++# Enable Clang's Source-based Code Coverage flags for a file or directory ++# depending on variables LLVM_COV_PROFILE_obj.o, LLVM_COV_PROFILE and ++# CONFIG_LLVM_COV_PROFILE_ALL. ++# ++ifeq ($(CONFIG_LLVM_COV_KERNEL),y) ++_c_flags += $(if $(patsubst n%,, \ ++ $(LLVM_COV_PROFILE_$(target-stem).o)$(LLVM_COV_PROFILE)$(if $(is-kernel-object),$(CONFIG_LLVM_COV_PROFILE_ALL))), \ ++ $(CFLAGS_LLVM_COV)) ++endif ++ + # + # 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 107393a8c..9a287074c 100644 +--- a/scripts/mod/modpost.c ++++ b/scripts/mod/modpost.c +@@ -742,6 +742,8 @@ static const char *const section_white_list[] = + ".gnu.lto*", + ".discard.*", + ".llvm.call-graph-profile", /* call graph */ ++ "__llvm_covfun", ++ "__llvm_covmap", + NULL + }; + +-- +2.45.2 + diff --git a/patches/v3.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch b/patches/v3.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch new file mode 100644 index 0000000..1b72bc8 --- /dev/null +++ b/patches/v3.0/0002-llvm-cov-add-Clang-s-MC-DC-support.patch @@ -0,0 +1,175 @@ +From 5f7e376a3d4974b54c075dcc5094df7f30c44863 Mon Sep 17 00:00:00 2001 +From: Wentao Zhang +Date: Fri, 30 Aug 2024 14:49:24 -0500 +Subject: [PATCH v3 2/5] llvm-cov: add Clang's MC/DC support + +Add infrastructure to enable Clang's Modified Condition/Decision Coverage +(MC/DC) [1]. + +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 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; + +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%). + +As of Clang 19, users can determine the max number of conditions in a +decision to measure via option LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS, which +controls -fmcdc-max-conditions flag of Clang cc1 [3]. Since MC/DC +implementation utilizes bitmaps to track the execution of test vectors, +more memory is consumed if larger decisions are getting counted. The +maximum value supported by Clang is 32767. According to local experiments, +the working maximum for Linux kernel is 46, with the largest decisions in +kernel codebase (with 47 conditions, as of v6.11) excluded, otherwise the +kernel image size limit will be exceeded. The largest decisions in kernel +are contributed for example by macros checking CPUID. + +Code exceeding LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS will produce compiler +warnings. + +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." [4] + +Link: https://en.wikipedia.org/wiki/Modified_condition%2Fdecision_coverage [1] +Link: https://digital-library.theiet.org/content/journals/10.1049/sej.1994.0025 [2] +Link: https://discourse.llvm.org/t/rfc-coverage-new-algorithm-and-file-format-for-mc-dc/76798 [3] +Link: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#mc-dc-instrumentation [4] +Signed-off-by: Wentao Zhang +Reviewed-by: Chuck Wolber +Tested-by: Chuck Wolber +Reviewed-by: Nathan Chancellor +--- + Makefile | 6 ++++++ + kernel/llvm-cov/Kconfig | 43 +++++++++++++++++++++++++++++++++++++++++ + scripts/Makefile.lib | 12 ++++++++++++ + 3 files changed, 61 insertions(+) + +diff --git a/Makefile b/Makefile +index 1823f04fb..e03d16c16 100644 +--- a/Makefile ++++ b/Makefile +@@ -739,6 +739,12 @@ all: vmlinux + CFLAGS_LLVM_COV := -fprofile-instr-generate -fcoverage-mapping + export CFLAGS_LLVM_COV + ++CFLAGS_LLVM_COV_MCDC := -fcoverage-mcdc ++ifdef CONFIG_LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS ++CFLAGS_LLVM_COV_MCDC += -Xclang -fmcdc-max-conditions=$(CONFIG_LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS) ++endif ++export CFLAGS_LLVM_COV_MCDC ++ + CFLAGS_GCOV := -fprofile-arcs -ftest-coverage + ifdef CONFIG_CC_IS_GCC + CFLAGS_GCOV += -fno-tree-loop-im +diff --git a/kernel/llvm-cov/Kconfig b/kernel/llvm-cov/Kconfig +index e0533be92..4f0290753 100644 +--- a/kernel/llvm-cov/Kconfig ++++ b/kernel/llvm-cov/Kconfig +@@ -72,4 +72,47 @@ config LLVM_COV_PROFILE_ALL + Note that a kernel compiled with profiling flags will be significantly + larger and run slower. + ++config LLVM_COV_KERNEL_MCDC ++ bool "Enable measuring modified condition/decision coverage (MC/DC)" ++ depends on LLVM_COV_KERNEL ++ depends on CLANG_VERSION >= 180000 ++ help ++ This option enables modified condition/decision coverage (MC/DC) ++ code coverage instrumentation. ++ ++ If unsure, say N. ++ ++ This will add Clang's Source-based Code Coverage MC/DC ++ instrumentation to your kernel. 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." ++ ++ https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#mc-dc-instrumentation ++ ++config LLVM_COV_KERNEL_MCDC_MAX_CONDITIONS ++ int "Maximum number of conditions in a decision to instrument" ++ range 6 32767 ++ depends on LLVM_COV_KERNEL_MCDC ++ depends on CLANG_VERSION >= 190000 ++ default "6" ++ help ++ This value is passed to "-fmcdc-max-conditions" flag of Clang cc1. ++ Expressions whose number of conditions is greater than this value will ++ produce warnings and will not be instrumented. ++ ++ Since MC/DC implementation utilizes bitmaps to track the execution of ++ test vectors, more memory is consumed if larger decisions are getting ++ counted. The maximum value supported by Clang is 32767. In practice, ++ the working maximum for Linux kernel depends on the version and ++ configuration and is much smaller (for example, 47 as for defconfig of ++ v6.11). Otherwise the kernel image size limit will be exceeded. ++ + endmenu +diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib +index 5bf907575..36d1c41f4 100644 +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -148,6 +148,18 @@ _c_flags += $(if $(patsubst n%,, \ + $(CFLAGS_LLVM_COV)) + endif + ++# ++# Flag that turns on modified condition/decision coverage (MC/DC) measurement ++# with Clang's Source-based Code Coverage. Enable the flag for a file or ++# directory depending on variables LLVM_COV_PROFILE_obj.o, LLVM_COV_PROFILE and ++# CONFIG_LLVM_COV_PROFILE_ALL. ++# ++ifeq ($(CONFIG_LLVM_COV_KERNEL_MCDC),y) ++_c_flags += $(if $(patsubst n%,, \ ++ $(LLVM_COV_PROFILE_$(target-stem).o)$(LLVM_COV_PROFILE)$(if $(is-kernel-object),$(CONFIG_LLVM_COV_PROFILE_ALL))), \ ++ $(CFLAGS_LLVM_COV_MCDC)) ++endif ++ + # + # 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) +-- +2.45.2 + diff --git a/patches/v3.0/0003-x86-disable-llvm-cov-instrumentation.patch b/patches/v3.0/0003-x86-disable-llvm-cov-instrumentation.patch new file mode 100644 index 0000000..4a49863 --- /dev/null +++ b/patches/v3.0/0003-x86-disable-llvm-cov-instrumentation.patch @@ -0,0 +1,35 @@ +From 955a004f004ccb7df2bfd32a1e72f9e4d83343ed Mon Sep 17 00:00:00 2001 +From: Wentao Zhang +Date: Fri, 30 Aug 2024 14:48:53 -0500 +Subject: [PATCH v3 3/5] x86: disable llvm-cov instrumentation + +Disable instrumentation for arch/x86/crypto/curve25519-x86_64.c. Otherwise +compilation would fail with "error: inline assembly requires more registers +than available". + +Similar behavior was reported with gcov as well. See c390c452ebeb ("crypto: +x86/curve25519 - disable gcov"). + +Signed-off-by: Wentao Zhang +Reviewed-by: Chuck Wolber +Tested-by: Chuck Wolber +Reviewed-by: Nathan Chancellor +--- + arch/x86/crypto/Makefile | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile +index 53b4a2778..57f3d4921 100644 +--- a/arch/x86/crypto/Makefile ++++ b/arch/x86/crypto/Makefile +@@ -119,5 +119,6 @@ quiet_cmd_perlasm = PERLASM $@ + $(obj)/%.S: $(src)/%.pl FORCE + $(call if_changed,perlasm) + +-# Disable GCOV in odd or sensitive code ++# Disable GCOV and llvm-cov in odd or sensitive code + GCOV_PROFILE_curve25519-x86_64.o := n ++LLVM_COV_PROFILE_curve25519-x86_64.o := n +-- +2.45.2 + diff --git a/patches/v3.0/0004-x86-enable-llvm-cov-support.patch b/patches/v3.0/0004-x86-enable-llvm-cov-support.patch new file mode 100644 index 0000000..845cd5d --- /dev/null +++ b/patches/v3.0/0004-x86-enable-llvm-cov-support.patch @@ -0,0 +1,46 @@ +From d9d79d28f719b831d4cd2bc0d5031cfe0d01aedd Mon Sep 17 00:00:00 2001 +From: Wentao Zhang +Date: Fri, 30 Aug 2024 14:49:06 -0500 +Subject: [PATCH v3 4/5] x86: enable llvm-cov support + +Set ARCH_HAS_* options to "y" in kconfig and include sections related to +llvm-cov in vmlinux. + +Signed-off-by: Wentao Zhang +Reviewed-by: Chuck Wolber +Tested-by: Chuck Wolber +Reviewed-by: Nathan Chancellor +--- + arch/x86/Kconfig | 2 ++ + arch/x86/kernel/vmlinux.lds.S | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig +index 2852fcd82..4dff48085 100644 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -87,6 +87,8 @@ config X86 + select ARCH_HAS_FORTIFY_SOURCE + select ARCH_HAS_GCOV_PROFILE_ALL + select ARCH_HAS_KCOV if X86_64 ++ select ARCH_HAS_LLVM_COV if X86_64 ++ select ARCH_HAS_LLVM_COV_PROFILE_ALL if X86_64 + select ARCH_HAS_KERNEL_FPU_SUPPORT + select ARCH_HAS_MEM_ENCRYPT + select ARCH_HAS_MEMBARRIER_SYNC_CORE +diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S +index 6726be89b..13504e746 100644 +--- a/arch/x86/kernel/vmlinux.lds.S ++++ b/arch/x86/kernel/vmlinux.lds.S +@@ -191,6 +191,8 @@ SECTIONS + + BUG_TABLE + ++ LLVM_COV_DATA ++ + ORC_UNWIND_TABLE + + . = ALIGN(PAGE_SIZE); +-- +2.45.2 + diff --git a/patches/v3.0/0005-MAINTAINERS-add-LLVM-COV-BASED-KERNEL-PROFILING-entr.patch b/patches/v3.0/0005-MAINTAINERS-add-LLVM-COV-BASED-KERNEL-PROFILING-entr.patch new file mode 100644 index 0000000..e8a7998 --- /dev/null +++ b/patches/v3.0/0005-MAINTAINERS-add-LLVM-COV-BASED-KERNEL-PROFILING-entr.patch @@ -0,0 +1,33 @@ +From fba32f3502243c3f1bcb967667f6618897b8bf80 Mon Sep 17 00:00:00 2001 +From: Wentao Zhang +Date: Tue, 8 Oct 2024 16:47:23 -0500 +Subject: [PATCH v3 5/5] MAINTAINERS: add "LLVM-COV BASED KERNEL PROFILING" + entry + +Add Wentao Zhang and Chuck Wolber as the maintainer for llvm-cov. + +Signed-off-by: Wentao Zhang +--- + MAINTAINERS | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index a097afd76..55438cb90 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -13262,6 +13262,12 @@ F: include/net/llc* + F: include/uapi/linux/llc.h + F: net/llc/ + ++LLVM-COV BASED KERNEL PROFILING ++M: Wentao Zhang ++M: Chuck Wolber ++S: Maintained ++F: kernel/llvm-cov/ ++ + LM73 HARDWARE MONITOR DRIVER + M: Guillaume Ligneul + L: linux-hwmon@vger.kernel.org +-- +2.45.2 + diff --git a/patches/v3.0/README.md b/patches/v3.0/README.md new file mode 100644 index 0000000..eba0a2e --- /dev/null +++ b/patches/v3.0/README.md @@ -0,0 +1 @@ +These patches were tested on Linux Kernel 6.12-rc2.