[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2] Support LLVM raw profile versions 8, 9, and 10


  • To: Saman Dehghan <samaan.dehghan@xxxxxxxxx>
  • From: Roger Pau Monné <roger.pau@xxxxxxxxxx>
  • Date: Fri, 10 Oct 2025 17:28:15 +0200
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=citrix.com; dmarc=pass action=none header.from=citrix.com; dkim=pass header.d=citrix.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Q+UdOyKUf753zj9Sp4iy1yJwvoCATUX+ffAq4+hFdGE=; b=t6WUw9GPWZkbLQHAJ3DPO0icS8i6Xr5AI5YCnAm4vW7N3q/j+sAR/n611uDI7sll2Enp+7Ba7uVk3Ft0bqxulSe/Qk1v2aSCIIU2Ocd7FzxQZwqKkWYpdNYBEobtOm4e3WL1MhObGgMD6i1ce3LF6VGqtA4oeSvzGru5TGs+RwP1H6hV5CA4zfnS47/sWJ6HJZWXdrg5/u+OpuBrCy4PjpmxfJSTPE3wAan44nk50VRIXUtER3oJqAH3J0uJSUx8CAwvzM3CLCzd3umc3Yk8pBV/wj6eclTtiLI8fftacgxFdXu3O5D5xqxKHcJNtCaEZ0Ntlgl6nXms+y8WXsL+Fg==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=jAhCLUSCBcwB9dGurkjZ13J+Sl/PTti7qJLK8PxQRl9cfTMWNQgwU8Eg5gRwTIOaeuJDeNz/9dJ51TbURLPWCJogLuweM35Y+a66UbbNLYPkwOQGvSMwh6mWnk8BOlv4fnwXQPY7TtcwtUdRwzyMEbx05+sYy7aTFvgAOFnfZDKIOXzBMyKH0sIlfnjqzY1kzjSULRtCeH71K4OXYqx9v1QNVdJBA1mqb3LCgZCLBWTzJBGaks4dg69721aG3gawWhUskw+gM3hQ9eSc/LURUynM8FPRiC6LGSHF+EDOrAeYXpDHrwfUCVjVNrWS2S9Xz0tRoEd73PSHfB45f7R4aA==
  • Authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Cc: xen-devel@xxxxxxxxxxxxxxxxxxxx, Jan Beulich <jbeulich@xxxxxxxx>
  • Delivery-date: Fri, 10 Oct 2025 15:28:37 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

On Wed, Oct 01, 2025 at 05:09:52PM -0500, Saman Dehghan wrote:
> This change enables compatibility for measuring code coverage
> with Clang versions 14 through 20 by supporting their
> respective raw profile formats.
> 
> 1- Add support for LLVM raw profile versions 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 14 through 20 on both ARM 
> and X86 platform
> 
> Signed-off-by: Saman Dehghan <samaan.dehghan@xxxxxxxxx>
> ---
>  xen/common/coverage/llvm.c | 78 +++++++++++++++++++++++++++-----------
>  xen/include/xen/types.h    |  1 +
>  2 files changed, 57 insertions(+), 22 deletions(-)
> 
> diff --git a/xen/common/coverage/llvm.c b/xen/common/coverage/llvm.c
> index 517b2aa8c2..f92f10654c 100644
> --- a/xen/common/coverage/llvm.c
> +++ b/xen/common/coverage/llvm.c
> @@ -44,27 +44,55 @@
>      ((uint64_t)'f' << 16) | ((uint64_t)'R' << 8)  | ((uint64_t)129)
>  #endif
>  
> -#define LLVM_PROFILE_VERSION    4
> +#if __clang_major__ >= 19
> +#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
> +#else
> +#error "Unsupported Clang version"

No strong opinion, but it would be nice if we could support all
profiles from clang >= 11, as that's the minimum stated clang version.
Again I don't know how much work this will involve, so not going to
insist.

It would be extremely helpful if clang could provide a set of builtin
functions for freestanding environments to handle the performance data
in an opaque way:

https://github.com/llvm/llvm-project/issues/123034

At a minimum it would be good if the compiler exported a define
signaling the profiling version it's using, as we could then fail the
build nicely if coverage support is enabled against a version of clang
we don't support.  Right now anything >= 19 will assume to be using
profiling version 10, but that will go stale sooner or later.

> +#endif
>  
>  struct llvm_profile_data {
>      uint64_t name_ref;
>      uint64_t function_hash;
> -    void *counter;
> -    void *function;
> -    void *values;
> +    intptr_t *relative_counter;
> +#if __clang_major__ >= 18
> +    intptr_t *relative_bitmap;
> +#endif

I would prefer if this was done based on LLVM_PROFILE_VERSION rather
than __clang_major__ (same for the instances below).  The field are
related to the profile version implemented by the compiler, so it's
clearer if the check is against the profile version.

> +    intptr_t *function;
> +    intptr_t *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;
> +    uint64_t binary_ids_size;
> +    uint64_t num_data;
> +    uint64_t padding_bytes_before_counters;
> +    uint64_t num_counters;
> +    uint64_t padding_bytes_after_counters;
> +    uint64_t num_bitmap_bytes;
> +    uint64_t padding_bytes_after_bitmap_bytes;
>      uint64_t names_size;
> +#if __clang_major__ >= 18
>      uint64_t counters_delta;
> +    uint64_t bitmap_delta;
> +#endif
>      uint64_t names_delta;
> +#if __clang_major__ >= 19
> +    uint64_t num_vtables;
> +    uint64_t vnames_size;
> +#endif
>      uint64_t value_kind_last;
>  };
>  
> @@ -76,19 +104,20 @@ struct llvm_profile_header {
>   */
>  int __llvm_profile_runtime;
>  
> -extern const struct llvm_profile_data __start___llvm_prf_data[];
> -extern const struct llvm_profile_data __stop___llvm_prf_data[];
> -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 char __start___llvm_prf_data[];
> +extern char __stop___llvm_prf_data[];
> +extern char __start___llvm_prf_names[];
> +extern char __stop___llvm_prf_names[];
> +extern char __start___llvm_prf_cnts[];
> +extern char __stop___llvm_prf_cnts[];

What's the point of defining those uniformly as char instead of the
more accurate types used previously?

> +#define START_DATA      ((const char *)__start___llvm_prf_data)
> +#define END_DATA        ((const char *)__stop___llvm_prf_data)
> +#define START_NAMES     ((const char *)__start___llvm_prf_names)
> +#define END_NAMES       ((const char *)__stop___llvm_prf_names)
> +#define START_COUNTERS  ((char *)__start___llvm_prf_cnts)
> +#define END_COUNTERS    ((char *)__stop___llvm_prf_cnts)
>  
> -#define START_DATA      ((const void *)__start___llvm_prf_data)
> -#define END_DATA        ((const void *)__stop___llvm_prf_data)
> -#define START_NAMES     ((const void *)__start___llvm_prf_names)
> -#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)

Kind of similar question here, what's the benefit of using char *
instead of void *?

>  static void cf_check reset_counters(void)
>  {
> @@ -107,10 +136,15 @@ 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),
> -        .names_size = END_NAMES - START_NAMES,
> -        .counters_delta = (uintptr_t)START_COUNTERS,
> +        .binary_ids_size = 0,

We don't usually explicitly initialize fields to 0, as that's the
default already.

> +        .num_data = (((intptr_t)END_DATA + sizeof(struct llvm_profile_data) 
> - 1)
> +                - (intptr_t)START_DATA) / sizeof(struct llvm_profile_data),
> +        .padding_bytes_before_counters = 0,
> +        .num_counters = (((intptr_t)END_COUNTERS + sizeof(uint64_t) - 1)
> +                - (intptr_t)START_COUNTERS) / sizeof(uint64_t),
> +        .padding_bytes_after_counters = 0,
> +        .names_size = (END_NAMES - START_NAMES) * sizeof(char),
> +        .counters_delta = (uintptr_t)START_COUNTERS - (uintptr_t)START_DATA,

The casting here seems not uniform.  Why is intptr_t used in some
instances while others use uintptr_t?

I also think you could just use void *, as Xen uses the GCC extension
that allows arithmetic on void pointers.

Thanks, Roger.



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.