diff mbox series

[dwarves,v1,1/2] btf_loader: support for multiple BTF_DECL_TAGs pointing to same tag

Message ID 20241211021227.2341735-1-eddyz87@gmail.com (mailing list archive)
State Not Applicable
Headers show
Series [dwarves,v1,1/2] btf_loader: support for multiple BTF_DECL_TAGs pointing to same tag | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Eduard Zingerman Dec. 11, 2024, 2:12 a.m. UTC
btf_loader issues a warning when it sees several BTF_DECL_TAGs
pointing to the same type. Such situation is possible in practice
after patch [0], that marks certain functions with kfunc and
bpf_fastcall tags. E.g.:

  $ pfunct vmlinux -F btf -f bpf_rdonly_cast
WARNING: still unsuported BTF_KIND_DECL_TAG(bpf_fastcall) for bpf_cast_to_kern_ctx already with attribute (bpf_kfunc), ignoring
WARNING: still unsuported BTF_KIND_DECL_TAG(bpf_fastcall) for bpf_rdonly_cast already with attribute (bpf_kfunc), ignoring
  bpf_kfunc void * bpf_rdonly_cast(const void  * obj__ign, u32 btf_id__k);

This commit extends 'struct tag' to allow attaching multiple
attributes. Define 'struct attributes' as follows:

  struct attributes {
        uint64_t cnt;
        const char *values[];
  };

In order to avoid adding counter field in 'struct tag',
as not many instances of 'struct tag' would have attributes.

Same command after this patch:

  $ pfunct vmlinux -F btf -f bpf_rdonly_cast
  bpf_kfunc bpf_fastcall void * bpf_rdonly_cast(const void  * obj__ign, u32 btf_id__k);

[0] https://lore.kernel.org/dwarves/094b626d44e817240ae8e44b6f7933b13c26d879.camel@gmail.com/T/#m8a6cb49a99d1b2ba38d616495a540ae8fc5f3a76

Reported-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Closes: https://lore.kernel.org/dwarves/Z1dFXVFYmQ-nHSVO@x1/
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
 btf_loader.c           | 31 +++++++++++++++++++++++--------
 dwarf_loader.c         |  2 +-
 dwarves.c              |  3 +++
 dwarves.h              |  8 +++++++-
 dwarves_fprintf.c      |  6 ++++--
 tests/btf_functions.sh |  2 +-
 6 files changed, 39 insertions(+), 13 deletions(-)

Comments

Alan Maguire Dec. 12, 2024, 8:29 p.m. UTC | #1
On 11/12/2024 02:12, Eduard Zingerman wrote:
> btf_loader issues a warning when it sees several BTF_DECL_TAGs
> pointing to the same type. Such situation is possible in practice
> after patch [0], that marks certain functions with kfunc and
> bpf_fastcall tags. E.g.:
> 
>   $ pfunct vmlinux -F btf -f bpf_rdonly_cast
> WARNING: still unsuported BTF_KIND_DECL_TAG(bpf_fastcall) for bpf_cast_to_kern_ctx already with attribute (bpf_kfunc), ignoring
> WARNING: still unsuported BTF_KIND_DECL_TAG(bpf_fastcall) for bpf_rdonly_cast already with attribute (bpf_kfunc), ignoring
>   bpf_kfunc void * bpf_rdonly_cast(const void  * obj__ign, u32 btf_id__k);
> 
> This commit extends 'struct tag' to allow attaching multiple
> attributes. Define 'struct attributes' as follows:
> 
>   struct attributes {
>         uint64_t cnt;
>         const char *values[];
>   };
> 
> In order to avoid adding counter field in 'struct tag',
> as not many instances of 'struct tag' would have attributes.
> 
> Same command after this patch:
> 
>   $ pfunct vmlinux -F btf -f bpf_rdonly_cast
>   bpf_kfunc bpf_fastcall void * bpf_rdonly_cast(const void  * obj__ign, u32 btf_id__k);
> 
> [0] https://lore.kernel.org/dwarves/094b626d44e817240ae8e44b6f7933b13c26d879.camel@gmail.com/T/#m8a6cb49a99d1b2ba38d616495a540ae8fc5f3a76
> 
> Reported-by: Arnaldo Carvalho de Melo <acme@kernel.org>
> Closes: https://lore.kernel.org/dwarves/Z1dFXVFYmQ-nHSVO@x1/
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>

Looks good to me. I _think_ we're safe enough in assuming the tag
ordering "bpf_kfunc bpf_fastcall" in the btf_functions.sh test, right?

Reviewed-by: Alan Maguire <alan.maguire@oracle.com>
Tested-by: Alan Maguire <alan.maguire@oracle.com>

> ---
>  btf_loader.c           | 31 +++++++++++++++++++++++--------
>  dwarf_loader.c         |  2 +-
>  dwarves.c              |  3 +++
>  dwarves.h              |  8 +++++++-
>  dwarves_fprintf.c      |  6 ++++--
>  tests/btf_functions.sh |  2 +-
>  6 files changed, 39 insertions(+), 13 deletions(-)
> 
> diff --git a/btf_loader.c b/btf_loader.c
> index 4814f29..af9e1db 100644
> --- a/btf_loader.c
> +++ b/btf_loader.c
> @@ -459,9 +459,28 @@ static int create_new_tag(struct cu *cu, int type, const struct btf_type *tp, ui
>  	return 0;
>  }
>  
> +static struct attributes *attributes__realloc(struct attributes *attributes, const char *value)
> +{
> +	struct attributes *result;
> +	uint64_t cnt;
> +	size_t sz;
> +
> +	cnt = attributes ? attributes->cnt : 0;
> +	sz = sizeof(*attributes) + (cnt + 1) * sizeof(*attributes->values);
> +	result = realloc(attributes, sz);
> +	if (!result)
> +		return NULL;
> +	if (!attributes)
> +		result->cnt = 0;
> +	result->values[cnt] = value;
> +	result->cnt++;
> +	return result;
> +}
> +
>  static int process_decl_tag(struct cu *cu, const struct btf_type *tp)
>  {
>  	struct tag *tag = cu__type(cu, tp->type);
> +	struct attributes *tmp;
>  
>  	if (tag == NULL)
>  		tag = cu__function(cu, tp->type);
> @@ -475,15 +494,11 @@ static int process_decl_tag(struct cu *cu, const struct btf_type *tp)
>  	}
>  
>  	const char *attribute = cu__btf_str(cu, tp->name_off);
> +	tmp = attributes__realloc(tag->attributes, attribute);
> +	if (!tmp)
> +		return -ENOMEM;
>  
> -	if (tag->attribute != NULL) {
> -		char bf[128];
> -
> -		fprintf(stderr, "WARNING: still unsuported BTF_KIND_DECL_TAG(%s) for %s already with attribute (%s), ignoring\n",
> -		       attribute, tag__name(tag, cu, bf, sizeof(bf), NULL), tag->attribute);
> -	} else {
> -		tag->attribute = attribute;
> -	}
> +	tag->attributes = tmp;
>  
>  	return 0;
>  }
> diff --git a/dwarf_loader.c b/dwarf_loader.c
> index 598fde4..34376b2 100644
> --- a/dwarf_loader.c
> +++ b/dwarf_loader.c
> @@ -513,7 +513,7 @@ static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
>  
>  	dwarf_tag__set_attr_type(dtag, abstract_origin, die, DW_AT_abstract_origin);
>  	tag->recursivity_level = 0;
> -	tag->attribute = NULL;
> +	tag->attributes = NULL;
>  
>  	if (cu->extra_dbg_info) {
>  		pthread_mutex_lock(&libdw__lock);
> diff --git a/dwarves.c b/dwarves.c
> index ae512b9..f970dd2 100644
> --- a/dwarves.c
> +++ b/dwarves.c
> @@ -210,6 +210,9 @@ void tag__delete(struct tag *tag, struct cu *cu)
>  
>  	assert(list_empty(&tag->node));
>  
> +	if (tag->attributes)
> +		free(tag->attributes);
> +
>  	switch (tag->tag) {
>  	case DW_TAG_union_type:
>  		type__delete(tag__type(tag), cu);		break;
> diff --git a/dwarves.h b/dwarves.h
> index 1cb0d62..0a4d5a2 100644
> --- a/dwarves.h
> +++ b/dwarves.h
> @@ -516,10 +516,16 @@ int cu__for_all_tags(struct cu *cu,
>  				     struct cu *cu, void *cookie),
>  		     void *cookie);
>  
> +struct attributes {
> +	uint64_t cnt;
> +	const char *values[];
> +};
> +
>  /** struct tag - basic representation of a debug info element
>   * @priv - extra data, for instance, DWARF offset, id, decl_{file,line}
>   * @top_level -
>   * @shared_tags: used by struct namespace
> + * @attributes - attributes specified by BTF_DECL_TAGs targeting this tag
>   */
>  struct tag {
>  	struct list_head node;
> @@ -530,7 +536,7 @@ struct tag {
>  	bool		 has_btf_type_tag:1;
>  	bool		 shared_tags:1;
>  	uint8_t		 recursivity_level;
> -	const char	 *attribute;
> +	struct attributes *attributes;
>  };
>  
>  // To use with things like type->type_enum == perf_event_type+perf_user_event_type
> diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
> index e16a6b4..c3e7f3c 100644
> --- a/dwarves_fprintf.c
> +++ b/dwarves_fprintf.c
> @@ -1405,9 +1405,11 @@ static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
>  	struct ftype *ftype = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto;
>  	size_t printed = 0;
>  	bool inlined = !conf->strip_inline && function__declared_inline(func);
> +	int i;
>  
> -	if (tag->attribute)
> -		printed += fprintf(fp, "%s ", tag->attribute);
> +	if (tag->attributes)
> +		for (i = 0; i < tag->attributes->cnt; ++i)
> +			printed += fprintf(fp, "%s ", tag->attributes->values[i]);
>  
>  	if (func->virtuality == DW_VIRTUALITY_virtual ||
>  	    func->virtuality == DW_VIRTUALITY_pure_virtual)
> diff --git a/tests/btf_functions.sh b/tests/btf_functions.sh
> index 61f8a00..c92e5ae 100755
> --- a/tests/btf_functions.sh
> +++ b/tests/btf_functions.sh
> @@ -66,7 +66,7 @@ pfunct --all --no_parm_names --format_path=dwarf $vmlinux | \
>  	sort|uniq > $outdir/dwarf.funcs
>  # all functions from BTF (removing bpf_kfunc prefix where found)
>  pfunct --all --no_parm_names --format_path=btf $outdir/vmlinux.btf 2>/dev/null|\
> -	awk '{ gsub("^bpf_kfunc ",""); print $0}'|sort|uniq > $outdir/btf.funcs
> +	awk '{ gsub("^(bpf_kfunc |bpf_fastcall )+",""); print $0}'|sort|uniq > $outdir/btf.funcs
>  
>  exact=0
>  inline=0
Eduard Zingerman Dec. 12, 2024, 8:36 p.m. UTC | #2
On Thu, 2024-12-12 at 20:29 +0000, Alan Maguire wrote:

[...]

> Looks good to me. I _think_ we're safe enough in assuming the tag
> ordering "bpf_kfunc bpf_fastcall" in the btf_functions.sh test, right?

For "bpf_kfunc bpf_fastcall" yes, we should be safe.
On the other hand, I don't think the below could be simplified with
this knowledge:

+	awk '{ gsub("^(bpf_kfunc |bpf_fastcall )+",""); print $0}'|sort|uniq

Because there are situations when only "bpf_kfunc" is present
and when both "bpf_kfunc", "bpf_fastcall" are present.

[...]
diff mbox series

Patch

diff --git a/btf_loader.c b/btf_loader.c
index 4814f29..af9e1db 100644
--- a/btf_loader.c
+++ b/btf_loader.c
@@ -459,9 +459,28 @@  static int create_new_tag(struct cu *cu, int type, const struct btf_type *tp, ui
 	return 0;
 }
 
+static struct attributes *attributes__realloc(struct attributes *attributes, const char *value)
+{
+	struct attributes *result;
+	uint64_t cnt;
+	size_t sz;
+
+	cnt = attributes ? attributes->cnt : 0;
+	sz = sizeof(*attributes) + (cnt + 1) * sizeof(*attributes->values);
+	result = realloc(attributes, sz);
+	if (!result)
+		return NULL;
+	if (!attributes)
+		result->cnt = 0;
+	result->values[cnt] = value;
+	result->cnt++;
+	return result;
+}
+
 static int process_decl_tag(struct cu *cu, const struct btf_type *tp)
 {
 	struct tag *tag = cu__type(cu, tp->type);
+	struct attributes *tmp;
 
 	if (tag == NULL)
 		tag = cu__function(cu, tp->type);
@@ -475,15 +494,11 @@  static int process_decl_tag(struct cu *cu, const struct btf_type *tp)
 	}
 
 	const char *attribute = cu__btf_str(cu, tp->name_off);
+	tmp = attributes__realloc(tag->attributes, attribute);
+	if (!tmp)
+		return -ENOMEM;
 
-	if (tag->attribute != NULL) {
-		char bf[128];
-
-		fprintf(stderr, "WARNING: still unsuported BTF_KIND_DECL_TAG(%s) for %s already with attribute (%s), ignoring\n",
-		       attribute, tag__name(tag, cu, bf, sizeof(bf), NULL), tag->attribute);
-	} else {
-		tag->attribute = attribute;
-	}
+	tag->attributes = tmp;
 
 	return 0;
 }
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 598fde4..34376b2 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -513,7 +513,7 @@  static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
 
 	dwarf_tag__set_attr_type(dtag, abstract_origin, die, DW_AT_abstract_origin);
 	tag->recursivity_level = 0;
-	tag->attribute = NULL;
+	tag->attributes = NULL;
 
 	if (cu->extra_dbg_info) {
 		pthread_mutex_lock(&libdw__lock);
diff --git a/dwarves.c b/dwarves.c
index ae512b9..f970dd2 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -210,6 +210,9 @@  void tag__delete(struct tag *tag, struct cu *cu)
 
 	assert(list_empty(&tag->node));
 
+	if (tag->attributes)
+		free(tag->attributes);
+
 	switch (tag->tag) {
 	case DW_TAG_union_type:
 		type__delete(tag__type(tag), cu);		break;
diff --git a/dwarves.h b/dwarves.h
index 1cb0d62..0a4d5a2 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -516,10 +516,16 @@  int cu__for_all_tags(struct cu *cu,
 				     struct cu *cu, void *cookie),
 		     void *cookie);
 
+struct attributes {
+	uint64_t cnt;
+	const char *values[];
+};
+
 /** struct tag - basic representation of a debug info element
  * @priv - extra data, for instance, DWARF offset, id, decl_{file,line}
  * @top_level -
  * @shared_tags: used by struct namespace
+ * @attributes - attributes specified by BTF_DECL_TAGs targeting this tag
  */
 struct tag {
 	struct list_head node;
@@ -530,7 +536,7 @@  struct tag {
 	bool		 has_btf_type_tag:1;
 	bool		 shared_tags:1;
 	uint8_t		 recursivity_level;
-	const char	 *attribute;
+	struct attributes *attributes;
 };
 
 // To use with things like type->type_enum == perf_event_type+perf_user_event_type
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
index e16a6b4..c3e7f3c 100644
--- a/dwarves_fprintf.c
+++ b/dwarves_fprintf.c
@@ -1405,9 +1405,11 @@  static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
 	struct ftype *ftype = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto;
 	size_t printed = 0;
 	bool inlined = !conf->strip_inline && function__declared_inline(func);
+	int i;
 
-	if (tag->attribute)
-		printed += fprintf(fp, "%s ", tag->attribute);
+	if (tag->attributes)
+		for (i = 0; i < tag->attributes->cnt; ++i)
+			printed += fprintf(fp, "%s ", tag->attributes->values[i]);
 
 	if (func->virtuality == DW_VIRTUALITY_virtual ||
 	    func->virtuality == DW_VIRTUALITY_pure_virtual)
diff --git a/tests/btf_functions.sh b/tests/btf_functions.sh
index 61f8a00..c92e5ae 100755
--- a/tests/btf_functions.sh
+++ b/tests/btf_functions.sh
@@ -66,7 +66,7 @@  pfunct --all --no_parm_names --format_path=dwarf $vmlinux | \
 	sort|uniq > $outdir/dwarf.funcs
 # all functions from BTF (removing bpf_kfunc prefix where found)
 pfunct --all --no_parm_names --format_path=btf $outdir/vmlinux.btf 2>/dev/null|\
-	awk '{ gsub("^bpf_kfunc ",""); print $0}'|sort|uniq > $outdir/btf.funcs
+	awk '{ gsub("^(bpf_kfunc |bpf_fastcall )+",""); print $0}'|sort|uniq > $outdir/btf.funcs
 
 exact=0
 inline=0