Clang >= 18 supports Modified Condition/Decision Coverage (MC/DC). This patch enables the detection and usage of this feature when compiling Xen with Clang.
- Update detection logic in Kconfig to check for the required set of Clang flags for MC/DC: '-fprofile-instr-generate -fcoverage-mapping -fcoverage-mcdc'. This bundle is necessary because '-fcoverage-mcdc' requires '-fcoverage-mapping', which in turn requires '-fprofile-instr-generate'. - Update llvm.c to handle the profile format changes (bitmap section) required for MC/DC. - Guard -Wno-error=coverage-too-many-conditions with CONFIG_CC_IS_GCC to avoid passing a GCC-only warning option to Clang Signed-off-by: Saman Dehghan <[email protected]> Acked-by: Jan Beulich <[email protected]> --- xen/Kconfig | 9 +++++++-- xen/Rules.mk | 1 + xen/arch/x86/Makefile | 2 +- xen/common/coverage/llvm.c | 18 +++++++++++++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/xen/Kconfig b/xen/Kconfig index a5e5af3b76..8f2cc111cd 100644 --- a/xen/Kconfig +++ b/xen/Kconfig @@ -51,9 +51,14 @@ config CC_HAS_ASM_GOTO_OUTPUT depends on !GCC_ASM_GOTO_OUTPUT_BROKEN depends on $(success,echo 'int foo(int x) { asm goto ("": "=r"(x) ::: bar); return x; bar: return 0; }' | $(CC) -x c - -c -o /dev/null) -# Compiler supports -fcondition-coverage aka MC/DC +# Compiler supports Modified Condition/Decision Coverage (MC/DC). +# MC/DC is a rigorous code coverage metric that requires every condition +# within a decision (boolean expression) to be shown to independently +# influence the decision's final outcome. +# +# Minimum toolchain baseline: GCC >= 14, or Clang >= 18. config CC_HAS_MCDC - def_bool $(cc-option,-fcondition-coverage) + def_bool $(cc-option,-fcondition-coverage) || $(cc-option,-fprofile-instr-generate -fcoverage-mapping -fcoverage-mcdc) # Set code alignment. # diff --git a/xen/Rules.mk b/xen/Rules.mk index 24f447b957..2b28d1ac3c 100644 --- a/xen/Rules.mk +++ b/xen/Rules.mk @@ -136,6 +136,7 @@ non-init-objects = $(filter-out %.init.o, $(obj-y) $(obj-bin-y) $(extra-y)) ifeq ($(CONFIG_CC_IS_CLANG),y) cov-cflags-$(CONFIG_COVERAGE) := -fprofile-instr-generate -fcoverage-mapping + cov-cflags-$(CONFIG_CONDITION_COVERAGE) += -fcoverage-mcdc else cov-cflags-$(CONFIG_COVERAGE) := -fprofile-arcs -ftest-coverage cov-cflags-$(CONFIG_CONDITION_COVERAGE) += -fcondition-coverage diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile index 407571c510..6c0ff67fa8 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -98,7 +98,7 @@ $(obj)/usercopy.o: CFLAGS-y += -iquote . ifneq ($(CONFIG_HVM),y) $(obj)/x86_emulate.o: CFLAGS-y += -Wno-unused-label endif -ifeq ($(CONFIG_CONDITION_COVERAGE),y) +ifeq ($(CONFIG_CONDITION_COVERAGE)$(CONFIG_CC_IS_GCC),yy) $(obj)/x86_emulate.o: CFLAGS-y += -Wno-error=coverage-too-many-conditions endif diff --git a/xen/common/coverage/llvm.c b/xen/common/coverage/llvm.c index 532889c857..5663fb10dd 100644 --- a/xen/common/coverage/llvm.c +++ b/xen/common/coverage/llvm.c @@ -120,6 +120,8 @@ extern const char __start___llvm_prf_names[]; extern const char __stop___llvm_prf_names[]; extern uint64_t __start___llvm_prf_cnts[]; extern uint64_t __stop___llvm_prf_cnts[]; +extern const char __start___llvm_prf_bits[]; +extern const char __stop___llvm_prf_bits[]; #define START_DATA ((const void *)__start___llvm_prf_data) #define END_DATA ((const void *)__stop___llvm_prf_data) @@ -127,16 +129,23 @@ extern uint64_t __stop___llvm_prf_cnts[]; #define END_NAMES ((const void *)__stop___llvm_prf_names) #define START_COUNTERS ((void *)__start___llvm_prf_cnts) #define END_COUNTERS ((void *)__stop___llvm_prf_cnts) +#define START_BITMAP ((void *)__start___llvm_prf_bits) +#define END_BITMAP ((void *)__stop___llvm_prf_bits) static void cf_check reset_counters(void) { memset(START_COUNTERS, 0, END_COUNTERS - START_COUNTERS); + if ( IS_ENABLED(CONFIG_CONDITION_COVERAGE) ) + memset(START_BITMAP, 0, END_BITMAP - START_BITMAP); } static uint32_t cf_check get_size(void) { - return ROUNDUP(sizeof(struct llvm_profile_header) + END_DATA - START_DATA + + uint32_t size = ROUNDUP(sizeof(struct llvm_profile_header) + END_DATA - START_DATA + END_COUNTERS - START_COUNTERS + END_NAMES - START_NAMES, 8); + if ( IS_ENABLED(CONFIG_CONDITION_COVERAGE) ) + size += ROUNDUP(END_BITMAP - START_BITMAP, 8); + return size; } static int cf_check dump( @@ -155,6 +164,10 @@ static int cf_check dump( #endif .names_delta = (uintptr_t)START_NAMES, .value_kind_last = LLVM_PROFILE_NUM_KINDS - 1, +#if defined(CONFIG_CONDITION_COVERAGE) && LLVM_PROFILE_VERSION >= 9 + .num_bitmap_bytes = END_BITMAP - START_BITMAP, + .bitmap_delta = START_BITMAP - START_DATA, +#endif }; unsigned int off = 0; @@ -168,6 +181,9 @@ static int cf_check dump( APPEND_TO_BUFFER(&header, sizeof(header)); APPEND_TO_BUFFER(START_DATA, END_DATA - START_DATA); APPEND_TO_BUFFER(START_COUNTERS, END_COUNTERS - START_COUNTERS); +#if defined(CONFIG_CONDITION_COVERAGE) + APPEND_TO_BUFFER(START_BITMAP, END_BITMAP - START_BITMAP); +#endif APPEND_TO_BUFFER(START_NAMES, END_NAMES - START_NAMES); #undef APPEND_TO_BUFFER -- 2.49.0
