public inbox for dwarves@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Initial support for some Rust tags + way to ask for CU merging at load time
@ 2026-03-23 21:15 Arnaldo Carvalho de Melo
  2026-03-23 21:15 ` [PATCH 1/3] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-03-23 21:15 UTC (permalink / raw)
  To: Alan Maguire
  Cc: Jiri Olsa, Clark Williams, Kate Carcia, dwarves,
	Arnaldo Carvalho de Melo

Hi,

	Here is a series with some initial support for some Rust DWARF
tags and a way to ask for CUs to be merged just like we do for LTO to
cope with inter CU tag references.

	This makes one of the regression tests to pass again as perf now
has some rust source files and thus Rust CUs in a perf binary built with
DWARF, which is used to test pahole's pretty printing features, where it
uses the DWARF in a perf binary to decode perf.data records.

	As mentioned in the man page, this is hackish, to properly
support LTO, rust, and any inter-CU references we need to yet again
rethink how we do DWARF parallel loading, perhaps leaving the recoding
of ids to be a last step, using a common hash for types to be used at
the recoding of ids, etc, but for now reusing the existing merging of
CUs in such cases provides a workaround.

- Arnaldo

Arnaldo Carvalho de Melo (3):
  dwarf_loader: Initial support for DW_TAG_variant_part
  dwarf_loader: Initial support for DW_TAG_subprogram in
    DW_TAG_enumeration
  dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag
    references

 dwarf_loader.c              | 66 +++++++++++++++++++++++++++++++------
 dwarves.c                   |  6 ++++
 dwarves.h                   |  8 +++++
 man-pages/pahole.1          | 12 +++++++
 pahole.c                    |  8 +++++
 tests/prettify_perf.data.sh |  4 +--
 6 files changed, 92 insertions(+), 12 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/3] dwarf_loader: Initial support for DW_TAG_variant_part
  2026-03-23 21:15 [PATCH 0/3] Initial support for some Rust tags + way to ask for CU merging at load time Arnaldo Carvalho de Melo
@ 2026-03-23 21:15 ` Arnaldo Carvalho de Melo
  2026-04-08 14:05   ` Alan Maguire
  2026-03-23 21:15 ` [PATCH 2/3] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
  2026-03-23 21:15 ` [PATCH 3/3] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
  2 siblings, 1 reply; 9+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-03-23 21:15 UTC (permalink / raw)
  To: Alan Maguire
  Cc: Jiri Olsa, Clark Williams, Kate Carcia, dwarves,
	Arnaldo Carvalho de Melo

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

Still doesn't handle its sub hierarchy, i.e. the DW_TAG_variant entries
and its underlying DW_TAG_member entries.

This was noticed when running the regression test that uses a debug
build of perf to process a perf.data file and test pahole's pretty
printing features, as now perf has a synthetic workload that is written
in rust:

 <0><20c95a>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <20c95b>   DW_AT_producer    : (indirect string, offset: 0x4ee5a): clang LLVM (rustc version 1.93.1 (01f6ddf75 2026-02-11) (Fedora 1.93.1-1.fc43))
    <20c95f>   DW_AT_language    : 28	(Rust)
    <20c961>   DW_AT_name        : (indirect string, offset: 0x4eeaa): tests/workloads/code_with_type.rs/@/code_with_type.d6e680867bfb8b27-cgu.0
    <20c965>   DW_AT_stmt_list   : 0x5e1ed
    <20c969>   DW_AT_comp_dir    : (indirect string, offset: 0x487f1): /home/acme/git/perf-tools/tools/perf
    <20c96d>   DW_AT_low_pc      : 0
    <20c975>   DW_AT_ranges      : 0x2d0
⬢ [acme@toolbx pahole]$

So lets add some scaffolding for the Rust DWARF constructs involved for
us to be able to continue using perf with DWARF to test the pretty
printing features.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 dwarf_loader.c | 26 +++++++++++++++++++++++++-
 dwarves.c      |  6 ++++++
 dwarves.h      |  7 +++++++
 3 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/dwarf_loader.c b/dwarf_loader.c
index 16fb7becffee56f6..f0833e8c44a944a8 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -508,6 +508,8 @@ static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
 
 	if (tag->tag == DW_TAG_imported_module || tag->tag == DW_TAG_imported_declaration)
 		dwarf_tag__set_attr_type(dtag, type, die, DW_AT_import);
+	else if (tag->tag == DW_TAG_variant_part)
+		dwarf_tag__set_attr_type(dtag, type, die, DW_AT_discr);
 	else
 		dwarf_tag__set_attr_type(dtag, type, die, DW_AT_type);
 
@@ -1170,6 +1172,18 @@ static struct template_parameter_pack *template_parameter_pack__new(Dwarf_Die *d
 	return pack;
 }
 
+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));
+
+	if (vpart != NULL) {
+		tag__init(&vpart->tag, cu, die);
+		INIT_LIST_HEAD(&vpart->variants);
+	}
+
+	return vpart;
+}
+
 /* Returns number of locations found or negative value for errors. */
 static ptrdiff_t __dwarf_getlocations(Dwarf_Attribute *attr,
 				      ptrdiff_t offset, Dwarf_Addr *basep,
@@ -1990,9 +2004,19 @@ static int die__process_class(Dwarf_Die *die, struct type *class,
 		case DW_TAG_GNU_template_template_param:
 #endif
 		case DW_TAG_subrange_type: // XXX: ADA stuff, its a type tho, will have other entries referencing it...
-		case DW_TAG_variant_part: // XXX: Rust stuff
 			tag__print_not_supported(die);
 			continue;
+		case DW_TAG_variant_part: {
+			struct variant_part *vpart = variant_part__new(die, cu, conf);
+
+			if (vpart == NULL)
+				return -ENOMEM;
+
+			// For rust it seems we have just one, but DWARF, according to Gemini, support having
+			// more than one DW_TAG_variant_part for a given DW_TAG_structure_type, so future proof it
+			type__add_variant_part(class, vpart);
+			continue;
+		}
 		case DW_TAG_template_type_parameter: {
 			struct template_type_param *ttparm = template_type_param__new(die, cu, conf);
 
diff --git a/dwarves.c b/dwarves.c
index ef93239d26827711..efc7ae214376e1c1 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -428,6 +428,7 @@ void __type__init(struct type *type)
 	INIT_LIST_HEAD(&type->type_enum);
 	INIT_LIST_HEAD(&type->template_type_params);
 	INIT_LIST_HEAD(&type->template_value_params);
+	INIT_LIST_HEAD(&type->variant_parts);
 	type->template_parameter_pack = NULL;
 	type->sizeof_member = NULL;
 	type->member_prefix = NULL;
@@ -1366,6 +1367,11 @@ void type__add_template_value_param(struct type *type, struct template_value_par
 	list_add_tail(&tvparam->tag.node, &type->template_value_params);
 }
 
+void type__add_variant_part(struct type *type, struct variant_part *vpart)
+{
+	list_add_tail(&vpart->tag.node, &type->variant_parts);
+}
+
 struct class_member *type__last_member(struct type *type)
 {
 	struct class_member *pos;
diff --git a/dwarves.h b/dwarves.h
index d7c64742c29a9e9f..95d84b8ce3a6e95d 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -1011,6 +1011,11 @@ 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_part {
+	struct tag	 tag;
+	struct list_head variants;
+};
+
 /*
  * tag.tag can be DW_TAG_subprogram_type or DW_TAG_subroutine_type.
  */
@@ -1281,6 +1286,7 @@ struct type {
 	uint8_t		 is_signed_enum:1;
 	struct list_head template_type_params;
 	struct list_head template_value_params;
+	struct list_head variant_parts;
 	struct template_parameter_pack *template_parameter_pack;
 };
 
@@ -1401,6 +1407,7 @@ static inline struct class_member *class_member__next(struct class_member *membe
 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);
 
 struct class_member *
 	type__find_first_biggest_size_base_type_member(struct type *type,
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/3] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration
  2026-03-23 21:15 [PATCH 0/3] Initial support for some Rust tags + way to ask for CU merging at load time Arnaldo Carvalho de Melo
  2026-03-23 21:15 ` [PATCH 1/3] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
@ 2026-03-23 21:15 ` Arnaldo Carvalho de Melo
  2026-03-30  9:05   ` Alan Maguire
  2026-03-23 21:15 ` [PATCH 3/3] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
  2 siblings, 1 reply; 9+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-03-23 21:15 UTC (permalink / raw)
  To: Alan Maguire
  Cc: Jiri Olsa, Clark Williams, Kate Carcia, dwarves,
	Arnaldo Carvalho de Melo

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

In Rust enums can have subprograms, add initial support for it.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 dwarf_loader.c | 38 ++++++++++++++++++++++++++++++--------
 1 file changed, 30 insertions(+), 8 deletions(-)

diff --git a/dwarf_loader.c b/dwarf_loader.c
index f0833e8c44a944a8..b5a92160ecf82f74 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -1943,6 +1943,8 @@ out_delete:
 	return NULL;
 }
 
+static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu, struct conf_load *conf);
+
 static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
 {
 	Dwarf_Die child;
@@ -1964,18 +1966,38 @@ static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, st
 
 	die = &child;
 	do {
-		struct enumerator *enumerator;
+		switch (dwarf_tag(die)) {
+		case DW_TAG_enumerator: {
+			struct enumerator *enumerator = enumerator__new(die, cu, conf);
+
+			if (enumerator == NULL)
+				goto out_delete;
+
+			enumeration__add(enumeration, enumerator);
+			cu__hash(cu, &enumerator->tag);
+		}
+			continue;
+		case DW_TAG_subprogram: {
+			struct tag *tag = die__create_new_function(die, cu, conf);
+			uint32_t id;
+
+			if (tag == NULL)
+				goto out_delete;
 
-		if (dwarf_tag(die) != DW_TAG_enumerator) {
+			if (cu__add_tag(cu, tag, &id) < 0) {
+				tag__delete(tag, cu);
+				goto out_delete;
+			}
+
+			cu__hash(cu, tag);
+			struct dwarf_tag *dtag = tag__dwarf(tag);
+			dtag->small_id = id;
+			break;
+		}
+		default:
 			cu__tag_not_handled(cu, die);
 			continue;
 		}
-		enumerator = enumerator__new(die, cu, conf);
-		if (enumerator == NULL)
-			goto out_delete;
-
-		enumeration__add(enumeration, enumerator);
-		cu__hash(cu, &enumerator->tag);
 	} while (dwarf_siblingof(die, die) == 0);
 out:
 	return &enumeration->namespace.tag;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/3] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references
  2026-03-23 21:15 [PATCH 0/3] Initial support for some Rust tags + way to ask for CU merging at load time Arnaldo Carvalho de Melo
  2026-03-23 21:15 ` [PATCH 1/3] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
  2026-03-23 21:15 ` [PATCH 2/3] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
@ 2026-03-23 21:15 ` Arnaldo Carvalho de Melo
  2026-03-30  8:58   ` Alan Maguire
  2 siblings, 1 reply; 9+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-03-23 21:15 UTC (permalink / raw)
  To: Alan Maguire
  Cc: Jiri Olsa, Clark Williams, Kate Carcia, dwarves,
	Arnaldo Carvalho de Melo

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

The Linux perf tool now includes some Rust code that then gets linked
into perf and comes with its DWARF that has tags referencing tags in
different CUs, and as the current DWARF loading algorithm uses
parallelization and recodes the big DWARF types (DWARF_off, usually
64-bit) into smaller ones as a step into converting to CTF (initially)
and later BTF, the resolution fails.

There is a case whe this inter CU happens, LTO builds, and so there is
an alternative algorithm for that case, that serializes DWARF CU loading
and merges all the CUs into just one meta/mega-CU, which then has all
the types and thus doesn't have a problem with inter CU references, as
the recoding into smaller ids is done only after all CUs are loaded.

So while we don't refactor the loading in a way that allows for inter CU
while allowing parallelization, maybe by doing the recoding just at the
end of parallel loading, add minimal code to force this CU merging for
experimentation in such cases, getting back the regression test
prettify_perf.data.sh to work, making it force CU merging.

  $ pahole ~/bin/perf > unmerged.txt
  <Suppress lots of warnings when recoding DWARF types.>
  $ pahole --force_cu_merging ~/bin/perf > merged.txt
  $

With the current set of Rust types that are representable with the
pahole data structures and then pretty printed as if they were C we see
12 differences:

  $ diff -u unmerged.txt merged.txt | grep ^@@ | wc -l
  12
  $ diff -u unmerged.txt merged.txt | wc -l
  198

Of this kind, due to some types not being resolved as tags are
referencing tags in other CUs.

  $ diff -u unmerged.txt merged.txt | head
  --- unmerged.txt	2026-03-23 17:56:54.971785023 -0300
  +++ merged.txt	2026-03-23 17:56:59.826872178 -0300
  @@ -9643,10 +9643,11 @@
   	u64                        __0 __attribute__((__aligned__(8))); /*     0     8 */
   	struct Abbreviation        __1 __attribute__((__aligned__(8))); /*     8   112 */

  -	/* XXX last struct has 5 bytes of padding */
  +	/* XXX last struct has 16 bytes of padding, 1 hole */

   	/* size: 120, cachelines: 2, members: 2 */
  $

Now the pretty printing perf.data test case passes:

  ⬢ [acme@toolbx tests]$ ./prettify_perf.data.sh
  Pretty printing of files using DWARF type information.
  Test ./prettify_perf.data.sh passed
  ⬢ [acme@toolbx tests]$

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 dwarf_loader.c              |  2 +-
 dwarves.h                   |  1 +
 man-pages/pahole.1          | 12 ++++++++++++
 pahole.c                    |  8 ++++++++
 tests/prettify_perf.data.sh |  4 ++--
 5 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/dwarf_loader.c b/dwarf_loader.c
index b5a92160ecf82f74..de2e9b70c32f85de 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -3967,7 +3967,7 @@ static int cus__load_module(struct cus *cus, struct conf_load *conf,
 		}
 	}
 
-	if (cus__merging_cu(dw, elf)) {
+	if (conf->force_cu_merging || cus__merging_cu(dw, elf)) {
 		res = cus__merge_and_process_cu(cus, conf, mod, dw, elf, filename,
 						build_id, build_id_len,
 						type_cu ? &type_dcu : NULL);
diff --git a/dwarves.h b/dwarves.h
index 95d84b8ce3a6e95d..7887af93693ebad5 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -102,6 +102,7 @@ struct conf_load {
 	bool			btf_gen_distilled_base;
 	bool			btf_attributes;
 	bool			true_signature;
+	bool			force_cu_merging;
 	uint8_t			hashtable_bits;
 	uint8_t			max_hashtable_bits;
 	uint16_t		kabi_prefix_len;
diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
index 90a8f4566de621d3..39bb53816f4fac9f 100644
--- a/man-pages/pahole.1
+++ b/man-pages/pahole.1
@@ -515,6 +515,18 @@ This is useful for scripts where it provides a way to ask for that exclusion
 for pahole and pfunct, no need to use --lang_exclude in all calls to those
 tools, just set that environment variable.
 
+.TP
+.B \-\-force_cu_merging
+Force merging all CUs into one. Use when there are references across CUs.
+
+This happens in some LTO cases and was observed with Rust CUs, where types
+of tags (function parameters, abstract origins for inlines, etc) reference
+types in another CU.
+
+For LTO this is being autodetected and the merging of cus is done
+automatically, but for the Rust case, and maybe others this is needed with the
+current DWARF loading algorithm.
+
 .TP
 .B \-y, \-\-prefix_filter=PREFIX
 Include PREFIXed classes.
diff --git a/pahole.c b/pahole.c
index e4bfb69de56ada59..05e61b61dddad8ea 100644
--- a/pahole.c
+++ b/pahole.c
@@ -1153,6 +1153,7 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
 #define ARG_padding		   348
 #define ARGP_with_embedded_flexible_array 349
 #define ARGP_btf_attributes	   350
+#define ARGP_force_cu_merging	   351
 
 /* --btf_features=feature1[,feature2,..] allows us to specify
  * a list of requested BTF features or "default" to enable all default
@@ -1818,6 +1819,11 @@ static const struct argp_option pahole__options[] = {
 		.key  = ARGP_btf_attributes,
 		.doc  = "Allow generation of attributes in BTF. Attributes are the type tags and decl tags with the kind_flag set to 1.",
 	},
+	{
+		.name = "force_cu_merging",
+		.key  = ARGP_force_cu_merging,
+		.doc  = "Force merging all CUs into one. Use when there are references across CUs.",
+	},
 	{
 		.name = NULL,
 	}
@@ -2014,6 +2020,8 @@ static error_t pahole__options_parser(int key, char *arg,
 		parse_btf_features(arg, true);		break;
 	case ARGP_btf_attributes:
 		conf_load.btf_attributes = true;	break;
+	case ARGP_force_cu_merging:
+		conf_load.force_cu_merging = true;	break;
 	default:
 		return ARGP_ERR_UNKNOWN;
 	}
diff --git a/tests/prettify_perf.data.sh b/tests/prettify_perf.data.sh
index 1fae95154d710aae..3b903e32da24b489 100755
--- a/tests/prettify_perf.data.sh
+++ b/tests/prettify_perf.data.sh
@@ -25,7 +25,7 @@ fi
 perf_lacks_type_info() {
 	local type_keyword=$1
 	local type_name=$2
-	if ! pahole -C $type_name $perf | grep -q "^$type_keyword $type_name {"; then
+	if ! pahole --force_cu_merging -C $type_name $perf | grep -q "^$type_keyword $type_name {"; then
 		info_log "skip: $perf doesn't have '$type_keyword $type_name' type info"
 		test_skip
 	fi
@@ -41,7 +41,7 @@ $perf record --quiet -o $perf_data sleep 0.00001
 
 number_of_filtered_perf_record_metadata() {
 	local metadata_record=$1
-	local count=$(pahole -F dwarf -V $perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C "perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type,filter=type==PERF_RECORD_$metadata_record)" --prettify $perf_data | grep ".type = PERF_RECORD_$metadata_record," | wc -l)
+	local count=$(pahole --force_cu_merging -F dwarf -V $perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C "perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type,filter=type==PERF_RECORD_$metadata_record)" --prettify $perf_data | grep ".type = PERF_RECORD_$metadata_record," | wc -l)
 	echo "$count"
 }
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/3] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references
  2026-03-23 21:15 ` [PATCH 3/3] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
@ 2026-03-30  8:58   ` Alan Maguire
  0 siblings, 0 replies; 9+ messages in thread
From: Alan Maguire @ 2026-03-30  8:58 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Clark Williams, Kate Carcia, dwarves,
	Arnaldo Carvalho de Melo

On 23/03/2026 21:15, Arnaldo Carvalho de Melo wrote:
> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> The Linux perf tool now includes some Rust code that then gets linked
> into perf and comes with its DWARF that has tags referencing tags in
> different CUs, and as the current DWARF loading algorithm uses
> parallelization and recodes the big DWARF types (DWARF_off, usually
> 64-bit) into smaller ones as a step into converting to CTF (initially)
> and later BTF, the resolution fails.
> 
> There is a case whe this inter CU happens, LTO builds, and so there is
> an alternative algorithm for that case, that serializes DWARF CU loading
> and merges all the CUs into just one meta/mega-CU, which then has all
> the types and thus doesn't have a problem with inter CU references, as
> the recoding into smaller ids is done only after all CUs are loaded.
> 
> So while we don't refactor the loading in a way that allows for inter CU
> while allowing parallelization, maybe by doing the recoding just at the
> end of parallel loading, add minimal code to force this CU merging for
> experimentation in such cases, getting back the regression test
> prettify_perf.data.sh to work, making it force CU merging.
> 
>   $ pahole ~/bin/perf > unmerged.txt
>   <Suppress lots of warnings when recoding DWARF types.>
>   $ pahole --force_cu_merging ~/bin/perf > merged.txt
>   $
>

Is there then a case for adding this as a pahole flag automatically if we are doing
an LTO build? If so, it might make sense to rework this into a btf_feature since they
have a better compatibility story; if --btf_features=force_cu_merging is unknown, pahole
encoding will continue. I realize it's not strictly a BTF feature but given that
defining it as such will reduce pahole compatibility pain it might be worth doing it
that way.
 
> With the current set of Rust types that are representable with the
> pahole data structures and then pretty printed as if they were C we see
> 12 differences:
> 
>   $ diff -u unmerged.txt merged.txt | grep ^@@ | wc -l
>   12
>   $ diff -u unmerged.txt merged.txt | wc -l
>   198
> 
> Of this kind, due to some types not being resolved as tags are
> referencing tags in other CUs.
> 
>   $ diff -u unmerged.txt merged.txt | head
>   --- unmerged.txt	2026-03-23 17:56:54.971785023 -0300
>   +++ merged.txt	2026-03-23 17:56:59.826872178 -0300
>   @@ -9643,10 +9643,11 @@
>    	u64                        __0 __attribute__((__aligned__(8))); /*     0     8 */
>    	struct Abbreviation        __1 __attribute__((__aligned__(8))); /*     8   112 */
> 
>   -	/* XXX last struct has 5 bytes of padding */
>   +	/* XXX last struct has 16 bytes of padding, 1 hole */
> 
>    	/* size: 120, cachelines: 2, members: 2 */
>   $
> 
> Now the pretty printing perf.data test case passes:
> 
>   ⬢ [acme@toolbx tests]$ ./prettify_perf.data.sh
>   Pretty printing of files using DWARF type information.
>   Test ./prettify_perf.data.sh passed
>   ⬢ [acme@toolbx tests]$
> 
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
> ---
>  dwarf_loader.c              |  2 +-
>  dwarves.h                   |  1 +
>  man-pages/pahole.1          | 12 ++++++++++++
>  pahole.c                    |  8 ++++++++
>  tests/prettify_perf.data.sh |  4 ++--
>  5 files changed, 24 insertions(+), 3 deletions(-)
> 
> diff --git a/dwarf_loader.c b/dwarf_loader.c
> index b5a92160ecf82f74..de2e9b70c32f85de 100644
> --- a/dwarf_loader.c
> +++ b/dwarf_loader.c
> @@ -3967,7 +3967,7 @@ static int cus__load_module(struct cus *cus, struct conf_load *conf,
>  		}
>  	}
>  
> -	if (cus__merging_cu(dw, elf)) {
> +	if (conf->force_cu_merging || cus__merging_cu(dw, elf)) {
>  		res = cus__merge_and_process_cu(cus, conf, mod, dw, elf, filename,
>  						build_id, build_id_len,
>  						type_cu ? &type_dcu : NULL);
> diff --git a/dwarves.h b/dwarves.h
> index 95d84b8ce3a6e95d..7887af93693ebad5 100644
> --- a/dwarves.h
> +++ b/dwarves.h
> @@ -102,6 +102,7 @@ struct conf_load {
>  	bool			btf_gen_distilled_base;
>  	bool			btf_attributes;
>  	bool			true_signature;
> +	bool			force_cu_merging;
>  	uint8_t			hashtable_bits;
>  	uint8_t			max_hashtable_bits;
>  	uint16_t		kabi_prefix_len;
> diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
> index 90a8f4566de621d3..39bb53816f4fac9f 100644
> --- a/man-pages/pahole.1
> +++ b/man-pages/pahole.1
> @@ -515,6 +515,18 @@ This is useful for scripts where it provides a way to ask for that exclusion
>  for pahole and pfunct, no need to use --lang_exclude in all calls to those
>  tools, just set that environment variable.
>  
> +.TP
> +.B \-\-force_cu_merging
> +Force merging all CUs into one. Use when there are references across CUs.
> +
> +This happens in some LTO cases and was observed with Rust CUs, where types
> +of tags (function parameters, abstract origins for inlines, etc) reference
> +types in another CU.
> +
> +For LTO this is being autodetected and the merging of cus is done
> +automatically, but for the Rust case, and maybe others this is needed with the
> +current DWARF loading algorithm.
> +
>  .TP
>  .B \-y, \-\-prefix_filter=PREFIX
>  Include PREFIXed classes.
> diff --git a/pahole.c b/pahole.c
> index e4bfb69de56ada59..05e61b61dddad8ea 100644
> --- a/pahole.c
> +++ b/pahole.c
> @@ -1153,6 +1153,7 @@ ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
>  #define ARG_padding		   348
>  #define ARGP_with_embedded_flexible_array 349
>  #define ARGP_btf_attributes	   350
> +#define ARGP_force_cu_merging	   351
>  
>  /* --btf_features=feature1[,feature2,..] allows us to specify
>   * a list of requested BTF features or "default" to enable all default
> @@ -1818,6 +1819,11 @@ static const struct argp_option pahole__options[] = {
>  		.key  = ARGP_btf_attributes,
>  		.doc  = "Allow generation of attributes in BTF. Attributes are the type tags and decl tags with the kind_flag set to 1.",
>  	},
> +	{
> +		.name = "force_cu_merging",
> +		.key  = ARGP_force_cu_merging,
> +		.doc  = "Force merging all CUs into one. Use when there are references across CUs.",
> +	},
>  	{
>  		.name = NULL,
>  	}
> @@ -2014,6 +2020,8 @@ static error_t pahole__options_parser(int key, char *arg,
>  		parse_btf_features(arg, true);		break;
>  	case ARGP_btf_attributes:
>  		conf_load.btf_attributes = true;	break;
> +	case ARGP_force_cu_merging:
> +		conf_load.force_cu_merging = true;	break;
>  	default:
>  		return ARGP_ERR_UNKNOWN;
>  	}
> diff --git a/tests/prettify_perf.data.sh b/tests/prettify_perf.data.sh
> index 1fae95154d710aae..3b903e32da24b489 100755
> --- a/tests/prettify_perf.data.sh
> +++ b/tests/prettify_perf.data.sh
> @@ -25,7 +25,7 @@ fi
>  perf_lacks_type_info() {
>  	local type_keyword=$1
>  	local type_name=$2
> -	if ! pahole -C $type_name $perf | grep -q "^$type_keyword $type_name {"; then
> +	if ! pahole --force_cu_merging -C $type_name $perf | grep -q "^$type_keyword $type_name {"; then
>  		info_log "skip: $perf doesn't have '$type_keyword $type_name' type info"
>  		test_skip
>  	fi
> @@ -41,7 +41,7 @@ $perf record --quiet -o $perf_data sleep 0.00001
>  
>  number_of_filtered_perf_record_metadata() {
>  	local metadata_record=$1
> -	local count=$(pahole -F dwarf -V $perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C "perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type,filter=type==PERF_RECORD_$metadata_record)" --prettify $perf_data | grep ".type = PERF_RECORD_$metadata_record," | wc -l)
> +	local count=$(pahole --force_cu_merging -F dwarf -V $perf --header=perf_file_header --seek_bytes '$header.data.offset' --size_bytes='$header.data.size' -C "perf_event_header(sizeof,type,type_enum=perf_event_type+perf_user_event_type,filter=type==PERF_RECORD_$metadata_record)" --prettify $perf_data | grep ".type = PERF_RECORD_$metadata_record," | wc -l)
>  	echo "$count"
>  }
>  


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/3] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration
  2026-03-23 21:15 ` [PATCH 2/3] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
@ 2026-03-30  9:05   ` Alan Maguire
  2026-03-30 22:39     ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 9+ messages in thread
From: Alan Maguire @ 2026-03-30  9:05 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Clark Williams, Kate Carcia, dwarves,
	Arnaldo Carvalho de Melo

On 23/03/2026 21:15, Arnaldo Carvalho de Melo wrote:
> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> In Rust enums can have subprograms, add initial support for it.
> 

I guess we'd need some form of enhanced enumerator kind to support this in BTF,
since I don't see any means to enhance the existing enumerator kind info to
support references to other type ids like functions.

> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

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

> ---
>  dwarf_loader.c | 38 ++++++++++++++++++++++++++++++--------
>  1 file changed, 30 insertions(+), 8 deletions(-)
> 
> diff --git a/dwarf_loader.c b/dwarf_loader.c
> index f0833e8c44a944a8..b5a92160ecf82f74 100644
> --- a/dwarf_loader.c
> +++ b/dwarf_loader.c
> @@ -1943,6 +1943,8 @@ out_delete:
>  	return NULL;
>  }
>  
> +static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu, struct conf_load *conf);
> +
>  static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
>  {
>  	Dwarf_Die child;
> @@ -1964,18 +1966,38 @@ static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, st
>  
>  	die = &child;
>  	do {
> -		struct enumerator *enumerator;
> +		switch (dwarf_tag(die)) {
> +		case DW_TAG_enumerator: {
> +			struct enumerator *enumerator = enumerator__new(die, cu, conf);
> +
> +			if (enumerator == NULL)
> +				goto out_delete;
> +
> +			enumeration__add(enumeration, enumerator);
> +			cu__hash(cu, &enumerator->tag);
> +		}
> +			continue;
> +		case DW_TAG_subprogram: {
> +			struct tag *tag = die__create_new_function(die, cu, conf);
> +			uint32_t id;
> +
> +			if (tag == NULL)
> +				goto out_delete;
>  
> -		if (dwarf_tag(die) != DW_TAG_enumerator) {
> +			if (cu__add_tag(cu, tag, &id) < 0) {
> +				tag__delete(tag, cu);
> +				goto out_delete;
> +			}
> +
> +			cu__hash(cu, tag);
> +			struct dwarf_tag *dtag = tag__dwarf(tag);
> +			dtag->small_id = id;
> +			break;
> +		}
> +		default:
>  			cu__tag_not_handled(cu, die);
>  			continue;
>  		}
> -		enumerator = enumerator__new(die, cu, conf);
> -		if (enumerator == NULL)
> -			goto out_delete;
> -
> -		enumeration__add(enumeration, enumerator);
> -		cu__hash(cu, &enumerator->tag);
>  	} while (dwarf_siblingof(die, die) == 0);
>  out:
>  	return &enumeration->namespace.tag;


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/3] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration
  2026-03-30  9:05   ` Alan Maguire
@ 2026-03-30 22:39     ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 9+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-03-30 22:39 UTC (permalink / raw)
  To: Alan Maguire
  Cc: Jiri Olsa, Clark Williams, Kate Carcia, dwarves,
	Arnaldo Carvalho de Melo, Miguel Ojeda

On Mon, Mar 30, 2026 at 10:05:26AM +0100, Alan Maguire wrote:
> On 23/03/2026 21:15, Arnaldo Carvalho de Melo wrote:
> > In Rust enums can have subprograms, add initial support for it.
 
> I guess we'd need some form of enhanced enumerator kind to support this in BTF,
> since I don't see any means to enhance the existing enumerator kind info to
> support references to other type ids like functions.

Right, the internal representation for the DWARF tags should cover all
languages aspects, be it Rust, C, C++, Go, etc.

But BTF is about being compact, so no forward thinking about supporting
other languages, Like Rust, that is for DWARF, that ends up being not
compact.

I think the best we can do at this point is to support as many aspects
of Rust as can be encoded in BTF and that may end up being enough for a
subset of Rust to be used to generate BTF bytecode CO-RE able.

I'll do some more work on checking how much of Rust we can encode in BTF
as it is possible right now.

Also I want to have a way to output rust code from DWARF/BTF/whatever,
as we have now for C and C++ in pahole :-)

- Arnaldo

> > Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> Reviewed-by: Alan Maguire <alan.maguire@oracle.com>
> 
> > ---
> >  dwarf_loader.c | 38 ++++++++++++++++++++++++++++++--------
> >  1 file changed, 30 insertions(+), 8 deletions(-)
> > 
> > diff --git a/dwarf_loader.c b/dwarf_loader.c
> > index f0833e8c44a944a8..b5a92160ecf82f74 100644
> > --- a/dwarf_loader.c
> > +++ b/dwarf_loader.c
> > @@ -1943,6 +1943,8 @@ out_delete:
> >  	return NULL;
> >  }
> >  
> > +static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu, struct conf_load *conf);
> > +
> >  static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
> >  {
> >  	Dwarf_Die child;
> > @@ -1964,18 +1966,38 @@ static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu, st
> >  
> >  	die = &child;
> >  	do {
> > -		struct enumerator *enumerator;
> > +		switch (dwarf_tag(die)) {
> > +		case DW_TAG_enumerator: {
> > +			struct enumerator *enumerator = enumerator__new(die, cu, conf);
> > +
> > +			if (enumerator == NULL)
> > +				goto out_delete;
> > +
> > +			enumeration__add(enumeration, enumerator);
> > +			cu__hash(cu, &enumerator->tag);
> > +		}
> > +			continue;
> > +		case DW_TAG_subprogram: {
> > +			struct tag *tag = die__create_new_function(die, cu, conf);
> > +			uint32_t id;
> > +
> > +			if (tag == NULL)
> > +				goto out_delete;
> >  
> > -		if (dwarf_tag(die) != DW_TAG_enumerator) {
> > +			if (cu__add_tag(cu, tag, &id) < 0) {
> > +				tag__delete(tag, cu);
> > +				goto out_delete;
> > +			}
> > +
> > +			cu__hash(cu, tag);
> > +			struct dwarf_tag *dtag = tag__dwarf(tag);
> > +			dtag->small_id = id;
> > +			break;
> > +		}
> > +		default:
> >  			cu__tag_not_handled(cu, die);
> >  			continue;
> >  		}
> > -		enumerator = enumerator__new(die, cu, conf);
> > -		if (enumerator == NULL)
> > -			goto out_delete;
> > -
> > -		enumeration__add(enumeration, enumerator);
> > -		cu__hash(cu, &enumerator->tag);
> >  	} while (dwarf_siblingof(die, die) == 0);
> >  out:
> >  	return &enumeration->namespace.tag;

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] dwarf_loader: Initial support for DW_TAG_variant_part
  2026-03-23 21:15 ` [PATCH 1/3] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
@ 2026-04-08 14:05   ` Alan Maguire
  2026-04-08 17:36     ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 9+ messages in thread
From: Alan Maguire @ 2026-04-08 14:05 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Clark Williams, Kate Carcia, dwarves,
	Arnaldo Carvalho de Melo

On 23/03/2026 21:15, Arnaldo Carvalho de Melo wrote:
> From: Arnaldo Carvalho de Melo <acme@redhat.com>
> 
> Still doesn't handle its sub hierarchy, i.e. the DW_TAG_variant entries
> and its underlying DW_TAG_member entries.
> 
> This was noticed when running the regression test that uses a debug
> build of perf to process a perf.data file and test pahole's pretty
> printing features, as now perf has a synthetic workload that is written
> in rust:
> 
>  <0><20c95a>: Abbrev Number: 1 (DW_TAG_compile_unit)
>     <20c95b>   DW_AT_producer    : (indirect string, offset: 0x4ee5a): clang LLVM (rustc version 1.93.1 (01f6ddf75 2026-02-11) (Fedora 1.93.1-1.fc43))
>     <20c95f>   DW_AT_language    : 28	(Rust)
>     <20c961>   DW_AT_name        : (indirect string, offset: 0x4eeaa): tests/workloads/code_with_type.rs/@/code_with_type.d6e680867bfb8b27-cgu.0
>     <20c965>   DW_AT_stmt_list   : 0x5e1ed
>     <20c969>   DW_AT_comp_dir    : (indirect string, offset: 0x487f1): /home/acme/git/perf-tools/tools/perf
>     <20c96d>   DW_AT_low_pc      : 0
>     <20c975>   DW_AT_ranges      : 0x2d0
> ⬢ [acme@toolbx pahole]$
> 
> So lets add some scaffolding for the Rust DWARF constructs involved for
> us to be able to continue using perf with DWARF to test the pretty
> printing features.
> 
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
> ---
>  dwarf_loader.c | 26 +++++++++++++++++++++++++-
>  dwarves.c      |  6 ++++++
>  dwarves.h      |  7 +++++++
>  3 files changed, 38 insertions(+), 1 deletion(-)
> 
> diff --git a/dwarf_loader.c b/dwarf_loader.c
> index 16fb7becffee56f6..f0833e8c44a944a8 100644
> --- a/dwarf_loader.c
> +++ b/dwarf_loader.c
> @@ -508,6 +508,8 @@ static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
>  
>  	if (tag->tag == DW_TAG_imported_module || tag->tag == DW_TAG_imported_declaration)
>  		dwarf_tag__set_attr_type(dtag, type, die, DW_AT_import);
> +	else if (tag->tag == DW_TAG_variant_part)
> +		dwarf_tag__set_attr_type(dtag, type, die, DW_AT_discr);
>  	else
>  		dwarf_tag__set_attr_type(dtag, type, die, DW_AT_type);
>  
> @@ -1170,6 +1172,18 @@ static struct template_parameter_pack *template_parameter_pack__new(Dwarf_Die *d
>  	return pack;
>  }
>  
> +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));
> +
> +	if (vpart != NULL) {
> +		tag__init(&vpart->tag, cu, die);
> +		INIT_LIST_HEAD(&vpart->variants);
> +	}
> +
> +	return vpart;
> +}
> +
>  /* Returns number of locations found or negative value for errors. */
>  static ptrdiff_t __dwarf_getlocations(Dwarf_Attribute *attr,
>  				      ptrdiff_t offset, Dwarf_Addr *basep,
> @@ -1990,9 +2004,19 @@ static int die__process_class(Dwarf_Die *die, struct type *class,
>  		case DW_TAG_GNU_template_template_param:
>  #endif
>  		case DW_TAG_subrange_type: // XXX: ADA stuff, its a type tho, will have other entries referencing it...
> -		case DW_TAG_variant_part: // XXX: Rust stuff
>  			tag__print_not_supported(die);
>  			continue;
> +		case DW_TAG_variant_part: {
> +			struct variant_part *vpart = variant_part__new(die, cu, conf);
> +
> +			if (vpart == NULL)
> +				return -ENOMEM;
> +
> +			// For rust it seems we have just one, but DWARF, according to Gemini, support having
> +			// more than one DW_TAG_variant_part for a given DW_TAG_structure_type, so future proof it
> +			type__add_variant_part(class, vpart);
> +			continue;
> +		}
>  		case DW_TAG_template_type_parameter: {
>  			struct template_type_param *ttparm = template_type_param__new(die, cu, conf);
>  
> diff --git a/dwarves.c b/dwarves.c
> index ef93239d26827711..efc7ae214376e1c1 100644
> --- a/dwarves.c
> +++ b/dwarves.c
> @@ -428,6 +428,7 @@ void __type__init(struct type *type)
>  	INIT_LIST_HEAD(&type->type_enum);
>  	INIT_LIST_HEAD(&type->template_type_params);
>  	INIT_LIST_HEAD(&type->template_value_params);
> +	INIT_LIST_HEAD(&type->variant_parts);
>  	type->template_parameter_pack = NULL;
>  	type->sizeof_member = NULL;
>  	type->member_prefix = NULL;
> @@ -1366,6 +1367,11 @@ void type__add_template_value_param(struct type *type, struct template_value_par
>  	list_add_tail(&tvparam->tag.node, &type->template_value_params);
>  }
>  
> +void type__add_variant_part(struct type *type, struct variant_part *vpart)
> +{
> +	list_add_tail(&vpart->tag.node, &type->variant_parts);
> +}
> +
>  struct class_member *type__last_member(struct type *type)
>  {
>  	struct class_member *pos;
> diff --git a/dwarves.h b/dwarves.h
> index d7c64742c29a9e9f..95d84b8ce3a6e95d 100644
> --- a/dwarves.h
> +++ b/dwarves.h
> @@ -1011,6 +1011,11 @@ 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_part {
> +	struct tag	 tag;
> +	struct list_head variants;
> +};
> +
>  /*
>   * tag.tag can be DW_TAG_subprogram_type or DW_TAG_subroutine_type.
>   */
> @@ -1281,6 +1286,7 @@ struct type {
>  	uint8_t		 is_signed_enum:1;
>  	struct list_head template_type_params;
>  	struct list_head template_value_params;
> +	struct list_head variant_parts;
>  	struct template_parameter_pack *template_parameter_pack;
>  };
>  
> @@ -1401,6 +1407,7 @@ static inline struct class_member *class_member__next(struct class_member *membe
>  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);
>  
>  struct class_member *
>  	type__find_first_biggest_size_base_type_member(struct type *type,


do we also need some cleanup for the variant parts in type__delete() something like:

	list_for_each_entry_safe(pos, n, type->variant_parts, node) {
		list_del_int(&pos->node);
		free(pos);
	}

?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] dwarf_loader: Initial support for DW_TAG_variant_part
  2026-04-08 14:05   ` Alan Maguire
@ 2026-04-08 17:36     ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 9+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-04-08 17:36 UTC (permalink / raw)
  To: Alan Maguire
  Cc: Jiri Olsa, Clark Williams, Kate Carcia, dwarves,
	Arnaldo Carvalho de Melo

On Wed, Apr 08, 2026 at 03:05:35PM +0100, Alan Maguire wrote:
> On 23/03/2026 21:15, Arnaldo Carvalho de Melo wrote:
> > From: Arnaldo Carvalho de Melo <acme@redhat.com>
> > 
> > Still doesn't handle its sub hierarchy, i.e. the DW_TAG_variant entries
> > and its underlying DW_TAG_member entries.
> > 
> > This was noticed when running the regression test that uses a debug
> > build of perf to process a perf.data file and test pahole's pretty
> > printing features, as now perf has a synthetic workload that is written
> > in rust:
> > 
> >  <0><20c95a>: Abbrev Number: 1 (DW_TAG_compile_unit)
> >     <20c95b>   DW_AT_producer    : (indirect string, offset: 0x4ee5a): clang LLVM (rustc version 1.93.1 (01f6ddf75 2026-02-11) (Fedora 1.93.1-1.fc43))
> >     <20c95f>   DW_AT_language    : 28	(Rust)
> >     <20c961>   DW_AT_name        : (indirect string, offset: 0x4eeaa): tests/workloads/code_with_type.rs/@/code_with_type.d6e680867bfb8b27-cgu.0
> >     <20c965>   DW_AT_stmt_list   : 0x5e1ed
> >     <20c969>   DW_AT_comp_dir    : (indirect string, offset: 0x487f1): /home/acme/git/perf-tools/tools/perf
> >     <20c96d>   DW_AT_low_pc      : 0
> >     <20c975>   DW_AT_ranges      : 0x2d0
> > ⬢ [acme@toolbx pahole]$
> > 
> > So lets add some scaffolding for the Rust DWARF constructs involved for
> > us to be able to continue using perf with DWARF to test the pretty
> > printing features.
> > 
> > Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
> > ---
> >  dwarf_loader.c | 26 +++++++++++++++++++++++++-
> >  dwarves.c      |  6 ++++++
> >  dwarves.h      |  7 +++++++
> >  3 files changed, 38 insertions(+), 1 deletion(-)
> > 
> > diff --git a/dwarf_loader.c b/dwarf_loader.c
> > index 16fb7becffee56f6..f0833e8c44a944a8 100644
> > --- a/dwarf_loader.c
> > +++ b/dwarf_loader.c
> > @@ -508,6 +508,8 @@ static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
> >  
> >  	if (tag->tag == DW_TAG_imported_module || tag->tag == DW_TAG_imported_declaration)
> >  		dwarf_tag__set_attr_type(dtag, type, die, DW_AT_import);
> > +	else if (tag->tag == DW_TAG_variant_part)
> > +		dwarf_tag__set_attr_type(dtag, type, die, DW_AT_discr);
> >  	else
> >  		dwarf_tag__set_attr_type(dtag, type, die, DW_AT_type);
> >  
> > @@ -1170,6 +1172,18 @@ static struct template_parameter_pack *template_parameter_pack__new(Dwarf_Die *d
> >  	return pack;
> >  }
> >  
> > +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));
> > +
> > +	if (vpart != NULL) {
> > +		tag__init(&vpart->tag, cu, die);
> > +		INIT_LIST_HEAD(&vpart->variants);
> > +	}
> > +
> > +	return vpart;
> > +}
> > +
> >  /* Returns number of locations found or negative value for errors. */
> >  static ptrdiff_t __dwarf_getlocations(Dwarf_Attribute *attr,
> >  				      ptrdiff_t offset, Dwarf_Addr *basep,
> > @@ -1990,9 +2004,19 @@ static int die__process_class(Dwarf_Die *die, struct type *class,
> >  		case DW_TAG_GNU_template_template_param:
> >  #endif
> >  		case DW_TAG_subrange_type: // XXX: ADA stuff, its a type tho, will have other entries referencing it...
> > -		case DW_TAG_variant_part: // XXX: Rust stuff
> >  			tag__print_not_supported(die);
> >  			continue;
> > +		case DW_TAG_variant_part: {
> > +			struct variant_part *vpart = variant_part__new(die, cu, conf);
> > +
> > +			if (vpart == NULL)
> > +				return -ENOMEM;
> > +
> > +			// For rust it seems we have just one, but DWARF, according to Gemini, support having
> > +			// more than one DW_TAG_variant_part for a given DW_TAG_structure_type, so future proof it
> > +			type__add_variant_part(class, vpart);
> > +			continue;
> > +		}
> >  		case DW_TAG_template_type_parameter: {
> >  			struct template_type_param *ttparm = template_type_param__new(die, cu, conf);
> >  
> > diff --git a/dwarves.c b/dwarves.c
> > index ef93239d26827711..efc7ae214376e1c1 100644
> > --- a/dwarves.c
> > +++ b/dwarves.c
> > @@ -428,6 +428,7 @@ void __type__init(struct type *type)
> >  	INIT_LIST_HEAD(&type->type_enum);
> >  	INIT_LIST_HEAD(&type->template_type_params);
> >  	INIT_LIST_HEAD(&type->template_value_params);
> > +	INIT_LIST_HEAD(&type->variant_parts);
> >  	type->template_parameter_pack = NULL;
> >  	type->sizeof_member = NULL;
> >  	type->member_prefix = NULL;
> > @@ -1366,6 +1367,11 @@ void type__add_template_value_param(struct type *type, struct template_value_par
> >  	list_add_tail(&tvparam->tag.node, &type->template_value_params);
> >  }
> >  
> > +void type__add_variant_part(struct type *type, struct variant_part *vpart)
> > +{
> > +	list_add_tail(&vpart->tag.node, &type->variant_parts);
> > +}
> > +
> >  struct class_member *type__last_member(struct type *type)
> >  {
> >  	struct class_member *pos;
> > diff --git a/dwarves.h b/dwarves.h
> > index d7c64742c29a9e9f..95d84b8ce3a6e95d 100644
> > --- a/dwarves.h
> > +++ b/dwarves.h
> > @@ -1011,6 +1011,11 @@ 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_part {
> > +	struct tag	 tag;
> > +	struct list_head variants;
> > +};
> > +
> >  /*
> >   * tag.tag can be DW_TAG_subprogram_type or DW_TAG_subroutine_type.
> >   */
> > @@ -1281,6 +1286,7 @@ struct type {
> >  	uint8_t		 is_signed_enum:1;
> >  	struct list_head template_type_params;
> >  	struct list_head template_value_params;
> > +	struct list_head variant_parts;
> >  	struct template_parameter_pack *template_parameter_pack;
> >  };
> >  
> > @@ -1401,6 +1407,7 @@ static inline struct class_member *class_member__next(struct class_member *membe
> >  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);
> >  
> >  struct class_member *
> >  	type__find_first_biggest_size_base_type_member(struct type *type,
 
> do we also need some cleanup for the variant parts in type__delete() something like:
> 
> 	list_for_each_entry_safe(pos, n, type->variant_parts, node) {
> 		list_del_int(&pos->node);
> 		free(pos);
> 	}

Right, I'll fix it in v2, there are some other issues that Claude
detected that I'll address in the enumeration case, one of them can be
seen here:

⬢ [acme@toolbx pahole]$ pahole -C ProgramKind /tmp/build/perf-tools-next/tests/workloads/code_with_type.a
enum ProgramKind {
	PathLookup = 0,
	Relative   = 1,
	Absolute   = 2,
	�3"��     = 140698500347136,
} __attribute__((__packed__));

⬢ [acme@toolbx pahole]$

Namely this Rust enum has a DW_TAG_subprogram, that
enumeration__fprintf() doesn't know about, fixing it now.

- Arnaldo

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-04-08 17:36 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-23 21:15 [PATCH 0/3] Initial support for some Rust tags + way to ask for CU merging at load time Arnaldo Carvalho de Melo
2026-03-23 21:15 ` [PATCH 1/3] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
2026-04-08 14:05   ` Alan Maguire
2026-04-08 17:36     ` Arnaldo Carvalho de Melo
2026-03-23 21:15 ` [PATCH 2/3] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
2026-03-30  9:05   ` Alan Maguire
2026-03-30 22:39     ` Arnaldo Carvalho de Melo
2026-03-23 21:15 ` [PATCH 3/3] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
2026-03-30  8:58   ` Alan Maguire

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox