All of lore.kernel.org
 help / color / mirror / Atom feed
From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Alan Maguire <alan.maguire@oracle.com>
Cc: Jiri Olsa <jolsa@kernel.org>,
	Clark Williams <williams@redhat.com>,
	dwarves@vger.kernel.org, bpf@vger.kernel.org,
	Andrii Nakryiko <andrii@kernel.org>,
	Yonghong Song <yonghong.song@linux.dev>,
	Mark Wieelard <mjw@redhat.com>,
	Arnaldo Carvalho de Melo <acme@redhat.com>
Subject: [PATCH 03/12] dwarf_loader: Populate DW_TAG_variant children in DW_TAG_variant_part
Date: Fri, 31 Jul 2026 16:30:51 -0300	[thread overview]
Message-ID: <20260731193102.110693-4-acme@kernel.org> (raw)
In-Reply-To: <20260731193102.110693-1-acme@kernel.org>

From: Arnaldo Carvalho de Melo <acme@redhat.com>

Rust discriminated unions (enums like Option<T> and Result<T,E>) are
represented in DWARF as:

  DW_TAG_structure_type
    DW_TAG_variant_part (DW_AT_discr -> discriminant member)
      DW_TAG_variant (DW_AT_discr_value = 0)
        DW_TAG_member "None" -> struct None
      DW_TAG_variant (DW_AT_discr_value = 1)
        DW_TAG_member "Some" -> struct Some

Commit 7b135647cbf22e0c ("dwarf_loader: Initial support for
DW_TAG_variant_part") added the variant_part container to pahole's
internal representation, but did not process its DW_TAG_variant children.
This meant the variant parts were always empty, and any Rust struct
backed by a variant_part appeared as a zero-member struct in the output.

Add a struct variant with name and discriminant value fields, a
variant__new() loader that extracts the DW_AT_discr_value and the child
DW_TAG_member's name and type reference, and wire it into
variant_part__new() so DW_TAG_variant children are populated at load
time.

Also add type recoding for variant type references in
namespace__recode_dwarf_types, so variant member types are resolved from
DWARF offsets to CU-local type indices, following the same pattern used
for regular struct/union members.

The variant_part__delete destructor is made non-static and extended to
clean up variant children, and helper functions and iterator macros are
added for the new types.

Before, with a Rust binary like sashiko-cli:

  $ pahole -F btf -C 'Option<u32>' code_with_type.o
  struct Option<u32> {

          /* size: 8, cachelines: 1, members: 0 */
          /* padding: 8 */
          /* last cacheline: 8 bytes */
  } __attribute__((__aligned__(16)));

After:

  $ pahole -F dwarf -C 'Option<u32>' code_with_type.o
  struct Option<u32> {
          struct None { ... } __attribute__((__aligned__(4)));
          struct Some {
                  u32 __0 __attribute__((__aligned__(4)));  /*  4  4 */
          } __attribute__((__aligned__(4)));

          /* size: 8, cachelines: 1, members: 0 */
  } __attribute__((__aligned__(4)));

The variant parts are now populated in pahole's internal representation
and available for downstream consumers (BTF/CTF encoders, pretty
printers).

Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 dwarf_loader.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++
 dwarves.c      | 42 ++++++++++++++++++++++++++++-
 dwarves.h      | 24 +++++++++++++++++
 3 files changed, 137 insertions(+), 1 deletion(-)

diff --git a/dwarf_loader.c b/dwarf_loader.c
index 11477dcd7373a844..4979968828c63d8f 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1240,6 +1240,32 @@ static struct template_parameter_pack *template_parameter_pack__new(Dwarf_Die *d
 	return pack;
 }
 
+static struct variant *variant__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
+{
+	struct variant *var = tag__alloc(cu, sizeof(*var));
+
+	if (var != NULL) {
+		tag__init(&var->tag, cu, die);
+		var->discr_value = attr_numeric(die, DW_AT_discr_value);
+		var->name = NULL;
+
+		Dwarf_Die child;
+		if (dwarf_child(die, &child) == 0) {
+			do {
+				if (dwarf_tag(&child) == DW_TAG_member) {
+					struct dwarf_tag *dtag = tag__dwarf(&var->tag);
+
+					var->name = attr_string(&child, DW_AT_name, conf);
+					dwarf_tag__set_attr_type(dtag, type, &child, DW_AT_type);
+					break;
+				}
+			} while (dwarf_siblingof(&child, &child) == 0);
+		}
+	}
+
+	return var;
+}
+
 static struct variant_part *variant_part__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
 {
 	struct variant_part *vpart = tag__alloc(cu, sizeof(*vpart));
@@ -1247,6 +1273,20 @@ static struct variant_part *variant_part__new(Dwarf_Die *die, struct cu *cu, str
 	if (vpart != NULL) {
 		tag__init(&vpart->tag, cu, die);
 		INIT_LIST_HEAD(&vpart->variants);
+
+		Dwarf_Die child;
+		if (dwarf_child(die, &child) == 0) {
+			do {
+				if (dwarf_tag(&child) == DW_TAG_variant) {
+					struct variant *var = variant__new(&child, cu, conf);
+					if (var == NULL) {
+						variant_part__delete(vpart, cu);
+						return NULL;
+					}
+					variant_part__add_variant(vpart, var);
+				}
+			} while (dwarf_siblingof(&child, &child) == 0);
+		}
 	}
 
 	return vpart;
@@ -3003,6 +3043,38 @@ check_type:
 next:
 		pos->type = dtype->small_id;
 	}
+
+	if (tag__is_struct(tag) || tag__is_union(tag)) {
+		struct type *type = tag__type(tag);
+		struct variant_part *vpart;
+		struct dwarf_cu *dcu = cu->priv;
+
+		type__for_each_variant_part(type, vpart) {
+			struct variant *variant;
+			struct dwarf_tag *dvpart = tag__dwarf(&vpart->tag);
+
+			if (dvpart->type != 0) {
+				struct dwarf_tag *dtype = dwarf_cu__find_tag_by_ref(dcu, dvpart, type);
+				if (dtype != NULL)
+					vpart->tag.type = dtype->small_id;
+			}
+
+			variant_part__for_each_variant(vpart, variant) {
+				struct dwarf_tag *dvar = tag__dwarf(&variant->tag);
+
+				if (dvar->type == 0)
+					continue;
+
+				struct dwarf_tag *dtype = dwarf_cu__find_type_by_ref(dcu, dvar, type);
+				if (dtype == NULL) {
+					tag__print_type_not_found(&variant->tag);
+					continue;
+				}
+				variant->tag.type = dtype->small_id;
+			}
+		}
+	}
+
 	return 0;
 }
 
diff --git a/dwarves.c b/dwarves.c
index 58852d3783b894ff..8bcc0860e97d95f5 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -1211,11 +1211,18 @@ static void type__delete_class_members(struct type *type, struct cu *cu)
 	}
 }
 
-static void variant_part__delete(struct variant_part *vpart, struct cu *cu)
+void variant_part__delete(struct variant_part *vpart, struct cu *cu)
 {
+	struct variant *pos, *next;
+
 	if (vpart == NULL)
 		return;
 
+	list_for_each_entry_safe(pos, next, &vpart->variants, tag.node) {
+		list_del_init(&pos->tag.node);
+		cu__tag_free(cu, &pos->tag);
+	}
+
 	cu__tag_free(cu, &vpart->tag);
 }
 
@@ -1309,6 +1316,11 @@ void type__add_variant_part(struct type *type, struct variant_part *vpart)
 	list_add_tail(&vpart->tag.node, &type->variant_parts);
 }
 
+void variant_part__add_variant(struct variant_part *vpart, struct variant *var)
+{
+	list_add_tail(&var->tag.node, &vpart->variants);
+}
+
 struct class_member *type__last_member(struct type *type)
 {
 	struct class_member *pos;
@@ -1339,6 +1351,34 @@ static int type__clone_members(struct type *type, const struct type *from, struc
 		type__add_member(type, clone);
 	}
 
+	struct variant_part *vpart;
+
+	type__for_each_variant_part(from, vpart) {
+		struct variant_part *vp_clone = cu__tag_alloc(cu, sizeof(*vp_clone));
+
+		if (vp_clone == NULL)
+			return -1;
+
+		memcpy(vp_clone, vpart, sizeof(*vp_clone));
+		INIT_LIST_HEAD(&vp_clone->variants);
+
+		struct variant *variant;
+
+		variant_part__for_each_variant(vpart, variant) {
+			struct variant *v_clone = cu__tag_alloc(cu, sizeof(*v_clone));
+
+			if (v_clone == NULL) {
+				variant_part__delete(vp_clone, cu);
+				return -1;
+			}
+
+			memcpy(v_clone, variant, sizeof(*v_clone));
+			variant_part__add_variant(vp_clone, v_clone);
+		}
+
+		type__add_variant_part(type, vp_clone);
+	}
+
 	return 0;
 }
 
diff --git a/dwarves.h b/dwarves.h
index f5368f68c2460488..dd9aa332e61c77f0 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -1021,6 +1021,12 @@ static inline struct formal_parameter_pack *tag__formal_parameter_pack(const str
 
 void formal_parameter_pack__add(struct formal_parameter_pack *pack, struct parameter *param);
 
+struct variant {
+	struct tag	 tag;
+	const char	 *name;
+	uint64_t	 discr_value;
+};
+
 struct variant_part {
 	struct tag	 tag;
 	struct list_head variants;
@@ -1424,10 +1430,28 @@ static inline struct class_member *class_member__next(struct class_member *membe
 #define type__for_each_variant_part_safe_reverse(type, pos, n) \
 	list_for_each_entry_safe_reverse(pos, n, &(type)->variant_parts, tag.node)
 
+/**
+ * type__for_each_variant_part - iterate thru all variant_parts in a type
+ * @type: struct type instance to iterate
+ * @pos: struct variant_part iterator
+ */
+#define type__for_each_variant_part(type, pos) \
+	list_for_each_entry(pos, &(type)->variant_parts, tag.node)
+
 void type__add_member(struct type *type, struct class_member *member);
 void type__add_template_type_param(struct type *type, struct template_type_param *ttparm);
 void type__add_template_value_param(struct type *type, struct template_value_param *tvparam);
 void type__add_variant_part(struct type *type, struct variant_part *vpart);
+void variant_part__delete(struct variant_part *vpart, struct cu *cu);
+void variant_part__add_variant(struct variant_part *vpart, struct variant *var);
+
+/**
+ * variant_part__for_each_variant - iterate thru all variants in a variant_part
+ * @vpart: struct variant_part instance to iterate
+ * @pos: struct variant iterator
+ */
+#define variant_part__for_each_variant(vpart, pos) \
+	list_for_each_entry(pos, &(vpart)->variants, tag.node)
 
 struct class_member *
 	type__find_first_biggest_size_base_type_member(struct type *type,
-- 
2.55.0


  parent reply	other threads:[~2026-07-31 19:31 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-31 19:30 [PATCHES 00/12] pahole: Support more rust tags and references to dwz alternate debug files Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 01/12] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 02/12] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
2026-07-31 19:30 ` Arnaldo Carvalho de Melo [this message]
2026-07-31 19:30 ` [PATCH 04/12] btf_encoder: Encode variant parts as union members in BTF Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 05/12] dwarf_loader: Handle DW_FORM_block in attr_numeric for Rust discriminant values Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 06/12] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 07/12] dwarf_loader: Support DW_TAG_imported_unit for same-file partial units Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 08/12] dwarf_loader: Fix cus__merging_cu failing to detect DW_FORM_ref_addr Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 09/12] dwarf_loader: Add cu parameter to tag__set_spec() and dwarf_tag__set_attr_type() Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 10/12] dwarf_loader: Support DW_FORM_GNU_ref_alt references to dwz alternate debug files Arnaldo Carvalho de Melo
2026-07-31 19:30 ` [PATCH 11/12] tests: Add inter-CU type reference comparison test Arnaldo Carvalho de Melo
2026-07-31 19:31 ` [PATCH 12/12] scripts: Add vmlinux_comparison.py for DWARF/BTF analysis Arnaldo Carvalho de Melo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260731193102.110693-4-acme@kernel.org \
    --to=acme@kernel.org \
    --cc=acme@redhat.com \
    --cc=alan.maguire@oracle.com \
    --cc=andrii@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=dwarves@vger.kernel.org \
    --cc=jolsa@kernel.org \
    --cc=mjw@redhat.com \
    --cc=williams@redhat.com \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.