[PATCH v3] Support LLVM raw profile versions 5, 6, 7, 8, 9, and 10

Saman Dehghan posted 1 patch 5 days, 19 hours ago
Patches applied successfully (tree, apply log)
git fetch https://gitlab.com/xen-project/patchew/xen tags/patchew/06646f747f21c3f388cf6e9d59a20238a4a91170.1761263588.git.samaan.dehghan@gmail.com
xen/arch/x86/xen.lds.S     |  6 ++++
xen/common/coverage/llvm.c | 73 ++++++++++++++++++++++++++++++++++----
xen/include/xen/xen.lds.h  | 18 ++++++++++
3 files changed, 91 insertions(+), 6 deletions(-)
[PATCH v3] Support LLVM raw profile versions 5, 6, 7, 8, 9, and 10
Posted by Saman Dehghan 5 days, 19 hours ago
This change enables compatibility for measuring code coverage
with Clang versions 14 through 20 by supporting their
respective raw profile formats.

1- Added support for LLVM raw profile versions 5, 6, 7, 8, 9, and 10.
2- Initialized llvm_profile_header for all versions based on llvm source code in
   compiler-rt/include/profile/InstrProfData.inc for each version.
3- We tested this patch for all Clang versions from 11 through 20 on x86 platform.
4- Fixed linking warnings related to coverage code in x86.

Signed-off-by: Saman Dehghan <samaan.dehghan@gmail.com>
---
 xen/arch/x86/xen.lds.S     |  6 ++++
 xen/common/coverage/llvm.c | 73 ++++++++++++++++++++++++++++++++++----
 xen/include/xen/xen.lds.h  | 18 ++++++++++
 3 files changed, 91 insertions(+), 6 deletions(-)

diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 966e514f20..5d02f83a40 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -186,6 +186,8 @@ SECTIONS
   } PHDR(note) PHDR(text)
 #endif
 
+  LLVM_COV_RO_DATA
+
   _erodata = .;
 
   . = ALIGN(SECTION_ALIGN);
@@ -323,6 +325,8 @@ SECTIONS
        *(.data .data.*)
   } PHDR(text)
 
+  LLVM_COV_RW_DATA
+
   DECL_SECTION(.bss) {
        __bss_start = .;
        *(.bss.page_aligned*)
@@ -357,6 +361,8 @@ SECTIONS
 
   DWARF2_DEBUG_SECTIONS
 
+  LLVM_COV_DEBUG
+
 #ifdef CONFIG_HYPERV_GUEST
   hv_hcall_page = ABSOLUTE(HV_HCALL_PAGE - XEN_VIRT_START + __XEN_VIRT_START);
 #endif
diff --git a/xen/common/coverage/llvm.c b/xen/common/coverage/llvm.c
index 517b2aa8c2..e3272a546f 100644
--- a/xen/common/coverage/llvm.c
+++ b/xen/common/coverage/llvm.c
@@ -44,27 +44,65 @@
     ((uint64_t)'f' << 16) | ((uint64_t)'R' << 8)  | ((uint64_t)129)
 #endif
 
-#define LLVM_PROFILE_VERSION    4
+#if __clang_major__ >= 19 && __clang_major__ <= 20
+#define LLVM_PROFILE_VERSION    10
+#define LLVM_PROFILE_NUM_KINDS  3
+#elif __clang_major__ == 18
+#define LLVM_PROFILE_VERSION    9
 #define LLVM_PROFILE_NUM_KINDS  2
+#elif __clang_major__ >= 14
+#define LLVM_PROFILE_VERSION    8
+#define LLVM_PROFILE_NUM_KINDS  2
+#elif __clang_major__ == 13
+#define LLVM_PROFILE_VERSION    7
+#define LLVM_PROFILE_NUM_KINDS  2
+#elif __clang_major__ >= 11
+#define LLVM_PROFILE_VERSION    5
+#define LLVM_PROFILE_NUM_KINDS  2
+#else
+#error "LLVM coverage selected but an unsupported clang version is used"
+#endif
 
 struct llvm_profile_data {
     uint64_t name_ref;
     uint64_t function_hash;
-    void *counter;
+    void *relative_counter;
+#if __clang_major__ >= 18
+    void *relative_bitmap;
+#endif
     void *function;
     void *values;
     uint32_t nr_counters;
     uint16_t nr_value_sites[LLVM_PROFILE_NUM_KINDS];
+#if __clang_major__ >= 18
+    uint32_t numbitmap_bytes;
+#endif
 };
 
 struct llvm_profile_header {
     uint64_t magic;
     uint64_t version;
-    uint64_t data_size;
-    uint64_t counters_size;
+#if __clang_major__ >= 13
+    uint64_t binary_ids_size;
+#endif
+    uint64_t num_data;
+    uint64_t padding_bytes_before_counters;
+    uint64_t num_counters;
+    uint64_t padding_bytes_after_counters;
+#if __clang_major__ >= 18
+    uint64_t num_bitmap_bytes;
+    uint64_t padding_bytes_after_bitmap_bytes;
+#endif
     uint64_t names_size;
     uint64_t counters_delta;
+#if __clang_major__ >= 18
+    uint64_t bitmap_delta;
+#endif
     uint64_t names_delta;
+#if __clang_major__ >= 19 && __clang_major__ <= 20
+    uint64_t num_vtables;
+    uint64_t vnames_size;
+#endif
     uint64_t value_kind_last;
 };
 
@@ -107,11 +145,34 @@ static int cf_check dump(
     struct llvm_profile_header header = {
         .magic = LLVM_PROFILE_MAGIC,
         .version = LLVM_PROFILE_VERSION,
-        .data_size = (END_DATA - START_DATA) / sizeof(struct llvm_profile_data),
-        .counters_size = (END_COUNTERS - START_COUNTERS) / sizeof(uint64_t),
+#if __clang_major__ >= 13
+        .binary_ids_size = 0,
+#endif
+        .num_data = ((END_DATA + sizeof(struct llvm_profile_data) - 1)
+                - START_DATA) / sizeof(struct llvm_profile_data),
+        .padding_bytes_before_counters = 0,
+        .num_counters = ((END_COUNTERS + sizeof(uint64_t) - 1)
+                - START_COUNTERS) / sizeof(uint64_t),
+        .padding_bytes_after_counters = 0,
+#if __clang_major__ >= 18
+        .num_bitmap_bytes = 0,
+        .padding_bytes_after_bitmap_bytes = 0,
+#endif
         .names_size = END_NAMES - START_NAMES,
+#if __clang_major__ >= 14
+        .counters_delta = START_COUNTERS - START_DATA,
+#else
         .counters_delta = (uintptr_t)START_COUNTERS,
+#endif
+
+#if __clang_major__ >= 18
+        .bitmap_delta = 0,
+#endif
         .names_delta = (uintptr_t)START_NAMES,
+#if __clang_major__ >= 19 && __clang_major__ <= 20
+        .num_vtables = 0,
+        .vnames_size = 0,
+#endif
         .value_kind_last = LLVM_PROFILE_NUM_KINDS - 1,
     };
     unsigned int off = 0;
diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
index b126dfe887..42550a85a2 100644
--- a/xen/include/xen/xen.lds.h
+++ b/xen/include/xen/xen.lds.h
@@ -81,6 +81,24 @@
   .stab.index 0 : { *(.stab.index) }         \
   .stab.indexstr 0 : { *(.stab.indexstr) }
 
+#if defined(CONFIG_COVERAGE) && defined(CONFIG_CC_IS_CLANG)
+
+#define LLVM_COV_RW_DATA                                   \
+    DECL_SECTION(__llvm_prf_cnts) { *(__llvm_prf_cnts) }   \
+    DECL_SECTION(__llvm_prf_data) { *(__llvm_prf_data) }   \
+    DECL_SECTION(__llvm_prf_bits) { *(__llvm_prf_bits) }
+
+#define LLVM_COV_RO_DATA                                   \
+    DECL_SECTION(__llvm_prf_names) { *(__llvm_prf_names) }
+
+#define LLVM_COV_DEBUG                                     \
+    DECL_DEBUG(__llvm_covfun, 8)                           \
+    DECL_DEBUG(__llvm_covmap, 8)
+#else
+#define LLVM_COV_RW_DATA
+#define LLVM_COV_RO_DATA
+#define LLVM_COV_DEBUG
+#endif
 /*
  * ELF sections.
  *
-- 
2.49.0
Re: [PATCH v3] Support LLVM raw profile versions 5, 6, 7, 8, 9, and 10
Posted by Andrew Cooper 5 days, 9 hours ago
The subject should have a "xen: " prefix, as this applies to the
hypervisor and not other

On 24/10/2025 1:16 am, Saman Dehghan wrote:
> This change enables compatibility for measuring code coverage
> with Clang versions 14 through 20 by supporting their

Stale 14?  It looks to be 11 now.

> respective raw profile formats.
>
> 1- Added support for LLVM raw profile versions 5, 6, 7, 8, 9, and 10.
> 2- Initialized llvm_profile_header for all versions based on llvm source code in
>    compiler-rt/include/profile/InstrProfData.inc for each version.
> 3- We tested this patch for all Clang versions from 11 through 20 on x86 platform.
> 4- Fixed linking warnings related to coverage code in x86.
>
> Signed-off-by: Saman Dehghan <samaan.dehghan@gmail.com>
> ---

When sending multiple revisions, it's customary to put a short list here
if what you've changed from the previous revision.

Also, you didn't accumulate your release ack from v2.

Release-Acked-By: Oleksii Kurochko <oleksii.kurochko@gmail.com>

>  xen/arch/x86/xen.lds.S     |  6 ++++
>  xen/common/coverage/llvm.c | 73 ++++++++++++++++++++++++++++++++++----
>  xen/include/xen/xen.lds.h  | 18 ++++++++++
>  3 files changed, 91 insertions(+), 6 deletions(-)
>
> diff --git a/xen/common/coverage/llvm.c b/xen/common/coverage/llvm.c
> index 517b2aa8c2..e3272a546f 100644
> --- a/xen/common/coverage/llvm.c
> +++ b/xen/common/coverage/llvm.c
> @@ -107,11 +145,34 @@ static int cf_check dump(
>      struct llvm_profile_header header = {
>          .magic = LLVM_PROFILE_MAGIC,
>          .version = LLVM_PROFILE_VERSION,
> -        .data_size = (END_DATA - START_DATA) / sizeof(struct llvm_profile_data),
> -        .counters_size = (END_COUNTERS - START_COUNTERS) / sizeof(uint64_t),
> +#if __clang_major__ >= 13
> +        .binary_ids_size = 0,
> +#endif
> +        .num_data = ((END_DATA + sizeof(struct llvm_profile_data) - 1)
> +                - START_DATA) / sizeof(struct llvm_profile_data),

There's a helper for this expression.

DIV_ROUND_UP(END_DATA - START_DATA, sizeof(llvm_profile_data))

> +        .padding_bytes_before_counters = 0,
> +        .num_counters = ((END_COUNTERS + sizeof(uint64_t) - 1)
> +                - START_COUNTERS) / sizeof(uint64_t),

DIV_ROUND_UP(END_COUNTERS - START_COUNTERS, sizeof(uint64_t))


> +        .padding_bytes_after_counters = 0,
> +#if __clang_major__ >= 18
> +        .num_bitmap_bytes = 0,
> +        .padding_bytes_after_bitmap_bytes = 0,
> +#endif
>          .names_size = END_NAMES - START_NAMES,
> +#if __clang_major__ >= 14
> +        .counters_delta = START_COUNTERS - START_DATA,
> +#else
>          .counters_delta = (uintptr_t)START_COUNTERS,
> +#endif
> +
> +#if __clang_major__ >= 18
> +        .bitmap_delta = 0,
> +#endif
>          .names_delta = (uintptr_t)START_NAMES,
> +#if __clang_major__ >= 19 && __clang_major__ <= 20
> +        .num_vtables = 0,
> +        .vnames_size = 0,
> +#endif

Because this is a structure initialiser, everything set explicitly to 0
can be omitted.  This removes all #ifdef-ary except the .counters_delta
I believe, as well as the .padding_byte_* fields.

The resulting diff is far smaller.

>          .value_kind_last = LLVM_PROFILE_NUM_KINDS - 1,
>      };
>      unsigned int off = 0;
> diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
> index b126dfe887..42550a85a2 100644
> --- a/xen/include/xen/xen.lds.h
> +++ b/xen/include/xen/xen.lds.h
> @@ -81,6 +81,24 @@
>    .stab.index 0 : { *(.stab.index) }         \
>    .stab.indexstr 0 : { *(.stab.indexstr) }
>  
> +#if defined(CONFIG_COVERAGE) && defined(CONFIG_CC_IS_CLANG)
> +
> +#define LLVM_COV_RW_DATA                                   \
> +    DECL_SECTION(__llvm_prf_cnts) { *(__llvm_prf_cnts) }   \
> +    DECL_SECTION(__llvm_prf_data) { *(__llvm_prf_data) }   \
> +    DECL_SECTION(__llvm_prf_bits) { *(__llvm_prf_bits) }
> +
> +#define LLVM_COV_RO_DATA                                   \
> +    DECL_SECTION(__llvm_prf_names) { *(__llvm_prf_names) }
> +
> +#define LLVM_COV_DEBUG                                     \
> +    DECL_DEBUG(__llvm_covfun, 8)                           \
> +    DECL_DEBUG(__llvm_covmap, 8)
> +#else
> +#define LLVM_COV_RW_DATA
> +#define LLVM_COV_RO_DATA
> +#define LLVM_COV_DEBUG
> +#endif

Newline here.

But, there's no problem stating sections which are unused.  I think the
outer #if/#else can be dropped.

Otherwise, Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>

I can fix these all up on commit, seeing as it's release acked for 4.21

~Andrew

Re: [PATCH v3] Support LLVM raw profile versions 5, 6, 7, 8, 9, and 10
Posted by Saman Dehghan 4 days, 19 hours ago
On Fri, Oct 24, 2025 at 4:33 AM Andrew Cooper <andrew.cooper3@citrix.com> wrote:
>
> The subject should have a "xen: " prefix, as this applies to the
> hypervisor and not other
>
> On 24/10/2025 1:16 am, Saman Dehghan wrote:
> > This change enables compatibility for measuring code coverage
> > with Clang versions 14 through 20 by supporting their
>
> Stale 14?  It looks to be 11 now.

Sorry for the mistake. I meant "with Clang versions 11 through 20 by
supporting their...".

>
> > respective raw profile formats.
> >
> > 1- Added support for LLVM raw profile versions 5, 6, 7, 8, 9, and 10.
> > 2- Initialized llvm_profile_header for all versions based on llvm source code in
> >    compiler-rt/include/profile/InstrProfData.inc for each version.
> > 3- We tested this patch for all Clang versions from 11 through 20 on x86 platform.
> > 4- Fixed linking warnings related to coverage code in x86.
> >
> > Signed-off-by: Saman Dehghan <samaan.dehghan@gmail.com>
> > ---
>
> When sending multiple revisions, it's customary to put a short list here
> if what you've changed from the previous revision.

Changes since version 2:
  1- Additionally support raw profile version 5, 6, 7 in clang 11, 12, 13.
  2- Fix coverage related linking warnings in x86.
  3- Revert unnecessary type changes, casting, etc.

>
> Also, you didn't accumulate your release ack from v2.
>
> Release-Acked-By: Oleksii Kurochko <oleksii.kurochko@gmail.com>

Sorry we missed this. Thanks for reminding us.

>
> >  xen/arch/x86/xen.lds.S     |  6 ++++
> >  xen/common/coverage/llvm.c | 73 ++++++++++++++++++++++++++++++++++----
> >  xen/include/xen/xen.lds.h  | 18 ++++++++++
> >  3 files changed, 91 insertions(+), 6 deletions(-)
> >
> > diff --git a/xen/common/coverage/llvm.c b/xen/common/coverage/llvm.c
> > index 517b2aa8c2..e3272a546f 100644
> > --- a/xen/common/coverage/llvm.c
> > +++ b/xen/common/coverage/llvm.c
> > @@ -107,11 +145,34 @@ static int cf_check dump(
> >      struct llvm_profile_header header = {
> >          .magic = LLVM_PROFILE_MAGIC,
> >          .version = LLVM_PROFILE_VERSION,
> > -        .data_size = (END_DATA - START_DATA) / sizeof(struct llvm_profile_data),
> > -        .counters_size = (END_COUNTERS - START_COUNTERS) / sizeof(uint64_t),
> > +#if __clang_major__ >= 13
> > +        .binary_ids_size = 0,
> > +#endif
> > +        .num_data = ((END_DATA + sizeof(struct llvm_profile_data) - 1)
> > +                - START_DATA) / sizeof(struct llvm_profile_data),
>
> There's a helper for this expression.
>
> DIV_ROUND_UP(END_DATA - START_DATA, sizeof(llvm_profile_data))
>
> > +        .padding_bytes_before_counters = 0,
> > +        .num_counters = ((END_COUNTERS + sizeof(uint64_t) - 1)
> > +                - START_COUNTERS) / sizeof(uint64_t),
>
> DIV_ROUND_UP(END_COUNTERS - START_COUNTERS, sizeof(uint64_t))
>
>
> > +        .padding_bytes_after_counters = 0,
> > +#if __clang_major__ >= 18
> > +        .num_bitmap_bytes = 0,
> > +        .padding_bytes_after_bitmap_bytes = 0,
> > +#endif
> >          .names_size = END_NAMES - START_NAMES,
> > +#if __clang_major__ >= 14
> > +        .counters_delta = START_COUNTERS - START_DATA,
> > +#else
> >          .counters_delta = (uintptr_t)START_COUNTERS,
> > +#endif
> > +
> > +#if __clang_major__ >= 18
> > +        .bitmap_delta = 0,
> > +#endif
> >          .names_delta = (uintptr_t)START_NAMES,
> > +#if __clang_major__ >= 19 && __clang_major__ <= 20
> > +        .num_vtables = 0,
> > +        .vnames_size = 0,
> > +#endif
>
> Because this is a structure initialiser, everything set explicitly to 0
> can be omitted.  This removes all #ifdef-ary except the .counters_delta
> I believe, as well as the .padding_byte_* fields.
>

Is it undefined behaviour to leave struct members uninitialized for
local variables?

> The resulting diff is far smaller.
>
> >          .value_kind_last = LLVM_PROFILE_NUM_KINDS - 1,
> >      };
> >      unsigned int off = 0;
> > diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
> > index b126dfe887..42550a85a2 100644
> > --- a/xen/include/xen/xen.lds.h
> > +++ b/xen/include/xen/xen.lds.h
> > @@ -81,6 +81,24 @@
> >    .stab.index 0 : { *(.stab.index) }         \
> >    .stab.indexstr 0 : { *(.stab.indexstr) }
> >
> > +#if defined(CONFIG_COVERAGE) && defined(CONFIG_CC_IS_CLANG)
> > +
> > +#define LLVM_COV_RW_DATA                                   \
> > +    DECL_SECTION(__llvm_prf_cnts) { *(__llvm_prf_cnts) }   \
> > +    DECL_SECTION(__llvm_prf_data) { *(__llvm_prf_data) }   \
> > +    DECL_SECTION(__llvm_prf_bits) { *(__llvm_prf_bits) }
> > +
> > +#define LLVM_COV_RO_DATA                                   \
> > +    DECL_SECTION(__llvm_prf_names) { *(__llvm_prf_names) }
> > +
> > +#define LLVM_COV_DEBUG                                     \
> > +    DECL_DEBUG(__llvm_covfun, 8)                           \
> > +    DECL_DEBUG(__llvm_covmap, 8)
> > +#else
> > +#define LLVM_COV_RW_DATA
> > +#define LLVM_COV_RO_DATA
> > +#define LLVM_COV_DEBUG
> > +#endif
>
> Newline here.
>
> But, there's no problem stating sections which are unused.  I think the
> outer #if/#else can be dropped.
>
> Otherwise, Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
>
> I can fix these all up on commit, seeing as it's release acked for 4.21

Thank you for offering to fix them up. Let me know how I can help or
if I need to send another version.

Thanks,
Saman

>
> ~Andrew
Re: [PATCH v3] Support LLVM raw profile versions 5, 6, 7, 8, 9, and 10
Posted by Andrew Cooper 4 days ago
On 25/10/2025 12:38 am, Saman Dehghan wrote:
>>> +        .padding_bytes_after_counters = 0,
>>> +#if __clang_major__ >= 18
>>> +        .num_bitmap_bytes = 0,
>>> +        .padding_bytes_after_bitmap_bytes = 0,
>>> +#endif
>>>          .names_size = END_NAMES - START_NAMES,
>>> +#if __clang_major__ >= 14
>>> +        .counters_delta = START_COUNTERS - START_DATA,
>>> +#else
>>>          .counters_delta = (uintptr_t)START_COUNTERS,
>>> +#endif
>>> +
>>> +#if __clang_major__ >= 18
>>> +        .bitmap_delta = 0,
>>> +#endif
>>>          .names_delta = (uintptr_t)START_NAMES,
>>> +#if __clang_major__ >= 19 && __clang_major__ <= 20
>>> +        .num_vtables = 0,
>>> +        .vnames_size = 0,
>>> +#endif
>> Because this is a structure initialiser, everything set explicitly to 0
>> can be omitted.  This removes all #ifdef-ary except the .counters_delta
>> I believe, as well as the .padding_byte_* fields.
>>
> Is it undefined behaviour to leave struct members uninitialized for
> local variables?

They are well defined as 0 when using a structure initialiser.

>
>> The resulting diff is far smaller.
>>
>>>          .value_kind_last = LLVM_PROFILE_NUM_KINDS - 1,
>>>      };
>>>      unsigned int off = 0;
>>> diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
>>> index b126dfe887..42550a85a2 100644
>>> --- a/xen/include/xen/xen.lds.h
>>> +++ b/xen/include/xen/xen.lds.h
>>> @@ -81,6 +81,24 @@
>>>    .stab.index 0 : { *(.stab.index) }         \
>>>    .stab.indexstr 0 : { *(.stab.indexstr) }
>>>
>>> +#if defined(CONFIG_COVERAGE) && defined(CONFIG_CC_IS_CLANG)
>>> +
>>> +#define LLVM_COV_RW_DATA                                   \
>>> +    DECL_SECTION(__llvm_prf_cnts) { *(__llvm_prf_cnts) }   \
>>> +    DECL_SECTION(__llvm_prf_data) { *(__llvm_prf_data) }   \
>>> +    DECL_SECTION(__llvm_prf_bits) { *(__llvm_prf_bits) }
>>> +
>>> +#define LLVM_COV_RO_DATA                                   \
>>> +    DECL_SECTION(__llvm_prf_names) { *(__llvm_prf_names) }
>>> +
>>> +#define LLVM_COV_DEBUG                                     \
>>> +    DECL_DEBUG(__llvm_covfun, 8)                           \
>>> +    DECL_DEBUG(__llvm_covmap, 8)
>>> +#else
>>> +#define LLVM_COV_RW_DATA
>>> +#define LLVM_COV_RO_DATA
>>> +#define LLVM_COV_DEBUG
>>> +#endif
>> Newline here.
>>
>> But, there's no problem stating sections which are unused.  I think the
>> outer #if/#else can be dropped.
>>
>> Otherwise, Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
>>
>> I can fix these all up on commit, seeing as it's release acked for 4.21
> Thank you for offering to fix them up. Let me know how I can help or
> if I need to send another version.

This is the version with the fixups I suggested, run through CI:

https://gitlab.com/xen-project/hardware/xen-staging/-/pipelines/2118913271
https://gitlab.com/xen-project/hardware/xen-staging/-/commit/c06dadb80a1e4058c7cdef4144a7e4c4799a38a7

However, one further thing I noticed.  On v2, Roger asked you to express
struct llvm_profile_{header,data} in terms of LLVM_PROFILE_VERSION,
rather than __clang_major__.

If you want to start from the version I cleaned up, and then submit a v4
addressing Roger's feedback, please feel free.  This will save needing
to fix it up later.

~Andrew

Re: [PATCH v3] Support LLVM raw profile versions 5, 6, 7, 8, 9, and 10
Posted by Jan Beulich 5 days, 11 hours ago
On 24.10.2025 02:16, Saman Dehghan wrote:
> This change enables compatibility for measuring code coverage
> with Clang versions 14 through 20 by supporting their
> respective raw profile formats.

Isn't the 14 here stale now?

> 1- Added support for LLVM raw profile versions 5, 6, 7, 8, 9, and 10.
> 2- Initialized llvm_profile_header for all versions based on llvm source code in
>    compiler-rt/include/profile/InstrProfData.inc for each version.
> 3- We tested this patch for all Clang versions from 11 through 20 on x86 platform.
> 4- Fixed linking warnings related to coverage code in x86.
> 
> Signed-off-by: Saman Dehghan <samaan.dehghan@gmail.com>
> ---
>  xen/arch/x86/xen.lds.S     |  6 ++++
>  xen/common/coverage/llvm.c | 73 ++++++++++++++++++++++++++++++++++----
>  xen/include/xen/xen.lds.h  | 18 ++++++++++
>  3 files changed, 91 insertions(+), 6 deletions(-)
> 
> diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
> index 966e514f20..5d02f83a40 100644
> --- a/xen/arch/x86/xen.lds.S
> +++ b/xen/arch/x86/xen.lds.S
> @@ -186,6 +186,8 @@ SECTIONS
>    } PHDR(note) PHDR(text)
>  #endif
>  
> +  LLVM_COV_RO_DATA
> +
>    _erodata = .;
>  
>    . = ALIGN(SECTION_ALIGN);
> @@ -323,6 +325,8 @@ SECTIONS
>         *(.data .data.*)
>    } PHDR(text)
>  
> +  LLVM_COV_RW_DATA
> +
>    DECL_SECTION(.bss) {
>         __bss_start = .;
>         *(.bss.page_aligned*)
> @@ -357,6 +361,8 @@ SECTIONS
>  
>    DWARF2_DEBUG_SECTIONS
>  
> +  LLVM_COV_DEBUG
> +
>  #ifdef CONFIG_HYPERV_GUEST
>    hv_hcall_page = ABSOLUTE(HV_HCALL_PAGE - XEN_VIRT_START + __XEN_VIRT_START);
>  #endif

Since only x86 is documented to support building with Clang, adding just to
x86's linker script is likely fine.

Oleksii, what's your thought towards still taking this for 4.21?

In any event, a ChangeLog entry should likely be added.

Jan
Re: [PATCH v3] Support LLVM raw profile versions 5, 6, 7, 8, 9, and 10
Posted by Wentao Zhang 5 days, 17 hours ago
Tested with Clang 20.1.8 on Debian bookworm x86_64 and arm64.

Carry this if appropriate:

Tested-by: Wentao Zhang <wentaoz5@illinois.edu>

Steps for build:

$ make CC=/lib/llvm-20/bin/clang -C xen defconfig
$ pushd xen
$ kconfig-tweak -d LIVEPATCH
$ kconfig-tweak -e COVERAGE
  # The below two are Arm only
$ kconfig-tweak -e UNSUPPORTED
$ kconfig-tweak -e ACPI
$ popd
$ make CC=/lib/llvm-20/bin/clang -C xen olddefconfig
$ /usr/bin/time -v make CC=/lib/llvm-20/bin/clang -j$(nproc) efi-y= dist-xen
$ sudo make CC=/lib/llvm-20/bin/clang -j$(nproc) efi-y= install-xen
$ sudo update-grub

Steps after reinstall:

$ COVERAGE_REPORT_DIR=`mktemp -d`
$ XEN_BUILD=$HOME/v4
$ sudo xencov read | tee >/dev/null default.profraw
$ file default.profraw
$ /lib/llvm-20/bin/llvm-profdata merge default.profraw -o default.profdata
$ file default.profdata
$ /lib/llvm-20/bin/llvm-cov show \
    -instr-profile default.profdata \
    -output-dir $COVERAGE_REPORT_DIR \
    -show-directory-coverage \
    -show-branches=count \
    -use-color=false \
    $XEN_BUILD/xen/xen-syms

Example reports:

$ less $COVERAGE_REPORT_DIR/index.txt
$ less $COVERAGE_REPORT_DIR/coverage/$XEN_BUILD/xen/common/coverage/llvm.c.txt

Notes:

1. On x86_64, LD=ld.lld also works (in fact, faster). On arm64, the build
   would fail.
2. On arm64, a workaround is needed regardless of the linker:

diff --git a/xen/arch/arm/arm64/vfp.c b/xen/arch/arm/arm64/vfp.c
index c4f89c7b0e..dbe87f3f34 100644
--- a/xen/arch/arm/arm64/vfp.c
+++ b/xen/arch/arm/arm64/vfp.c
@@ -4,6 +4,7 @@
 #include <asm/vfp.h>
 #include <asm/arm64/sve.h>

+__attribute__((target("+fp+simd")))
 static inline void save_state(uint64_t *fpregs)
 {
     asm volatile("stp q0, q1, [%1, #16 * 0]\n\t"
@@ -25,6 +26,7 @@ static inline void save_state(uint64_t *fpregs)
                  : "=Q" (*fpregs) : "r" (fpregs));
 }

+__attribute__((target("+fp+simd")))
 static inline void restore_state(const uint64_t *fpregs)
 {
     asm volatile("ldp q0, q1, [%1, #16 * 0]\n\t"
@@ -46,6 +48,7 @@ static inline void restore_state(const uint64_t *fpregs)
                  : : "Q" (*fpregs), "r" (fpregs));
 }

+__attribute__((target("+fp+simd")))
 void vfp_save_state(struct vcpu *v)
 {
     if ( !cpu_has_fp )
@@ -62,6 +65,7 @@ void vfp_save_state(struct vcpu *v)
         v->arch.vfp.fpexc32_el2 = READ_SYSREG(FPEXC32_EL2);
 }

+__attribute__((target("+fp+simd")))
 void vfp_restore_state(struct vcpu *v)
 {
     if ( !cpu_has_fp )

For the above two issues, see the report in [1].

Once this patch gets in, we can help update [2] with Andrew Cooper and
send a follow-up supporting llvm-cov MC/DC [3].

[1] https://lists.xenproject.org/archives/html/xen-devel/2025-10/msg00805.html
[2] https://xenbits.xen.org/docs/latest/hypervisor-guide/code-coverage.html
[3] https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#mc-dc-instrumentation

Thanks,
Wentao
[PATCH v4] xen: Support LLVM raw profile versions 5, 6, 7, 8, 9, and 10
Posted by Saman Dehghan 1 day, 21 hours ago
This change enables compatibility for measuring code coverage
with Clang versions 11 through 20 by supporting their respective raw
profile formats.

1- Added support for LLVM raw profile versions 5, 6, 7, 8, 9, and 10.
2- Initialized llvm_profile_header for all versions based on llvm source
   code in compiler-rt/include/profile/InstrProfData.inc for each version.
3- We tested this patch for all Clang versions from 11 through 20
   on x86 platform.
4- Fixed linking warnings related to LLVM profile sections in x86.


Signed-off-by: Saman Dehghan <samaan.dehghan@gmail.com>
Release-Acked-By: Oleksii Kurochko <oleksii.kurochko@gmail.com>
Tested-by: Wentao Zhang <wentaoz5@illinois.edu>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
Changes from v3 to v4:
  1- Use LLVM_PROFILE_VERSION in preprocessor conditionals
     instead of __clang_major__.
  2- Use DIV_ROUND_UP helper.
  3- Remove unnecessary zero initialization inside struct.
  4- Remove fallback macro definitions in linker script.
Changes from v2 to v3:
  1- Additionally support raw profile version 5, 6, 7 in clang 11, 12, 13.
  2- Fix coverage related linking warnings in x86.
  3- Revert unnecessary type changes, casting, etc.
---
 xen/arch/x86/xen.lds.S     |  6 +++++
 xen/common/coverage/llvm.c | 54 +++++++++++++++++++++++++++++++++-----
 xen/include/xen/xen.lds.h  | 13 +++++++++
 3 files changed, 67 insertions(+), 6 deletions(-)

diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 966e514f20..5d02f83a40 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -186,6 +186,8 @@ SECTIONS
   } PHDR(note) PHDR(text)
 #endif
 
+  LLVM_COV_RO_DATA
+
   _erodata = .;
 
   . = ALIGN(SECTION_ALIGN);
@@ -323,6 +325,8 @@ SECTIONS
        *(.data .data.*)
   } PHDR(text)
 
+  LLVM_COV_RW_DATA
+
   DECL_SECTION(.bss) {
        __bss_start = .;
        *(.bss.page_aligned*)
@@ -357,6 +361,8 @@ SECTIONS
 
   DWARF2_DEBUG_SECTIONS
 
+  LLVM_COV_DEBUG
+
 #ifdef CONFIG_HYPERV_GUEST
   hv_hcall_page = ABSOLUTE(HV_HCALL_PAGE - XEN_VIRT_START + __XEN_VIRT_START);
 #endif
diff --git a/xen/common/coverage/llvm.c b/xen/common/coverage/llvm.c
index 517b2aa8c2..532889c857 100644
--- a/xen/common/coverage/llvm.c
+++ b/xen/common/coverage/llvm.c
@@ -44,27 +44,65 @@
     ((uint64_t)'f' << 16) | ((uint64_t)'R' << 8)  | ((uint64_t)129)
 #endif
 
-#define LLVM_PROFILE_VERSION    4
+#if __clang_major__ >= 19 && __clang_major__ <= 20
+#define LLVM_PROFILE_VERSION    10
+#define LLVM_PROFILE_NUM_KINDS  3
+#elif __clang_major__ == 18
+#define LLVM_PROFILE_VERSION    9
 #define LLVM_PROFILE_NUM_KINDS  2
+#elif __clang_major__ >= 14
+#define LLVM_PROFILE_VERSION    8
+#define LLVM_PROFILE_NUM_KINDS  2
+#elif __clang_major__ == 13
+#define LLVM_PROFILE_VERSION    7
+#define LLVM_PROFILE_NUM_KINDS  2
+#elif __clang_major__ >= 11
+#define LLVM_PROFILE_VERSION    5
+#define LLVM_PROFILE_NUM_KINDS  2
+#else
+#error "LLVM coverage selected but an unsupported clang version is used"
+#endif
 
 struct llvm_profile_data {
     uint64_t name_ref;
     uint64_t function_hash;
-    void *counter;
+    void *relative_counter;
+#if LLVM_PROFILE_VERSION >= 9
+    void *relative_bitmap;
+#endif
     void *function;
     void *values;
     uint32_t nr_counters;
     uint16_t nr_value_sites[LLVM_PROFILE_NUM_KINDS];
+#if LLVM_PROFILE_VERSION >= 9
+    uint32_t numbitmap_bytes;
+#endif
 };
 
 struct llvm_profile_header {
     uint64_t magic;
     uint64_t version;
-    uint64_t data_size;
-    uint64_t counters_size;
+#if LLVM_PROFILE_VERSION >= 7
+    uint64_t binary_ids_size;
+#endif
+    uint64_t num_data;
+    uint64_t padding_bytes_before_counters;
+    uint64_t num_counters;
+    uint64_t padding_bytes_after_counters;
+#if LLVM_PROFILE_VERSION >= 9
+    uint64_t num_bitmap_bytes;
+    uint64_t padding_bytes_after_bitmap_bytes;
+#endif
     uint64_t names_size;
     uint64_t counters_delta;
+#if LLVM_PROFILE_VERSION >= 9
+    uint64_t bitmap_delta;
+#endif
     uint64_t names_delta;
+#if LLVM_PROFILE_VERSION == 10
+    uint64_t num_vtables;
+    uint64_t vnames_size;
+#endif
     uint64_t value_kind_last;
 };
 
@@ -107,10 +145,14 @@ static int cf_check dump(
     struct llvm_profile_header header = {
         .magic = LLVM_PROFILE_MAGIC,
         .version = LLVM_PROFILE_VERSION,
-        .data_size = (END_DATA - START_DATA) / sizeof(struct llvm_profile_data),
-        .counters_size = (END_COUNTERS - START_COUNTERS) / sizeof(uint64_t),
+        .num_data = DIV_ROUND_UP(END_DATA - START_DATA, sizeof(struct llvm_profile_data)),
+        .num_counters = DIV_ROUND_UP(END_COUNTERS - START_COUNTERS, sizeof(uint64_t)),
         .names_size = END_NAMES - START_NAMES,
+#if LLVM_PROFILE_VERSION >= 8
+        .counters_delta = START_COUNTERS - START_DATA,
+#else
         .counters_delta = (uintptr_t)START_COUNTERS,
+#endif
         .names_delta = (uintptr_t)START_NAMES,
         .value_kind_last = LLVM_PROFILE_NUM_KINDS - 1,
     };
diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
index b126dfe887..d80c895959 100644
--- a/xen/include/xen/xen.lds.h
+++ b/xen/include/xen/xen.lds.h
@@ -81,6 +81,19 @@
   .stab.index 0 : { *(.stab.index) }         \
   .stab.indexstr 0 : { *(.stab.indexstr) }
 
+/* Clang coverage sections. */
+#define LLVM_COV_RW_DATA                                   \
+    DECL_SECTION(__llvm_prf_cnts) { *(__llvm_prf_cnts) }   \
+    DECL_SECTION(__llvm_prf_data) { *(__llvm_prf_data) }   \
+    DECL_SECTION(__llvm_prf_bits) { *(__llvm_prf_bits) }
+
+#define LLVM_COV_RO_DATA                                   \
+    DECL_SECTION(__llvm_prf_names) { *(__llvm_prf_names) }
+
+#define LLVM_COV_DEBUG                                     \
+    DECL_DEBUG(__llvm_covfun, 8)                           \
+    DECL_DEBUG(__llvm_covmap, 8)
+
 /*
  * ELF sections.
  *
-- 
2.49.0
Re: [PATCH v4] xen: Support LLVM raw profile versions 5, 6, 7, 8, 9, and 10
Posted by Andrew Cooper 1 day, 20 hours ago
On 27/10/2025 9:30 pm, Saman Dehghan wrote:
> This change enables compatibility for measuring code coverage
> with Clang versions 11 through 20 by supporting their respective raw
> profile formats.
>
> 1- Added support for LLVM raw profile versions 5, 6, 7, 8, 9, and 10.
> 2- Initialized llvm_profile_header for all versions based on llvm source
>    code in compiler-rt/include/profile/InstrProfData.inc for each version.
> 3- We tested this patch for all Clang versions from 11 through 20
>    on x86 platform.
> 4- Fixed linking warnings related to LLVM profile sections in x86.
>
>
> Signed-off-by: Saman Dehghan <samaan.dehghan@gmail.com>
> Release-Acked-By: Oleksii Kurochko <oleksii.kurochko@gmail.com>
> Tested-by: Wentao Zhang <wentaoz5@illinois.edu>
> Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
> ---
> Changes from v3 to v4:
>   1- Use LLVM_PROFILE_VERSION in preprocessor conditionals
>      instead of __clang_major__.
>   2- Use DIV_ROUND_UP helper.
>   3- Remove unnecessary zero initialization inside struct.
>   4- Remove fallback macro definitions in linker script.
> Changes from v2 to v3:
>   1- Additionally support raw profile version 5, 6, 7 in clang 11, 12, 13.
>   2- Fix coverage related linking warnings in x86.
>   3- Revert unnecessary type changes, casting, etc.
> ---

Excellent.  Thankyou.  This all looks in order.  I've committed it.

~Andrew