* [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
@ 2026-08-21 21:34 Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 1/9] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
` (9 more replies)
0 siblings, 10 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:34 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Add support for cross-CU type references and dwz alternate debug
files so pahole can process binaries (e.g. a perf binary with rust
objects, or Firefox) that use DW_FORM_ref_addr inter-CU references or
dwz-compressed .dwz alternate debug files.
Cross-CU type references:
Force-merge CUs that contain inter-CU references (DW_FORM_ref_addr)
so type lookups resolve correctly. Handle same-file partial units
where types are shared across CUs via DW_TAG_imported_unit. Fix
cus__merging_cu failing to detect DW_FORM_ref_addr when
DW_FORM_implicit_const causes dwarf_getabbrevattr() to fail.
dwz alternate debug file support:
Handle DW_FORM_GNU_ref_alt references to dwz-compressed alternate
debug files (.dwz). Pre-processes all alternate partial units with
separate hash tables and dedup tracking.
Before (Firefox): 1774 "has no entry in cu" errors
After: processes cleanly with 0 errors
Also add an inter-CU type reference comparison test and
scripts/vmlinux_comparison.py for DWARF/BTF analysis across kernel
configs.
What changed from v1:
v1 (12 patches) also carried the Rust enum discriminant support
(DW_TAG_variant_part population, BTF union-member encoding and
DW_FORM_block discriminant handling, plus tests/block_endian.sh).
That work is being submitted as a separate series based on this one,
so v2 drops those 3 patches and the block_endian.sh test. The
remaining 9 patches are the cross-CU reference and dwz alternate
debug file support, now self-contained (tests/dwz_alt_file.sh and
tests/inter_cu_refs.sh are included).
Best regards,
- Arnaldo
Assisted-by: opencode:hy3-free
Arnaldo Carvalho de Melo (9):
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: Support DW_TAG_imported_unit for same-file partial units
dwarf_loader: Fix cus__merging_cu failing to detect DW_FORM_ref_addr
tests: Add inter-CU type reference comparison test
dwarf_loader: Add cu parameter to tag__set_spec() and
dwarf_tag__set_attr_type()
dwarf_loader: Support DW_FORM_GNU_ref_alt references to dwz alternate
debug files
scripts: Add vmlinux_comparison.py for DWARF/BTF analysis
btf_encoder.c | 22 +-
ctf_encoder.c | 26 +-
dwarf_loader.c | 829 ++++++++++++++++++++++++++++++----
dwarves.c | 44 +-
dwarves.h | 20 +-
dwarves_emit.c | 10 +-
dwarves_fprintf.c | 54 ++-
man-pages/pahole.1 | 19 +-
pahole.c | 28 +-
scripts/vmlinux_comparison.py | 612 +++++++++++++++++++++++++
tests/dwz_alt_file.sh | 204 +++++++++
tests/inter_cu_refs.sh | 50 ++
tests/prettify_perf.data.sh | 4 +-
13 files changed, 1801 insertions(+), 121 deletions(-)
create mode 100755 scripts/vmlinux_comparison.py
create mode 100755 tests/dwz_alt_file.sh
create mode 100755 tests/inter_cu_refs.sh
--
2.55.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v2 1/9] dwarf_loader: Initial support for DW_TAG_variant_part
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
@ 2026-08-21 21:35 ` Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 2/9] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
` (8 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, 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.
Assisted-by: opencode:deepseek-v4-flash
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
dwarf_loader.c | 25 ++++++++++++++++++++++++-
dwarves.c | 27 +++++++++++++++++++++++++++
dwarves.h | 16 ++++++++++++++++
3 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 61d55bd70d643731..ac576f0936ac86a9 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -522,6 +522,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);
@@ -1238,6 +1240,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,
@@ -2357,9 +2371,18 @@ 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;
+
+ /* DWARF permits more than one DW_TAG_variant_part for a structure. */
+ 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 1748889bc051fcb4..b48d9a3b9c43eebc 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -405,6 +405,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;
@@ -1210,12 +1211,31 @@ static void type__delete_class_members(struct type *type, struct cu *cu)
}
}
+static void variant_part__delete(struct variant_part *vpart, struct cu *cu)
+{
+ if (vpart == NULL)
+ return;
+
+ cu__tag_free(cu, &vpart->tag);
+}
+
+static void type__delete_variant_parts(struct type *type, struct cu *cu)
+{
+ struct variant_part *pos, *next;
+
+ type__for_each_variant_part_safe_reverse(type, pos, next) {
+ list_del_init(&pos->tag.node);
+ variant_part__delete(pos, cu);
+ }
+}
+
void class__delete(struct class *class, struct cu *cu)
{
if (class == NULL)
return;
type__delete_class_members(&class->type, cu);
+ type__delete_variant_parts(&class->type, cu);
cu__tag_free(cu, class__tag(class));
}
@@ -1225,6 +1245,7 @@ void type__delete(struct type *type, struct cu *cu)
return;
type__delete_class_members(type, cu);
+ type__delete_variant_parts(type, cu);
if (type->suffix_disambiguation)
zfree(&type->namespace.name);
@@ -1288,6 +1309,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;
@@ -1308,6 +1334,7 @@ static int type__clone_members(struct type *type, const struct type *from, struc
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__for_each_member(from, pos) {
struct class_member *clone = class_member__clone(pos, cu);
diff --git a/dwarves.h b/dwarves.h
index 99e9d183c853ea8b..0fc57ed0c7576c01 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -1021,6 +1021,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.
*/
@@ -1292,6 +1297,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;
};
@@ -1409,9 +1415,19 @@ static inline struct class_member *class_member__next(struct class_member *membe
#define type__for_each_tag_safe_reverse(type, pos, n) \
list_for_each_entry_safe_reverse(pos, n, &(type)->namespace.tags, tag.node)
+/**
+ * type__for_each_variant_part_safe_reverse - safely iterate thru all variant_parts in a type, in reverse order
+ * @type: struct type instance to iterate
+ * @pos: struct variant_part iterator
+ * @n: struct variant_part temp iterator
+ */
+#define type__for_each_variant_part_safe_reverse(type, pos, n) \
+ list_for_each_entry_safe_reverse(pos, n, &(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);
struct class_member *
type__find_first_biggest_size_base_type_member(struct type *type,
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2 2/9] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 1/9] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
@ 2026-08-21 21:35 ` Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 3/9] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
` (7 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
From: Arnaldo Carvalho de Melo <acme@redhat.com>
In Rust enums can have subprograms, add initial support for it.
Example of a Rust enumeration with a DW_TAG_subprogram tag.
$ pahole -C ProgramKind /tmp/build/perf-tools-next/tests/workloads/code_with_type.a
enum ProgramKind {
PathLookup = 0,
Relative = 1,
Absolute = 2,
enum ProgramKind new(struct &std::ffi::os_str::OsStr),
} __attribute__((__packed__));
$
The subprograms are added to the enumeration namespace but are not
counted in type->nr_members, which the CTF encoder uses as the enum
vlen and the fprintf/emit paths use to detect forward declarations.
Assisted-by: opencode:deepseek-v4-flash
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
btf_encoder.c | 19 ++++++++++++++---
ctf_encoder.c | 15 +++++++++++--
dwarf_loader.c | 42 +++++++++++++++++++++++++++++-------
dwarves.c | 17 +++++++++------
dwarves.h | 2 +-
dwarves_emit.c | 10 ++++-----
dwarves_fprintf.c | 54 +++++++++++++++++++++++++++++++++++------------
pahole.c | 9 +++++---
8 files changed, 126 insertions(+), 42 deletions(-)
diff --git a/btf_encoder.c b/btf_encoder.c
index 4b422e09800f3fbb..8779db65f428b2ac 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -1900,9 +1900,22 @@ static int32_t btf_encoder__add_enum_type(struct btf_encoder *encoder, struct ta
return type_id;
type__for_each_enumerator(etype, pos) {
- name = enumerator__name(pos);
- if (btf_encoder__add_enum_val(encoder, name, pos->value, etype, conf_load))
- return -1;
+ switch (pos->tag.tag) {
+ case DW_TAG_enumerator:
+ name = enumerator__name(pos);
+ if (btf_encoder__add_enum_val(encoder, name, pos->value, etype, conf_load))
+ return -1;
+ break;
+ case DW_TAG_subprogram:
+ if (encoder->verbose)
+ fprintf(stderr, "BTF: DW_TAG_subprogram in enumeration '%s' not supported, skipping\n",
+ type__name(etype) ?: "(anonymous)");
+ break;
+ default:
+ fprintf(stderr, "BTF: unexpected DW_TAG_%s in enumeration '%s', skipping\n",
+ dwarf_tag_name(pos->tag.tag), type__name(etype) ?: "(anonymous)");
+ break;
+ }
}
return type_id;
diff --git a/ctf_encoder.c b/ctf_encoder.c
index b761287d45348c59..f00e4607cbae7875 100644
--- a/ctf_encoder.c
+++ b/ctf_encoder.c
@@ -154,8 +154,19 @@ static int enumeration_type__encode(struct tag *tag, uint32_t core_id, struct ct
return -1;
struct enumerator *pos;
- type__for_each_enumerator(etype, pos)
- ctf__add_enumerator(ctf, pos->name, pos->value, &position);
+ type__for_each_enumerator(etype, pos) {
+ switch (pos->tag.tag) {
+ case DW_TAG_enumerator:
+ ctf__add_enumerator(ctf, pos->name, pos->value, &position);
+ break;
+ case DW_TAG_subprogram:
+ break;
+ default:
+ fprintf(stderr, "CTF: unexpected DW_TAG_%s in enumeration '%s', skipping\n",
+ dwarf_tag_name(pos->tag.tag), type__name(etype) ?: "(anonymous)");
+ break;
+ }
+ }
return 0;
}
diff --git a/dwarf_loader.c b/dwarf_loader.c
index ac576f0936ac86a9..aa584a0939ff842c 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -2310,6 +2310,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;
@@ -2331,18 +2333,42 @@ 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;
- if (dwarf_tag(die) != DW_TAG_enumerator) {
+ 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 (cu__table_add_tag(cu, tag, &id) < 0) {
+ tag__delete(tag, cu);
+ goto out_delete;
+ }
+
+ struct dwarf_tag *dtag = tag__dwarf(tag);
+ dtag->small_id = id;
+ /* Not counted in enumeration->type.nr_members: it is the
+ * enumerator count (CTF enum vlen, forward-decl heuristic).
+ */
+ namespace__add_tag(&enumeration->namespace, tag);
+ cu__hash(cu, tag);
+ 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;
diff --git a/dwarves.c b/dwarves.c
index b48d9a3b9c43eebc..1d03554d6a26f264 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -1256,11 +1256,6 @@ void type__delete(struct type *type, struct cu *cu)
cu__tag_free(cu, type__tag(type));
}
-static void enumerator__delete(struct enumerator *enumerator, struct cu *cu)
-{
- cu__tag_free(cu, &enumerator->tag);
-}
-
void enumeration__delete(struct type *type, struct cu *cu)
{
struct enumerator *pos, *n;
@@ -1270,7 +1265,7 @@ void enumeration__delete(struct type *type, struct cu *cu)
type__for_each_enumerator_safe_reverse(type, pos, n) {
list_del_init(&pos->tag.node);
- enumerator__delete(pos, cu);
+ tag__delete(&pos->tag, cu);
}
if (type->suffix_disambiguation)
@@ -1373,6 +1368,13 @@ struct class *class__clone(const struct class *from, const char *new_class_name,
void enumeration__add(struct type *type, struct enumerator *enumerator)
{
+ /*
+ * nr_members is the enumerator count, not the number of entries in the
+ * enumeration namespace: DW_TAG_subprogram members of Rust enumerations
+ * are added to the namespace but must not be counted here, since the
+ * CTF encoder uses nr_members as the enum vlen and the fprintf/emit
+ * paths rely on nr_members == 0 to detect forward declarations.
+ */
++type->nr_members;
namespace__add_tag(&type->namespace, &enumerator->tag);
}
@@ -1928,6 +1930,9 @@ static void enumeration__calc_prefix(struct type *enumeration)
struct enumerator *entry;
type__for_each_enumerator(enumeration, entry) {
+ if (entry->tag.tag != DW_TAG_enumerator)
+ continue;
+
const char *curr_name = enumerator__name(entry);
if (previous_name) {
diff --git a/dwarves.h b/dwarves.h
index 0fc57ed0c7576c01..f5368f68c2460488 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -1646,7 +1646,7 @@ static inline const char *enumerator__name(const struct enumerator *enumerator)
void enumeration__delete(struct type *type, struct cu *cu);
void enumeration__add(struct type *type, struct enumerator *enumerator);
-size_t enumeration__fprintf(const struct tag *tag_enum,
+size_t enumeration__fprintf(const struct tag *tag_enum, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp);
int dwarves__init(void);
diff --git a/dwarves_emit.c b/dwarves_emit.c
index 01b33b7ec41eb947..aaf0f8f9a7ea815a 100644
--- a/dwarves_emit.c
+++ b/dwarves_emit.c
@@ -100,7 +100,7 @@ static struct type *type_emissions__find_fwd_decl(const struct type_emissions *e
return NULL;
}
-static int enumeration__emit_definitions(struct tag *tag,
+static int enumeration__emit_definitions(struct tag *tag, const struct cu *cu,
struct type_emissions *emissions,
const struct conf_fprintf *conf,
FILE *fp)
@@ -121,7 +121,7 @@ static int enumeration__emit_definitions(struct tag *tag,
return 0;
}
- enumeration__fprintf(tag, conf, fp);
+ enumeration__fprintf(tag, cu, conf, fp);
fputs(";\n", fp);
// See comment on enumeration__fprintf(), it seems this happens with DWARF as well
@@ -198,10 +198,10 @@ static int typedef__emit_definitions(struct tag *tdef, struct cu *cu,
if (type__name(ctype) == NULL) {
fputs("typedef ", fp);
conf.suffix = type__name(def);
- enumeration__emit_definitions(type, emissions, &conf, fp);
+ enumeration__emit_definitions(type, cu, emissions, &conf, fp);
goto out;
} else
- enumeration__emit_definitions(type, emissions, &conf, fp);
+ enumeration__emit_definitions(type, cu, emissions, &conf, fp);
}
break;
case DW_TAG_structure_type:
@@ -380,7 +380,7 @@ next_indirection:
struct conf_fprintf conf = {
.suffix = NULL,
};
- return enumeration__emit_definitions(type, emissions, &conf, fp);
+ return enumeration__emit_definitions(type, cu, emissions, &conf, fp);
}
break;
case DW_TAG_structure_type:
diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c
index d7edb0cb14a5b803..75615072d03d32a0 100644
--- a/dwarves_fprintf.c
+++ b/dwarves_fprintf.c
@@ -155,6 +155,8 @@ const char tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
static size_t union__fprintf(struct type *type, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp);
+static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp);
/*
* In dwarves_emit.c we can call type__emit() using a locally setup conf_fprintf for which
@@ -406,7 +408,7 @@ next_type:
struct conf_fprintf tconf = *pconf;
tconf.suffix = type__name(type);
- return printed + enumeration__fprintf(tag_type, &tconf, fp);
+ return printed + enumeration__fprintf(tag_type, cu, &tconf, fp);
}
}
@@ -450,7 +452,15 @@ static int enumeration__max_entry_name_len(struct type *type)
struct enumerator *pos;
type__for_each_enumerator(type, pos) {
- int len = strlen(enumerator__name(pos));
+ int len = 0;
+
+ if (pos->tag.tag == DW_TAG_enumerator)
+ len = strlen(enumerator__name(pos));
+ else if (pos->tag.tag == DW_TAG_subprogram) {
+ const char *fname = function__name(tag__function(&pos->tag));
+ if (fname)
+ len = strlen(fname);
+ }
if (type->max_tag_name_len < len)
type->max_tag_name_len = len;
@@ -459,7 +469,8 @@ out:
return type->max_tag_name_len;
}
-size_t enumeration__fprintf(const struct tag *tag, const struct conf_fprintf *conf, FILE *fp)
+size_t enumeration__fprintf(const struct tag *tag, const struct cu *cu,
+ const struct conf_fprintf *conf, FILE *fp)
{
struct type *type = tag__type(tag);
struct enumerator *pos;
@@ -480,13 +491,25 @@ size_t enumeration__fprintf(const struct tag *tag, const struct conf_fprintf *co
}
type__for_each_enumerator(type, pos) {
- printed += fprintf(fp, "%.*s\t%-*s = ", indent, tabs,
- max_entry_name_len, enumerator__name(pos));
- if (conf->hex_fmt)
- printed += fprintf(fp, "%#llx", (unsigned long long)pos->value);
- else
- printed += fprintf(fp, type->is_signed_enum ? "%lld" : "%llu",
- (unsigned long long)pos->value);
+ printed += fprintf(fp, "%.*s\t", indent, tabs);
+
+ switch (pos->tag.tag) {
+ case DW_TAG_subprogram:
+ printed += function__fprintf(&pos->tag, cu, conf, fp);
+ break;
+ case DW_TAG_enumerator:
+ printed += fprintf(fp, "%-*s = ", max_entry_name_len, enumerator__name(pos));
+ if (conf->hex_fmt)
+ printed += fprintf(fp, "%#llx", (unsigned long long)pos->value);
+ else
+ printed += fprintf(fp, type->is_signed_enum ? "%lld" : "%llu",
+ (unsigned long long)pos->value);
+ break;
+ default:
+ printed += fprintf(fp, "/* Unexpected %s <%llx> */\n", dwarf_tag_name(pos->tag.tag),
+ tag__orig_id(&pos->tag, cu));
+ continue;
+ }
printed += fprintf(fp, ",\n");
}
@@ -586,9 +609,12 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu,
strncpy(bf, name, len);
}
break;
- case DW_TAG_subprogram:
- strncpy(bf, function__name(tag__function(tag)), len);
+ case DW_TAG_subprogram: {
+ const char *fname = function__name(tag__function(tag));
+ if (fname)
+ strncpy(bf, fname, len);
break;
+ }
case DW_TAG_pointer_type:
return tag__ptr_name(tag, cu, bf, len, "*", conf);
case DW_TAG_reference_type:
@@ -937,7 +963,7 @@ print_modifier: {
if (type__name(ctype) != NULL && !expand_types)
printed += fprintf(fp, "enum %-*s %s", tconf.type_spacing - 5, type__name(ctype), name ?: "");
else
- printed += enumeration__fprintf(type, &tconf, fp);
+ printed += enumeration__fprintf(type, cu, &tconf, fp);
break;
case DW_TAG_LLVM_annotation:
case DW_TAG_GNU_annotation: {
@@ -2173,7 +2199,7 @@ size_t tag__fprintf(struct tag *tag, const struct cu *cu,
printed += array_type__fprintf(tag, cu, "array", pconf, fp);
break;
case DW_TAG_enumeration_type:
- printed += enumeration__fprintf(tag, pconf, fp);
+ printed += enumeration__fprintf(tag, cu, pconf, fp);
break;
case DW_TAG_typedef:
printed += typedef__fprintf(tag, cu, pconf, fp);
diff --git a/pahole.c b/pahole.c
index 5cf92833e6e84df9..0a7b810568f0dfeb 100644
--- a/pahole.c
+++ b/pahole.c
@@ -2162,7 +2162,7 @@ static const char *enumeration__lookup_value(struct type *enumeration, uint64_t
struct enumerator *entry;
type__for_each_enumerator(enumeration, entry) {
- if (entry->value == value)
+ if (entry->tag.tag == DW_TAG_enumerator && entry->value == value)
return enumerator__name(entry);
}
@@ -2187,7 +2187,7 @@ static struct enumerator *enumeration__lookup_entry_from_value(struct type *enum
struct enumerator *entry;
type__for_each_enumerator(enumeration, entry) {
- if (entry->value == value)
+ if (entry->tag.tag == DW_TAG_enumerator && entry->value == value)
return entry;
}
@@ -2213,6 +2213,9 @@ static struct enumerator *enumeration__find_enumerator(struct type *enumeration,
struct enumerator *entry;
type__for_each_enumerator(enumeration, entry) {
+ if (entry->tag.tag != DW_TAG_enumerator)
+ continue;
+
const char *entry_name = enumerator__name(entry);
if (!strcmp(entry_name, name))
@@ -3268,7 +3271,7 @@ static bool print_enumeration_with_enumerator(struct cu *cu, const char *name)
cu__for_each_enumeration(cu, id, enumeration) {
if (enumeration__find_enumerator(enumeration, name) != NULL) {
- enumeration__fprintf(type__tag(enumeration), &conf, stdout);
+ enumeration__fprintf(type__tag(enumeration), cu, &conf, stdout);
fputc('\n', stdout);
return true;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2 3/9] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 1/9] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 2/9] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
@ 2026-08-21 21:35 ` Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 4/9] dwarf_loader: Support DW_TAG_imported_unit for same-file partial units Arnaldo Carvalho de Melo
` (6 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, 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 where 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 --features=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]$
This was implemented reusing the --btf_features mechanism that now can
be accessed as well via --features, as this is not strictly a BTF
feature but, as Alan Maguire suggested, it is desirable to ask for that
feature to be enabled when we know it is needed but can't guarantee that
the available pahole version has the feature and not have it fail
because it doesn't implement --force_cu_merging, which the
--btf_features=force_cu_merging, now also available as
--features=force_cu_merging, allows as it ignores unknown features.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
dwarf_loader.c | 2 +-
dwarves.h | 1 +
man-pages/pahole.1 | 18 ++++++++++++++++--
pahole.c | 19 ++++++++++++++++++-
tests/prettify_perf.data.sh | 4 ++--
5 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/dwarf_loader.c b/dwarf_loader.c
index aa584a0939ff842c..a4c54d569e0788cd 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -4586,7 +4586,7 @@ static int cus__load_module(struct cus *cus, struct conf_load *conf,
cus__remove(cus, type_cu);
}
- 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 f5368f68c2460488..79b10725fec08a44 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -112,6 +112,7 @@ struct conf_load {
const char *kabi_prefix;
struct btf *base_btf;
struct conf_fprintf *conf_fprintf;
+ bool force_cu_merging;
};
/** struct conf_fprintf - hints to the __fprintf routines
diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
index 21378a224847eb45..e3ce737460efec72 100644
--- a/man-pages/pahole.1
+++ b/man-pages/pahole.1
@@ -314,8 +314,10 @@ Generate BTF for functions with optimization-related suffixes (.isra, .constprop
Allow using all the BTF features supported by pahole.
.TP
-.B \-\-btf_features=FEATURE_LIST
-Encode BTF using the specified feature list, or specify 'default' for all
+.B \-\-features=FEATURE_LIST
+This is also available as \-\-btf_features=FEATURE_LIST, unknown features,
+like those added in newer versions of pahole, are ignored. BTF encoding
+configuration is the major user. Using 'default' will enable all
standard features supported.
This option can be used as an alternative to using multiple BTF-related options,
and 'default' represents the standard set of BTF features that are in use for
@@ -364,6 +366,18 @@ Supported non-standard features (not enabled for 'default')
layout Encode information about BTF kinds available at encoding
time in layout section in BTF.
+Non-standard, non-BTF related features:
+
+ 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 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.
+
.fi
So for example, specifying \-\-btf_encode=var,enum64 will result in a BTF
diff --git a/pahole.c b/pahole.c
index 0a7b810568f0dfeb..372b9c3bd78ae227 100644
--- a/pahole.c
+++ b/pahole.c
@@ -1154,6 +1154,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_features 351
/* --btf_features=feature1[,feature2,..] allows us to specify
* a list of requested BTF features or "default" to enable all default
@@ -1241,7 +1242,8 @@ struct btf_feature {
BTF_NON_DEFAULT_FEATURE_CHECK(attributes, btf_attributes, false,
attributes_check),
BTF_NON_DEFAULT_FEATURE(true_signature, true_signature, false),
- BTF_NON_DEFAULT_FEATURE_CHECK(layout, btf_gen_layout, false, layout_check)
+ BTF_NON_DEFAULT_FEATURE_CHECK(layout, btf_gen_layout, false, layout_check),
+ BTF_NON_DEFAULT_FEATURE(force_cu_merging, force_cu_merging, false),
};
#define BTF_MAX_FEATURE_STR 1024
@@ -1312,6 +1314,14 @@ static void btf_features__enable_all(void)
* want distilled_base must request it explicitly. */
if (btf_features[i].conf_value == &conf_load.btf_gen_distilled_base)
continue;
+ /* force_cu_merging forces the serialized, single-threaded
+ * merged-CU loading path. Enabling it unconditionally
+ * with --btf_features=all or --btf_gen_all changes output
+ * and slows loading for files that don't need CU merging.
+ * Users who want force_cu_merging must request it
+ * explicitly. */
+ if (btf_features[i].conf_value == &conf_load.force_cu_merging)
+ continue;
enable_btf_feature(&btf_features[i]);
}
}
@@ -1816,6 +1826,12 @@ static const struct argp_option pahole__options[] = {
.arg = "FEATURE_LIST",
.doc = "Specify supported BTF features in FEATURE_LIST or 'default' for default set of supported features. See the pahole manual page for the list of supported, default features."
},
+ {
+ .name = "features",
+ .key = ARGP_features,
+ .arg = "FEATURE_LIST",
+ .doc = "Specify supported features in FEATURE_LIST or 'default' for default set of supported features. See the pahole manual page for the list of supported, default features."
+ },
{
.name = "supported_btf_features",
.key = ARGP_supported_btf_features,
@@ -2028,6 +2044,7 @@ static error_t pahole__options_parser(int key, char *arg,
conf_load.reproducible_build = true; break;
case ARGP_running_kernel_vmlinux:
show_running_kernel_vmlinux = true; break;
+ case ARGP_features:
case ARGP_btf_features:
parse_btf_features(arg, false); break;
case ARGP_supported_btf_features:
diff --git a/tests/prettify_perf.data.sh b/tests/prettify_perf.data.sh
index 1fae95154d710aae..384c250ff4e01a4c 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 --features=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 --features=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.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2 4/9] dwarf_loader: Support DW_TAG_imported_unit for same-file partial units
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
` (2 preceding siblings ...)
2026-08-21 21:35 ` [PATCH v2 3/9] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
@ 2026-08-21 21:35 ` Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 5/9] dwarf_loader: Fix cus__merging_cu failing to detect DW_FORM_ref_addr Arnaldo Carvalho de Melo
` (5 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Binaries processed by the dwz(1) tool have their DWARF type information
deduplicated into DW_TAG_partial_unit entries that are then referenced
via DW_TAG_imported_unit from each DW_TAG_compile_unit that uses those
types. This is the standard DWARF mechanism for cross-CU type sharing.
On Fedora/RHEL, most debuginfo packages are built with dwz, making this
a common pattern. For instance, bash-debuginfo has 10,486
DW_TAG_partial_unit, 384 DW_TAG_compile_unit, and 8,572
DW_TAG_imported_unit entries — all using same-file references (no .dwz
alternate DWARF file involved).
Before this patch, pahole skipped DW_TAG_partial_unit with a warning:
WARNING: DW_TAG_partial_unit used, some types will not be considered!
Probably this was optimized using a tool like 'dwz'
A future version of pahole will support this.
And DW_TAG_imported_unit was silently ignored (returned NULL), causing
pahole to report "file has no dwarf type information" for binaries like
bash and glibc.
The fix adds die__process_imported_unit(), called from die__process_unit()
when encountering DW_TAG_imported_unit. It follows DW_AT_import to the
referenced DW_TAG_partial_unit DIE and processes its children inline into
the importing compile unit's type tables. This works because
dwarf_formref_die() already handles all DWARF reference forms, and each
CU maintains its own independent hash tables — so the same partial unit
can be safely imported by multiple CUs, each getting its own copy of the
types.
Since imported units can themselves contain DW_TAG_imported_unit entries
(nested imports), a depth limit of 64 is enforced to prevent stack
overflow from pathological or corrupted DWARF. A warning is emitted if
the limit is reached.
Some binaries (e.g. chromium-browser on Fedora 44, built with Rust
components) also have DW_TAG_imported_unit entries that reference partial
units in an alternate debug file via DW_FORM_GNU_ref_alt (the
.gnu_debugaltlink mechanism). When elfutils resolves such a reference, it
returns DIEs from the alternate file whose offsets are in a different
address space — processing these into the main CU's hash tables corrupts
type references and causes a crash during type recoding.
The same DW_FORM_GNU_ref_alt form can also appear on regular type
attributes (DW_AT_type, DW_AT_abstract_origin, DW_AT_specification,
etc.), not just on DW_TAG_imported_unit's DW_AT_import. Guard all paths
via attr_form_is_ref_alt(), which skips the reference and warns once, so
users know why some types are missing rather than getting a crash.
The korg/alt_dwarf branch had a previous attempt at this that also
handled the .dwz alternate DWARF file case (DW_FORM_GNU_ref_alt), but it
was never merged and is now 294 commits behind master. This patch takes a
simpler approach focused on the same-file case first, which covers dwz
output on Fedora/RHEL where all partial units are within the same .debug
file.
Before (bash-5.3.9-3.fc44.x86_64 debuginfo):
$ pahole -F dwarf /usr/lib/debug/usr/bin/bash-5.3.9-3.fc44.x86_64.debug
WARNING: DW_TAG_partial_unit used, some types will not be considered!
pahole: /usr/lib/debug/usr/bin/bash-5.3.9-3.fc44.x86_64.debug: file has no dwarf type information
After:
$ pahole -F dwarf /usr/lib/debug/usr/bin/bash-5.3.9-3.fc44.x86_64.debug | wc -l
1605
$ pahole -F dwarf -C variable /usr/lib/debug/usr/bin/bash-5.3.9-3.fc44.x86_64.debug
struct variable {
char * name; /* 0 8 */
char * value; /* 8 8 */
...
/* size: 48, cachelines: 1, members: 7 */
};
Before (chromium-browser debuginfo, Fedora 44):
$ pahole /usr/lib/debug/.../chromium-browser-149.0.7827.155-1.fc44.x86_64.debug
Segmentation fault
After:
$ pahole /usr/lib/debug/.../chromium-browser-149.0.7827.155-1.fc44.x86_64.debug
WARNING: DW_FORM_GNU_ref_alt (dwz alternate debug file) not yet supported,
some types will not be available.
Reported-by: Sashiko:gemini-3-1-pro-preview # Running on a local machine
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
dwarf_loader.c | 155 ++++++++++++++++++++++++++++++++++++++-------
man-pages/pahole.1 | 7 +-
2 files changed, 136 insertions(+), 26 deletions(-)
diff --git a/dwarf_loader.c b/dwarf_loader.c
index a4c54d569e0788cd..abe43ee0f6f816f1 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -153,6 +153,9 @@ struct dwarf_cu {
struct dwarf_tag *last_type_lookup;
struct cu *cu;
struct dwarf_cu *type_unit;
+ Dwarf_Off *imported_units;
+ uint32_t nr_imported_units;
+ uint32_t allocated_imported_units;
};
static int dwarf_cu__init(struct dwarf_cu *dcu, struct cu *cu)
@@ -178,6 +181,9 @@ static int dwarf_cu__init(struct dwarf_cu *dcu, struct cu *cu)
INIT_HLIST_HEAD(&dcu->hash_types[i]);
}
dcu->type_unit = NULL;
+ dcu->imported_units = NULL;
+ dcu->nr_imported_units = 0;
+ dcu->allocated_imported_units = 0;
// To avoid a per-lookup check against NULL in dwarf_cu__find_type_by_ref()
dcu->last_type_lookup = &sentinel_dtag;
return 0;
@@ -202,6 +208,7 @@ static void dwarf_cu__delete(struct cu *cu)
struct dwarf_cu *dcu = cu->priv;
+ free(dcu->imported_units);
// dcu->hash_tags & dcu->hash_types are on cu->obstack
cu__free(cu, dcu);
cu->priv = NULL;
@@ -450,12 +457,32 @@ static const char *attr_string(Dwarf_Die *die, uint32_t name, struct conf_load *
return str;
}
+static bool attr_form_is_ref_alt(Dwarf_Attribute *attr)
+{
+ if (attr->form == DW_FORM_GNU_ref_alt) {
+ static bool warned;
+
+ if (!warned) {
+ fprintf(stderr,
+ "WARNING: DW_FORM_GNU_ref_alt (dwz alternate debug file) not yet supported,\n"
+ " some types will not be available.\n");
+ warned = true;
+ }
+ return true;
+ }
+ return false;
+}
+
static bool attr_type(Dwarf_Die *die, uint32_t attr_name, Dwarf_Off *offset)
{
Dwarf_Attribute attr;
if (dwarf_attr(die, attr_name, &attr) != NULL) {
Dwarf_Die type_die;
+ if (attr_form_is_ref_alt(&attr)) {
+ *offset = 0;
+ return 0;
+ }
if (dwarf_formref_die(&attr, &type_die) != NULL) {
*offset = dwarf_dieoffset(&type_die);
return attr.form == DW_FORM_ref_sig8;
@@ -686,7 +713,8 @@ static void type__init(struct type *type, Dwarf_Die *die, struct cu *cu, struct
Dwarf_Attribute attr;
if (dwarf_attr(die, DW_AT_type, &attr) != NULL) {
Dwarf_Die type_die;
- if (dwarf_formref_die(&attr, &type_die) != NULL) {
+ if (!attr_form_is_ref_alt(&attr) &&
+ dwarf_formref_die(&attr, &type_die) != NULL) {
uint64_t encoding = attr_numeric(&type_die, DW_AT_encoding);
if (encoding == DW_ATE_signed || encoding == DW_ATE_signed_char)
@@ -1000,9 +1028,14 @@ static int add_gnu_annotation_chain(Dwarf_Die *die, int component_idx,
Dwarf_Attribute attr;
Dwarf_Die annot_die;
- while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL &&
- dwarf_formref_die(&attr, &annot_die) != NULL &&
- dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) {
+ while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL) {
+ if (attr_form_is_ref_alt(&attr))
+ break;
+ if (dwarf_formref_die(&attr, &annot_die) == NULL)
+ break;
+ if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
+ break;
+
int ret = add_tag_annotation(&annot_die, component_idx, conf, head);
if (ret)
return ret;
@@ -2002,9 +2035,13 @@ check_gnu_attr:
goto out;
/* Handle GCC-style DW_AT_GNU_annotation attribute */
- while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL &&
- dwarf_formref_die(&attr, &annot_die) != NULL &&
- dwarf_tag(&annot_die) == DW_TAG_GNU_annotation) {
+ while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL) {
+ if (attr_form_is_ref_alt(&attr))
+ break;
+ if (dwarf_formref_die(&attr, &annot_die) == NULL)
+ break;
+ if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
+ break;
name = attr_string(&annot_die, DW_AT_name, conf);
if (strcmp(name, "btf_type_tag") != 0)
break;
@@ -2831,7 +2868,7 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
switch (dwarf_tag(die)) {
case DW_TAG_imported_unit:
- return NULL; // We don't support imported units yet, so to avoid segfaults
+ return &unsupported_tag; // Handled in die__process_unit()
case DW_TAG_array_type:
tag = die__create_new_array(die, cu); break;
case DW_TAG_string_type: // FORTRAN stuff, looks like an array
@@ -2899,9 +2936,92 @@ static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu,
return tag;
}
-static int die__process_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
+#define MAX_IMPORTED_UNIT_DEPTH 64
+
+static int die__process_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *conf, int import_depth);
+
+static bool dwarf_cu__imported_unit_visited(struct dwarf_cu *dcu, Dwarf_Off offset)
+{
+ for (uint32_t i = 0; i < dcu->nr_imported_units; i++)
+ if (dcu->imported_units[i] == offset)
+ return true;
+ return false;
+}
+
+static int dwarf_cu__mark_imported_unit(struct dwarf_cu *dcu, Dwarf_Off offset)
+{
+ if (dcu->nr_imported_units == dcu->allocated_imported_units) {
+ uint32_t new_size = dcu->allocated_imported_units ? dcu->allocated_imported_units * 2 : 16;
+ if (new_size <= dcu->allocated_imported_units)
+ return -ENOMEM;
+ Dwarf_Off *new_array = realloc(dcu->imported_units, new_size * sizeof(Dwarf_Off));
+ if (new_array == NULL)
+ return -ENOMEM;
+ dcu->imported_units = new_array;
+ dcu->allocated_imported_units = new_size;
+ }
+ dcu->imported_units[dcu->nr_imported_units++] = offset;
+ return 0;
+}
+
+static int die__process_imported_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *conf, int import_depth)
+{
+ Dwarf_Attribute attr;
+
+ if (dwarf_attr(die, DW_AT_import, &attr) == NULL)
+ return 0;
+
+ if (attr_form_is_ref_alt(&attr))
+ return 0;
+
+ Dwarf_Die imported_die;
+
+ if (dwarf_formref_die(&attr, &imported_die) == NULL)
+ return 0;
+
+ if (dwarf_tag(&imported_die) != DW_TAG_partial_unit)
+ return 0;
+
+ if (import_depth >= MAX_IMPORTED_UNIT_DEPTH) {
+ static bool warned;
+
+ if (!warned) {
+ fprintf(stderr,
+ "WARNING: DW_TAG_imported_unit nesting too deep (>%d), "
+ "some types will not be available.\n",
+ MAX_IMPORTED_UNIT_DEPTH);
+ warned = true;
+ }
+ return 0;
+ }
+
+ Dwarf_Off offset = dwarf_dieoffset(&imported_die);
+ struct dwarf_cu *dcu = cu->priv;
+
+ if (dwarf_cu__imported_unit_visited(dcu, offset))
+ return 0;
+
+ if (dwarf_cu__mark_imported_unit(dcu, offset))
+ return -ENOMEM;
+
+ Dwarf_Die child;
+
+ if (dwarf_child(&imported_die, &child) == 0)
+ return die__process_unit(&child, cu, conf, import_depth + 1);
+
+ return 0;
+}
+
+static int die__process_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *conf, int import_depth)
{
do {
+ if (dwarf_tag(die) == DW_TAG_imported_unit) {
+ int err = die__process_imported_unit(die, cu, conf, import_depth);
+ if (err)
+ return err;
+ continue;
+ }
+
struct tag *tag = die__process_tag(die, cu, 1, conf);
if (tag == NULL)
return -ENOMEM;
@@ -3715,17 +3835,8 @@ static int die__process(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
return 0; // so that other units can be processed
}
- if (tag == DW_TAG_partial_unit) {
- static bool warned;
-
- if (!warned) {
- fprintf(stderr, "WARNING: DW_TAG_partial_unit used, some types will not be considered!\n"
- " Probably this was optimized using a tool like 'dwz'\n"
- " A future version of pahole will support this.\n");
- warned = true;
- }
- return 0; // so that other units can be processed
- }
+ if (tag == DW_TAG_partial_unit)
+ return 0; // Processed inline when reached via DW_TAG_imported_unit
if (tag != DW_TAG_compile_unit && tag != DW_TAG_type_unit) {
fprintf(stderr, "%s: DW_TAG_compile_unit, DW_TAG_type_unit, DW_TAG_partial_unit or DW_TAG_skeleton_unit expected got %s (0x%x) @ %llx!\n",
@@ -3747,7 +3858,7 @@ static int die__process(Dwarf_Die *die, struct cu *cu, struct conf_load *conf)
return DWARF_CB_OK;
if (dwarf_child(die, &child) == 0) {
- int err = die__process_unit(&child, cu, conf);
+ int err = die__process_unit(&child, cu, conf, 0);
if (err)
return err;
}
@@ -4519,7 +4630,7 @@ static int cus__merge_and_process_cu(struct cus *cus, struct conf_load *conf,
filtered = conf->early_cu_filter(&unmerged_cu) == NULL;
}
- if (!filtered && die__process_unit(&child, cu, conf) != 0)
+ if (!filtered && die__process_unit(&child, cu, conf, 0) != 0)
goto out_abort;
}
diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
index e3ce737460efec72..577c4e8b76185aba 100644
--- a/man-pages/pahole.1
+++ b/man-pages/pahole.1
@@ -373,10 +373,9 @@ Non-standard, non-BTF related features:
and was observed with Rust CUs, where types 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.
+ Same-file imported units (DW_TAG_imported_unit) are
+ auto-detected, so force_cu_merging is rarely needed with
+ current pahole.
.fi
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2 5/9] dwarf_loader: Fix cus__merging_cu failing to detect DW_FORM_ref_addr
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
` (3 preceding siblings ...)
2026-08-21 21:35 ` [PATCH v2 4/9] dwarf_loader: Support DW_TAG_imported_unit for same-file partial units Arnaldo Carvalho de Melo
@ 2026-08-21 21:35 ` Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 6/9] tests: Add inter-CU type reference comparison test Arnaldo Carvalho de Melo
` (4 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
From: Arnaldo Carvalho de Melo <acme@redhat.com>
cus__merging_cu() scans abbreviation tables looking for DW_FORM_ref_addr
to detect binaries with inter-CU type references (like Rust CUs in
perf). When found, it triggers the merged CU loading path that can
resolve cross-CU references.
However, dwarf_getabbrevattr() can fail on certain attributes, notably
when DW_FORM_implicit_const is used (DWARF5). The function was treating
this failure as terminal, returning false immediately without scanning
the remaining abbreviations. This prevented detection of
DW_FORM_ref_addr in later CUs, causing the parallel path to be taken
instead — which cannot resolve cross-CU references.
For example, with the perf binary containing 507 CUs where 7 Rust CUs
(CU 209-215) use DW_FORM_ref_addr, the function was failing at CU 0
abbreviation 20 attribute 8 and returning false, never reaching the
Rust CUs.
Before:
$ pahole -F dwarf ~/bin/perf 2>&1 | grep "couldn't find" | wc -l
314
$ diff <(pahole -F dwarf ~/bin/perf 2>/dev/null) \
<(pahole --features=force_cu_merging -F dwarf ~/bin/perf 2>/dev/null) \
| grep '^[<>]' | wc -l
70
After:
$ pahole -F dwarf ~/bin/perf 2>&1 | grep "couldn't find" | wc -l
0
$ diff <(pahole -F dwarf ~/bin/perf 2>/dev/null) \
<(pahole --features=force_cu_merging -F dwarf ~/bin/perf 2>/dev/null) \
| wc -l
0
The fix changes dwarf_getattrcnt() failure to skip the current
abbreviation (goto next_abbrev) and dwarf_getabbrevattr() failure to
skip to the next attribute (continue), both continuing to scan for
DW_FORM_ref_addr instead of aborting the entire detection.
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
dwarf_loader.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/dwarf_loader.c b/dwarf_loader.c
index abe43ee0f6f816f1..086dd2bb9161dcae 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -4182,7 +4182,7 @@ static bool cus__merging_cu(Dwarf *dw, Elf *elf)
size_t attrcnt;
if (dwarf_getattrcnt (abbrev, &attrcnt) != 0)
- return false;
+ goto next_abbrev;
unsigned int attr_num, attr_form;
Dwarf_Off aboffset;
@@ -4190,10 +4190,11 @@ static bool cus__merging_cu(Dwarf *dw, Elf *elf)
for (j = 0; j < attrcnt; ++j) {
if (dwarf_getabbrevattr (abbrev, j, &attr_num, &attr_form,
&aboffset))
- return false;
+ continue;
if (attr_form == DW_FORM_ref_addr)
return true;
}
+next_abbrev:
offset += length;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2 6/9] tests: Add inter-CU type reference comparison test
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
` (4 preceding siblings ...)
2026-08-21 21:35 ` [PATCH v2 5/9] dwarf_loader: Fix cus__merging_cu failing to detect DW_FORM_ref_addr Arnaldo Carvalho de Melo
@ 2026-08-21 21:35 ` Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 7/9] dwarf_loader: Add cu parameter to tag__set_spec() and dwarf_tag__set_attr_type() Arnaldo Carvalho de Melo
` (3 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
From: Arnaldo Carvalho de Melo <acme@redhat.com>
When the perf binary includes Rust CUs, some DWARF types reference types
in other CUs via DW_FORM_ref_addr. The parallel loading path processes
each CU independently, so cross-CU references fail during
cu__recode_dwarf_types() and the types become unresolved (void).
The force_cu_merging feature worked around this by serializing loading
into a mega-CU, but disables parallel DIE processing. This test
compares both outputs to validate that cross-CU references are
correctly resolved without needing force_cu_merging.
Before the cus__merging_cu() fix earlier in this series, 314 cross-CU
references failed, producing 70
lines of output difference across 12 diff hunks:
$ pahole -F dwarf ~/bin/perf 2>/dev/null > parallel.txt
$ pahole --features=force_cu_merging -F dwarf ~/bin/perf 2>/dev/null > merged.txt
$ diff parallel.txt merged.txt | grep '^[<>]' | wc -l
70
$ diff -u parallel.txt merged.txt | head -20
--- parallel.txt
+++ merged.txt
@@ -10821,7 +10821,11 @@
usize __0 __attribute__((__aligned__(8)));
struct ThreadInfo __1 __attribute__((__aligned__(8)));
+ /* XXX last struct has 8 bytes of padding, 1 hole */
+
/* size: 48, cachelines: 1, members: 2 */
+ /* member types with holes: 1, total: 1 */
+ /* paddings: 1, sum paddings: 8 */
/* forced alignments: 2 */
/* last cacheline: 48 bytes */
} __attribute__((__aligned__(8)));
An earlier commit in this series fixed the detection of DW_FORM_ref_addr
in cus__merging_cu(), making this test pass.
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tests/inter_cu_refs.sh | 50 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100755 tests/inter_cu_refs.sh
diff --git a/tests/inter_cu_refs.sh b/tests/inter_cu_refs.sh
new file mode 100755
index 0000000000000000..0df0ce13cd948f1f
--- /dev/null
+++ b/tests/inter_cu_refs.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright © 2026 Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+#
+# Compare default CU loading with explicit force_cu_merging for binaries
+# with inter-CU type references (e.g. Rust CUs in perf).
+#
+# When cus__merging_cu() correctly detects DW_FORM_ref_addr usage, it
+# automatically falls back to the merged CU path. This test verifies
+# that the automatic detection produces the same output as explicitly
+# forcing CU merging.
+
+. "$(dirname "$0")/test_lib.sh"
+
+outdir=$(make_tmpdir)
+trap cleanup EXIT
+
+title_log "Compare parallel vs merged CU loading for inter-CU type references."
+
+perf=$(which perf 2>/dev/null)
+if [ -z "$perf" ] ; then
+ info_log "skip: No 'perf' binary available"
+ test_skip
+fi
+
+if ! pahole --features=force_cu_merging -F dwarf -C perf_event_header "$perf" 2>/dev/null | grep -q "^struct perf_event_header {" ; then
+ info_log "skip: $perf doesn't have 'struct perf_event_header' type info"
+ test_skip
+fi
+
+parallel_out=$outdir/parallel.txt
+merged_out=$outdir/merged.txt
+
+if ! pahole -F dwarf "$perf" > "$parallel_out" 2>/dev/null ; then
+ error_log "FAIL: pahole failed processing $perf (parallel mode)"
+ test_fail
+fi
+
+if ! pahole --features=force_cu_merging -F dwarf "$perf" > "$merged_out" 2>/dev/null ; then
+ error_log "FAIL: pahole failed processing $perf (merged mode)"
+ test_fail
+fi
+
+if diff -u "$parallel_out" "$merged_out" > /dev/null 2>&1 ; then
+ test_pass
+else
+ nr_diff=$(diff "$parallel_out" "$merged_out" | grep '^[<>]' | wc -l)
+ error_log "FAIL: $nr_diff lines differ between parallel and merged CU loading"
+ test_fail
+fi
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2 7/9] dwarf_loader: Add cu parameter to tag__set_spec() and dwarf_tag__set_attr_type()
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
` (5 preceding siblings ...)
2026-08-21 21:35 ` [PATCH v2 6/9] tests: Add inter-CU type reference comparison test Arnaldo Carvalho de Melo
@ 2026-08-21 21:35 ` Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 8/9] dwarf_loader: Support DW_FORM_GNU_ref_alt references to dwz alternate debug files Arnaldo Carvalho de Melo
` (2 subsequent siblings)
9 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Thread the cu parameter through the reference-setting helpers, in
preparation for using it to track which offset space (main file vs dwz
alternate file) each reference targets.
The cu parameter is currently unused: dwarf_tag__set_attr_type() silently
ignores the extra macro argument, and tag__set_spec() marks it
__maybe_unused.
No functional change.
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
dwarf_loader.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 086dd2bb9161dcae..103381c4dbfdf860 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -536,7 +536,7 @@ static void tag__free(struct tag *tag, struct cu *cu)
cu__free(cu, dtag);
}
-#define dwarf_tag__set_attr_type(dtag, field, die, attr_name) \
+#define dwarf_tag__set_attr_type(dtag, field, die, attr_name, cu) \
dtag->from_types_section.field = attr_type(die, attr_name, &dtag->field)
static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
@@ -548,13 +548,13 @@ static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
dtag->id = dwarf_dieoffset(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);
+ dwarf_tag__set_attr_type(dtag, type, die, DW_AT_import, cu);
else if (tag->tag == DW_TAG_variant_part)
- dwarf_tag__set_attr_type(dtag, type, die, DW_AT_discr);
+ dwarf_tag__set_attr_type(dtag, type, die, DW_AT_discr, cu);
else
- dwarf_tag__set_attr_type(dtag, type, die, DW_AT_type);
+ dwarf_tag__set_attr_type(dtag, type, die, DW_AT_type, cu);
- dwarf_tag__set_attr_type(dtag, abstract_origin, die, DW_AT_abstract_origin);
+ dwarf_tag__set_attr_type(dtag, abstract_origin, die, DW_AT_abstract_origin, cu);
tag->recursivity_level = 0;
tag->attributes = NULL;
@@ -597,10 +597,10 @@ static struct tag *tag__new(Dwarf_Die *die, struct cu *cu)
return tag;
}
-static void tag__set_spec(struct tag *tag, Dwarf_Die *die)
+static void tag__set_spec(struct tag *tag, Dwarf_Die *die, struct cu *cu __maybe_unused)
{
struct dwarf_tag *dtag = tag__dwarf(tag);
- dwarf_tag__set_attr_type(dtag, specification, die, DW_AT_specification);
+ dwarf_tag__set_attr_type(dtag, specification, die, DW_AT_specification, cu);
}
static struct ptr_to_member_type *ptr_to_member_type__new(Dwarf_Die *die,
@@ -611,7 +611,7 @@ static struct ptr_to_member_type *ptr_to_member_type__new(Dwarf_Die *die,
if (ptr != NULL) {
tag__init(&ptr->tag, cu, die);
struct dwarf_tag *dtag = tag__dwarf(&ptr->tag);
- dwarf_tag__set_attr_type(dtag, containing_type, die, DW_AT_containing_type);
+ dwarf_tag__set_attr_type(dtag, containing_type, die, DW_AT_containing_type, cu);
}
return ptr;
@@ -702,7 +702,7 @@ static void type__init(struct type *type, Dwarf_Die *die, struct cu *cu, struct
type->size = attr_numeric(die, DW_AT_byte_size);
type->alignment = attr_alignment(die, conf);
type->declaration = attr_numeric(die, DW_AT_declaration);
- tag__set_spec(&type->namespace.tag, die);
+ tag__set_spec(&type->namespace.tag, die, cu);
type->definition_emitted = 0;
type->fwd_decl_emitted = 0;
type->resized = 0;
@@ -815,7 +815,7 @@ static struct variable *variable__new(Dwarf_Die *die, struct cu *cu, struct conf
if (!var->declaration && cu->has_addr_info)
var->scope = dwarf__location(die, &var->ip.addr, &var->location);
if (has_specification) {
- tag__set_spec(&var->ip.tag, die);
+ tag__set_spec(&var->ip.tag, die, cu);
}
}
@@ -1702,7 +1702,7 @@ static struct inline_expansion *inline_expansion__new(Dwarf_Die *die, struct cu
tag__init(&exp->ip.tag, cu, die);
dtag->decl_file = attr_string(die, DW_AT_call_file, conf);
dtag->decl_line = attr_numeric(die, DW_AT_call_line);
- dwarf_tag__set_attr_type(dtag, type, die, DW_AT_abstract_origin);
+ dwarf_tag__set_attr_type(dtag, type, die, DW_AT_abstract_origin, cu);
exp->ip.addr = 0;
exp->high_pc = 0;
@@ -1864,7 +1864,7 @@ static struct function *function__new(Dwarf_Die *die, struct cu *cu, struct conf
func->declaration = dwarf_hasattr(die, DW_AT_declaration);
func->external = dwarf_hasattr(die, DW_AT_external);
func->abstract_origin = dwarf_hasattr(die, DW_AT_abstract_origin);
- tag__set_spec(&func->proto.tag, die);
+ tag__set_spec(&func->proto.tag, die, cu);
func->accessibility = attr_numeric(die, DW_AT_accessibility);
func->virtuality = attr_numeric(die, DW_AT_virtuality);
INIT_LIST_HEAD(&func->vtable_node);
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2 8/9] dwarf_loader: Support DW_FORM_GNU_ref_alt references to dwz alternate debug files
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
` (6 preceding siblings ...)
2026-08-21 21:35 ` [PATCH v2 7/9] dwarf_loader: Add cu parameter to tag__set_spec() and dwarf_tag__set_attr_type() Arnaldo Carvalho de Melo
@ 2026-08-21 21:35 ` Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 9/9] scripts: Add vmlinux_comparison.py for DWARF/BTF analysis Arnaldo Carvalho de Melo
2026-08-26 0:52 ` [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
9 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Binaries processed by dwz(1) can have shared types deduplicated into a
separate alternate debug file (.dwz), referenced via DW_FORM_GNU_ref_alt
through the .gnu_debugaltlink ELF section. elfutils resolves these
references transparently via dwarf_getalt(), but pahole was skipping
them with a warning because the alt file's DWARF offset space is
separate from the main file's — mixing them in the same hash tables
caused type corruption and crashes.
Add a separate dwarf_cu (dcu->alt) with its own hash tables for the alt
file's offset space. This mirrors the existing type_unit pattern but
uses direct lookup instead of fallback: the DWARF form tells us exactly
which hash table to use.
Key changes:
- Add 'from_alt' bitfield to struct dwarf_tag, parallel to
from_types_section, marking references that point to the alt file.
- Add 'alt' pointer and 'processing_alt' flag to struct dwarf_cu.
When processing children of an alt-file imported unit,
processing_alt directs cu__hash() to use dcu->alt hash tables
and causes all type references to be marked from_alt.
- Update attr_type() to resolve DW_FORM_GNU_ref_alt via
dwarf_formref_die() instead of skipping. Returns the alt offset
and sets is_alt so the caller marks from_alt on the dwarf_tag.
- Turn dwarf_tag__set_attr_type() from a macro into a static
function that selects which offset/flags fields to fill via an
enum (DWARF_TAG__REF_*). The previous plain macro form
(from_types_section.field = attr_type(...)) could not indicate
which offset/flags to fill for different reference types; now
explicit.
- When the alt file cannot be resolved (missing .gnu_debugaltlink
target or build-id mismatch), warn once per file, naming it.
cus__merging_cu()'s abbreviation scan records whether the file
uses DW_FORM_GNU_ref_alt, so cus__load_module() knows to warn
when dwarf_getalt() fails — a process-wide static would hide
later files with the same problem when processing many debuginfo
files in a single invocation.
- Update dwarf_cu__find_{tag,type}_by_ref() to check from_alt and
go directly to dcu->alt for lookup (not a fallback — the form
is unambiguous).
- Create the alt dwarf_cu in cus__merge_and_process_cu() via
dwarf_getalt() before processing any CUs, so it's available
when die__process_imported_unit() encounters the first alt ref.
- Trigger the merged CU path when dwarf_getalt() finds an alt file,
since the alt hash tables are only set up in that path.
- Prune unreferenced alt partial units after processing all CUs:
PUs directly imported by main-file CUs, or imported by any
other PU, are conservatively retained — the hit-marking
during pre-processing is not limited to main-CU-reachable
PUs, so some unreferenced PUs may survive pruning. Pruning
creates NULL holes in types_table; btf_encoder's
btf_encoder__tag_type() adjusts for these holes via
type_id_null_adj[]. This relies on a documented dwz invariant: inter-PU
dependencies use DW_TAG_imported_unit, not bare DW_FORM_ref_addr.
tag__check_pruned_alt_ref() detects violations at recode time by
checking whether a failed from_alt lookup targets a pruned PU.
- Alt PU ranges are kept in a sorted array and located via binary
search (dwarf_cu__find_alt_pu), since large dwz alt files can
contain thousands of partial units and every type reference needs
to identify which PU it belongs to.
- dwarf_cu__delete() now explicitly frees hash_tags and
hash_types via cu__free() for all dwarf_cu instances, not just
the alt one. Under obstack this is a no-op, but in the
non-obstack path it fixes a pre-existing leak.
- Remove attr_form_is_ref_alt() and all its callers — no longer
needed since alt refs are now resolved instead of skipped.
- Add tests/dwz_alt_file.sh: creates two binaries sharing types,
runs dwz to produce an alt file, verifies pahole resolves the
shared types. Includes a negative sub-test that hides the alt
file and verifies the per-file warning fires exactly once and
pahole does not crash.
The btf_encoder__tag_type() call site consolidation and the
dwarf_tag__set_attr_type() / tag__set_spec() cu parameter threading
were split into separate prep patches.
Before (chromium-browser debuginfo, Fedora 44):
$ pahole -F dwarf /usr/lib/debug/usr/lib64/chromium-browser/chromium-browser-149.0.7827.155-1.fc44.x86_64.debug
WARNING: DW_FORM_GNU_ref_alt (dwz alternate debug file) not yet supported,
some types will not be available.
[no output]
After:
$ pahole -F dwarf /usr/lib/debug/usr/lib64/chromium-browser/chromium-browser-149.0.7827.155-1.fc44.x86_64.debug | wc -l
6378
$
This was based in previous, not AI assisted work back in 2023, as stored
in these branches:
https://git.kernel.org/pub/scm/devel/pahole/pahole.git/log/?h=alt_dwarf and
https://git.kernel.org/pub/scm/devel/pahole/pahole.git/commit/?h=WIP-imported-unit
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
btf_encoder.c | 3 +-
ctf_encoder.c | 11 +
dwarf_loader.c | 666 +++++++++++++++++++++++++++++++++++++-----
dwarves.h | 1 +
man-pages/pahole.1 | 6 +-
tests/dwz_alt_file.sh | 204 +++++++++++++
6 files changed, 808 insertions(+), 83 deletions(-)
create mode 100755 tests/dwz_alt_file.sh
diff --git a/btf_encoder.c b/btf_encoder.c
index 8779db65f428b2ac..d6b9be6a215df72b 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -768,7 +768,8 @@ static int32_t btf_encoder__tag_type(struct btf_encoder *encoder, uint32_t tag_t
if (encoder->btf_id_map && tag_type < encoder->btf_id_map_sz)
return encoder->btf_id_map[tag_type];
- /* Fallback for map-absent CUs (nr == 0) or out-of-range ids */
+ /* Fallback for map-absent CUs (nr == 0) or out-of-range ids;
+ * adjust for NULL holes left by dwz alt PU pruning */
uint32_t adj = encoder->type_id_null_adj ? encoder->type_id_null_adj[tag_type] : 0;
return encoder->type_id_off + tag_type - adj;
diff --git a/ctf_encoder.c b/ctf_encoder.c
index f00e4607cbae7875..a1d9052f34c44cf8 100644
--- a/ctf_encoder.c
+++ b/ctf_encoder.c
@@ -263,6 +263,17 @@ int cu__encode_ctf(struct cu *cu, int verbose)
uint32_t id;
struct tag *pos;
+
+ /* CTF IDs must be sequential — NULL holes in the types table
+ * (from dwz alternate debug files) would desync core vs CTF IDs.
+ * Bail out rather than generate corrupt CTF. */
+ for (id = 1; id < cu->types_table.nr_entries; ++id) {
+ if (cu->types_table.entries[id] == NULL) {
+ fprintf(stderr, "ctf_encoder: NULL holes in types table not supported for CTF encoding\n");
+ goto out_delete;
+ }
+ }
+
cu__for_each_type(cu, id, pos)
tag__encode_ctf(pos, id, ctf);
diff --git a/dwarf_loader.c b/dwarf_loader.c
index 103381c4dbfdf860..61ef52f291dee035 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -117,6 +117,16 @@ static struct tag unsupported_tag;
#define cu__tag_not_handled(cu, die) __cu__tag_not_handled(cu, die, __FUNCTION__)
+/*
+ * Each DWARF reference attribute can point to one of three offset spaces:
+ * 1. Main file — the default, looked up in dcu->hash_*
+ * 2. .debug_types — DW_FORM_ref_sig8, looked up in dcu->type_unit->hash_*
+ * 3. dwz alt file — DW_FORM_GNU_ref_alt, looked up in dcu->alt->hash_*
+ *
+ * The from_types_section and from_alt bitfields record which space each
+ * reference attribute targets, so the lookup functions go directly to the
+ * right hash table without fallback searches.
+ */
struct dwarf_tag {
struct hlist_node hash_node;
Dwarf_Off type;
@@ -132,6 +142,12 @@ struct dwarf_tag {
bool containing_type:1;
bool specification:1;
} from_types_section;
+ struct {
+ bool type:1;
+ bool abstract_origin:1;
+ bool containing_type:1;
+ bool specification:1;
+ } from_alt;
uint16_t decl_line;
uint32_t small_id;
const char *decl_file;
@@ -147,15 +163,26 @@ static inline struct dwarf_tag *tag__dwarf(const struct tag *tag)
return ((struct dwarf_tag *)tag) - 1;
}
+struct alt_pu {
+ Dwarf_Off start; /* first DIE offset in this PU */
+ Dwarf_Off end; /* first offset past this PU (next CU header) */
+ bool hit; /* referenced by main CUs? */
+};
+
struct dwarf_cu {
- struct hlist_head *hash_tags;
- struct hlist_head *hash_types;
+ struct hlist_head *hash_tags; /* tags (functions, etc.) keyed by DWARF offset */
+ struct hlist_head *hash_types; /* types keyed by DWARF offset */
struct dwarf_tag *last_type_lookup;
struct cu *cu;
- struct dwarf_cu *type_unit;
- Dwarf_Off *imported_units;
+ struct dwarf_cu *type_unit; /* .debug_types offset space */
+ struct dwarf_cu *alt; /* dwz alt file offset space */
+ Dwarf_Off *imported_units; /* dedup: visited same-file partial unit offsets */
uint32_t nr_imported_units;
uint32_t allocated_imported_units;
+ struct alt_pu *alt_pus;
+ uint32_t nr_alt_pus;
+ uint32_t allocated_alt_pus;
+ bool processing_alt; /* true while processing alt-file DIEs */
};
static int dwarf_cu__init(struct dwarf_cu *dcu, struct cu *cu)
@@ -181,9 +208,21 @@ static int dwarf_cu__init(struct dwarf_cu *dcu, struct cu *cu)
INIT_HLIST_HEAD(&dcu->hash_types[i]);
}
dcu->type_unit = NULL;
+ dcu->alt = NULL;
dcu->imported_units = NULL;
dcu->nr_imported_units = 0;
dcu->allocated_imported_units = 0;
+ dcu->alt_pus = NULL;
+ dcu->nr_alt_pus = 0;
+ dcu->allocated_alt_pus = 0;
+ /*
+ * processing_alt is per-merged-CU state, not thread-safe.
+ * The merged-CU path (cus__merge_and_process_cu) is
+ * single-threaded; do not parallelize merged loading
+ * without addressing this and the alt hash routing in
+ * cu__hash().
+ */
+ dcu->processing_alt = false;
// To avoid a per-lookup check against NULL in dwarf_cu__find_type_by_ref()
dcu->last_type_lookup = &sentinel_dtag;
return 0;
@@ -208,8 +247,16 @@ static void dwarf_cu__delete(struct cu *cu)
struct dwarf_cu *dcu = cu->priv;
- free(dcu->imported_units);
- // dcu->hash_tags & dcu->hash_types are on cu->obstack
+ zfree(&dcu->imported_units);
+ zfree(&dcu->alt_pus);
+ if (dcu->alt) {
+ cu__free(cu, dcu->alt->hash_tags);
+ cu__free(cu, dcu->alt->hash_types);
+ cu__free(cu, dcu->alt);
+ dcu->alt = NULL;
+ }
+ cu__free(cu, dcu->hash_tags);
+ cu__free(cu, dcu->hash_types);
cu__free(cu, dcu);
cu->priv = NULL;
}
@@ -251,39 +298,198 @@ static struct dwarf_tag *hashtags__find(const struct hlist_head *hashtable,
return NULL;
}
+/*
+ * cu__hash - insert a tag into the appropriate hash table
+ *
+ * Three hash table sets exist per merged CU:
+ * - dcu->hash_types / hash_tags: main-file DWARF offsets
+ * - dcu->alt->hash_types / hash_tags: dwz alt-file offsets
+ * - dcu->type_unit->hash_types: .debug_types offsets
+ *
+ * When dcu->processing_alt is set (i.e. we are processing children of
+ * an alt-file partial unit), tags are hashed into dcu->alt so that
+ * their alt-file offsets don't collide with main-file offsets.
+ */
static void cu__hash(struct cu *cu, struct tag *tag)
{
struct dwarf_cu *dcu = cu->priv;
+ struct dwarf_cu *target = (dcu->processing_alt && dcu->alt) ? dcu->alt : dcu;
struct hlist_head *hashtable = tag__is_tag_type(tag) ?
- dcu->hash_types :
- dcu->hash_tags;
+ target->hash_types :
+ target->hash_tags;
hashtags__hash(hashtable, tag__dwarf(tag));
}
+/* Binary search for the alt PU whose [start, end) range contains offset.
+ * The array is sorted by .start (built monotonically by dwarf_nextcu)
+ * with non-overlapping ranges. Returns NULL if offset is not in any PU. */
+static struct alt_pu *dwarf_cu__find_alt_pu(struct dwarf_cu *dcu, Dwarf_Off offset)
+{
+ uint32_t lo = 0, hi = dcu->nr_alt_pus;
+
+ while (lo < hi) {
+ uint32_t mid = lo + (hi - lo) / 2;
+
+ if (offset < dcu->alt_pus[mid].start)
+ hi = mid;
+ else if (offset >= dcu->alt_pus[mid].end)
+ lo = mid + 1;
+ else
+ return &dcu->alt_pus[mid];
+ }
+ return NULL;
+}
+
+static bool dwarf_cu__alt_pu_is_hit(struct dwarf_cu *dcu, Dwarf_Off offset)
+{
+ struct alt_pu *pu = dwarf_cu__find_alt_pu(dcu, offset);
+ return pu ? pu->hit : false;
+}
+
+/* True when offset falls inside an alt PU that was not marked as hit —
+ * i.e. it was pruned by dwarf_cu__prune_unreferenced_alt_pus(). */
+static bool dwarf_cu__offset_in_pruned_alt_pu(struct dwarf_cu *dcu, Dwarf_Off offset)
+{
+ struct alt_pu *pu = dwarf_cu__find_alt_pu(dcu, offset);
+ return pu ? !pu->hit : false;
+}
+
+/* After a from_alt lookup miss during recode, check whether the target
+ * offset falls in a PU that was pruned. This catches dwz invariant
+ * violations where an inter-PU dependency was not expressed via
+ * DW_TAG_imported_unit.
+ *
+ * Works for plain-form refs (ref4 etc.) within the alt file too:
+ * dwarf_tag__set_attr_type ORs processing_alt into from_alt, so
+ * intra-alt refs carry the bit even though their DWARF form is not
+ * DW_FORM_GNU_ref_alt. */
+static void tag__check_pruned_alt_ref(struct dwarf_cu *dcu, Dwarf_Off ref,
+ bool from_alt)
+{
+ if (!from_alt || dcu == NULL || dcu->nr_alt_pus == 0)
+ return;
+ if (dwarf_cu__offset_in_pruned_alt_pu(dcu, ref))
+ fprintf(stderr,
+ " reference %#llx is in a pruned dwz alternate partial unit — "
+ "possible dwz invariant violation\n"
+ " (inter-PU dependency not expressed via DW_TAG_imported_unit)\n",
+ (unsigned long long)ref);
+}
+
+/*
+ * Remove types/functions from dwz alternate partial units that were
+ * never marked as hit. A PU is marked hit when:
+ *
+ * (a) a main-file CU references it via DW_FORM_GNU_ref_alt
+ * (attribute ref or DW_TAG_imported_unit), or
+ * (b) any other alt PU imports it via DW_TAG_imported_unit —
+ * regardless of whether that importing PU is itself reachable
+ * from a main CU, since the pre-processing pass in
+ * cus__merge_and_process_cu() walks ALL alt PUs with
+ * hit-marking enabled.
+ *
+ * (b) makes this a conservative over-approximation of "transitively
+ * reachable from main CUs": clusters of PUs that only import each
+ * other survive pruning even if no main CU references the cluster.
+ * Correctness is unaffected — only the amount pruned. Tightening
+ * this to true main-rooted reachability would require recording
+ * PU→PU import edges during pre-processing and propagating hits
+ * from main-referenced roots before pruning.
+ *
+ * Safety invariant (why pruning can't remove something needed):
+ * dwz expresses inter-PU dependencies via DW_TAG_imported_unit,
+ * not bare DW_FORM_ref_addr. If dwz violated this, the target PU
+ * could be pruned while still referenced; tag__check_pruned_alt_ref()
+ * detects that at recode time and prints a diagnostic.
+ */
+static void dwarf_cu__prune_unreferenced_alt_pus(struct dwarf_cu *dcu)
+{
+ struct dwarf_cu *alt = dcu->alt;
+ struct cu *cu = dcu->cu;
+
+ if (alt == NULL || dcu->nr_alt_pus == 0)
+ return;
+
+ uint64_t hashtags_size = 1UL << hashtags__bits;
+
+ for (uint64_t i = 0; i < hashtags_size; i++) {
+ struct dwarf_tag *dtag;
+ struct hlist_node *pos, *tmp;
+
+ hlist_for_each_entry_safe(dtag, pos, tmp, &alt->hash_types[i], hash_node) {
+ if (!dwarf_cu__alt_pu_is_hit(dcu, dtag->id)) {
+ struct tag *tag = dtag__tag(dtag);
+ hlist_del(&dtag->hash_node);
+ list_del_init(&tag->node);
+ cu->types_table.entries[dtag->small_id] = NULL;
+ }
+ }
+ hlist_for_each_entry_safe(dtag, pos, tmp, &alt->hash_tags[i], hash_node) {
+ if (!dwarf_cu__alt_pu_is_hit(dcu, dtag->id)) {
+ struct tag *tag = dtag__tag(dtag);
+ hlist_del(&dtag->hash_node);
+ list_del_init(&tag->node);
+ if (tag__is_function(tag)) {
+ rb_erase(&tag__function(tag)->rb_node, &cu->functions);
+ cu->functions_table.entries[dtag->small_id] = NULL;
+ } else
+ cu->tags_table.entries[dtag->small_id] = NULL;
+ }
+ }
+ /*
+ * Don't free pruned tags here: child tags (members) have
+ * list nodes linked through their parent's namespace.tags,
+ * so freeing parents in hash_types before processing children
+ * in hash_tags would cause use-after-free on list_del_init().
+ * All tag memory is freed when the CU is destroyed.
+ */
+ }
+}
+
+/*
+ * Lookup a tag (function, lexblock, etc.) by DWARF offset.
+ *
+ * The from_alt bit tells us the reference came from DW_FORM_GNU_ref_alt
+ * or was inside an alt partial unit (processing_alt), so we go directly
+ * to dcu->alt->hash_tags. This is a direct lookup, not a fallback —
+ * the DWARF form tells us which offset space the reference belongs to.
+ */
static struct dwarf_tag *__dwarf_cu__find_tag_by_ref(const struct dwarf_cu *cu,
- const Dwarf_Off ref, bool from_types)
+ const Dwarf_Off ref,
+ bool from_types, bool from_alt)
{
if (cu == NULL)
return NULL;
- if (from_types) {
+ if (from_types)
return NULL;
+ if (from_alt) {
+ cu = cu->alt;
+ if (cu == NULL)
+ return NULL;
}
return hashtags__find(cu->hash_tags, ref);
}
#define dwarf_cu__find_tag_by_ref(cu, dtag, field) \
- __dwarf_cu__find_tag_by_ref(cu, dtag->field, dtag->from_types_section.field)
+ __dwarf_cu__find_tag_by_ref(cu, dtag->field, \
+ dtag->from_types_section.field, \
+ dtag->from_alt.field)
+/* Same as __dwarf_cu__find_tag_by_ref but for type lookups (hash_types). */
static struct dwarf_tag *__dwarf_cu__find_type_by_ref(struct dwarf_cu *dcu,
- const Dwarf_Off ref, bool from_types)
+ const Dwarf_Off ref,
+ bool from_types, bool from_alt)
{
if (dcu == NULL)
return NULL;
if (from_types) {
dcu = dcu->type_unit;
- if (dcu == NULL) {
+ if (dcu == NULL)
+ return NULL;
+ } else if (from_alt) {
+ dcu = dcu->alt;
+ if (dcu == NULL)
return NULL;
- }
}
if (dcu->last_type_lookup->id == ref)
@@ -298,7 +504,9 @@ static struct dwarf_tag *__dwarf_cu__find_type_by_ref(struct dwarf_cu *dcu,
}
#define dwarf_cu__find_type_by_ref(dcu, dtag, field) \
- __dwarf_cu__find_type_by_ref(dcu, dtag->field, dtag->from_types_section.field)
+ __dwarf_cu__find_type_by_ref(dcu, dtag->field, \
+ dtag->from_types_section.field, \
+ dtag->from_alt.field)
static void *memdup(const void *src, size_t len, struct cu *cu)
{
@@ -457,31 +665,54 @@ static const char *attr_string(Dwarf_Die *die, uint32_t name, struct conf_load *
return str;
}
-static bool attr_form_is_ref_alt(Dwarf_Attribute *attr)
-{
- if (attr->form == DW_FORM_GNU_ref_alt) {
- static bool warned;
-
- if (!warned) {
- fprintf(stderr,
- "WARNING: DW_FORM_GNU_ref_alt (dwz alternate debug file) not yet supported,\n"
- " some types will not be available.\n");
- warned = true;
- }
- return true;
- }
- return false;
-}
-
-static bool attr_type(Dwarf_Die *die, uint32_t attr_name, Dwarf_Off *offset)
+/**
+ * attr_type - extract a type reference attribute from a DWARF DIE
+ * @die: the DWARF DIE to read the attribute from
+ * @attr_name: the attribute to read (DW_AT_type, DW_AT_import, etc)
+ * @offset: output DWARF offset of the referenced DIE
+ * @is_alt: output flag, set when the reference points to a dwz alt file
+ *
+ * Resolves a DWARF reference attribute to a DIE offset. Handles three
+ * reference forms:
+ *
+ * - DW_FORM_GNU_ref_alt: reference into a dwz alternate debug file.
+ * Resolved transparently by elfutils via dwarf_getalt(). Sets
+ * @is_alt so callers use the alt hash tables for lookup.
+ *
+ * - DW_FORM_ref_sig8: reference into a .debug_types section (DWARF4).
+ * Returns true so callers use the type_unit hash tables.
+ *
+ * - All other ref forms (ref1/ref2/ref4/ref8/ref_addr): same-file
+ * references resolved by dwarf_formref_die().
+ *
+ * Return: true if the reference is from the types section (DW_FORM_ref_sig8),
+ * false otherwise. @offset and @is_alt are set on output.
+ */
+static bool attr_type(Dwarf_Die *die, uint32_t attr_name, Dwarf_Off *offset,
+ bool *is_alt)
{
Dwarf_Attribute attr;
+ *is_alt = false;
+
if (dwarf_attr(die, attr_name, &attr) != NULL) {
Dwarf_Die type_die;
- if (attr_form_is_ref_alt(&attr)) {
+ if (attr.form == DW_FORM_GNU_ref_alt) {
+ if (dwarf_formref_die(&attr, &type_die) != NULL) {
+ *offset = dwarf_dieoffset(&type_die);
+ *is_alt = true;
+ return false;
+ }
+ /* elfutils couldn't resolve the alt reference:
+ * the .gnu_debugaltlink target is missing or
+ * has a build-id mismatch. cus__load_module()
+ * already warned once for this file after
+ * detecting DW_FORM_GNU_ref_alt in the abbrevs,
+ * so stay silent here — this fires once per
+ * reference. Report the reference as
+ * unresolvable (offset 0). */
*offset = 0;
- return 0;
+ return false;
}
if (dwarf_formref_die(&attr, &type_die) != NULL) {
*offset = dwarf_dieoffset(&type_die);
@@ -489,7 +720,7 @@ static bool attr_type(Dwarf_Die *die, uint32_t attr_name, Dwarf_Off *offset)
}
}
*offset = 0;
- return 0;
+ return false;
}
static int attr_location(Dwarf_Die *die, Dwarf_Op **expr, size_t *exprlen)
@@ -536,8 +767,78 @@ static void tag__free(struct tag *tag, struct cu *cu)
cu__free(cu, dtag);
}
-#define dwarf_tag__set_attr_type(dtag, field, die, attr_name, cu) \
- dtag->from_types_section.field = attr_type(die, attr_name, &dtag->field)
+static void dwarf_cu__mark_alt_pu_hit(struct dwarf_cu *dcu, Dwarf_Off offset)
+{
+ struct alt_pu *pu = dwarf_cu__find_alt_pu(dcu, offset);
+ if (pu)
+ pu->hit = true;
+}
+
+/*
+ * Extract a reference attribute and record which offset space it targets.
+ *
+ * from_alt is set when either:
+ * (a) attr_type() saw DW_FORM_GNU_ref_alt — an explicit alt reference, OR
+ * (b) we are inside an alt partial unit (processing_alt) — a same-file
+ * ref form like DW_FORM_ref4 that still refers to the alt offset space.
+ *
+ * This bit drives the lookup functions to search dcu->alt hash tables.
+ */
+enum dwarf_tag__ref_type {
+ DWARF_TAG__REF_TYPE,
+ DWARF_TAG__REF_ABSTRACT_ORIGIN,
+ DWARF_TAG__REF_CONTAINING_TYPE,
+ DWARF_TAG__REF_SPECIFICATION,
+};
+
+static void dwarf_tag__set_attr_type(struct dwarf_tag *dtag,
+ enum dwarf_tag__ref_type ref_type,
+ Dwarf_Die *die, uint32_t attr_name,
+ struct cu *cu)
+{
+ bool is_alt;
+ Dwarf_Off offset;
+ struct dwarf_cu *dcu = cu->priv;
+
+ bool from_types = attr_type(die, attr_name, &offset, &is_alt);
+ bool from_alt = is_alt || (dcu && dcu->processing_alt);
+
+ switch (ref_type) {
+ case DWARF_TAG__REF_TYPE:
+ dtag->type = offset;
+ dtag->from_types_section.type = from_types;
+ dtag->from_alt.type = from_alt;
+ break;
+ case DWARF_TAG__REF_ABSTRACT_ORIGIN:
+ dtag->abstract_origin = offset;
+ dtag->from_types_section.abstract_origin = from_types;
+ dtag->from_alt.abstract_origin = from_alt;
+ break;
+ case DWARF_TAG__REF_CONTAINING_TYPE:
+ dtag->containing_type = offset;
+ dtag->from_types_section.containing_type = from_types;
+ dtag->from_alt.containing_type = from_alt;
+ break;
+ case DWARF_TAG__REF_SPECIFICATION:
+ dtag->specification = offset;
+ dtag->from_types_section.specification = from_types;
+ dtag->from_alt.specification = from_alt;
+ break;
+ }
+
+ /* Only mark alt PU as hit for actual cross-file refs
+ * (DW_FORM_GNU_ref_alt). During processing_alt,
+ * internal refs within alt PUs use same-file forms
+ * (ref4 etc.) and must NOT mark PUs as hit — doing
+ * so would mark every PU via its own internal refs
+ * and defeat pruning entirely. Transitive deps are
+ * handled by DW_TAG_imported_unit chains: if PU A
+ * imports PU B, then B gets marked hit during the
+ * alt pre-processing pass in cus__merge_and_process_cu().
+ */
+ if (dcu && is_alt)
+ dwarf_cu__mark_alt_pu_hit(dcu, offset);
+}
static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
{
@@ -548,13 +849,13 @@ static void tag__init(struct tag *tag, struct cu *cu, Dwarf_Die *die)
dtag->id = dwarf_dieoffset(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, cu);
+ dwarf_tag__set_attr_type(dtag, DWARF_TAG__REF_TYPE, die, DW_AT_import, cu);
else if (tag->tag == DW_TAG_variant_part)
- dwarf_tag__set_attr_type(dtag, type, die, DW_AT_discr, cu);
+ dwarf_tag__set_attr_type(dtag, DWARF_TAG__REF_TYPE, die, DW_AT_discr, cu);
else
- dwarf_tag__set_attr_type(dtag, type, die, DW_AT_type, cu);
+ dwarf_tag__set_attr_type(dtag, DWARF_TAG__REF_TYPE, die, DW_AT_type, cu);
- dwarf_tag__set_attr_type(dtag, abstract_origin, die, DW_AT_abstract_origin, cu);
+ dwarf_tag__set_attr_type(dtag, DWARF_TAG__REF_ABSTRACT_ORIGIN, die, DW_AT_abstract_origin, cu);
tag->recursivity_level = 0;
tag->attributes = NULL;
@@ -597,10 +898,10 @@ static struct tag *tag__new(Dwarf_Die *die, struct cu *cu)
return tag;
}
-static void tag__set_spec(struct tag *tag, Dwarf_Die *die, struct cu *cu __maybe_unused)
+static void tag__set_spec(struct tag *tag, Dwarf_Die *die, struct cu *cu)
{
struct dwarf_tag *dtag = tag__dwarf(tag);
- dwarf_tag__set_attr_type(dtag, specification, die, DW_AT_specification, cu);
+ dwarf_tag__set_attr_type(dtag, DWARF_TAG__REF_SPECIFICATION, die, DW_AT_specification, cu);
}
static struct ptr_to_member_type *ptr_to_member_type__new(Dwarf_Die *die,
@@ -611,7 +912,7 @@ static struct ptr_to_member_type *ptr_to_member_type__new(Dwarf_Die *die,
if (ptr != NULL) {
tag__init(&ptr->tag, cu, die);
struct dwarf_tag *dtag = tag__dwarf(&ptr->tag);
- dwarf_tag__set_attr_type(dtag, containing_type, die, DW_AT_containing_type, cu);
+ dwarf_tag__set_attr_type(dtag, DWARF_TAG__REF_CONTAINING_TYPE, die, DW_AT_containing_type, cu);
}
return ptr;
@@ -713,8 +1014,7 @@ static void type__init(struct type *type, Dwarf_Die *die, struct cu *cu, struct
Dwarf_Attribute attr;
if (dwarf_attr(die, DW_AT_type, &attr) != NULL) {
Dwarf_Die type_die;
- if (!attr_form_is_ref_alt(&attr) &&
- dwarf_formref_die(&attr, &type_die) != NULL) {
+ if (dwarf_formref_die(&attr, &type_die) != NULL) {
uint64_t encoding = attr_numeric(&type_die, DW_AT_encoding);
if (encoding == DW_ATE_signed || encoding == DW_ATE_signed_char)
@@ -1029,8 +1329,6 @@ static int add_gnu_annotation_chain(Dwarf_Die *die, int component_idx,
Dwarf_Die annot_die;
while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL) {
- if (attr_form_is_ref_alt(&attr))
- break;
if (dwarf_formref_die(&attr, &annot_die) == NULL)
break;
if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
@@ -1443,7 +1741,9 @@ static void parameter__record_true_sig_member(struct parameter *parm, Dwarf_Die
if (!parm->true_sig_member_name)
return;
- parm->true_sig_type_from_types = attr_type(&member_die, DW_AT_type, &parm->true_sig_type);
+ bool is_alt;
+ parm->true_sig_type_from_types = attr_type(&member_die, DW_AT_type, &parm->true_sig_type, &is_alt);
+ parm->true_sig_type_from_alt = is_alt;
if (parm->true_sig_type == 0)
parm->true_sig_member_name = NULL;
}
@@ -1702,7 +2002,7 @@ static struct inline_expansion *inline_expansion__new(Dwarf_Die *die, struct cu
tag__init(&exp->ip.tag, cu, die);
dtag->decl_file = attr_string(die, DW_AT_call_file, conf);
dtag->decl_line = attr_numeric(die, DW_AT_call_line);
- dwarf_tag__set_attr_type(dtag, type, die, DW_AT_abstract_origin, cu);
+ dwarf_tag__set_attr_type(dtag, DWARF_TAG__REF_TYPE, die, DW_AT_abstract_origin, cu);
exp->ip.addr = 0;
exp->high_pc = 0;
@@ -2035,23 +2335,55 @@ check_gnu_attr:
goto out;
/* Handle GCC-style DW_AT_GNU_annotation attribute */
- while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL) {
- if (attr_form_is_ref_alt(&attr))
- break;
- if (dwarf_formref_die(&attr, &annot_die) == NULL)
- break;
- if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
- break;
- name = attr_string(&annot_die, DW_AT_name, conf);
- if (strcmp(name, "btf_type_tag") != 0)
- break;
+ {
+ struct dwarf_cu *annot_dcu = cu->priv;
+ bool was_alt = annot_dcu->processing_alt;
- /* GCC chain is already in BTF order; append to preserve it. */
- tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf, false);
- if (tag == NULL)
- return NULL;
+ while (dwarf_attr(die, DW_AT_GNU_annotation, &attr) != NULL) {
+ bool is_alt_annot = (attr.form == DW_FORM_GNU_ref_alt);
- die = &annot_die;
+ if (dwarf_formref_die(&attr, &annot_die) == NULL)
+ break;
+ if (dwarf_tag(&annot_die) != DW_TAG_GNU_annotation)
+ break;
+ name = attr_string(&annot_die, DW_AT_name, conf);
+ if (strcmp(name, "btf_type_tag") != 0)
+ break;
+
+ /*
+ * Create the base wrapper before entering alt context
+ * so the pointer's type ref stays in the main file's
+ * hash tables. Then set processing_alt so the
+ * annotation itself is hashed into the alt tables.
+ */
+ /* Mark annotation as hit for both cross-file refs
+ * (DW_FORM_GNU_ref_alt) and intra-alt refs when
+ * already in alt context, so annotations aren't
+ * pruned by dwarf_cu__prune_unreferenced_alt_pus */
+ if (is_alt_annot || annot_dcu->processing_alt) {
+ dwarf_cu__mark_alt_pu_hit(annot_dcu,
+ dwarf_dieoffset(&annot_die));
+ if (tag == NULL) {
+ tag = die__create_new_btf_type_tag_ptr_type(die, cu);
+ if (tag == NULL) {
+ annot_dcu->processing_alt = was_alt;
+ return NULL;
+ }
+ }
+ annot_dcu->processing_alt = true;
+ }
+
+ /* GCC chain is already in BTF order; append to preserve it. */
+ tag = die__add_btf_type_tag(tag, die, &annot_die, cu, conf, false);
+ if (tag == NULL) {
+ annot_dcu->processing_alt = was_alt;
+ return NULL;
+ }
+
+ die = &annot_die;
+ }
+
+ annot_dcu->processing_alt = was_alt;
}
out:
@@ -2964,6 +3296,45 @@ static int dwarf_cu__mark_imported_unit(struct dwarf_cu *dcu, Dwarf_Off offset)
return 0;
}
+/*
+ * Defensive: dwarf_nextcu iterates monotonically so duplicates
+ * should not occur, but guard against it anyway.
+ */
+static bool dwarf_cu__alt_pu_visited(struct dwarf_cu *dcu, Dwarf_Off offset)
+{
+ /* Exact start match — reuse the range search since PU starts
+ * are unique and searching for a start offset will land in
+ * the PU whose start == offset (if it exists). */
+ struct alt_pu *pu = dwarf_cu__find_alt_pu(dcu, offset);
+ return pu && pu->start == offset;
+}
+
+static int dwarf_cu__add_alt_pu(struct dwarf_cu *dcu, Dwarf_Off start, Dwarf_Off end)
+{
+ if (dcu->nr_alt_pus == dcu->allocated_alt_pus) {
+ uint32_t new_size = dcu->allocated_alt_pus ? dcu->allocated_alt_pus * 2 : 16;
+ if (new_size <= dcu->allocated_alt_pus)
+ return -ENOMEM;
+ struct alt_pu *new_array = realloc(dcu->alt_pus, new_size * sizeof(*new_array));
+ if (new_array == NULL)
+ return -ENOMEM;
+ dcu->alt_pus = new_array;
+ dcu->allocated_alt_pus = new_size;
+ }
+ dcu->alt_pus[dcu->nr_alt_pus++] = (struct alt_pu){ .start = start, .end = end, .hit = false };
+ return 0;
+}
+
+/**
+ * die__process_imported_unit - process a DW_TAG_imported_unit reference
+ *
+ * For alt-file imports (DW_FORM_GNU_ref_alt / dwz): marks the partial
+ * unit as referenced for pruning. Alt PUs are pre-processed in bulk
+ * by cus__merge_and_process_cu().
+ *
+ * For same-file imports: processes children inline into the CU's type
+ * tables, with dedup to avoid re-processing.
+ */
static int die__process_imported_unit(Dwarf_Die *die, struct cu *cu, struct conf_load *conf, int import_depth)
{
Dwarf_Attribute attr;
@@ -2971,8 +3342,7 @@ static int die__process_imported_unit(Dwarf_Die *die, struct cu *cu, struct conf
if (dwarf_attr(die, DW_AT_import, &attr) == NULL)
return 0;
- if (attr_form_is_ref_alt(&attr))
- return 0;
+ bool is_alt = (attr.form == DW_FORM_GNU_ref_alt);
Dwarf_Die imported_die;
@@ -2998,6 +3368,18 @@ static int die__process_imported_unit(Dwarf_Die *die, struct cu *cu, struct conf
Dwarf_Off offset = dwarf_dieoffset(&imported_die);
struct dwarf_cu *dcu = cu->priv;
+ if (is_alt || dcu->processing_alt) {
+ /*
+ * Alt PUs are pre-processed in cus__merge_and_process_cu(),
+ * so just mark this one as referenced for pruning.
+ * Mark hits from both main CU imports (is_alt) and
+ * inter-alt-PU imports (processing_alt) to handle
+ * transitive dependencies between partial units.
+ */
+ dwarf_cu__mark_alt_pu_hit(dcu, offset);
+ return 0;
+ }
+
if (dwarf_cu__imported_unit_visited(dcu, offset))
return 0;
@@ -3286,7 +3668,8 @@ static bool parameter__apply_true_sig_member(struct parameter *parm, struct cu *
tmp.type = parm->true_sig_type;
tmp.from_types_section.type = parm->true_sig_type_from_types;
- dtype = __dwarf_cu__find_type_by_ref(cu->priv, tmp.type, tmp.from_types_section.type);
+ dtype = __dwarf_cu__find_type_by_ref(cu->priv, tmp.type, tmp.from_types_section.type,
+ parm->true_sig_type_from_alt);
if (!dtype)
return false;
@@ -3442,10 +3825,15 @@ static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu)
else
dtype = dwarf_cu__find_tag_by_ref(dcu, dpos, abstract_origin);
if (dtype == NULL) {
- if (dpos->type != 0)
+ if (dpos->type != 0) {
tag__print_type_not_found(pos);
- else
+ tag__check_pruned_alt_ref(dcu, dpos->type,
+ dpos->from_alt.type);
+ } else {
tag__print_abstract_origin_not_found(pos);
+ tag__check_pruned_alt_ref(dcu, dpos->abstract_origin,
+ dpos->from_alt.abstract_origin);
+ }
continue;
}
ftype__recode_dwarf_types(dtag__tag(dtype), cu);
@@ -3507,6 +3895,7 @@ static void lexblock__recode_dwarf_types(struct lexblock *tag, struct cu *cu)
dtype = dwarf_cu__find_type_by_ref(dcu, dpos, type);
if (dtype == NULL) {
tag__print_type_not_found(pos);
+ tag__check_pruned_alt_ref(dcu, dpos->type, dpos->from_alt.type);
continue;
}
pos->type = dtype->small_id;
@@ -3627,10 +4016,14 @@ static int tag__recode_dwarf_type(struct tag *tag, struct cu *cu)
case DW_TAG_namespace:
return namespace__recode_dwarf_types(tag, cu);
- /* Damn, DW_TAG_inlined_subroutine is an special case
- as dwarf_tag->id is in fact an abtract origin, i.e. must be
- looked up in the tags_table, not in the types_table.
- The others also point to routines, so are in tags_table */
+ /*
+ * DW_TAG_inlined_subroutine uses DW_AT_abstract_origin to
+ * reference the out-of-line subprogram. inline_expansion__new()
+ * stores this in dtag->type (not dtag->abstract_origin) via
+ * dwarf_tag__set_attr_type() with DW_AT_abstract_origin.
+ * For dwz binaries, the target subprogram lives in the alt file,
+ * so dtag->from_alt.type drives the lookup to dcu->alt->hash_tags.
+ */
case DW_TAG_inlined_subroutine:
case DW_TAG_imported_module:
dtype = dwarf_cu__find_tag_by_ref(cu->priv, dtag, type);
@@ -3668,6 +4061,7 @@ find_type:
check_type:
if (dtype == NULL) {
tag__print_type_not_found(tag);
+ tag__check_pruned_alt_ref(cu->priv, dtag->type, dtag->from_alt.type);
return 0;
}
out:
@@ -3706,9 +4100,13 @@ static int cu__resolve_func_ret_types_optimized(struct cu *cu, struct conf_load
for (i = 0; i < pt->nr_entries; ++i) {
struct tag *tag = pt->entries[i];
struct parameter *pos;
- struct function *fn = tag__function(tag);
+ struct function *fn;
bool has_unexpected_reg = false, has_struct_param = false;
+ if (tag == NULL)
+ continue;
+
+ fn = tag__function(tag);
function__analyze_parameter_locations(fn, cu, conf);
/* mark function as optimized if parameter is, or
@@ -4135,8 +4533,23 @@ static int __cus__load_debug_types(struct cus *cus, struct conf_load *conf, Dwfl
/* Match the define in linux:include/linux/elfnote-lto.h */
#define LINUX_ELFNOTE_LTO_INFO 0x101
-static bool cus__merging_cu(Dwarf *dw, Elf *elf)
+/* Decide whether the CUs in this file must be merged into a single
+ * CU. Two conditions require it:
+ *
+ * - LTO-produced binaries carry a Linux ELF note (LINUX_ELFNOTE_LTO_INFO)
+ * saying so;
+ * - any DW_FORM_ref_addr attribute means CUs reference each other by
+ * absolute offset and cannot be processed independently.
+ *
+ * While scanning the abbreviations, also record whether the file uses
+ * DW_FORM_GNU_ref_alt, so cus__load_module() can warn once per file
+ * when the dwz alternate debug file is missing. */
+static bool cus__merging_cu(Dwarf *dw, Elf *elf, bool *uses_alt_refs)
{
+ bool merging_cu = false;
+
+ *uses_alt_refs = false;
+
Elf_Scn *section = NULL;
while ((section = elf_nextscn(elf, section)) != 0) {
GElf_Shdr header;
@@ -4158,7 +4571,8 @@ static bool cus__merging_cu(Dwarf *dw, Elf *elf)
if (strcmp((char *)data->d_buf + name_off, "Linux") != 0)
continue;
- return *(int *)(data->d_buf + desc_off) != 0;
+ if (*(int *)(data->d_buf + desc_off) != 0)
+ merging_cu = true;
}
}
}
@@ -4192,7 +4606,9 @@ static bool cus__merging_cu(Dwarf *dw, Elf *elf)
&aboffset))
continue;
if (attr_form == DW_FORM_ref_addr)
- return true;
+ merging_cu = true;
+ else if (attr_form == DW_FORM_GNU_ref_alt)
+ *uses_alt_refs = true;
}
next_abbrev:
@@ -4202,7 +4618,7 @@ next_abbrev:
off = noff;
}
- return false;
+ return merging_cu;
}
struct dwarf_cus {
@@ -4613,6 +5029,75 @@ static int cus__merge_and_process_cu(struct cus *cus, struct conf_load *conf,
dcu->type_unit = type_dcu;
cu->priv = dcu;
cu->dfops = &dwarf__ops;
+
+ /*
+ * Check for a dwz alternate debug file. Create
+ * a separate dwarf_cu with its own hash tables
+ * for the alt file's offset space, so that
+ * DW_FORM_GNU_ref_alt references during
+ * die__process_unit() can be hashed and looked
+ * up without colliding with main-file offsets.
+ *
+ * Pre-process all partial units in the alt file
+ * so that DW_FORM_GNU_ref_alt references from
+ * regular attributes (DW_AT_abstract_origin,
+ * DW_AT_type, etc. on inlined_subroutines and
+ * other DIEs) can be resolved. These direct
+ * references can point to any DIE in the alt
+ * file, not just those in explicitly imported
+ * partial units.
+ */
+ Dwarf *alt_dw = dwarf_getalt(dw);
+
+ if (alt_dw != NULL) {
+ struct dwarf_cu *alt_dcu = dwarf_cu__new(cu);
+
+ if (alt_dcu == NULL)
+ goto out_abort;
+
+ alt_dcu->cu = cu;
+ dcu->alt = alt_dcu;
+
+ Dwarf_Off alt_off = 0, alt_noff;
+ size_t alt_cuhl;
+
+ /*
+ * First pass: register all alt PUs so that
+ * forward references between them can be
+ * resolved during processing.
+ */
+ while (dwarf_nextcu(alt_dw, alt_off, &alt_noff,
+ &alt_cuhl, NULL, NULL, NULL) == 0) {
+ Dwarf_Die alt_die_mem;
+
+ if (dwarf_offdie(alt_dw, alt_off + alt_cuhl,
+ &alt_die_mem) != NULL &&
+ !dwarf_cu__alt_pu_visited(dcu, alt_off + alt_cuhl)) {
+ if (dwarf_cu__add_alt_pu(dcu, alt_off + alt_cuhl, alt_noff))
+ goto out_abort;
+ }
+ alt_off = alt_noff;
+ }
+
+ /* Second pass: process DIEs now that all PUs
+ * are in the array and can be marked as hit.
+ */
+ dcu->processing_alt = true;
+
+ for (uint32_t i = 0; i < dcu->nr_alt_pus; i++) {
+ Dwarf_Die alt_cu_die, alt_child;
+
+ if (dwarf_offdie(alt_dw, dcu->alt_pus[i].start,
+ &alt_cu_die) != NULL &&
+ dwarf_child(&alt_cu_die, &alt_child) == 0) {
+ if (die__process_unit(&alt_child, cu, conf, 0) != 0)
+ goto out_abort;
+ }
+ }
+
+ dcu->processing_alt = false;
+ }
+
cu->language = attr_numeric(cu_die, DW_AT_language);
cu->producer_clang = attr_producer_clang(cu_die);
cus__add(cus, cu);
@@ -4641,6 +5126,9 @@ static int cus__merge_and_process_cu(struct cus *cus, struct conf_load *conf,
if (cu == NULL)
return 0;
+ if (dcu)
+ dwarf_cu__prune_unreferenced_alt_pus(dcu);
+
/* process merged cu */
if (cu__recode_dwarf_types(cu) != LSK__KEEPIT)
goto out_abort;
@@ -4698,7 +5186,25 @@ static int cus__load_module(struct cus *cus, struct conf_load *conf,
cus__remove(cus, type_cu);
}
- if (conf->force_cu_merging || cus__merging_cu(dw, elf)) {
+ bool uses_alt_refs;
+ bool merging_cu = cus__merging_cu(dw, elf, &uses_alt_refs);
+ Dwarf *alt_dw = dwarf_getalt(dw);
+
+ /* The file was dwz'd with -m (types moved to an alternate
+ * debug file) but the alt file couldn't be loaded. Warn once
+ * per file — a process-wide static would hide later files
+ * with the same problem when processing many debuginfo files
+ * in a single invocation. */
+ if (uses_alt_refs && alt_dw == NULL) {
+ fprintf(stderr,
+ "WARNING: could not resolve dwz alternate debug file for %s\n"
+ " (.gnu_debugaltlink target missing or build-id mismatch?)\n"
+ " Some types will not be available.\n"
+ " Installing the matching debuginfo package may fix this.\n",
+ filename);
+ }
+
+ if (conf->force_cu_merging || merging_cu || alt_dw != NULL) {
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 79b10725fec08a44..fc913a891bf32622 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -949,6 +949,7 @@ struct parameter {
int loc_reg;
uint16_t type_byte_size;
uint8_t true_sig_type_from_types:1;
+ uint8_t true_sig_type_from_alt:1;
uint8_t has_const_value:1;
uint8_t loc_const_value:1;
uint8_t loc_stack:1;
diff --git a/man-pages/pahole.1 b/man-pages/pahole.1
index 577c4e8b76185aba..35ea9f41223e09e7 100644
--- a/man-pages/pahole.1
+++ b/man-pages/pahole.1
@@ -374,8 +374,10 @@ Non-standard, non-BTF related features:
(function parameters, abstract origins for inlines, etc)
reference types in another CU.
Same-file imported units (DW_TAG_imported_unit) are
- auto-detected, so force_cu_merging is rarely needed with
- current pahole.
+ auto-detected. Alternate debug files using
+ DW_FORM_GNU_ref_alt are also handled automatically when
+ the matching .dwz file is available, so force_cu_merging
+ is rarely needed with current pahole.
.fi
diff --git a/tests/dwz_alt_file.sh b/tests/dwz_alt_file.sh
new file mode 100755
index 0000000000000000..fc94f3618d86938b
--- /dev/null
+++ b/tests/dwz_alt_file.sh
@@ -0,0 +1,204 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright © 2026 Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+#
+# Test that pahole correctly resolves types from dwz alternate debug
+# files (.gnu_debugaltlink / DW_FORM_GNU_ref_alt).
+#
+# Creates two binaries sharing common types, runs dwz in multifile
+# mode to deduplicate them into a .dwz alt file, then verifies pahole
+# can resolve the shared types.
+
+. "$(dirname "$0")/test_lib.sh"
+
+outdir=$(make_tmpdir)
+trap cleanup EXIT
+
+title_log "DWZ alternate debug file type resolution."
+
+CC=${CC:-gcc}
+if ! command -v ${CC%% *} > /dev/null 2>&1; then
+ info_log "skip: $CC not available"
+ test_skip
+fi
+
+if ! command -v dwz > /dev/null 2>&1; then
+ info_log "skip: dwz not available"
+ test_skip
+fi
+
+READELF=${READELF:-$(command -v readelf || command -v eu-readelf || true)}
+if [ -z "$READELF" ]; then
+ info_log "skip: neither readelf nor eu-readelf available"
+ test_skip
+fi
+
+cat > "$outdir/shared_types.h" << 'HEOF'
+struct shared_base {
+ int id;
+ long flags;
+ char *name;
+ void *data;
+};
+
+struct shared_nested {
+ struct shared_base base;
+ int count;
+ double value;
+ char buffer[64];
+};
+
+struct shared_list {
+ struct shared_nested item;
+ struct shared_list *next;
+ struct shared_list *prev;
+};
+
+enum shared_state {
+ STATE_INIT = 0,
+ STATE_RUNNING = 1,
+ STATE_PAUSED = 2,
+ STATE_STOPPED = 3,
+ STATE_ERROR = 4,
+};
+
+typedef unsigned long shared_handle_t;
+typedef int (*shared_callback_t)(struct shared_base *, enum shared_state);
+
+struct shared_context {
+ struct shared_base base;
+ struct shared_list *items;
+ shared_callback_t callback;
+ shared_handle_t handle;
+ enum shared_state state;
+ int refcount;
+ char description[128];
+};
+HEOF
+
+cat > "$outdir/prog1.c" << 'EOF'
+#include "shared_types.h"
+
+int lib_init(struct shared_context *ctx) {
+ ctx->state = STATE_INIT;
+ ctx->refcount = 1;
+ return 0;
+}
+
+int main(void) {
+ struct shared_context ctx = { .base = { .id = 1, .name = "prog1" } };
+ struct shared_nested item = { .count = 42, .value = 3.14 };
+ lib_init(&ctx);
+ return item.count;
+}
+EOF
+
+cat > "$outdir/prog2.c" << 'EOF'
+#include "shared_types.h"
+
+shared_handle_t prog2_work(struct shared_context *ctx) {
+ struct shared_list node = {
+ .item = { .base = { .id = 2 }, .count = 10 },
+ };
+ ctx->items = &node;
+ ctx->state = STATE_PAUSED;
+ return ctx->handle;
+}
+
+int main(void) {
+ struct shared_context ctx = { .base = { .id = 99, .name = "prog2" } };
+ return (int)prog2_work(&ctx);
+}
+EOF
+
+# Try DWARF 4 first (old dwz chokes on DWARF 5 input), then fall back
+# to default -g so we do not silently lose coverage on distros where
+# gcc defaults to DWARF 5 and dwz is too old for it.
+dwz_ok=0
+for dwarf_flag in -gdwarf-4 -g; do
+ if ! $CC $dwarf_flag -I"$outdir" -o "$outdir/prog1" "$outdir/prog1.c" 2>"$outdir/cc.log"; then
+ info_log " compile prog1 ($dwarf_flag) failed:"
+ info_log " $(cat "$outdir/cc.log")"
+ continue
+ fi
+
+ if ! $CC $dwarf_flag -I"$outdir" -o "$outdir/prog2" "$outdir/prog2.c" 2>"$outdir/cc.log"; then
+ info_log " compile prog2 ($dwarf_flag) failed:"
+ info_log " $(cat "$outdir/cc.log")"
+ continue
+ fi
+
+ if ! dwz -m "$outdir/dwz_alt" "$outdir/prog1" "$outdir/prog2" 2>/dev/null; then
+ info_log " dwz multifile mode failed with $dwarf_flag (types too small?)"
+ continue
+ fi
+
+ if ! $READELF -p .gnu_debugaltlink "$outdir/prog1" 2>/dev/null | grep -q dwz_alt; then
+ info_log " dwz ($dwarf_flag) did not produce .gnu_debugaltlink"
+ continue
+ fi
+
+ info_log " dwz multifile with $dwarf_flag: ok"
+ dwz_ok=1
+ break
+done
+
+if [ "$dwz_ok" -eq 0 ]; then
+ info_log "skip: could not produce dwz alt file with any DWARF version"
+ test_skip
+fi
+
+# Verify pahole can resolve the shared type from the alt file
+if ! pahole -F dwarf -C shared_context "$outdir/prog1" 2>/dev/null | grep -q "struct shared_context {"; then
+ error_log "FAIL: pahole could not resolve shared_context from prog1"
+ test_fail
+fi
+
+if ! pahole -F dwarf -C shared_context "$outdir/prog2" 2>/dev/null | grep -q "struct shared_context {"; then
+ error_log "FAIL: pahole could not resolve shared_context from prog2"
+ test_fail
+fi
+
+# Verify member resolution (shared_base is in the alt file)
+if ! pahole -F dwarf -C shared_context "$outdir/prog1" 2>/dev/null | grep -q "shared_base"; then
+ error_log "FAIL: shared_base member not resolved in shared_context"
+ test_fail
+fi
+
+# Verify no unexpected stderr output — catches warnings, errors, and
+# any new class of dwz-related diagnostics regardless of format
+pahole -F dwarf "$outdir/prog1" >/dev/null 2>"$outdir/pahole_stderr.log"
+if test -s "$outdir/pahole_stderr.log"; then
+ error_log "FAIL: pahole produced unexpected stderr processing dwz file:"
+ error_log "$(cat "$outdir/pahole_stderr.log")"
+ test_fail
+fi
+
+# Negative test: hide the alt file and verify pahole warns exactly once
+# and does not crash. This is the scenario users hit in the wild with
+# partially installed debuginfo packages.
+mv "$outdir/dwz_alt" "$outdir/dwz_alt.hidden"
+
+pahole -F dwarf "$outdir/prog1" >/dev/null 2>"$outdir/pahole_noalt_stderr.log"
+noalt_rc=$?
+
+mv "$outdir/dwz_alt.hidden" "$outdir/dwz_alt"
+
+if [ "$noalt_rc" -ne 0 ]; then
+ error_log "FAIL: pahole crashed (rc=$noalt_rc) with missing alt file"
+ test_fail
+fi
+
+if ! grep -q "could not resolve dwz alternate debug file" "$outdir/pahole_noalt_stderr.log"; then
+ error_log "FAIL: missing-alt warning not emitted"
+ error_log "$(cat "$outdir/pahole_noalt_stderr.log")"
+ test_fail
+fi
+
+noalt_warn_count=$(grep -c "could not resolve dwz alternate debug file" "$outdir/pahole_noalt_stderr.log")
+if [ "$noalt_warn_count" -ne 1 ]; then
+ error_log "FAIL: missing-alt warning emitted $noalt_warn_count times, expected 1"
+ test_fail
+fi
+
+test_pass
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2 9/9] scripts: Add vmlinux_comparison.py for DWARF/BTF analysis
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
` (7 preceding siblings ...)
2026-08-21 21:35 ` [PATCH v2 8/9] dwarf_loader: Support DW_FORM_GNU_ref_alt references to dwz alternate debug files Arnaldo Carvalho de Melo
@ 2026-08-21 21:35 ` Arnaldo Carvalho de Melo
2026-08-26 0:52 ` [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
9 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-21 21:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Analyzes vmlinux files to compare DWARF versions, compilers, and
pahole output across different builds. Validates that different DWARF
encodings (v4 vs v5, compressed vs uncompressed) produce equivalent
pahole output.
The kernel offers several CONFIG_DEBUG_INFO options that affect DWARF
encoding:
- CONFIG_DEBUG_INFO_DWARF4: DWARF version 4 format
- CONFIG_DEBUG_INFO_DWARF5: DWARF version 5 (~15% smaller sections)
- CONFIG_DEBUG_INFO_COMPRESSED_ZLIB: zlib-compressed debug sections
- CONFIG_DEBUG_INFO_COMPRESSED_ZSTD: zstd-compressed (smaller/faster)
- CONFIG_DEBUG_INFO_SPLIT: split DWARF with .dwo files (NOT supported yet)
This script validates pahole correctly handles all supported modes by
comparing output hashes across different vmlinux builds.
Features:
- Extracts DWARF version and DW_AT_producer from debug info
- Runs pahole -F dwarf and -F btf, computing output hashes
- Times the BTF encoding with perf stat --null -r5, repeating each
encoding 5 times to rule out variation from system caches, reporting
mean ± stddev (rel.err) as the BTF-Enc column
- Detects inter-CU references (DW_FORM_ref_addr in .debug_abbrev),
which pahole auto-detects and switches to the single-threaded
merged-CU mode when present, reported as the Inter-CU column
- Reports DWARF and BTF ELF section sizes (DW-Sect, BTF-Sect columns)
- Streams pahole output for memory efficiency (handles 800MB+ files)
- Optimized readelf: reads only first 100-200 lines for metadata
instead of processing entire debug sections (minutes → seconds)
- Displays results in compact ASCII table similar to coverage-table
- Configurable timeouts via --timeout-readelf and --timeout-pahole
Example output below shows a real run: DW-Hash and BTF-Hash are the
same across all files, and BTF-Enc shows the mean ± stddev (rel.err)
of 5 perf stat runs of the parallel BTF encoding.
Usage: scripts/vmlinux_comparison.py [directory]
Default directory: ~/.dws/
Test vmlinux files covering supported configurations are available in
~/dws/. Split DWARF (DW_TAG_skeleton_unit) support is deferred to the
next patch series.
Example:
acme@number:~/git/pahole$ ls -la ~/dws/vmlinux.*
Scanning /home/acme/dws for vmlinux files...
Found 5 vmlinux file(s)
Analyzing vmlinux.dwarf4...
Reading ELF sections...
Extracting DWARF version...
Extracting producer...
Running pahole -F dwarf...
Running pahole -F btf...
Checking for inter-CU references...
Running perf stat --null -r5 pahole -j --btf_encode_detached...
Analyzing vmlinux.dwarf5...
Reading ELF sections...
Extracting DWARF version...
Extracting producer...
Running pahole -F dwarf...
Running pahole -F btf...
Checking for inter-CU references...
Running perf stat --null -r5 pahole -j --btf_encode_detached...
Analyzing vmlinux.dwarf5.zlib...
Reading ELF sections...
Extracting DWARF version...
Extracting producer...
Running pahole -F dwarf...
Running pahole -F btf...
Checking for inter-CU references...
Running perf stat --null -r5 pahole -j --btf_encode_detached...
Analyzing vmlinux.dwarf5.zstd...
Reading ELF sections...
Extracting DWARF version...
Extracting producer...
Running pahole -F dwarf...
Running pahole -F btf...
Checking for inter-CU references...
Running perf stat --null -r5 pahole -j --btf_encode_detached...
Analyzing vmlinux.toolchain_default...
Reading ELF sections...
Extracting DWARF version...
Extracting producer...
Running pahole -F dwarf...
Running pahole -F btf...
Checking for inter-CU references...
Running perf stat --null -r5 pahole -j --btf_encode_detached...
┌──────────────────────────┬────────┬─────┬───────────────────────────────────────────────────────┬────────────┬──────────┬────────┬────────┬────────────────────────┬────────┐
│File │ Size│ Ver │Producer │ DW-Sect│ BTF-Sect│ DW-Hash│BTF-Hash│ BTF-Enc │Inter-CU│
├──────────────────────────┼────────┼─────┼───────────────────────────────────────────────────────┼────────────┼──────────┼────────┼────────┼────────────────────────┼────────┤
│vmlinux.dwarf4 │ 835.7MB│ 4 │GNU C11 -gdwarf-4 │ 599.2MB│ 7.3MB│86e1b611│52123c24│ 2.565s ±0.006 (0.24%) │ no │
│vmlinux.dwarf5 │ 736.3MB│ 5 │GNU C11 -gdwarf-5 │ 499.7MB│ 7.3MB│86e1b611│52123c24│ 2.550s ±0.012 (0.48%) │ no │
│vmlinux.dwarf5.zlib │ 497.8MB│ 5 │GNU C11 -gdwarf-5 -gz=zlib │ 261.2MB│ 7.3MB│86e1b611│52123c24│ 3.362s ±0.007 (0.21%) │ no │
│vmlinux.dwarf5.zstd │ 454.7MB│ 5 │GNU C11 -gdwarf-5 -gz=zstd │ 218.1MB│ 7.3MB│86e1b611│52123c24│ 3.004s ±0.004 (0.13%) │ no │
│vmlinux.toolchain_default │ 736.3MB│ 5 │GNU C11 │ 499.7MB│ 7.3MB│86e1b611│52123c24│ 2.554s ±0.011 (0.43%) │ no │
└──────────────────────────┴────────┴─────┴───────────────────────────────────────────────────────┴────────────┴──────────┴────────┴────────┴────────────────────────┴────────┘
Column descriptions:
File : vmlinux filename
Size : Total file size
Ver : DWARF version
Producer : Compiler and flags (truncated)
DW-Sect : Total size of .debug_* sections
BTF-Sect : Total size of .btf* sections
DW-Hash : SHA256 hash (first 8 chars) of pahole -F dwarf output
BTF-Hash : SHA256 hash (first 8 chars) of pahole -F btf output
BTF-Enc : Mean ± stddev (rel.err) of 5 perf stat --null -r5
pahole -j --btf_encode_detached runs, e.g. '2.989s ±0.004 (0.13%)'
Inter-CU : Whether inter-CU references (DW_FORM_ref_addr) are present
in .debug_abbrev, which pahole auto-detects and switches
to the single-threaded merged-CU mode
The DW-Hash values must match across all files, and so must the
BTF-Hash values: this validates that pahole's output does not change
with the DWARF version or compression used to build the vmlinux.
BTF-Enc is the mean of 5 perf stat runs, repeating each encoding to
rule out variation from system caches. pahole automatically detects
inter-CU references (DW_FORM_ref_addr in .debug_abbrev) and uses the
parallel -j path only when there are none; with inter-CU references
(Rust, LTO) it switches to the merged-CU mode, which is single-threaded.
The Inter-CU column reflects which path was taken, so no CONFIG_DEBUG_*
knowledge is needed to interpret the BTF-Enc numbers.
The output shows that vmlinux variants (different DWARF versions, compression)
produce identical pahole output despite different file sizes,
validating pahole's consistency across different DWARF encodings.
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
scripts/vmlinux_comparison.py | 612 ++++++++++++++++++++++++++++++++++
1 file changed, 612 insertions(+)
create mode 100755 scripts/vmlinux_comparison.py
diff --git a/scripts/vmlinux_comparison.py b/scripts/vmlinux_comparison.py
new file mode 100755
index 0000000000000000..7c370b8e8776bece
--- /dev/null
+++ b/scripts/vmlinux_comparison.py
@@ -0,0 +1,612 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
+# Arnaldo Carvalho de Melo <acme@redhat.com>
+# Assisted-by: Claude:claude-sonnet-4-5
+#
+# Compare vmlinux files: DWARF versions, producers, section sizes, and pahole output.
+#
+# Prompt used to generate this script:
+#
+# Given a directory with a series of vmlinux files, look at the DW_AT_producer
+# DWARF tags to notice what's unique for each of the vmlinux files, look at the
+# DWARF version as well, then run pahole -F dwarf and btf to see if the output
+# produced changes for both formats in the different vmlinux files, create a
+# table like the one in the upcoming coverage-table make target. Add columns for
+# each row (DWARF files) with the size of the vmlinux files and the size of the
+# DWARF and BTF related ELF sections on each of the vmlinux files.
+#
+# Usage:
+# scripts/vmlinux_comparison.py [DIRECTORY]
+#
+# Default directory: ~/dws/
+
+import argparse
+import hashlib
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import shutil
+from dataclasses import dataclass
+from typing import Optional, List, Dict, Any
+
+
+@dataclass
+class ToolError(Exception):
+ """Exception for tool execution failures with context."""
+ tool: str
+ message: str
+ returncode: Optional[int] = None
+ stderr: str = ''
+
+
+def format_size(size_bytes: int) -> str:
+ """Format byte size in human-readable format."""
+ for unit in ['B', 'KB', 'MB', 'GB']:
+ if size_bytes < 1024.0:
+ return f"{size_bytes:.1f}{unit}"
+ size_bytes /= 1024.0
+ return f"{size_bytes:.1f}TB"
+
+
+def get_file_size(path: str) -> int:
+ """Get file size in bytes."""
+ return os.path.getsize(path)
+
+
+def check_tool_exists(tool_name: str) -> bool:
+ """Check if a command-line tool exists and is executable."""
+ try:
+ result = subprocess.run(['which', tool_name], capture_output=True)
+ return result.returncode == 0
+ except Exception:
+ return False
+
+
+def run_readelf_sections(vmlinux_path: str, timeout: int = 30) -> Dict[str, int]:
+ """Extract ELF section sizes using readelf -SW (wide output)."""
+ sections = {}
+ try:
+ cmd = ['readelf', '-SW', vmlinux_path]
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
+ if result.returncode != 0:
+ return sections
+
+ # Parse section headers using regex to avoid column alignment issues
+ # Format: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
+ # Example: [14] .BTF PROGBITS ffffffff874cc000 66cc000 755775 00 A 0 0 1
+ for line in result.stdout.splitlines():
+ match = re.search(r'\[\s*\d+\]\s+(\S+)\s+\S+\s+\S+\s+\S+\s+(\S+)', line)
+ if match:
+ name = match.group(1)
+ size_hex = match.group(2)
+ try:
+ size = int(size_hex, 16)
+ if size > 0:
+ sections[name] = size
+ except ValueError:
+ pass
+ except subprocess.TimeoutExpired:
+ raise ToolError('readelf', f'Timeout after {timeout}s', stderr='')
+ except Exception as e:
+ raise ToolError('readelf', str(e), stderr='')
+ return sections
+
+
+def extract_dwarf_version(vmlinux_path: str, timeout: int = 30, max_lines: int = 100) -> str:
+ """Extract DWARF version using readelf.
+
+ Note: Limited to max_lines lines to avoid timeout issues when reading
+ large debug info sections from vmlinux files.
+ """
+ try:
+ cmd = ['readelf', '--debug-dump=info', vmlinux_path]
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ try:
+ lines_read = 0
+ for line in proc.stdout:
+ if 'Version:' in line:
+ match = re.search(r'Version:\s+(\d+)', line)
+ if match:
+ proc.terminate()
+ try:
+ proc.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+ return match.group(1)
+ lines_read += 1
+ if lines_read >= max_lines:
+ break
+ proc.terminate()
+ try:
+ proc.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+ return 'N/A'
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+ raise ToolError('readelf', f'Timeout after {timeout}s', stderr='')
+ except ToolError:
+ raise
+ except Exception as e:
+ raise ToolError('readelf', str(e), stderr='')
+
+
+def extract_producer(vmlinux_path: str, timeout: int = 30, max_lines: int = 200) -> str:
+ """Extract DW_AT_producer using readelf.
+
+ Note: Limited to max_lines lines to avoid timeout issues when reading
+ large debug info sections from vmlinux files.
+ """
+ try:
+ cmd = ['readelf', '--debug-dump=info', vmlinux_path]
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ try:
+ lines_read = 0
+ for line in proc.stdout:
+ if 'DW_AT_producer' in line:
+ match = re.search(r'DW_AT_producer\s*:\s*(?:\([^)]+\):\s*)?(.+)', line)
+ if match:
+ producer = match.group(1).strip()
+ proc.terminate()
+ try:
+ proc.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+ # Extract key parts: compiler name, version, DWARF flag, compression
+ # Try to parse known patterns, but fall back to raw producer string
+ parts = []
+
+ # Get compiler and version (GCC, Clang, etc.)
+ # GCC: "GNU C11 16.1.1 20260515 (Red Hat 16.1.1-2)"
+ # Clang: "clang version 16.0.0"
+ # ICC: "Intel(R) C++ Compiler for applications targeting Intel(R) 64, Version 19.0.1.117"
+ # Vendor GCC: "gcc (GCC) 8.3.0 20191121"
+ compiler_match = re.search(r'(GNU C\d+|clang|Intel.*C.*Compiler|gcc\s+\()', producer, re.IGNORECASE)
+ if compiler_match:
+ parts.append(compiler_match.group(0).strip())
+ else:
+ # Try to extract any compiler-like prefix
+ generic_match = re.search(r'^(clang|icc|gcc|GNU|icc|Apple\s+LLVM)\s+', producer, re.IGNORECASE)
+ if generic_match:
+ parts.append(generic_match.group(0).strip())
+
+ # Get DWARF version flag
+ dwarf_match = re.search(r'-gdwarf-(\d+)', producer)
+ if dwarf_match:
+ parts.append(f"-gdwarf-{dwarf_match.group(1)}")
+
+ # Get compression if present
+ compress_match = re.search(r'-gz(=\w+)?', producer)
+ if compress_match:
+ parts.append(compress_match.group(0))
+
+ if parts:
+ return ' '.join(parts)
+ # Fallback: return first 80 chars of raw producer
+ return producer[:80] + ('...' if len(producer) > 80 else '')
+ lines_read += 1
+ if lines_read >= max_lines:
+ break
+ proc.terminate()
+ try:
+ proc.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+ return 'N/A'
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+ raise ToolError('readelf', f'Timeout after {timeout}s', stderr='')
+ except ToolError:
+ raise
+ except Exception as e:
+ raise ToolError('readelf', str(e), stderr='')
+
+
+def run_pahole_and_hash(vmlinux_path: str, format_type: str, timeout: int = 600) -> tuple[str, int]:
+ """Run pahole with given format and return output hash and size."""
+ try:
+ cmd = ['pahole', '-F', format_type, vmlinux_path]
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ try:
+ result = proc.communicate(timeout=timeout)
+ if proc.returncode != 0:
+ stderr_msg = result[1].decode('utf-8', errors='replace') if result[1] else 'no stderr'
+ raise ToolError('pahole', f'pahole -F {format_type} failed', proc.returncode, stderr_msg)
+
+ hasher = hashlib.sha256()
+ hasher.update(result[0])
+ hash_short = hasher.hexdigest()[:8]
+ return hash_short, len(result[0])
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+ raise ToolError('pahole', f'Timeout after {timeout}s', stderr='')
+ except ToolError:
+ raise
+ except Exception as e:
+ raise ToolError('pahole', str(e), stderr='')
+
+
+def has_inter_cu_references(vmlinux_path: str, timeout: int = 120) -> Optional[bool]:
+ """Detect inter-CU references (DW_FORM_ref_addr) in .debug_abbrev.
+
+ Mirrors cus__merging_cu() in dwarf_loader.c: any DW_FORM_ref_addr
+ attribute means CUs reference each other by absolute offset and
+ cannot be processed independently, so pahole falls back to the
+ merged-CU mode (force_cu_merging), which is single-threaded.
+ """
+ try:
+ cmd = ['readelf', '--debug-dump=abbrev', vmlinux_path]
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ try:
+ for line in proc.stdout:
+ if b'DW_FORM_ref_addr' in line:
+ proc.terminate()
+ try:
+ proc.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+ return True
+ proc.wait(timeout=timeout)
+ return False
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+ raise ToolError('readelf', f'Timeout after {timeout}s', stderr='')
+ except ToolError:
+ raise
+ except Exception as e:
+ raise ToolError('readelf', str(e), stderr='')
+
+
+def run_btf_encode_timed(vmlinux_path: str, threads: Optional[int] = None, timeout: int = 600) -> Optional[tuple[float, float, float]]:
+ """Run perf stat --null -r5 pahole -j --btf_encode_detached and return
+ (mean, stddev, rel_err) elapsed time in seconds, or None on failure.
+
+ Times the BTF encoding, which is what varies across the different
+ CONFIG_DEBUG_* DWARF modes. Each encoding is repeated 5 times to
+ rule out variation from system caches. pahole automatically detects
+ inter-CU references (DW_FORM_ref_addr in .debug_abbrev) and switches
+ to the merged-CU mode when present, which is single-threaded.
+ """
+ tmpfile: Optional[str] = None
+ try:
+ with tempfile.NamedTemporaryFile(prefix='vmlinux-cmp-', suffix='.btf', delete=False) as tmp:
+ tmpfile = tmp.name
+
+ try:
+ cmd = ['perf', 'stat', '--null', '-r5']
+ cmd.append('pahole')
+ if threads is not None:
+ cmd.append(f'-j{threads}')
+ else:
+ cmd.append('-j')
+ cmd.append(f'--btf_encode_detached={tmpfile}')
+ cmd.append(vmlinux_path)
+
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
+ if result.returncode != 0:
+ return None
+
+ # perf stat prints to stderr, e.g.:
+ # 3.023371323 +- 0.003831050 seconds time elapsed ( +- 0.13% )
+ # Also handle locale variations (comma as decimal separator)
+ stderr = result.stderr.replace(',', '.')
+ match = re.search(
+ r'([0-9.]+)\s+\+-\s+([0-9.]+)\s+seconds time elapsed\s+\(\s+\+-\s*([0-9.]+)%\s*\)',
+ stderr)
+ if match:
+ mean = float(match.group(1))
+ stddev = float(match.group(2))
+ rel_err = float(match.group(3))
+ return mean, stddev, rel_err
+ return None
+ finally:
+ if tmpfile is not None:
+ try:
+ os.unlink(tmpfile)
+ except OSError:
+ pass
+ except subprocess.TimeoutExpired:
+ return None
+ except Exception:
+ return None
+
+
+def find_vmlinux_files(directory: str) -> List[str]:
+ """Find all vmlinux* files in directory, excluding text files."""
+ vmlinux_files = []
+
+ if not os.path.isdir(directory):
+ print(f"Error: {directory} is not a directory", file=sys.stderr)
+ return []
+
+ for filename in os.listdir(directory):
+ path = os.path.join(directory, filename)
+
+ # Skip non-files
+ if not os.path.isfile(path):
+ continue
+
+ # Only include files starting with "vmlinux"
+ if not filename.startswith('vmlinux'):
+ continue
+
+ # Skip text files (.c, .txt, etc.)
+ if filename.endswith(('.c', '.txt', '.log', '.md')):
+ continue
+
+ # Check if it's an ELF file
+ try:
+ with open(path, 'rb') as f:
+ magic = f.read(4)
+ if magic != b'\x7fELF':
+ continue
+ except Exception:
+ continue
+
+ vmlinux_files.append(path)
+
+ return sorted(vmlinux_files)
+
+
+def analyze_vmlinux(vmlinux_path: str, timeouts: Dict[str, int]) -> Dict[str, Any]:
+ """Analyze a single vmlinux file and return metrics."""
+ filename = os.path.basename(vmlinux_path)
+
+ print(f" Analyzing {filename}...", file=sys.stderr)
+
+ file_size = get_file_size(vmlinux_path)
+
+ # Extract ELF section sizes
+ try:
+ print(f" Reading ELF sections...", file=sys.stderr)
+ sections = run_readelf_sections(vmlinux_path, timeout=timeouts.get('readelf', 30))
+ except ToolError as e:
+ print(f" Warning: Failed to read ELF sections: {e.message}", file=sys.stderr)
+ sections = {}
+
+ # DWARF version
+ try:
+ print(f" Extracting DWARF version...", file=sys.stderr)
+ dwarf_version = extract_dwarf_version(vmlinux_path, timeout=timeouts.get('readelf', 30))
+ except ToolError as e:
+ print(f" Error: Failed to extract DWARF version: {e.message}", file=sys.stderr)
+ dwarf_version = e.message
+
+ # Producer
+ try:
+ print(f" Extracting producer...", file=sys.stderr)
+ producer = extract_producer(vmlinux_path, timeout=timeouts.get('readelf', 30))
+ except ToolError as e:
+ print(f" Error: Failed to extract producer: {e.message}", file=sys.stderr)
+ producer = e.message
+
+ # pahole -F dwarf
+ try:
+ print(f" Running pahole -F dwarf...", file=sys.stderr)
+ dwarf_hash, _ = run_pahole_and_hash(vmlinux_path, 'dwarf', timeout=timeouts.get('pahole', 600))
+ except ToolError as e:
+ print(f" Error: pahole -F dwarf failed: {e.message}", file=sys.stderr)
+ dwarf_hash = e.message
+
+ # pahole -F btf
+ try:
+ print(f" Running pahole -F btf...", file=sys.stderr)
+ btf_hash, _ = run_pahole_and_hash(vmlinux_path, 'btf', timeout=timeouts.get('pahole', 600))
+ except ToolError as e:
+ print(f" Error: pahole -F btf failed: {e.message}", file=sys.stderr)
+ btf_hash = e.message
+
+ # Inter-CU references
+ try:
+ print(f" Checking for inter-CU references...", file=sys.stderr)
+ inter_cu_refs = has_inter_cu_references(vmlinux_path, timeout=timeouts.get('readelf', 120))
+ except ToolError as e:
+ print(f" Error: Failed to check inter-CU references: {e.message}", file=sys.stderr)
+ inter_cu_refs = None
+
+ # BTF encoding timing
+ print(f" Running perf stat --null -r5 pahole -j --btf_encode_detached...", file=sys.stderr)
+ btf_encoding_time = run_btf_encode_timed(vmlinux_path, timeout=timeouts.get('pahole', 600))
+
+ # Compute key section sizes
+ dwarf_sections = {k: v for k, v in sections.items() if k.startswith('.debug_')}
+ btf_sections = {k: v for k, v in sections.items() if k.lower().startswith('.btf')}
+
+ return {
+ 'filename': filename,
+ 'file_size': file_size,
+ 'sections': sections,
+ 'dwarf_sections': dwarf_sections,
+ 'btf_sections': btf_sections,
+ 'dwarf_version': dwarf_version,
+ 'producer': producer,
+ 'dwarf_output_hash': dwarf_hash,
+ 'btf_output_hash': btf_hash,
+ 'btf_encoding': btf_encoding_time,
+ 'inter_cu_refs': inter_cu_refs,
+ }
+
+
+def print_table(results: List[Dict[str, Any]]) -> None:
+ """Print comparison table with box-drawing characters."""
+ if not results:
+ print("No vmlinux files found.")
+ return
+
+ # Column widths
+ W_FILE = 26
+ W_SIZE = 8
+ W_VER = 5
+ W_PROD = 55
+ W_HASH = 8
+ W_TIME = 24
+ W_ICUREF = 8
+ W_DWSECT = 12
+ W_BTFSECT = 10
+
+ # Header
+ def print_sep(char='─'):
+ print(f"┌{char*W_FILE}┬{char*W_SIZE}┬{char*W_VER}┬{char*W_PROD}┬"
+ f"{char*W_DWSECT}┬{char*W_BTFSECT}┬"
+ f"{char*W_HASH}┬{char*W_HASH}┬"
+ f"{char*W_TIME}┬{char*W_ICUREF}┐")
+
+ def print_row_sep():
+ print(f"├{'─'*W_FILE}┼{'─'*W_SIZE}┼{'─'*W_VER}┼{'─'*W_PROD}┼"
+ f"{'─'*W_DWSECT}┼{'─'*W_BTFSECT}┼"
+ f"{'─'*W_HASH}┼{'─'*W_HASH}┼"
+ f"{'─'*W_TIME}┼{'─'*W_ICUREF}┤")
+
+ print_sep()
+
+ # Column headers
+ print(f"│{'File':<{W_FILE}}│{'Size':>{W_SIZE}}│{'Ver':^{W_VER}}│{'Producer':<{W_PROD}}│"
+ f"{'DW-Sect':>{W_DWSECT}}│{'BTF-Sect':>{W_BTFSECT}}│"
+ f"{'DW-Hash':>{W_HASH}}│{'BTF-Hash':>{W_HASH}}│"
+ f"{'BTF-Enc':^{W_TIME}}│{'Inter-CU':>{W_ICUREF}}│")
+
+ print_row_sep()
+
+ # Data rows
+ for r in results:
+ fname = r['filename']
+ if len(fname) > W_FILE:
+ fname = fname[:W_FILE-3] + '...'
+
+ fsize = format_size(r['file_size'])
+ ver = r['dwarf_version']
+
+ prod = r['producer']
+ if len(prod) > W_PROD:
+ prod = prod[:W_PROD-3] + '...'
+
+ # Sum of DWARF section sizes
+ dw_sect_total = sum(r['dwarf_sections'].values())
+ dw_sect_str = format_size(dw_sect_total)
+
+ # Sum of BTF section sizes
+ btf_sect_total = sum(r['btf_sections'].values())
+ btf_sect_str = format_size(btf_sect_total)
+
+ dw_hash = r['dwarf_output_hash']
+ btf_hash = r['btf_output_hash']
+
+ enc = r['btf_encoding']
+ if enc is None:
+ enc_str = 'N/A'
+ else:
+ mean, stddev, rel_err = enc
+ enc_str = f"{mean:.3f}s ±{stddev:.3f} ({rel_err:.2f}%)"
+
+ icuref = r['inter_cu_refs']
+ if icuref is None:
+ icuref_str = 'N/A'
+ elif icuref:
+ icuref_str = 'yes'
+ else:
+ icuref_str = 'no'
+
+ print(f"│{fname:<{W_FILE}}│{fsize:>{W_SIZE}}│{ver:^{W_VER}}│{prod:<{W_PROD}}│"
+ f"{dw_sect_str:>{W_DWSECT}}│{btf_sect_str:>{W_BTFSECT}}│"
+ f"{dw_hash:>{W_HASH}}│{btf_hash:>{W_HASH}}│"
+ f"{enc_str:>{W_TIME-2}} │{icuref_str:^{W_ICUREF}}│")
+
+ # Footer
+ print(f"└{'─'*W_FILE}┴{'─'*W_SIZE}┴{'─'*W_VER}┴{'─'*W_PROD}┴"
+ f"{'─'*W_DWSECT}┴{'─'*W_BTFSECT}┴"
+ f"{'─'*W_HASH}┴{'─'*W_HASH}┴"
+ f"{'─'*W_TIME}┴{'─'*W_ICUREF}┘")
+
+ print()
+ print("Column descriptions:")
+ print(" File : vmlinux filename")
+ print(" Size : Total file size")
+ print(" Ver : DWARF version")
+ print(" Producer : Compiler and flags (truncated)")
+ print(" DW-Sect : Total size of .debug_* sections")
+ print(" BTF-Sect : Total size of .btf* sections")
+ print(" DW-Hash : SHA256 hash (first 8 chars) of pahole -F dwarf output")
+ print(" BTF-Hash : SHA256 hash (first 8 chars) of pahole -F btf output")
+ print(" BTF-Enc : Mean ± stddev (rel.err) of 5 perf stat --null -r5")
+ print(" pahole -j --btf_encode_detached runs, e.g. '2.989s ±0.004 (0.13%)'")
+ print(" Inter-CU : Whether inter-CU references (DW_FORM_ref_addr) are present")
+ print(" in .debug_abbrev, which pahole auto-detects and switches")
+ print(" to the single-threaded merged-CU mode")
+ print()
+ print("The DW-Hash values must match across all files, and so must the")
+ print("BTF-Hash values: this validates that pahole's output does not change")
+ print("with the DWARF version or compression used to build the vmlinux.")
+ print()
+ print("BTF-Enc is the mean of 5 perf stat runs, repeating each encoding to")
+ print("rule out variation from system caches. pahole automatically detects")
+ print("inter-CU references (DW_FORM_ref_addr in .debug_abbrev) and uses the")
+ print("parallel -j path only when there are none; with inter-CU references")
+ print("(Rust, LTO) it switches to the merged-CU mode, which is single-threaded.")
+ print("The Inter-CU column reflects which path was taken, so no CONFIG_DEBUG_*")
+ print("knowledge is needed to interpret the BTF-Enc numbers.")
+ print()
+ print("The output shows that vmlinux variants (different DWARF versions, compression)")
+ print("produce identical pahole output despite different file sizes,")
+ print("validating pahole's consistency across different DWARF encodings.")
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser(
+ description='Compare vmlinux files: DWARF, BTF, and pahole output')
+ parser.add_argument('directory', nargs='?',
+ default=os.path.expanduser('~/.dws'),
+ help='Directory containing vmlinux files (default: ~/.dws)')
+ parser.add_argument('--timeout-readelf', type=int, default=30,
+ help='Timeout for readelf commands in seconds (default: 30)')
+ parser.add_argument('--timeout-pahole', type=int, default=600,
+ help='Timeout for pahole commands in seconds (default: 600)')
+ parser.add_argument('--threads', type=int, default=None,
+ help='Number of threads for pahole -j (default: auto)')
+ args = parser.parse_args()
+
+ directory = os.path.expanduser(args.directory)
+
+ timeouts = {
+ 'readelf': args.timeout_readelf,
+ 'pahole': args.timeout_pahole,
+ }
+
+ tools_to_check = ['readelf', 'pahole', 'perf']
+ for tool in tools_to_check:
+ if not check_tool_exists(tool):
+ print(f"Error: Required tool '{tool}' not found in PATH", file=sys.stderr)
+ sys.exit(1)
+
+ print(f"Scanning {directory} for vmlinux files...", file=sys.stderr)
+ vmlinux_files = find_vmlinux_files(directory)
+
+ if not vmlinux_files:
+ print(f"No vmlinux files found in {directory}", file=sys.stderr)
+ print("Hint: Build kernel with CONFIG_DEBUG_INFO and look for vmlinux in build directory", file=sys.stderr)
+ sys.exit(1)
+
+ print(f"Found {len(vmlinux_files)} vmlinux file(s)\n", file=sys.stderr)
+
+ results = []
+ for vmlinux_path in vmlinux_files:
+ result = analyze_vmlinux(vmlinux_path, timeouts)
+ results.append(result)
+
+ print(file=sys.stderr)
+ print_table(results)
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
--
2.55.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
` (8 preceding siblings ...)
2026-08-21 21:35 ` [PATCH v2 9/9] scripts: Add vmlinux_comparison.py for DWARF/BTF analysis Arnaldo Carvalho de Melo
@ 2026-08-26 0:52 ` Arnaldo Carvalho de Melo
2026-08-26 11:43 ` Alan Maguire
9 siblings, 1 reply; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-26 0:52 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
On Fri, Aug 21, 2026 at 06:34:59PM -0300, Arnaldo Carvalho de Melo wrote:
> From: Arnaldo Carvalho de Melo <acme@redhat.com>
>
> Add support for cross-CU type references and dwz alternate debug
> files so pahole can process binaries (e.g. a perf binary with rust
> objects, or Firefox) that use DW_FORM_ref_addr inter-CU references or
> dwz-compressed .dwz alternate debug files.
>
> Cross-CU type references:
>
> Force-merge CUs that contain inter-CU references (DW_FORM_ref_addr)
> so type lookups resolve correctly. Handle same-file partial units
> where types are shared across CUs via DW_TAG_imported_unit. Fix
> cus__merging_cu failing to detect DW_FORM_ref_addr when
> DW_FORM_implicit_const causes dwarf_getabbrevattr() to fail.
I did a lot of test runs, and with the patches that are waiting for this
series to be landed that stress even more this series, so I'll merge
this tomorrow unless anybody have anything against it.
And then as a followup I'll land a series of test patches that don't
affect the functioning of these tools, just do coverage analysis and add
lots more tests for its features.
We need to release a new version as there are people asking for features
developed and not yet released, so I'll make sure we get the goodies in
and the release out ASAP,
Best regards,
- Arnaldo
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
2026-08-26 0:52 ` [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
@ 2026-08-26 11:43 ` Alan Maguire
2026-08-26 13:10 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 19+ messages in thread
From: Alan Maguire @ 2026-08-26 11:43 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
On 26/08/2026 01:52, Arnaldo Carvalho de Melo wrote:
> On Fri, Aug 21, 2026 at 06:34:59PM -0300, Arnaldo Carvalho de Melo wrote:
>> From: Arnaldo Carvalho de Melo <acme@redhat.com>
>>
>> Add support for cross-CU type references and dwz alternate debug
>> files so pahole can process binaries (e.g. a perf binary with rust
>> objects, or Firefox) that use DW_FORM_ref_addr inter-CU references or
>> dwz-compressed .dwz alternate debug files.
>>
>> Cross-CU type references:
>>
>> Force-merge CUs that contain inter-CU references (DW_FORM_ref_addr)
>> so type lookups resolve correctly. Handle same-file partial units
>> where types are shared across CUs via DW_TAG_imported_unit. Fix
>> cus__merging_cu failing to detect DW_FORM_ref_addr when
>> DW_FORM_implicit_const causes dwarf_getabbrevattr() to fail.
>
> I did a lot of test runs, and with the patches that are waiting for this
> series to be landed that stress even more this series, so I'll merge
> this tomorrow unless anybody have anything against it.
>
> And then as a followup I'll land a series of test patches that don't
> affect the functioning of these tools, just do coverage analysis and add
> lots more tests for its features.
>
> We need to release a new version as there are people asking for features
> developed and not yet released, so I'll make sure we get the goodies in
> and the release out ASAP,
>
> Best regards,
>
> - Arnaldo
Changes look good CI-wise, one issue that we see (not just for this series)
is that perf on some systems is a shell script that selects the kernel-version-specific
binary, so the result is tests relying on perf being a binary get skipped.
Not a big deal now but maybe we could come up with variants in the future that
generate inter-cu references reliably locally.
I tried a few other approaches where we handle inter-cu references, but all seemed
like they were a bit too complex or involved multiple traversals of the CU set and
were too expensive time-wise.
I can land these if you'd prefer, whatever works. Thanks!
Alan
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
2026-08-26 11:43 ` Alan Maguire
@ 2026-08-26 13:10 ` Arnaldo Carvalho de Melo
2026-08-28 14:05 ` Alan Maguire
0 siblings, 1 reply; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-26 13:10 UTC (permalink / raw)
To: Alan Maguire; +Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
On Wed, Aug 26, 2026 at 12:43:09PM +0100, Alan Maguire wrote:
> On 26/08/2026 01:52, Arnaldo Carvalho de Melo wrote:
> > On Fri, Aug 21, 2026 at 06:34:59PM -0300, Arnaldo Carvalho de Melo wrote:
> >> Add support for cross-CU type references and dwz alternate debug
> >> files so pahole can process binaries (e.g. a perf binary with rust
> >> objects, or Firefox) that use DW_FORM_ref_addr inter-CU references or
> >> dwz-compressed .dwz alternate debug files.
> >> Cross-CU type references:
>> Force-merge CUs that contain inter-CU references (DW_FORM_ref_addr)
> >> so type lookups resolve correctly. Handle same-file partial units
> >> where types are shared across CUs via DW_TAG_imported_unit. Fix
> >> cus__merging_cu failing to detect DW_FORM_ref_addr when
> >> DW_FORM_implicit_const causes dwarf_getabbrevattr() to fail.
> > I did a lot of test runs, and with the patches that are waiting for this
> > series to be landed that stress even more this series, so I'll merge
> > this tomorrow unless anybody have anything against it.
> > And then as a followup I'll land a series of test patches that don't
> > affect the functioning of these tools, just do coverage analysis and add
> > lots more tests for its features.
> > We need to release a new version as there are people asking for features
> > developed and not yet released, so I'll make sure we get the goodies in
> > and the release out ASAP,
> Changes look good CI-wise, one issue that we see (not just for this series)
> is that perf on some systems is a shell script that selects the kernel-version-specific
> binary, so the result is tests relying on perf being a binary get skipped.
> Not a big deal now but maybe we could come up with variants in the future that
> generate inter-cu references reliably locally.
So, one of the patches in one of the test series is:
commit b7938d358c59c279a33a2c4de8943e5469a659bf
Author: Arnaldo Carvalho de Melo <acme@redhat.com>
Date: Tue Aug 4 11:07:38 2026 -0300
tests: Add automatic perf building for cross-distro compatibility
Make perf-dependent tests work across all distributions by building
perf from source when the system version lacks debug info. Previously
tests failed or skipped on Alpine, Debian without -dbgsym packages,
and other environments lacking perf debug packages.
Add get_perf_with_debug() helper to test_lib.sh that:
- Checks if system perf has debug info (file command, "not stripped")
- Downloads perf-specific tarball (~3MB) and builds with DEBUG=1
- Caches build in /tmp/pahole-test-perf-cache for reuse
- Supports wget and curl for download (Alpine compatibility)
- Skips gracefully if build fails
Uses kernel.org's perf-specific tarballs (HOWTO.build.perf) rather
than cloning the full kernel tree (~200MB+) for efficiency.
Before: 3 tests (inter_cu_refs, prettify_perf.data, prototypes) skip
on Alpine 3.20 and other distros without perf debug packages
After: Tests auto-download perf tarball and build from source
(one-time ~2min setup), then pass on all distros including
Alpine/musl systems
Update 3 tests to use get_perf_with_debug() instead of checking for
system perf.
Tested: All tests pass on Fedora with system perf, build-from-source
path exercises on systems without perf debug info.
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
> I tried a few other approaches where we handle inter-cu references, but all seemed
> like they were a bit too complex or involved multiple traversals of the CU set and
> were too expensive time-wise.
> I can land these if you'd prefer, whatever works. Thanks!
Please do it, I think its better so that we try to have multiple people
involved in landing features that touch the code code like in this case.
I'll then land the testing infra that doesn't touch the code code and is
less risky.
Thanks!
- Arnaldo
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
2026-08-26 13:10 ` Arnaldo Carvalho de Melo
@ 2026-08-28 14:05 ` Alan Maguire
2026-08-28 22:59 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 19+ messages in thread
From: Alan Maguire @ 2026-08-28 14:05 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo
On 26/08/2026 14:10, Arnaldo Carvalho de Melo wrote:
> On Wed, Aug 26, 2026 at 12:43:09PM +0100, Alan Maguire wrote:
>> On 26/08/2026 01:52, Arnaldo Carvalho de Melo wrote:
>>> On Fri, Aug 21, 2026 at 06:34:59PM -0300, Arnaldo Carvalho de Melo wrote:
>>>> Add support for cross-CU type references and dwz alternate debug
>>>> files so pahole can process binaries (e.g. a perf binary with rust
>>>> objects, or Firefox) that use DW_FORM_ref_addr inter-CU references or
>>>> dwz-compressed .dwz alternate debug files.
>
>>>> Cross-CU type references:
>
>>> Force-merge CUs that contain inter-CU references (DW_FORM_ref_addr)
>>>> so type lookups resolve correctly. Handle same-file partial units
>>>> where types are shared across CUs via DW_TAG_imported_unit. Fix
>>>> cus__merging_cu failing to detect DW_FORM_ref_addr when
>>>> DW_FORM_implicit_const causes dwarf_getabbrevattr() to fail.
>
>>> I did a lot of test runs, and with the patches that are waiting for this
>>> series to be landed that stress even more this series, so I'll merge
>>> this tomorrow unless anybody have anything against it.
>
>>> And then as a followup I'll land a series of test patches that don't
>>> affect the functioning of these tools, just do coverage analysis and add
>>> lots more tests for its features.
>
>>> We need to release a new version as there are people asking for features
>>> developed and not yet released, so I'll make sure we get the goodies in
>>> and the release out ASAP,
>
>> Changes look good CI-wise, one issue that we see (not just for this series)
>> is that perf on some systems is a shell script that selects the kernel-version-specific
>> binary, so the result is tests relying on perf being a binary get skipped.
>> Not a big deal now but maybe we could come up with variants in the future that
>> generate inter-cu references reliably locally.
>
> So, one of the patches in one of the test series is:
>
> commit b7938d358c59c279a33a2c4de8943e5469a659bf
> Author: Arnaldo Carvalho de Melo <acme@redhat.com>
> Date: Tue Aug 4 11:07:38 2026 -0300
>
> tests: Add automatic perf building for cross-distro compatibility
>
> Make perf-dependent tests work across all distributions by building
> perf from source when the system version lacks debug info. Previously
> tests failed or skipped on Alpine, Debian without -dbgsym packages,
> and other environments lacking perf debug packages.
>
> Add get_perf_with_debug() helper to test_lib.sh that:
> - Checks if system perf has debug info (file command, "not stripped")
> - Downloads perf-specific tarball (~3MB) and builds with DEBUG=1
> - Caches build in /tmp/pahole-test-perf-cache for reuse
> - Supports wget and curl for download (Alpine compatibility)
> - Skips gracefully if build fails
>
> Uses kernel.org's perf-specific tarballs (HOWTO.build.perf) rather
> than cloning the full kernel tree (~200MB+) for efficiency.
>
> Before: 3 tests (inter_cu_refs, prettify_perf.data, prototypes) skip
> on Alpine 3.20 and other distros without perf debug packages
>
> After: Tests auto-download perf tarball and build from source
> (one-time ~2min setup), then pass on all distros including
> Alpine/musl systems
>
> Update 3 tests to use get_perf_with_debug() instead of checking for
> system perf.
>
> Tested: All tests pass on Fedora with system perf, build-from-source
> path exercises on systems without perf debug info.
>
> Assisted-by: Claude:claude-sonnet-4-5
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
>
>> I tried a few other approaches where we handle inter-cu references, but all seemed
>> like they were a bit too complex or involved multiple traversals of the CU set and
>> were too expensive time-wise.
>
>> I can land these if you'd prefer, whatever works. Thanks!
>
> Please do it, I think its better so that we try to have multiple people
> involved in landing features that touch the code code like in this case.
>
> I'll then land the testing infra that doesn't touch the code code and is
> less risky.
>
> Thanks!
>
> - Arnaldo
series applied, thanks!
Alan
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
2026-08-28 14:05 ` Alan Maguire
@ 2026-08-28 22:59 ` Arnaldo Carvalho de Melo
2026-08-29 20:21 ` RFT: coverage analysis and lots more regression tests in the 'next' branch. Was: " Arnaldo Carvalho de Melo
0 siblings, 1 reply; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-28 22:59 UTC (permalink / raw)
To: Alan Maguire
Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo, bpf,
Yonghong Song, Andrii Nakryiko
On Fri, Aug 28, 2026 at 03:05:02PM +0100, Alan Maguire wrote:
> On 26/08/2026 14:10, Arnaldo Carvalho de Melo wrote:
> > On Wed, Aug 26, 2026 at 12:43:09PM +0100, Alan Maguire wrote:
> >> I tried a few other approaches where we handle inter-cu references, but all seemed
> >> like they were a bit too complex or involved multiple traversals of the CU set and
> >> were too expensive time-wise.
> >> I can land these if you'd prefer, whatever works. Thanks!
> > Please do it, I think its better so that we try to have multiple people
> > involved in landing features that touch the code code like in this case.
> > I'll then land the testing infra that doesn't touch the code code and is
> > less risky.
> series applied, thanks!
Thanks a lot!
I'm now in the process of cherry picking non-Rust related patches,
things that don't touch the .c files in the tree, just adding coverage
analysis, tons of tests and scripts for helping us do bisection tests,
etc.
I just pushed out the first batch to the next branch, I invite people to
try it and report results, it should help us to catch problems sooner by
testing most of the paths in the codebase, which is super important
these days where we are having (or will have) tons of patches being
submitted.
Tests 10-15 are different, just have the same description, this is a
preexisting problem that I think I have a fix but didn't cherry pick it
yet, there are lots more tests to cherry pick before I turn my attention
back to the rust work:
⬢ [acme@toolbx pahole]$ rm -rf build
⬢ [acme@toolbx pahole]$ ./build-and-test-cmd.sh
-- The C compiler identification is GNU 16.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Setting BUILD_SHARED_LIBS = ON
-- Checking availability of DWARF and ELF development libraries
-- Looking for dwfl_module_build_id in elf
-- Looking for dwfl_module_build_id in elf - found
-- Found dwarf.h header: /usr/include
-- Found elfutils/libdw.h header: /usr/include
-- Found libdw library: /usr/lib64/libdw.so
-- Found libelf library: /usr/lib64/libelf.so
-- Checking availability of DWARF and ELF development libraries - done
-- Found ZLIB: /usr/lib64/libz.so (found version "1.3.1")
-- Checking availability of argp library
-- Assuming argp is in libc
-- Checking availability of argp library - done
-- Checking availability of obstack library
-- Assuming obstack is in libc
-- Checking availability of obstack library - done
-- Submodule update
-- Submodule update - done
-- Version: v1.31-124-g2cb95a21681fa42e
-- Performing Test HAVE_REALLOCARRAY_SUPPORT
-- Performing Test HAVE_REALLOCARRAY_SUPPORT - Success
-- Configuring done (0.6s)
-- Generating done (0.0s)
-- Build files have been written to: /home/acme/git/pahole/build
make: Entering directory '/home/acme/git/pahole/build'
[ 1%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/bpf.c.o
[ 3%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf.c.o
[ 5%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_iter.c.o
[ 6%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/elf.c.o
[ 12%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/features.c.o
[ 12%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/bpf_prog_linfo.c.o
[ 12%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_dump.c.o
[ 13%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/gen_loader.c.o
[ 15%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/hashmap.c.o
[ 17%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/linker.c.o
[ 18%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_relocate.c.o
[ 20%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/netlink.c.o
[ 24%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/nlattr.c.o
[ 25%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf_utils.c.o
[ 25%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf.c.o
[ 27%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf_probes.c.o
[ 31%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/ringbuf.c.o
[ 31%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/zip.c.o
[ 32%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/relo_core.c.o
[ 34%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/strset.c.o
[ 36%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/usdt.c.o
[ 36%] Built target bpf
[ 39%] Building C object CMakeFiles/dwarves.dir/libctf.c.o
[ 39%] Building C object CMakeFiles/dwarves.dir/dwarves_fprintf.c.o
[ 43%] Building C object CMakeFiles/dwarves.dir/ctf_loader.c.o
[ 43%] Building C object CMakeFiles/dwarves.dir/gobuffer.c.o
[ 46%] Building C object CMakeFiles/dwarves.dir/dutil.c.o
[ 46%] Building C object CMakeFiles/dwarves.dir/btf_loader.c.o
[ 48%] Building C object CMakeFiles/dwarves.dir/rbtree.c.o
[ 50%] Building C object CMakeFiles/dwarves.dir/dwarves.c.o
[ 51%] Building C object CMakeFiles/dwarves.dir/elf_symtab.c.o
[ 53%] Building C object CMakeFiles/dwarves.dir/dwarf_loader.c.o
[ 55%] Building C object CMakeFiles/dwarves.dir/btf_encoder.c.o
[ 56%] Linking C shared library libdwarves.so
[ 56%] Built target dwarves
[ 58%] Building C object CMakeFiles/dwarves_emit.dir/dwarves_emit.c.o
[ 60%] Building C object CMakeFiles/dwarves_reorganize.dir/dwarves_reorganize.c.o
[ 62%] Building C object CMakeFiles/prefcnt.dir/prefcnt.c.o
[ 63%] Building C object CMakeFiles/scncopy.dir/scncopy.c.o
[ 65%] Building C object CMakeFiles/scncopy.dir/elfcreator.c.o
[ 68%] Building C object CMakeFiles/codiff.dir/codiff.c.o
[ 68%] Building C object CMakeFiles/pglobal.dir/pglobal.c.o
[ 70%] Building C object CMakeFiles/pdwtags.dir/pdwtags.c.o
[ 72%] Building C object CMakeFiles/dtagnames.dir/dtagnames.c.o
[ 74%] Building C object CMakeFiles/syscse.dir/syscse.c.o
[ 75%] Linking C executable dtagnames
[ 77%] Linking C executable prefcnt
[ 79%] Linking C executable syscse
[ 81%] Linking C executable pdwtags
[ 82%] Linking C executable pglobal
[ 82%] Built target dtagnames
[ 84%] Linking C executable scncopy
[ 84%] Built target prefcnt
[ 84%] Built target syscse
[ 86%] Linking C shared library libdwarves_reorganize.so
[ 86%] Built target pdwtags
[ 86%] Built target pglobal
[ 87%] Linking C shared library libdwarves_emit.so
[ 87%] Built target scncopy
[ 87%] Built target dwarves_reorganize
[ 87%] Built target dwarves_emit
[ 89%] Linking C executable codiff
[ 91%] Building C object CMakeFiles/ctracer.dir/ctracer.c.o
[ 93%] Building C object CMakeFiles/pahole.dir/pahole.c.o
[ 94%] Building C object CMakeFiles/pfunct.dir/pfunct.c.o
[ 94%] Built target codiff
[ 96%] Linking C executable pfunct
[ 98%] Linking C executable ctracer
[ 98%] Built target pfunct
[ 98%] Built target ctracer
[100%] Linking C executable pahole
[100%] Built target pahole
make: Leaving directory '/home/acme/git/pahole/build'
Testing pahole version:
v1.31-124-g2cb95a21681fa42e
Verbose mode enabled - showing diagnostic information:
Architecture: x86_64
CPUs: 32
Memory: 62.7 GB
Swap: 8.0 GB
Parallelism: unlimited (all tests run in parallel)
Tip: On low-memory systems, use -j to limit parallelism (e.g., -j 4 or -j 1)
Test artifacts: /tmp/pahole-tests/
Compiler: gcc (GCC) 16.2.1 20260819 (Red Hat 16.2.1-2)
libc: ldd (GNU libc) 2.43
bpftool: bpftool v7.6.0
VMLINUX: not set
PERF_BIN: not set
PERF_SRC_DIR: not set
1: Bitfield layout and data member filtering. : Ok
2: BTF arena type tag encoding for kfuncs. : Ok
3: BTF multi-dimensional array encoding and round-trip. : Ok
4: BTF bitfield encoding and loading round-trip. : Ok
5: BTF VAR and DATASEC encoding for global variables. : Ok
6: BTF_KIND_FWD encoding and loading round-trip. : Ok
8: BTF FLOAT and ENUM64 type encoding. : Ok
9: Check BTF type tag order. : Ok
10: Validation of BTF encoding of true_signatures. : Ok
11: Validation of BTF encoding of true_signatures. : Ok
12: Validation of BTF encoding of true_signatures. : Ok
13: Validation of BTF encoding of true_signatures. : Ok
14: Validation of BTF encoding of true_signatures. : Ok
15: Validation of BTF encoding of true_signatures. : Ok
16: Class name list from file. : Ok
17: codiff struct comparison. : Ok
18: Compilable output and type filtering. : Ok
19: Type containment and pointer search. : Ok
20: Class name filtering. : Ok
21: Default BTF on a system without BTF. : Ok
22: Display format options. : Ok
23: DWZ alternate debug file type resolution. : Ok
24: Atomic typedef emission. : Ok
25: Enumerator search. : Ok
26: Expand pointers option. : Ok
27: Type expansion and anonymous struct options. : Ok
29: Validation of GCC optimized parameters in default BTF. : Ok
30: Validation of BTF encoding of true_signatures. : Skip (no optimizations applied.)
31: Compare parallel vs merged CU loading for inter-CU type references.: Ok
32: Version output. : Ok
33: pdwtags DWARF tag display. : Ok
34: Check that pfunct can print btf_decl_tags read from BTF. : Ok
35: pfunct function statistics. : Ok
37: Sizes and holes display. : Ok
38: Sort output and separator. : Ok
39: Validation of BTF encoding of functions. : Ok
36: Pretty printing of files using DWARF type information. : Ok
28: Flexible arrays accounting. : Ok
7: Split BTF encoding (vmlinux base + kernel module). : Ok
40: Parallel reproducible DWARF Loading/Serial BTF encoding. : Ok
Saved timing data to .test-times (40 tests)
⬢ [acme@toolbx pahole]$
Cheers,
- Arnaldo
^ permalink raw reply [flat|nested] 19+ messages in thread
* RFT: coverage analysis and lots more regression tests in the 'next' branch. Was: Re: [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
2026-08-28 22:59 ` Arnaldo Carvalho de Melo
@ 2026-08-29 20:21 ` Arnaldo Carvalho de Melo
2026-08-30 17:08 ` Alan Maguire
0 siblings, 1 reply; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-29 20:21 UTC (permalink / raw)
To: Alan Maguire
Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo, bpf,
Yonghong Song, Andrii Nakryiko
On Fri, Aug 28, 2026 at 07:59:24PM -0300, Arnaldo Carvalho de Melo wrote:
> I'm now in the process of cherry picking non-Rust related patches,
> things that don't touch the .c files in the tree, just adding coverage
> analysis, tons of tests and scripts for helping us do bisection tests,
> etc.
> I just pushed out the first batch to the next branch, I invite people to
> try it and report results, it should help us to catch problems sooner by
> testing most of the paths in the codebase, which is super important
> these days where we are having (or will have) tons of patches being
> submitted.
Got all of the coverage analysis and new regression tests on the next
branch, and run the 'build-and-test.sh' script on my set of containers,
where all the test ran on:
toolsbuilder@five:~$ cat dm.log/summary
Subject: tools build test for http://192.168.86.5/pahole/dwarves-1.31.tar.xz build-and-test-cmd.sh
1 78.93 almalinux:8 : Ok gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28) , clang version 21.1.8 ( 21.1.8-1.module_el8.10.0+4172+b6b13d75) flex 2.6.1
2 158.15 almalinux:9 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14) , clang version 21.1.8 (AlmaLinux OS Foundation 21.1.8-2.el9) flex 2.6.4
3 36.95 almalinux:9-i386 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14) , clang version 21.1.8 (AlmaLinux OS Foundation 21.1.8-2.el9) flex 2.6.4
4 167.45 almalinux:10 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4) , clang version 21.1.8 (AlmaLinux OS Foundation 21.1.8-1.el10.alma.1) flex 2.6.4
5 293.08 alpine:3.22 : Ok gcc (Alpine 14.2.0) 14.2.0 , Alpine clang version 20.1.8 flex 2.6.4
6 289.06 alpine:3.23 : Ok gcc (Alpine 15.2.0) 15.2.0 , Alpine clang version 21.1.2 flex 2.6.4
7 255.51 alpine:3.24 : Ok gcc (Alpine 15.2.0) 15.2.0 , Alpine clang version 22.1.3 flex 2.6.4
8 262.55 alpine:edge : Ok gcc (Alpine 15.2.0) 15.2.0 , Alpine clang version 22.1.8 flex 2.6.4
9 152.16 amazonlinux:2023 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-5) , clang version 15.0.7 (AWS 15.0.7-3.amzn2023.0.4) flex 2.6.4
10 152.38 amazonlinux:devel : Ok gcc (GCC) 11.3.1 20221121 (Red Hat 11.3.1-4) , clang version 15.0.6 (Amazon Linux 15.0.6-3.amzn2023.0.2) flex 2.6.4
11 171.22 archlinux:base : Ok gcc (GCC) 16.2.1 20260810 , clang version 22.1.8 flex 2.6.4
12 160.21 centos:stream : Ok gcc (GCC) 14.4.1 20260724 (Red Hat 14.4.1-2) , clang version 22.1.8 (CentOS 22.1.8-1.el10) flex 2.6.4
13 77.34 debian:11 : Ok gcc (Debian 10.2.1-6) 10.2.1 20210110 , Debian clang version 13.0.1-6~deb11u1 flex 2.6.4
14 154.23 debian:12 : Ok gcc (Debian 12.2.0-14+deb12u1) 12.2.0 , Debian clang version 14.0.6 flex 2.6.4
15 157.32 debian:13 : Ok gcc (Debian 14.2.0-19) 14.2.0 , Debian clang version 19.1.7 (3+b1) flex 2.6.4
16 156.96 debian:experimental : Ok gcc (Debian 16.1.0-3) 16.1.0 , Debian clang version 21.1.8 (10) flex 2.6.4
17 157.12 fedora:40 : Ok gcc (GCC) 14.2.1 20240912 (Red Hat 14.2.1-3) , clang version 18.1.8 (Fedora 18.1.8-2.fc40) flex 2.6.4
18 158.62 fedora:41 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4) , clang version 19.1.7 (Fedora 19.1.7-5.fc41) flex 2.6.4
19 161.74 fedora:42 : Ok gcc (GCC) 15.2.1 20260123 (Red Hat 15.2.1-7) , clang version 20.1.8 (Fedora 20.1.8-4.fc42) flex 2.6.4
20 160.37 fedora:43 : Ok gcc (GCC) 15.3.1 20260722 (Red Hat 15.3.1-1) , clang version 21.1.8 (Fedora 21.1.8-6.fc43) flex 2.6.4
21 239.67 fedora:44 : Ok gcc (GCC) 16.2.1 20260819 (Red Hat 16.2.1-2) , clang version 22.1.8 (Fedora 22.1.8-4.fc44) flex 2.6.4
22 238.08 fedora:45 : Ok gcc (GCC) 16.2.1 20260819 (Red Hat 16.2.1-2) , clang version 22.1.8 (Fedora 22.1.8-20.fc45) flex 2.6.4
23 170.21 fedora:rawhide : Ok gcc (GCC) 16.1.1 20260703 (Red Hat 16.1.1-4) , clang version 22.1.8 (Fedora 22.1.8-20.fc45) flex 2.6.4
24 89.22 gentoo:stage3 : Ok gcc (Gentoo 15.3.0 p8) 15.3.0 flex 2.6.4
25 166.60 manjaro:base : Ok gcc (GCC) 16.1.1 20260625 , clang version 22.1.6 flex 2.6.4
26 172.84 opensuse:16.0 : Ok gcc (SUSE Linux) 15.3.0 , clang version 19.1.7 flex 2.6.4
27 166.50 opensuse:tumbleweed : Ok gcc (SUSE Linux) 15.3.0 , clang version 22.1.8 flex 2.6.4
28 80.73 oraclelinux:8 : Ok gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28.0.1) , clang version 21.1.8 ( 21.1.8-1.module+el8.10.0+90887+a5269809) flex 2.6.1
29 162.06 oraclelinux:9 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14.0.1) , clang version 21.1.8 ( 21.1.8-2.el9) flex 2.6.4
30 164.88 oraclelinux:10 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4.0.1) , clang version 21.1.8 (Oracle America 21.1.8-1.el10) flex 2.6.4
31 82.10 rockylinux:8 : Ok gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28) , clang version 21.1.8 (RESF 21.1.8-1.module+el8.10.0+40180+8e26bdb3) flex 2.6.1
32 162.01 rockylinux:9 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14) , clang version 21.1.8 (RESF 21.1.8-2.el9) flex 2.6.4
33 166.23 rockylinux:10 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4) , clang version 21.1.8 (RESF 21.1.8-1.el10) flex 2.6.4
34 169.75 ubuntu:22.04 : Ok gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0 , Ubuntu clang version 14.0.0-1ubuntu1.1 flex 2.6.4
35 162.24 ubuntu:24.04 : Ok gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 , Ubuntu clang version 18.1.3 (1ubuntu1) flex 2.6.4
36 162.16 ubuntu:25.04 : Ok gcc (Ubuntu 14.2.0-19ubuntu2) 14.2.0 , Ubuntu clang version 20.1.2 (0ubuntu1) flex 2.6.4
37 161.96 ubuntu:25.10 : Ok gcc (Ubuntu 15.2.0-4ubuntu4) 15.2.0 , Ubuntu clang version 20.1.8 (0ubuntu4) flex 2.6.4
38 163.52 ubuntu:26.04 : Ok gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0 , Ubuntu clang version 21.1.8 (6ubuntu1) flex 2.6.4
BUILD_TARBALL_HEAD=eeb6956261d2fbe71715f0633a68fdc840b5f5ce
39 6242.1
toolsbuilder@five:~$
For instance:
toolsbuilder@five:~$ cat dm.log/fedora\:rawhide
fedora:rawhide
Downloading http://192.168.86.5/pahole/dwarves-1.31.tar.xz...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2.34M 100 2.34M 0 0 530.8M 0 0
dwarves-1.31/config.h.cmake
dwarves-1.31/btfdiff
<SNIP>
dwarves-1.31/tests/typedef_chain.sh
dwarves-1.31/tests/.test-times.seed
dwarves-1.31/HEAD
BUILD_TARBALL_HEAD=eeb6956261d2fbe71715f0633a68fdc840b5f5ce
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/16/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,objc,obj-c++,ada,go,d,m2,cobol,algol68,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugzilla.redhat.com/ --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-libstdcxx-backtrace --with-libstdcxx-zoneinfo=/usr/share/zoneinfo --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl=/builddir/build/BUILD/gcc-16.1.1-build/gcc-16.1.1-20260703/obj-x86_64-redhat-linux/isl-install --enable-offload-targets=nvptx-none,amdgcn-amdhsa --enable-offload-defaulted --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-tls=gnu2 --with-arch_32=i686 --build=x86_64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1 --disable-libssp
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 16.1.1 20260703 (Red Hat 16.1.1-4) (GCC)
-- The C compiler identification is GNU 16.1.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Setting BUILD_SHARED_LIBS = ON
-- Checking availability of DWARF and ELF development libraries
-- Looking for dwfl_module_build_id in elf
-- Looking for dwfl_module_build_id in elf - found
-- Found dwarf.h header: /usr/include
-- Found elfutils/libdw.h header: /usr/include
-- Found libdw library: /usr/lib64/libdw.so
-- Found libelf library: /usr/lib64/libelf.so
-- Checking availability of DWARF and ELF development libraries - done
-- Found ZLIB: /usr/lib64/libz.so (found version "1.3.1")
-- Checking availability of argp library
-- Assuming argp is in libc
-- Checking availability of argp library - done
-- Checking availability of obstack library
-- Assuming obstack is in libc
-- Checking availability of obstack library - done
-- Version: v1.31-tarball-g416753b4ba90 (from HEAD file)
-- Performing Test HAVE_REALLOCARRAY_SUPPORT
-- Performing Test HAVE_REALLOCARRAY_SUPPORT - Success
-- Configuring done (0.6s)
-- Generating done (0.0s)
-- Build files have been written to: /git/dwarves-1.31/build
make: Entering directory '/git/dwarves-1.31/build'
[ 1%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf.c.o
[ 3%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/features.c.o
[ 5%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/bpf.c.o
[ 8%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_relocate.c.o
[ 8%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_dump.c.o
[ 10%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/hashmap.c.o
[ 13%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/bpf_prog_linfo.c.o
[ 13%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf.c.o
[ 15%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf_probes.c.o
[ 18%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf_utils.c.o
[ 20%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/linker.c.o
[ 20%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_iter.c.o
[ 22%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/elf.c.o
[ 24%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/gen_loader.c.o
[ 25%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/netlink.c.o
[ 27%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/nlattr.c.o
[ 29%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/ringbuf.c.o
[ 31%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/relo_core.c.o
[ 32%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/usdt.c.o
[ 36%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/strset.c.o
[ 36%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/zip.c.o
[ 36%] Built target bpf
[ 39%] Building C object CMakeFiles/dwarves.dir/dwarves.c.o
[ 39%] Building C object CMakeFiles/dwarves.dir/dwarves_fprintf.c.o
[ 43%] Building C object CMakeFiles/dwarves.dir/rbtree.c.o
[ 43%] Building C object CMakeFiles/dwarves.dir/libctf.c.o
[ 46%] Building C object CMakeFiles/dwarves.dir/ctf_loader.c.o
[ 48%] Building C object CMakeFiles/dwarves.dir/gobuffer.c.o
[ 51%] Building C object CMakeFiles/dwarves.dir/btf_loader.c.o
[ 51%] Building C object CMakeFiles/dwarves.dir/elf_symtab.c.o
[ 48%] Building C object CMakeFiles/dwarves.dir/btf_encoder.c.o
[ 53%] Building C object CMakeFiles/dwarves.dir/dwarf_loader.c.o
[ 55%] Building C object CMakeFiles/dwarves.dir/dutil.c.o
[ 56%] Linking C shared library libdwarves.so
[ 56%] Built target dwarves
[ 60%] Building C object CMakeFiles/syscse.dir/syscse.c.o
[ 62%] Building C object CMakeFiles/dtagnames.dir/dtagnames.c.o
[ 65%] Building C object CMakeFiles/prefcnt.dir/prefcnt.c.o
[ 65%] Building C object CMakeFiles/dwarves_emit.dir/dwarves_emit.c.o
[ 72%] Building C object CMakeFiles/scncopy.dir/scncopy.c.o
[ 68%] Building C object CMakeFiles/pglobal.dir/pglobal.c.o
[ 62%] Building C object CMakeFiles/pdwtags.dir/pdwtags.c.o
[ 72%] Building C object CMakeFiles/dwarves_reorganize.dir/dwarves_reorganize.c.o
[ 68%] Building C object CMakeFiles/codiff.dir/codiff.c.o
[ 74%] Building C object CMakeFiles/scncopy.dir/elfcreator.c.o
[ 75%] Linking C executable dtagnames
[ 79%] Linking C executable pdwtags
[ 81%] Linking C executable syscse
[ 81%] Linking C executable prefcnt
[ 82%] Linking C executable pglobal
[ 84%] Linking C executable scncopy
[ 86%] Linking C shared library libdwarves_reorganize.so
[ 86%] Built target dtagnames
[ 86%] Built target pdwtags
[ 86%] Built target pglobal
[ 86%] Built target prefcnt
[ 86%] Built target syscse
[ 87%] Linking C shared library libdwarves_emit.so
[ 87%] Built target scncopy
[ 87%] Built target dwarves_reorganize
[ 87%] Built target dwarves_emit
[ 89%] Linking C executable codiff
[ 91%] Building C object CMakeFiles/pahole.dir/pahole.c.o
[ 93%] Building C object CMakeFiles/ctracer.dir/ctracer.c.o
[ 94%] Building C object CMakeFiles/pfunct.dir/pfunct.c.o
[ 94%] Built target codiff
[ 98%] Linking C executable ctracer
[ 98%] Linking C executable pfunct
[ 98%] Built target pfunct
[ 98%] Built target ctracer
[100%] Linking C executable pahole
[100%] Built target pahole
make: Leaving directory '/git/dwarves-1.31/build'
Testing pahole version:
v1.31-tarball-g416753b4ba90
Verbose mode enabled - showing diagnostic information:
Architecture: x86_64
CPUs: 32
Memory: 62.7 GB
Swap: 8.0 GB
Parallelism: unlimited (all tests run in parallel)
Tip: On low-memory systems, use -j to limit parallelism (e.g., -j 4 or -j 1)
Test artifacts: /tmp/pahole-tests/
Compiler: gcc (GCC) 16.1.1 20260703 (Red Hat 16.1.1-4)
libc: ldd (GNU libc) 2.44
bpftool: bpftool v7.7.0
VMLINUX: /tmp/vmlinux
PERF_BIN: not set
PERF_SRC_DIR: not set
1: dwarves_emit.c: _Atomic type compile emission. : Ok
2: _Atomic type display and --skip_emitting_atomic_typedefs. : Ok
3: Auxiliary tools: pglobal, prefcnt, dtagnames. : Ok
4: Bitfield layout and data member filtering. : Ok
5: dwarf_loader coverage: bitfields, templates, enums, inlines. : Ok
6: Bitfield typedef recoding (tag__recode_dwarf_bitfield). : Ok
7: BTF arena type tag encoding for kfuncs. : Ok
8: BTF multi-dimensional array encoding and round-trip. : Ok
9: BTF bitfield encoding and loading round-trip. : Ok
10: BTF VAR and DATASEC encoding for global variables. : Ok
11: BTF deduplication of array types across CUs. : Ok
12: Distilled base BTF generation. : Ok
13: BTF encoding options. : Ok
14: BTF encoder coverage. : Ok
15: Validation of BTF encoding of functions. : Ok
16: BTF_KIND_FWD encoding and loading round-trip. : Ok
17: BTF encoding with invalid symbol names. : Ok
18: Split BTF encoding (vmlinux base + kernel module). : Skip (no loaded module with debuginfod-available debuginfo found)
19: Check BTF type tag order. : Ok
20: BTF FLOAT and ENUM64 type encoding. : Ok
21: BTF encoder verbose logging coverage. : Ok
23: BTF encoding into ELF via pahole -J (btf_encoder__write_elf). : Ok
24: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
25: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
26: BTF true_signature: clang optimized with large union parameter : Ok
27: BTF true_signature: clang optimized parameters (scalar) : Ok
28: BTF true_signature: clang optimized with stack parameters (9 params: Ok
29: BTF true_signature: clang optimized with stack parameters (non-stat: Ok
30: Class name list from file. : Ok
31: CLI display and filtering options. : Ok
32: codiff struct comparison. : Ok
33: codiff coverage: terse, functions, verbose, multi-CU. : Ok
34: codiff member change coverage. : Ok
35: codiff multi-CU: __cus__find_cu_by_name coverage. : Ok
36: Compilable output and type filtering. : Ok
37: Type containment and pointer search. : Ok
38: C++ namespace and using-declaration display. : Ok
39: C++ variadic template parameter pack handling. : Ok
40: Class name filtering. : Ok
41: Default BTF on a system without BTF. : Ok
42: Display format options. : Ok
43: dwarf_loader.c edge cases: bitfield recode, inlining, call sites. : Ok
44: dwarves.c core API coverage. : Ok
45: DWZ alternate debug file type resolution. : Ok
46: Atomic typedef emission. : Ok
47: Legacy atomic_ base type emission via --compile (hand-crafted DWARF: Ok
48: dwarves_emit.c / dwarves_reorganize.c coverage. : Ok
49: Enumerator search. : Ok
50: Expand pointers option. : Ok
51: Type expansion and anonymous struct options. : Ok
52: Flexible arrays accounting. : Ok
53: Validation of GCC optimized parameters in default BTF. : Ok
54: Validation of BTF encoding of true_signatures. : Skip (no optimizations applied.)
56: Multi-file loading (cus__load_files). : Ok
57: Version output. : Ok
58: Header, range and seek_bytes prettify paths. : Ok
59: pahole.c scattered option paths. : Ok
60: pdwtags DWARF tag display. : Ok
61: Check that pfunct can print btf_decl_tags read from BTF. : Ok
62: pfunct coverage: symtab, class, expand_types, compile. : Ok
63: pfunct function statistics. : Ok
64: pglobal basic: variables, functions, static exclusion. : Ok
65: prefcnt.c: reference counting coverage. : Ok
68: Cacheline boundary display with large structs. : Ok
70: Sizes and holes display. : Ok
71: Small files coverage: elf_symtab, gobuffer, dutil, pglobal. : Ok
72: Sort output and separator. : Ok
73: Typedef chain display. : Ok
55: Compare parallel vs merged CU loading for inter-CU type references.: Ok
66: Pretty printing of files using DWARF type information. : Ok
67: Prototype expression parsing and prettify. : Ok
69: Parallel reproducible DWARF Loading/Serial BTF encoding. : Ok
22: BTF encoding on vmlinux. : Ok
Saved timing data to .test-times (73 tests)
+ '[' ']'
+ set +o xtrace
clang version 22.1.8 (Fedora 22.1.8-20.fc45)
Target: x86_64-redhat-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Configuration file: /etc/clang/x86_64-redhat-linux-gnu-clang.cfg
System configuration file directory: /etc/clang/
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/16
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/16
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Selected multilib: .;@m64
mkdir: cannot create directory 'build': File exists
-- Setting BUILD_SHARED_LIBS = ON
-- Checking availability of DWARF and ELF development libraries
-- Checking availability of DWARF and ELF development libraries - done
-- Checking availability of argp library
-- Assuming argp is in libc
-- Checking availability of argp library - done
-- Checking availability of obstack library
-- Assuming obstack is in libc
-- Checking availability of obstack library - done
-- Version: v1.31-tarball-g416753b4ba90 (from HEAD file)
-- Configuring done (0.1s)
-- Generating done (0.0s)
-- Build files have been written to: /git/dwarves-1.31/build
make: Entering directory '/git/dwarves-1.31/build'
[ 36%] Built target bpf
[ 56%] Built target dwarves
[ 65%] Built target dwarves_reorganize
[ 67%] Built target dwarves_emit
[ 67%] Built target dtagnames
[ 70%] Built target codiff
[ 74%] Built target pdwtags
[ 77%] Built target pglobal
[ 82%] Built target scncopy
[ 86%] Built target syscse
[ 89%] Built target prefcnt
[ 93%] Built target ctracer
[ 96%] Built target pfunct
[100%] Built target pahole
make: Leaving directory '/git/dwarves-1.31/build'
Testing pahole version:
v1.31-tarball-g416753b4ba90
Verbose mode enabled - showing diagnostic information:
Architecture: x86_64
CPUs: 32
Memory: 62.7 GB
Swap: 8.0 GB
Parallelism: unlimited (all tests run in parallel)
Tip: On low-memory systems, use -j to limit parallelism (e.g., -j 4 or -j 1)
Test artifacts: /tmp/pahole-tests/
Compiler: clang version 22.1.8 (Fedora 22.1.8-20.fc45)
libc: ldd (GNU libc) 2.44
bpftool: bpftool v7.7.0
VMLINUX: /tmp/vmlinux
PERF_BIN: not set
PERF_SRC_DIR: not set
1: dwarves_emit.c: _Atomic type compile emission. : Ok
2: _Atomic type display and --skip_emitting_atomic_typedefs. : Ok
3: Auxiliary tools: pglobal, prefcnt, dtagnames. : Ok
4: Bitfield layout and data member filtering. : Ok
5: dwarf_loader coverage: bitfields, templates, enums, inlines. : Ok
6: Bitfield typedef recoding (tag__recode_dwarf_bitfield). : Ok
7: BTF arena type tag encoding for kfuncs. : Ok
8: BTF multi-dimensional array encoding and round-trip. : Ok
9: BTF bitfield encoding and loading round-trip. : Ok
10: BTF VAR and DATASEC encoding for global variables. : Ok
11: BTF deduplication of array types across CUs. : Ok
12: Distilled base BTF generation. : Ok
13: BTF encoding options. : Ok
14: BTF encoder coverage. : Ok
15: Validation of BTF encoding of functions. : Ok
16: BTF_KIND_FWD encoding and loading round-trip. : Ok
17: BTF encoding with invalid symbol names. : Ok
18: Split BTF encoding (vmlinux base + kernel module). : Skip (no loaded module with debuginfod-available debuginfo found)
19: Check BTF type tag order. : Ok
20: BTF FLOAT and ENUM64 type encoding. : Ok
21: BTF encoder verbose logging coverage. : Ok
22: BTF encoding into ELF via pahole -J (btf_encoder__write_elf). : Ok
23: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
24: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
25: BTF true_signature: clang optimized with large union parameter : Ok
26: BTF true_signature: clang optimized parameters (scalar) : Ok
27: BTF true_signature: clang optimized with stack parameters (9 params: Ok
28: BTF true_signature: clang optimized with stack parameters (non-stat: Ok
29: Class name list from file. : Ok
30: CLI display and filtering options. : Ok
31: codiff struct comparison. : Ok
32: codiff coverage: terse, functions, verbose, multi-CU. : Ok
33: codiff member change coverage. : Ok
34: codiff multi-CU: __cus__find_cu_by_name coverage. : Ok
35: Compilable output and type filtering. : Ok
36: Type containment and pointer search. : Ok
37: C++ namespace and using-declaration display. : Ok
38: C++ variadic template parameter pack handling. : Ok
39: Class name filtering. : Ok
40: Default BTF on a system without BTF. : Ok
41: Display format options. : Ok
42: dwarf_loader.c edge cases: bitfield recode, inlining, call sites. : Ok
43: dwarves.c core API coverage. : Ok
44: DWZ alternate debug file type resolution. : Ok
45: Atomic typedef emission. : Ok
46: Legacy atomic_ base type emission via --compile (hand-crafted DWARF: Ok
47: dwarves_emit.c / dwarves_reorganize.c coverage. : Ok
48: Enumerator search. : Ok
49: Expand pointers option. : Ok
50: Type expansion and anonymous struct options. : Ok
51: Flexible arrays accounting. : Ok
52: Validation of GCC optimized parameters in default BTF. : Ok
53: Validation of BTF encoding of true_signatures. : Skip (no optimizations applied.)
54: Multi-file loading (cus__load_files). : Ok
55: Version output. : Ok
56: Header, range and seek_bytes prettify paths. : Ok
57: pahole.c scattered option paths. : Ok
58: pdwtags DWARF tag display. : Ok
59: Check that pfunct can print btf_decl_tags read from BTF. : Ok
60: pfunct coverage: symtab, class, expand_types, compile. : Ok
61: pfunct function statistics. : Ok
62: pglobal basic: variables, functions, static exclusion. : Ok
63: prefcnt.c: reference counting coverage. : Ok
64: Cacheline boundary display with large structs. : Ok
65: Sizes and holes display. : Ok
66: Small files coverage: elf_symtab, gobuffer, dutil, pglobal. : Ok
67: Sort output and separator. : Ok
68: Typedef chain display. : Ok
70: Compare parallel vs merged CU loading for inter-CU type references.: Ok
71: Pretty printing of files using DWARF type information. : Ok
72: Prototype expression parsing and prettify. : Ok
73: Parallel reproducible DWARF Loading/Serial BTF encoding. : Ok
69: BTF encoding on vmlinux. : Ok
Saved timing data to .test-times (73 tests)
toolsbuilder@five:~$
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: RFT: coverage analysis and lots more regression tests in the 'next' branch. Was: Re: [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
2026-08-29 20:21 ` RFT: coverage analysis and lots more regression tests in the 'next' branch. Was: " Arnaldo Carvalho de Melo
@ 2026-08-30 17:08 ` Alan Maguire
2026-08-31 0:18 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 19+ messages in thread
From: Alan Maguire @ 2026-08-30 17:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo, bpf,
Yonghong Song, Andrii Nakryiko
On 29/08/2026 21:21, Arnaldo Carvalho de Melo wrote:
> On Fri, Aug 28, 2026 at 07:59:24PM -0300, Arnaldo Carvalho de Melo wrote:
>> I'm now in the process of cherry picking non-Rust related patches,
>> things that don't touch the .c files in the tree, just adding coverage
>> analysis, tons of tests and scripts for helping us do bisection tests,
>> etc.
>
>> I just pushed out the first batch to the next branch, I invite people to
>> try it and report results, it should help us to catch problems sooner by
>> testing most of the paths in the codebase, which is super important
>> these days where we are having (or will have) tons of patches being
>> submitted.
>
> Got all of the coverage analysis and new regression tests on the next
> branch, and run the 'build-and-test.sh' script on my set of containers,
> where all the test ran on:
This looks fantastic! I ran it thru CI:
https://github.com/alan-maguire/dwarves/actions/runs/33255515875
Let's get the coverage report enabled as part of CI too..
>
> toolsbuilder@five:~$ cat dm.log/summary
> Subject: tools build test for http://192.168.86.5/pahole/dwarves-1.31.tar.xz build-and-test-cmd.sh
>
> 1 78.93 almalinux:8 : Ok gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28) , clang version 21.1.8 ( 21.1.8-1.module_el8.10.0+4172+b6b13d75) flex 2.6.1
> 2 158.15 almalinux:9 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14) , clang version 21.1.8 (AlmaLinux OS Foundation 21.1.8-2.el9) flex 2.6.4
> 3 36.95 almalinux:9-i386 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14) , clang version 21.1.8 (AlmaLinux OS Foundation 21.1.8-2.el9) flex 2.6.4
> 4 167.45 almalinux:10 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4) , clang version 21.1.8 (AlmaLinux OS Foundation 21.1.8-1.el10.alma.1) flex 2.6.4
> 5 293.08 alpine:3.22 : Ok gcc (Alpine 14.2.0) 14.2.0 , Alpine clang version 20.1.8 flex 2.6.4
> 6 289.06 alpine:3.23 : Ok gcc (Alpine 15.2.0) 15.2.0 , Alpine clang version 21.1.2 flex 2.6.4
> 7 255.51 alpine:3.24 : Ok gcc (Alpine 15.2.0) 15.2.0 , Alpine clang version 22.1.3 flex 2.6.4
> 8 262.55 alpine:edge : Ok gcc (Alpine 15.2.0) 15.2.0 , Alpine clang version 22.1.8 flex 2.6.4
> 9 152.16 amazonlinux:2023 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-5) , clang version 15.0.7 (AWS 15.0.7-3.amzn2023.0.4) flex 2.6.4
> 10 152.38 amazonlinux:devel : Ok gcc (GCC) 11.3.1 20221121 (Red Hat 11.3.1-4) , clang version 15.0.6 (Amazon Linux 15.0.6-3.amzn2023.0.2) flex 2.6.4
> 11 171.22 archlinux:base : Ok gcc (GCC) 16.2.1 20260810 , clang version 22.1.8 flex 2.6.4
> 12 160.21 centos:stream : Ok gcc (GCC) 14.4.1 20260724 (Red Hat 14.4.1-2) , clang version 22.1.8 (CentOS 22.1.8-1.el10) flex 2.6.4
> 13 77.34 debian:11 : Ok gcc (Debian 10.2.1-6) 10.2.1 20210110 , Debian clang version 13.0.1-6~deb11u1 flex 2.6.4
> 14 154.23 debian:12 : Ok gcc (Debian 12.2.0-14+deb12u1) 12.2.0 , Debian clang version 14.0.6 flex 2.6.4
> 15 157.32 debian:13 : Ok gcc (Debian 14.2.0-19) 14.2.0 , Debian clang version 19.1.7 (3+b1) flex 2.6.4
> 16 156.96 debian:experimental : Ok gcc (Debian 16.1.0-3) 16.1.0 , Debian clang version 21.1.8 (10) flex 2.6.4
> 17 157.12 fedora:40 : Ok gcc (GCC) 14.2.1 20240912 (Red Hat 14.2.1-3) , clang version 18.1.8 (Fedora 18.1.8-2.fc40) flex 2.6.4
> 18 158.62 fedora:41 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4) , clang version 19.1.7 (Fedora 19.1.7-5.fc41) flex 2.6.4
> 19 161.74 fedora:42 : Ok gcc (GCC) 15.2.1 20260123 (Red Hat 15.2.1-7) , clang version 20.1.8 (Fedora 20.1.8-4.fc42) flex 2.6.4
> 20 160.37 fedora:43 : Ok gcc (GCC) 15.3.1 20260722 (Red Hat 15.3.1-1) , clang version 21.1.8 (Fedora 21.1.8-6.fc43) flex 2.6.4
> 21 239.67 fedora:44 : Ok gcc (GCC) 16.2.1 20260819 (Red Hat 16.2.1-2) , clang version 22.1.8 (Fedora 22.1.8-4.fc44) flex 2.6.4
> 22 238.08 fedora:45 : Ok gcc (GCC) 16.2.1 20260819 (Red Hat 16.2.1-2) , clang version 22.1.8 (Fedora 22.1.8-20.fc45) flex 2.6.4
> 23 170.21 fedora:rawhide : Ok gcc (GCC) 16.1.1 20260703 (Red Hat 16.1.1-4) , clang version 22.1.8 (Fedora 22.1.8-20.fc45) flex 2.6.4
> 24 89.22 gentoo:stage3 : Ok gcc (Gentoo 15.3.0 p8) 15.3.0 flex 2.6.4
> 25 166.60 manjaro:base : Ok gcc (GCC) 16.1.1 20260625 , clang version 22.1.6 flex 2.6.4
> 26 172.84 opensuse:16.0 : Ok gcc (SUSE Linux) 15.3.0 , clang version 19.1.7 flex 2.6.4
> 27 166.50 opensuse:tumbleweed : Ok gcc (SUSE Linux) 15.3.0 , clang version 22.1.8 flex 2.6.4
> 28 80.73 oraclelinux:8 : Ok gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28.0.1) , clang version 21.1.8 ( 21.1.8-1.module+el8.10.0+90887+a5269809) flex 2.6.1
> 29 162.06 oraclelinux:9 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14.0.1) , clang version 21.1.8 ( 21.1.8-2.el9) flex 2.6.4
> 30 164.88 oraclelinux:10 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4.0.1) , clang version 21.1.8 (Oracle America 21.1.8-1.el10) flex 2.6.4
> 31 82.10 rockylinux:8 : Ok gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28) , clang version 21.1.8 (RESF 21.1.8-1.module+el8.10.0+40180+8e26bdb3) flex 2.6.1
> 32 162.01 rockylinux:9 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14) , clang version 21.1.8 (RESF 21.1.8-2.el9) flex 2.6.4
> 33 166.23 rockylinux:10 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4) , clang version 21.1.8 (RESF 21.1.8-1.el10) flex 2.6.4
> 34 169.75 ubuntu:22.04 : Ok gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0 , Ubuntu clang version 14.0.0-1ubuntu1.1 flex 2.6.4
> 35 162.24 ubuntu:24.04 : Ok gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 , Ubuntu clang version 18.1.3 (1ubuntu1) flex 2.6.4
> 36 162.16 ubuntu:25.04 : Ok gcc (Ubuntu 14.2.0-19ubuntu2) 14.2.0 , Ubuntu clang version 20.1.2 (0ubuntu1) flex 2.6.4
> 37 161.96 ubuntu:25.10 : Ok gcc (Ubuntu 15.2.0-4ubuntu4) 15.2.0 , Ubuntu clang version 20.1.8 (0ubuntu4) flex 2.6.4
> 38 163.52 ubuntu:26.04 : Ok gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0 , Ubuntu clang version 21.1.8 (6ubuntu1) flex 2.6.4
> BUILD_TARBALL_HEAD=eeb6956261d2fbe71715f0633a68fdc840b5f5ce
> 39 6242.1
> toolsbuilder@five:~$
>
> For instance:
>
> toolsbuilder@five:~$ cat dm.log/fedora\:rawhide
> fedora:rawhide
> Downloading http://192.168.86.5/pahole/dwarves-1.31.tar.xz...
> % Total % Received % Xferd Average Speed Time Time Time Current
> Dload Upload Total Spent Left Speed
> 100 2.34M 100 2.34M 0 0 530.8M 0 0
> dwarves-1.31/config.h.cmake
> dwarves-1.31/btfdiff
> <SNIP>
> dwarves-1.31/tests/typedef_chain.sh
> dwarves-1.31/tests/.test-times.seed
> dwarves-1.31/HEAD
> BUILD_TARBALL_HEAD=eeb6956261d2fbe71715f0633a68fdc840b5f5ce
> Using built-in specs.
> COLLECT_GCC=gcc
> COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/16/lto-wrapper
> OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
> OFFLOAD_TARGET_DEFAULT=1
> Target: x86_64-redhat-linux
> Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,objc,obj-c++,ada,go,d,m2,cobol,algol68,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugzilla.redhat.com/ --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-libstdcxx-backtrace --with-libstdcxx-zoneinfo=/usr/share/zoneinfo --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl=/builddir/build/BUILD/gcc-16.1.1-build/gcc-16.1.1-20260703/obj-x86_64-redhat-linux/isl-install --enable-offload-targets=nvptx-none,amdgcn-amdhsa --enable-offload-defaulted --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-tls=gnu2 --with-arch_32=i686 --build=x86_64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1 --disable-libssp
> Thread model: posix
> Supported LTO compression algorithms: zlib zstd
> gcc version 16.1.1 20260703 (Red Hat 16.1.1-4) (GCC)
> -- The C compiler identification is GNU 16.1.1
> -- Detecting C compiler ABI info
> -- Detecting C compiler ABI info - done
> -- Check for working C compiler: /usr/bin/cc - skipped
> -- Detecting C compile features
> -- Detecting C compile features - done
> -- Setting BUILD_SHARED_LIBS = ON
> -- Checking availability of DWARF and ELF development libraries
> -- Looking for dwfl_module_build_id in elf
> -- Looking for dwfl_module_build_id in elf - found
> -- Found dwarf.h header: /usr/include
> -- Found elfutils/libdw.h header: /usr/include
> -- Found libdw library: /usr/lib64/libdw.so
> -- Found libelf library: /usr/lib64/libelf.so
> -- Checking availability of DWARF and ELF development libraries - done
> -- Found ZLIB: /usr/lib64/libz.so (found version "1.3.1")
> -- Checking availability of argp library
> -- Assuming argp is in libc
> -- Checking availability of argp library - done
> -- Checking availability of obstack library
> -- Assuming obstack is in libc
> -- Checking availability of obstack library - done
> -- Version: v1.31-tarball-g416753b4ba90 (from HEAD file)
> -- Performing Test HAVE_REALLOCARRAY_SUPPORT
> -- Performing Test HAVE_REALLOCARRAY_SUPPORT - Success
> -- Configuring done (0.6s)
> -- Generating done (0.0s)
> -- Build files have been written to: /git/dwarves-1.31/build
> make: Entering directory '/git/dwarves-1.31/build'
> [ 1%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf.c.o
> [ 3%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/features.c.o
> [ 5%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/bpf.c.o
> [ 8%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_relocate.c.o
> [ 8%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_dump.c.o
> [ 10%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/hashmap.c.o
> [ 13%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/bpf_prog_linfo.c.o
> [ 13%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf.c.o
> [ 15%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf_probes.c.o
> [ 18%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf_utils.c.o
> [ 20%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/linker.c.o
> [ 20%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_iter.c.o
> [ 22%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/elf.c.o
> [ 24%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/gen_loader.c.o
> [ 25%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/netlink.c.o
> [ 27%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/nlattr.c.o
> [ 29%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/ringbuf.c.o
> [ 31%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/relo_core.c.o
> [ 32%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/usdt.c.o
> [ 36%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/strset.c.o
> [ 36%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/zip.c.o
> [ 36%] Built target bpf
> [ 39%] Building C object CMakeFiles/dwarves.dir/dwarves.c.o
> [ 39%] Building C object CMakeFiles/dwarves.dir/dwarves_fprintf.c.o
> [ 43%] Building C object CMakeFiles/dwarves.dir/rbtree.c.o
> [ 43%] Building C object CMakeFiles/dwarves.dir/libctf.c.o
> [ 46%] Building C object CMakeFiles/dwarves.dir/ctf_loader.c.o
> [ 48%] Building C object CMakeFiles/dwarves.dir/gobuffer.c.o
> [ 51%] Building C object CMakeFiles/dwarves.dir/btf_loader.c.o
> [ 51%] Building C object CMakeFiles/dwarves.dir/elf_symtab.c.o
> [ 48%] Building C object CMakeFiles/dwarves.dir/btf_encoder.c.o
> [ 53%] Building C object CMakeFiles/dwarves.dir/dwarf_loader.c.o
> [ 55%] Building C object CMakeFiles/dwarves.dir/dutil.c.o
> [ 56%] Linking C shared library libdwarves.so
> [ 56%] Built target dwarves
> [ 60%] Building C object CMakeFiles/syscse.dir/syscse.c.o
> [ 62%] Building C object CMakeFiles/dtagnames.dir/dtagnames.c.o
> [ 65%] Building C object CMakeFiles/prefcnt.dir/prefcnt.c.o
> [ 65%] Building C object CMakeFiles/dwarves_emit.dir/dwarves_emit.c.o
> [ 72%] Building C object CMakeFiles/scncopy.dir/scncopy.c.o
> [ 68%] Building C object CMakeFiles/pglobal.dir/pglobal.c.o
> [ 62%] Building C object CMakeFiles/pdwtags.dir/pdwtags.c.o
> [ 72%] Building C object CMakeFiles/dwarves_reorganize.dir/dwarves_reorganize.c.o
> [ 68%] Building C object CMakeFiles/codiff.dir/codiff.c.o
> [ 74%] Building C object CMakeFiles/scncopy.dir/elfcreator.c.o
> [ 75%] Linking C executable dtagnames
> [ 79%] Linking C executable pdwtags
> [ 81%] Linking C executable syscse
> [ 81%] Linking C executable prefcnt
> [ 82%] Linking C executable pglobal
> [ 84%] Linking C executable scncopy
> [ 86%] Linking C shared library libdwarves_reorganize.so
> [ 86%] Built target dtagnames
> [ 86%] Built target pdwtags
> [ 86%] Built target pglobal
> [ 86%] Built target prefcnt
> [ 86%] Built target syscse
> [ 87%] Linking C shared library libdwarves_emit.so
> [ 87%] Built target scncopy
> [ 87%] Built target dwarves_reorganize
> [ 87%] Built target dwarves_emit
> [ 89%] Linking C executable codiff
> [ 91%] Building C object CMakeFiles/pahole.dir/pahole.c.o
> [ 93%] Building C object CMakeFiles/ctracer.dir/ctracer.c.o
> [ 94%] Building C object CMakeFiles/pfunct.dir/pfunct.c.o
> [ 94%] Built target codiff
> [ 98%] Linking C executable ctracer
> [ 98%] Linking C executable pfunct
> [ 98%] Built target pfunct
> [ 98%] Built target ctracer
> [100%] Linking C executable pahole
> [100%] Built target pahole
> make: Leaving directory '/git/dwarves-1.31/build'
> Testing pahole version:
> v1.31-tarball-g416753b4ba90
>
> Verbose mode enabled - showing diagnostic information:
> Architecture: x86_64
> CPUs: 32
> Memory: 62.7 GB
> Swap: 8.0 GB
> Parallelism: unlimited (all tests run in parallel)
> Tip: On low-memory systems, use -j to limit parallelism (e.g., -j 4 or -j 1)
> Test artifacts: /tmp/pahole-tests/
> Compiler: gcc (GCC) 16.1.1 20260703 (Red Hat 16.1.1-4)
> libc: ldd (GNU libc) 2.44
> bpftool: bpftool v7.7.0
> VMLINUX: /tmp/vmlinux
> PERF_BIN: not set
> PERF_SRC_DIR: not set
>
> 1: dwarves_emit.c: _Atomic type compile emission. : Ok
> 2: _Atomic type display and --skip_emitting_atomic_typedefs. : Ok
> 3: Auxiliary tools: pglobal, prefcnt, dtagnames. : Ok
> 4: Bitfield layout and data member filtering. : Ok
> 5: dwarf_loader coverage: bitfields, templates, enums, inlines. : Ok
> 6: Bitfield typedef recoding (tag__recode_dwarf_bitfield). : Ok
> 7: BTF arena type tag encoding for kfuncs. : Ok
> 8: BTF multi-dimensional array encoding and round-trip. : Ok
> 9: BTF bitfield encoding and loading round-trip. : Ok
> 10: BTF VAR and DATASEC encoding for global variables. : Ok
> 11: BTF deduplication of array types across CUs. : Ok
> 12: Distilled base BTF generation. : Ok
> 13: BTF encoding options. : Ok
> 14: BTF encoder coverage. : Ok
> 15: Validation of BTF encoding of functions. : Ok
> 16: BTF_KIND_FWD encoding and loading round-trip. : Ok
> 17: BTF encoding with invalid symbol names. : Ok
> 18: Split BTF encoding (vmlinux base + kernel module). : Skip (no loaded module with debuginfod-available debuginfo found)
> 19: Check BTF type tag order. : Ok
> 20: BTF FLOAT and ENUM64 type encoding. : Ok
> 21: BTF encoder verbose logging coverage. : Ok
> 23: BTF encoding into ELF via pahole -J (btf_encoder__write_elf). : Ok
> 24: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
> 25: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
> 26: BTF true_signature: clang optimized with large union parameter : Ok
> 27: BTF true_signature: clang optimized parameters (scalar) : Ok
> 28: BTF true_signature: clang optimized with stack parameters (9 params: Ok
> 29: BTF true_signature: clang optimized with stack parameters (non-stat: Ok
> 30: Class name list from file. : Ok
> 31: CLI display and filtering options. : Ok
> 32: codiff struct comparison. : Ok
> 33: codiff coverage: terse, functions, verbose, multi-CU. : Ok
> 34: codiff member change coverage. : Ok
> 35: codiff multi-CU: __cus__find_cu_by_name coverage. : Ok
> 36: Compilable output and type filtering. : Ok
> 37: Type containment and pointer search. : Ok
> 38: C++ namespace and using-declaration display. : Ok
> 39: C++ variadic template parameter pack handling. : Ok
> 40: Class name filtering. : Ok
> 41: Default BTF on a system without BTF. : Ok
> 42: Display format options. : Ok
> 43: dwarf_loader.c edge cases: bitfield recode, inlining, call sites. : Ok
> 44: dwarves.c core API coverage. : Ok
> 45: DWZ alternate debug file type resolution. : Ok
> 46: Atomic typedef emission. : Ok
> 47: Legacy atomic_ base type emission via --compile (hand-crafted DWARF: Ok
> 48: dwarves_emit.c / dwarves_reorganize.c coverage. : Ok
> 49: Enumerator search. : Ok
> 50: Expand pointers option. : Ok
> 51: Type expansion and anonymous struct options. : Ok
> 52: Flexible arrays accounting. : Ok
> 53: Validation of GCC optimized parameters in default BTF. : Ok
> 54: Validation of BTF encoding of true_signatures. : Skip (no optimizations applied.)
> 56: Multi-file loading (cus__load_files). : Ok
> 57: Version output. : Ok
> 58: Header, range and seek_bytes prettify paths. : Ok
> 59: pahole.c scattered option paths. : Ok
> 60: pdwtags DWARF tag display. : Ok
> 61: Check that pfunct can print btf_decl_tags read from BTF. : Ok
> 62: pfunct coverage: symtab, class, expand_types, compile. : Ok
> 63: pfunct function statistics. : Ok
> 64: pglobal basic: variables, functions, static exclusion. : Ok
> 65: prefcnt.c: reference counting coverage. : Ok
> 68: Cacheline boundary display with large structs. : Ok
> 70: Sizes and holes display. : Ok
> 71: Small files coverage: elf_symtab, gobuffer, dutil, pglobal. : Ok
> 72: Sort output and separator. : Ok
> 73: Typedef chain display. : Ok
> 55: Compare parallel vs merged CU loading for inter-CU type references.: Ok
> 66: Pretty printing of files using DWARF type information. : Ok
> 67: Prototype expression parsing and prettify. : Ok
> 69: Parallel reproducible DWARF Loading/Serial BTF encoding. : Ok
> 22: BTF encoding on vmlinux. : Ok
> Saved timing data to .test-times (73 tests)
>
> + '[' ']'
> + set +o xtrace
> clang version 22.1.8 (Fedora 22.1.8-20.fc45)
> Target: x86_64-redhat-linux-gnu
> Thread model: posix
> InstalledDir: /usr/bin
> Configuration file: /etc/clang/x86_64-redhat-linux-gnu-clang.cfg
> System configuration file directory: /etc/clang/
> Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/16
> Selected GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/16
> Candidate multilib: .;@m64
> Candidate multilib: 32;@m32
> Selected multilib: .;@m64
> mkdir: cannot create directory 'build': File exists
> -- Setting BUILD_SHARED_LIBS = ON
> -- Checking availability of DWARF and ELF development libraries
> -- Checking availability of DWARF and ELF development libraries - done
> -- Checking availability of argp library
> -- Assuming argp is in libc
> -- Checking availability of argp library - done
> -- Checking availability of obstack library
> -- Assuming obstack is in libc
> -- Checking availability of obstack library - done
> -- Version: v1.31-tarball-g416753b4ba90 (from HEAD file)
> -- Configuring done (0.1s)
> -- Generating done (0.0s)
> -- Build files have been written to: /git/dwarves-1.31/build
> make: Entering directory '/git/dwarves-1.31/build'
> [ 36%] Built target bpf
> [ 56%] Built target dwarves
> [ 65%] Built target dwarves_reorganize
> [ 67%] Built target dwarves_emit
> [ 67%] Built target dtagnames
> [ 70%] Built target codiff
> [ 74%] Built target pdwtags
> [ 77%] Built target pglobal
> [ 82%] Built target scncopy
> [ 86%] Built target syscse
> [ 89%] Built target prefcnt
> [ 93%] Built target ctracer
> [ 96%] Built target pfunct
> [100%] Built target pahole
> make: Leaving directory '/git/dwarves-1.31/build'
> Testing pahole version:
> v1.31-tarball-g416753b4ba90
>
> Verbose mode enabled - showing diagnostic information:
> Architecture: x86_64
> CPUs: 32
> Memory: 62.7 GB
> Swap: 8.0 GB
> Parallelism: unlimited (all tests run in parallel)
> Tip: On low-memory systems, use -j to limit parallelism (e.g., -j 4 or -j 1)
> Test artifacts: /tmp/pahole-tests/
> Compiler: clang version 22.1.8 (Fedora 22.1.8-20.fc45)
> libc: ldd (GNU libc) 2.44
> bpftool: bpftool v7.7.0
> VMLINUX: /tmp/vmlinux
> PERF_BIN: not set
> PERF_SRC_DIR: not set
>
> 1: dwarves_emit.c: _Atomic type compile emission. : Ok
> 2: _Atomic type display and --skip_emitting_atomic_typedefs. : Ok
> 3: Auxiliary tools: pglobal, prefcnt, dtagnames. : Ok
> 4: Bitfield layout and data member filtering. : Ok
> 5: dwarf_loader coverage: bitfields, templates, enums, inlines. : Ok
> 6: Bitfield typedef recoding (tag__recode_dwarf_bitfield). : Ok
> 7: BTF arena type tag encoding for kfuncs. : Ok
> 8: BTF multi-dimensional array encoding and round-trip. : Ok
> 9: BTF bitfield encoding and loading round-trip. : Ok
> 10: BTF VAR and DATASEC encoding for global variables. : Ok
> 11: BTF deduplication of array types across CUs. : Ok
> 12: Distilled base BTF generation. : Ok
> 13: BTF encoding options. : Ok
> 14: BTF encoder coverage. : Ok
> 15: Validation of BTF encoding of functions. : Ok
> 16: BTF_KIND_FWD encoding and loading round-trip. : Ok
> 17: BTF encoding with invalid symbol names. : Ok
> 18: Split BTF encoding (vmlinux base + kernel module). : Skip (no loaded module with debuginfod-available debuginfo found)
> 19: Check BTF type tag order. : Ok
> 20: BTF FLOAT and ENUM64 type encoding. : Ok
> 21: BTF encoder verbose logging coverage. : Ok
> 22: BTF encoding into ELF via pahole -J (btf_encoder__write_elf). : Ok
> 23: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
> 24: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
> 25: BTF true_signature: clang optimized with large union parameter : Ok
> 26: BTF true_signature: clang optimized parameters (scalar) : Ok
> 27: BTF true_signature: clang optimized with stack parameters (9 params: Ok
> 28: BTF true_signature: clang optimized with stack parameters (non-stat: Ok
> 29: Class name list from file. : Ok
> 30: CLI display and filtering options. : Ok
> 31: codiff struct comparison. : Ok
> 32: codiff coverage: terse, functions, verbose, multi-CU. : Ok
> 33: codiff member change coverage. : Ok
> 34: codiff multi-CU: __cus__find_cu_by_name coverage. : Ok
> 35: Compilable output and type filtering. : Ok
> 36: Type containment and pointer search. : Ok
> 37: C++ namespace and using-declaration display. : Ok
> 38: C++ variadic template parameter pack handling. : Ok
> 39: Class name filtering. : Ok
> 40: Default BTF on a system without BTF. : Ok
> 41: Display format options. : Ok
> 42: dwarf_loader.c edge cases: bitfield recode, inlining, call sites. : Ok
> 43: dwarves.c core API coverage. : Ok
> 44: DWZ alternate debug file type resolution. : Ok
> 45: Atomic typedef emission. : Ok
> 46: Legacy atomic_ base type emission via --compile (hand-crafted DWARF: Ok
> 47: dwarves_emit.c / dwarves_reorganize.c coverage. : Ok
> 48: Enumerator search. : Ok
> 49: Expand pointers option. : Ok
> 50: Type expansion and anonymous struct options. : Ok
> 51: Flexible arrays accounting. : Ok
> 52: Validation of GCC optimized parameters in default BTF. : Ok
> 53: Validation of BTF encoding of true_signatures. : Skip (no optimizations applied.)
> 54: Multi-file loading (cus__load_files). : Ok
> 55: Version output. : Ok
> 56: Header, range and seek_bytes prettify paths. : Ok
> 57: pahole.c scattered option paths. : Ok
> 58: pdwtags DWARF tag display. : Ok
> 59: Check that pfunct can print btf_decl_tags read from BTF. : Ok
> 60: pfunct coverage: symtab, class, expand_types, compile. : Ok
> 61: pfunct function statistics. : Ok
> 62: pglobal basic: variables, functions, static exclusion. : Ok
> 63: prefcnt.c: reference counting coverage. : Ok
> 64: Cacheline boundary display with large structs. : Ok
> 65: Sizes and holes display. : Ok
> 66: Small files coverage: elf_symtab, gobuffer, dutil, pglobal. : Ok
> 67: Sort output and separator. : Ok
> 68: Typedef chain display. : Ok
> 70: Compare parallel vs merged CU loading for inter-CU type references.: Ok
> 71: Pretty printing of files using DWARF type information. : Ok
> 72: Prototype expression parsing and prettify. : Ok
> 73: Parallel reproducible DWARF Loading/Serial BTF encoding. : Ok
> 69: BTF encoding on vmlinux. : Ok
> Saved timing data to .test-times (73 tests)
>
> toolsbuilder@five:~$
>
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: RFT: coverage analysis and lots more regression tests in the 'next' branch. Was: Re: [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
2026-08-30 17:08 ` Alan Maguire
@ 2026-08-31 0:18 ` Arnaldo Carvalho de Melo
2026-08-31 0:48 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-31 0:18 UTC (permalink / raw)
To: Alan Maguire
Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo, bpf,
Yonghong Song, Andrii Nakryiko
On Sun, Aug 30, 2026 at 06:08:49PM +0100, Alan Maguire wrote:
> On 29/08/2026 21:21, Arnaldo Carvalho de Melo wrote:
> > On Fri, Aug 28, 2026 at 07:59:24PM -0300, Arnaldo Carvalho de Melo wrote:
> >> I'm now in the process of cherry picking non-Rust related patches,
> >> things that don't touch the .c files in the tree, just adding coverage
> >> analysis, tons of tests and scripts for helping us do bisection tests,
> >> etc.
> >> I just pushed out the first batch to the next branch, I invite people to
> >> try it and report results, it should help us to catch problems sooner by
> >> testing most of the paths in the codebase, which is super important
> >> these days where we are having (or will have) tons of patches being
> >> submitted.
> > Got all of the coverage analysis and new regression tests on the next
> > branch, and run the 'build-and-test.sh' script on my set of containers,
> > where all the test ran on:
> This looks fantastic! I ran it thru CI:
Yeah, lots of tests, some simple, some more involved, and several
scripts to help in development, like the one for test build after a
range of patches :-)
> https://github.com/alan-maguire/dwarves/actions/runs/33255515875
> Let's get the coverage report enabled as part of CI too..
Right, taking a look at it before after patches seems like a good way to
notice the need for further tests to cover new code or further test
existing one.
Since it sat there for a couple days and you run it throug CI and it
passed, I'm moving it from next to master, I still have some branches to
merge but will concentrate on the rust patches as we need to experiment
for our talk at LPC, but will do it in a separate development branch,
leaving what is in master for a few more days to then cut 1.32, as there
are requests from at least Fedora for a new release as users are needing
features that are post 1.31.
Thanks for testing it!
- Arnaldo
> >
> > toolsbuilder@five:~$ cat dm.log/summary
> > Subject: tools build test for http://192.168.86.5/pahole/dwarves-1.31.tar.xz build-and-test-cmd.sh
> >
> > 1 78.93 almalinux:8 : Ok gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28) , clang version 21.1.8 ( 21.1.8-1.module_el8.10.0+4172+b6b13d75) flex 2.6.1
> > 2 158.15 almalinux:9 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14) , clang version 21.1.8 (AlmaLinux OS Foundation 21.1.8-2.el9) flex 2.6.4
> > 3 36.95 almalinux:9-i386 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14) , clang version 21.1.8 (AlmaLinux OS Foundation 21.1.8-2.el9) flex 2.6.4
> > 4 167.45 almalinux:10 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4) , clang version 21.1.8 (AlmaLinux OS Foundation 21.1.8-1.el10.alma.1) flex 2.6.4
> > 5 293.08 alpine:3.22 : Ok gcc (Alpine 14.2.0) 14.2.0 , Alpine clang version 20.1.8 flex 2.6.4
> > 6 289.06 alpine:3.23 : Ok gcc (Alpine 15.2.0) 15.2.0 , Alpine clang version 21.1.2 flex 2.6.4
> > 7 255.51 alpine:3.24 : Ok gcc (Alpine 15.2.0) 15.2.0 , Alpine clang version 22.1.3 flex 2.6.4
> > 8 262.55 alpine:edge : Ok gcc (Alpine 15.2.0) 15.2.0 , Alpine clang version 22.1.8 flex 2.6.4
> > 9 152.16 amazonlinux:2023 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-5) , clang version 15.0.7 (AWS 15.0.7-3.amzn2023.0.4) flex 2.6.4
> > 10 152.38 amazonlinux:devel : Ok gcc (GCC) 11.3.1 20221121 (Red Hat 11.3.1-4) , clang version 15.0.6 (Amazon Linux 15.0.6-3.amzn2023.0.2) flex 2.6.4
> > 11 171.22 archlinux:base : Ok gcc (GCC) 16.2.1 20260810 , clang version 22.1.8 flex 2.6.4
> > 12 160.21 centos:stream : Ok gcc (GCC) 14.4.1 20260724 (Red Hat 14.4.1-2) , clang version 22.1.8 (CentOS 22.1.8-1.el10) flex 2.6.4
> > 13 77.34 debian:11 : Ok gcc (Debian 10.2.1-6) 10.2.1 20210110 , Debian clang version 13.0.1-6~deb11u1 flex 2.6.4
> > 14 154.23 debian:12 : Ok gcc (Debian 12.2.0-14+deb12u1) 12.2.0 , Debian clang version 14.0.6 flex 2.6.4
> > 15 157.32 debian:13 : Ok gcc (Debian 14.2.0-19) 14.2.0 , Debian clang version 19.1.7 (3+b1) flex 2.6.4
> > 16 156.96 debian:experimental : Ok gcc (Debian 16.1.0-3) 16.1.0 , Debian clang version 21.1.8 (10) flex 2.6.4
> > 17 157.12 fedora:40 : Ok gcc (GCC) 14.2.1 20240912 (Red Hat 14.2.1-3) , clang version 18.1.8 (Fedora 18.1.8-2.fc40) flex 2.6.4
> > 18 158.62 fedora:41 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4) , clang version 19.1.7 (Fedora 19.1.7-5.fc41) flex 2.6.4
> > 19 161.74 fedora:42 : Ok gcc (GCC) 15.2.1 20260123 (Red Hat 15.2.1-7) , clang version 20.1.8 (Fedora 20.1.8-4.fc42) flex 2.6.4
> > 20 160.37 fedora:43 : Ok gcc (GCC) 15.3.1 20260722 (Red Hat 15.3.1-1) , clang version 21.1.8 (Fedora 21.1.8-6.fc43) flex 2.6.4
> > 21 239.67 fedora:44 : Ok gcc (GCC) 16.2.1 20260819 (Red Hat 16.2.1-2) , clang version 22.1.8 (Fedora 22.1.8-4.fc44) flex 2.6.4
> > 22 238.08 fedora:45 : Ok gcc (GCC) 16.2.1 20260819 (Red Hat 16.2.1-2) , clang version 22.1.8 (Fedora 22.1.8-20.fc45) flex 2.6.4
> > 23 170.21 fedora:rawhide : Ok gcc (GCC) 16.1.1 20260703 (Red Hat 16.1.1-4) , clang version 22.1.8 (Fedora 22.1.8-20.fc45) flex 2.6.4
> > 24 89.22 gentoo:stage3 : Ok gcc (Gentoo 15.3.0 p8) 15.3.0 flex 2.6.4
> > 25 166.60 manjaro:base : Ok gcc (GCC) 16.1.1 20260625 , clang version 22.1.6 flex 2.6.4
> > 26 172.84 opensuse:16.0 : Ok gcc (SUSE Linux) 15.3.0 , clang version 19.1.7 flex 2.6.4
> > 27 166.50 opensuse:tumbleweed : Ok gcc (SUSE Linux) 15.3.0 , clang version 22.1.8 flex 2.6.4
> > 28 80.73 oraclelinux:8 : Ok gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28.0.1) , clang version 21.1.8 ( 21.1.8-1.module+el8.10.0+90887+a5269809) flex 2.6.1
> > 29 162.06 oraclelinux:9 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14.0.1) , clang version 21.1.8 ( 21.1.8-2.el9) flex 2.6.4
> > 30 164.88 oraclelinux:10 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4.0.1) , clang version 21.1.8 (Oracle America 21.1.8-1.el10) flex 2.6.4
> > 31 82.10 rockylinux:8 : Ok gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28) , clang version 21.1.8 (RESF 21.1.8-1.module+el8.10.0+40180+8e26bdb3) flex 2.6.1
> > 32 162.01 rockylinux:9 : Ok gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14) , clang version 21.1.8 (RESF 21.1.8-2.el9) flex 2.6.4
> > 33 166.23 rockylinux:10 : Ok gcc (GCC) 14.3.1 20251022 (Red Hat 14.3.1-4) , clang version 21.1.8 (RESF 21.1.8-1.el10) flex 2.6.4
> > 34 169.75 ubuntu:22.04 : Ok gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0 , Ubuntu clang version 14.0.0-1ubuntu1.1 flex 2.6.4
> > 35 162.24 ubuntu:24.04 : Ok gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 , Ubuntu clang version 18.1.3 (1ubuntu1) flex 2.6.4
> > 36 162.16 ubuntu:25.04 : Ok gcc (Ubuntu 14.2.0-19ubuntu2) 14.2.0 , Ubuntu clang version 20.1.2 (0ubuntu1) flex 2.6.4
> > 37 161.96 ubuntu:25.10 : Ok gcc (Ubuntu 15.2.0-4ubuntu4) 15.2.0 , Ubuntu clang version 20.1.8 (0ubuntu4) flex 2.6.4
> > 38 163.52 ubuntu:26.04 : Ok gcc (Ubuntu 15.2.0-16ubuntu1) 15.2.0 , Ubuntu clang version 21.1.8 (6ubuntu1) flex 2.6.4
> > BUILD_TARBALL_HEAD=eeb6956261d2fbe71715f0633a68fdc840b5f5ce
> > 39 6242.1
> > toolsbuilder@five:~$
> >
> > For instance:
> >
> > toolsbuilder@five:~$ cat dm.log/fedora\:rawhide
> > fedora:rawhide
> > Downloading http://192.168.86.5/pahole/dwarves-1.31.tar.xz...
> > % Total % Received % Xferd Average Speed Time Time Time Current
> > Dload Upload Total Spent Left Speed
> > 100 2.34M 100 2.34M 0 0 530.8M 0 0
> > dwarves-1.31/config.h.cmake
> > dwarves-1.31/btfdiff
> > <SNIP>
> > dwarves-1.31/tests/typedef_chain.sh
> > dwarves-1.31/tests/.test-times.seed
> > dwarves-1.31/HEAD
> > BUILD_TARBALL_HEAD=eeb6956261d2fbe71715f0633a68fdc840b5f5ce
> > Using built-in specs.
> > COLLECT_GCC=gcc
> > COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/16/lto-wrapper
> > OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
> > OFFLOAD_TARGET_DEFAULT=1
> > Target: x86_64-redhat-linux
> > Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,objc,obj-c++,ada,go,d,m2,cobol,algol68,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugzilla.redhat.com/ --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-libstdcxx-backtrace --with-libstdcxx-zoneinfo=/usr/share/zoneinfo --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl=/builddir/build/BUILD/gcc-16.1.1-build/gcc-16.1.1-20260703/obj-x86_64-redhat-linux/isl-install --enable-offload-targets=nvptx-none,amdgcn-amdhsa --enable-offload-defaulted --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-tls=gnu2 --with-arch_32=i686 --build=x86_64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1 --disable-libssp
> > Thread model: posix
> > Supported LTO compression algorithms: zlib zstd
> > gcc version 16.1.1 20260703 (Red Hat 16.1.1-4) (GCC)
> > -- The C compiler identification is GNU 16.1.1
> > -- Detecting C compiler ABI info
> > -- Detecting C compiler ABI info - done
> > -- Check for working C compiler: /usr/bin/cc - skipped
> > -- Detecting C compile features
> > -- Detecting C compile features - done
> > -- Setting BUILD_SHARED_LIBS = ON
> > -- Checking availability of DWARF and ELF development libraries
> > -- Looking for dwfl_module_build_id in elf
> > -- Looking for dwfl_module_build_id in elf - found
> > -- Found dwarf.h header: /usr/include
> > -- Found elfutils/libdw.h header: /usr/include
> > -- Found libdw library: /usr/lib64/libdw.so
> > -- Found libelf library: /usr/lib64/libelf.so
> > -- Checking availability of DWARF and ELF development libraries - done
> > -- Found ZLIB: /usr/lib64/libz.so (found version "1.3.1")
> > -- Checking availability of argp library
> > -- Assuming argp is in libc
> > -- Checking availability of argp library - done
> > -- Checking availability of obstack library
> > -- Assuming obstack is in libc
> > -- Checking availability of obstack library - done
> > -- Version: v1.31-tarball-g416753b4ba90 (from HEAD file)
> > -- Performing Test HAVE_REALLOCARRAY_SUPPORT
> > -- Performing Test HAVE_REALLOCARRAY_SUPPORT - Success
> > -- Configuring done (0.6s)
> > -- Generating done (0.0s)
> > -- Build files have been written to: /git/dwarves-1.31/build
> > make: Entering directory '/git/dwarves-1.31/build'
> > [ 1%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf.c.o
> > [ 3%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/features.c.o
> > [ 5%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/bpf.c.o
> > [ 8%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_relocate.c.o
> > [ 8%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_dump.c.o
> > [ 10%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/hashmap.c.o
> > [ 13%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/bpf_prog_linfo.c.o
> > [ 13%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf.c.o
> > [ 15%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf_probes.c.o
> > [ 18%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/libbpf_utils.c.o
> > [ 20%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/linker.c.o
> > [ 20%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/btf_iter.c.o
> > [ 22%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/elf.c.o
> > [ 24%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/gen_loader.c.o
> > [ 25%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/netlink.c.o
> > [ 27%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/nlattr.c.o
> > [ 29%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/ringbuf.c.o
> > [ 31%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/relo_core.c.o
> > [ 32%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/usdt.c.o
> > [ 36%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/strset.c.o
> > [ 36%] Building C object CMakeFiles/bpf.dir/lib/bpf/src/zip.c.o
> > [ 36%] Built target bpf
> > [ 39%] Building C object CMakeFiles/dwarves.dir/dwarves.c.o
> > [ 39%] Building C object CMakeFiles/dwarves.dir/dwarves_fprintf.c.o
> > [ 43%] Building C object CMakeFiles/dwarves.dir/rbtree.c.o
> > [ 43%] Building C object CMakeFiles/dwarves.dir/libctf.c.o
> > [ 46%] Building C object CMakeFiles/dwarves.dir/ctf_loader.c.o
> > [ 48%] Building C object CMakeFiles/dwarves.dir/gobuffer.c.o
> > [ 51%] Building C object CMakeFiles/dwarves.dir/btf_loader.c.o
> > [ 51%] Building C object CMakeFiles/dwarves.dir/elf_symtab.c.o
> > [ 48%] Building C object CMakeFiles/dwarves.dir/btf_encoder.c.o
> > [ 53%] Building C object CMakeFiles/dwarves.dir/dwarf_loader.c.o
> > [ 55%] Building C object CMakeFiles/dwarves.dir/dutil.c.o
> > [ 56%] Linking C shared library libdwarves.so
> > [ 56%] Built target dwarves
> > [ 60%] Building C object CMakeFiles/syscse.dir/syscse.c.o
> > [ 62%] Building C object CMakeFiles/dtagnames.dir/dtagnames.c.o
> > [ 65%] Building C object CMakeFiles/prefcnt.dir/prefcnt.c.o
> > [ 65%] Building C object CMakeFiles/dwarves_emit.dir/dwarves_emit.c.o
> > [ 72%] Building C object CMakeFiles/scncopy.dir/scncopy.c.o
> > [ 68%] Building C object CMakeFiles/pglobal.dir/pglobal.c.o
> > [ 62%] Building C object CMakeFiles/pdwtags.dir/pdwtags.c.o
> > [ 72%] Building C object CMakeFiles/dwarves_reorganize.dir/dwarves_reorganize.c.o
> > [ 68%] Building C object CMakeFiles/codiff.dir/codiff.c.o
> > [ 74%] Building C object CMakeFiles/scncopy.dir/elfcreator.c.o
> > [ 75%] Linking C executable dtagnames
> > [ 79%] Linking C executable pdwtags
> > [ 81%] Linking C executable syscse
> > [ 81%] Linking C executable prefcnt
> > [ 82%] Linking C executable pglobal
> > [ 84%] Linking C executable scncopy
> > [ 86%] Linking C shared library libdwarves_reorganize.so
> > [ 86%] Built target dtagnames
> > [ 86%] Built target pdwtags
> > [ 86%] Built target pglobal
> > [ 86%] Built target prefcnt
> > [ 86%] Built target syscse
> > [ 87%] Linking C shared library libdwarves_emit.so
> > [ 87%] Built target scncopy
> > [ 87%] Built target dwarves_reorganize
> > [ 87%] Built target dwarves_emit
> > [ 89%] Linking C executable codiff
> > [ 91%] Building C object CMakeFiles/pahole.dir/pahole.c.o
> > [ 93%] Building C object CMakeFiles/ctracer.dir/ctracer.c.o
> > [ 94%] Building C object CMakeFiles/pfunct.dir/pfunct.c.o
> > [ 94%] Built target codiff
> > [ 98%] Linking C executable ctracer
> > [ 98%] Linking C executable pfunct
> > [ 98%] Built target pfunct
> > [ 98%] Built target ctracer
> > [100%] Linking C executable pahole
> > [100%] Built target pahole
> > make: Leaving directory '/git/dwarves-1.31/build'
> > Testing pahole version:
> > v1.31-tarball-g416753b4ba90
> >
> > Verbose mode enabled - showing diagnostic information:
> > Architecture: x86_64
> > CPUs: 32
> > Memory: 62.7 GB
> > Swap: 8.0 GB
> > Parallelism: unlimited (all tests run in parallel)
> > Tip: On low-memory systems, use -j to limit parallelism (e.g., -j 4 or -j 1)
> > Test artifacts: /tmp/pahole-tests/
> > Compiler: gcc (GCC) 16.1.1 20260703 (Red Hat 16.1.1-4)
> > libc: ldd (GNU libc) 2.44
> > bpftool: bpftool v7.7.0
> > VMLINUX: /tmp/vmlinux
> > PERF_BIN: not set
> > PERF_SRC_DIR: not set
> >
> > 1: dwarves_emit.c: _Atomic type compile emission. : Ok
> > 2: _Atomic type display and --skip_emitting_atomic_typedefs. : Ok
> > 3: Auxiliary tools: pglobal, prefcnt, dtagnames. : Ok
> > 4: Bitfield layout and data member filtering. : Ok
> > 5: dwarf_loader coverage: bitfields, templates, enums, inlines. : Ok
> > 6: Bitfield typedef recoding (tag__recode_dwarf_bitfield). : Ok
> > 7: BTF arena type tag encoding for kfuncs. : Ok
> > 8: BTF multi-dimensional array encoding and round-trip. : Ok
> > 9: BTF bitfield encoding and loading round-trip. : Ok
> > 10: BTF VAR and DATASEC encoding for global variables. : Ok
> > 11: BTF deduplication of array types across CUs. : Ok
> > 12: Distilled base BTF generation. : Ok
> > 13: BTF encoding options. : Ok
> > 14: BTF encoder coverage. : Ok
> > 15: Validation of BTF encoding of functions. : Ok
> > 16: BTF_KIND_FWD encoding and loading round-trip. : Ok
> > 17: BTF encoding with invalid symbol names. : Ok
> > 18: Split BTF encoding (vmlinux base + kernel module). : Skip (no loaded module with debuginfod-available debuginfo found)
> > 19: Check BTF type tag order. : Ok
> > 20: BTF FLOAT and ENUM64 type encoding. : Ok
> > 21: BTF encoder verbose logging coverage. : Ok
> > 23: BTF encoding into ELF via pahole -J (btf_encoder__write_elf). : Ok
> > 24: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
> > 25: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
> > 26: BTF true_signature: clang optimized with large union parameter : Ok
> > 27: BTF true_signature: clang optimized parameters (scalar) : Ok
> > 28: BTF true_signature: clang optimized with stack parameters (9 params: Ok
> > 29: BTF true_signature: clang optimized with stack parameters (non-stat: Ok
> > 30: Class name list from file. : Ok
> > 31: CLI display and filtering options. : Ok
> > 32: codiff struct comparison. : Ok
> > 33: codiff coverage: terse, functions, verbose, multi-CU. : Ok
> > 34: codiff member change coverage. : Ok
> > 35: codiff multi-CU: __cus__find_cu_by_name coverage. : Ok
> > 36: Compilable output and type filtering. : Ok
> > 37: Type containment and pointer search. : Ok
> > 38: C++ namespace and using-declaration display. : Ok
> > 39: C++ variadic template parameter pack handling. : Ok
> > 40: Class name filtering. : Ok
> > 41: Default BTF on a system without BTF. : Ok
> > 42: Display format options. : Ok
> > 43: dwarf_loader.c edge cases: bitfield recode, inlining, call sites. : Ok
> > 44: dwarves.c core API coverage. : Ok
> > 45: DWZ alternate debug file type resolution. : Ok
> > 46: Atomic typedef emission. : Ok
> > 47: Legacy atomic_ base type emission via --compile (hand-crafted DWARF: Ok
> > 48: dwarves_emit.c / dwarves_reorganize.c coverage. : Ok
> > 49: Enumerator search. : Ok
> > 50: Expand pointers option. : Ok
> > 51: Type expansion and anonymous struct options. : Ok
> > 52: Flexible arrays accounting. : Ok
> > 53: Validation of GCC optimized parameters in default BTF. : Ok
> > 54: Validation of BTF encoding of true_signatures. : Skip (no optimizations applied.)
> > 56: Multi-file loading (cus__load_files). : Ok
> > 57: Version output. : Ok
> > 58: Header, range and seek_bytes prettify paths. : Ok
> > 59: pahole.c scattered option paths. : Ok
> > 60: pdwtags DWARF tag display. : Ok
> > 61: Check that pfunct can print btf_decl_tags read from BTF. : Ok
> > 62: pfunct coverage: symtab, class, expand_types, compile. : Ok
> > 63: pfunct function statistics. : Ok
> > 64: pglobal basic: variables, functions, static exclusion. : Ok
> > 65: prefcnt.c: reference counting coverage. : Ok
> > 68: Cacheline boundary display with large structs. : Ok
> > 70: Sizes and holes display. : Ok
> > 71: Small files coverage: elf_symtab, gobuffer, dutil, pglobal. : Ok
> > 72: Sort output and separator. : Ok
> > 73: Typedef chain display. : Ok
> > 55: Compare parallel vs merged CU loading for inter-CU type references.: Ok
> > 66: Pretty printing of files using DWARF type information. : Ok
> > 67: Prototype expression parsing and prettify. : Ok
> > 69: Parallel reproducible DWARF Loading/Serial BTF encoding. : Ok
> > 22: BTF encoding on vmlinux. : Ok
> > Saved timing data to .test-times (73 tests)
> >
> > + '[' ']'
> > + set +o xtrace
> > clang version 22.1.8 (Fedora 22.1.8-20.fc45)
> > Target: x86_64-redhat-linux-gnu
> > Thread model: posix
> > InstalledDir: /usr/bin
> > Configuration file: /etc/clang/x86_64-redhat-linux-gnu-clang.cfg
> > System configuration file directory: /etc/clang/
> > Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/16
> > Selected GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/16
> > Candidate multilib: .;@m64
> > Candidate multilib: 32;@m32
> > Selected multilib: .;@m64
> > mkdir: cannot create directory 'build': File exists
> > -- Setting BUILD_SHARED_LIBS = ON
> > -- Checking availability of DWARF and ELF development libraries
> > -- Checking availability of DWARF and ELF development libraries - done
> > -- Checking availability of argp library
> > -- Assuming argp is in libc
> > -- Checking availability of argp library - done
> > -- Checking availability of obstack library
> > -- Assuming obstack is in libc
> > -- Checking availability of obstack library - done
> > -- Version: v1.31-tarball-g416753b4ba90 (from HEAD file)
> > -- Configuring done (0.1s)
> > -- Generating done (0.0s)
> > -- Build files have been written to: /git/dwarves-1.31/build
> > make: Entering directory '/git/dwarves-1.31/build'
> > [ 36%] Built target bpf
> > [ 56%] Built target dwarves
> > [ 65%] Built target dwarves_reorganize
> > [ 67%] Built target dwarves_emit
> > [ 67%] Built target dtagnames
> > [ 70%] Built target codiff
> > [ 74%] Built target pdwtags
> > [ 77%] Built target pglobal
> > [ 82%] Built target scncopy
> > [ 86%] Built target syscse
> > [ 89%] Built target prefcnt
> > [ 93%] Built target ctracer
> > [ 96%] Built target pfunct
> > [100%] Built target pahole
> > make: Leaving directory '/git/dwarves-1.31/build'
> > Testing pahole version:
> > v1.31-tarball-g416753b4ba90
> >
> > Verbose mode enabled - showing diagnostic information:
> > Architecture: x86_64
> > CPUs: 32
> > Memory: 62.7 GB
> > Swap: 8.0 GB
> > Parallelism: unlimited (all tests run in parallel)
> > Tip: On low-memory systems, use -j to limit parallelism (e.g., -j 4 or -j 1)
> > Test artifacts: /tmp/pahole-tests/
> > Compiler: clang version 22.1.8 (Fedora 22.1.8-20.fc45)
> > libc: ldd (GNU libc) 2.44
> > bpftool: bpftool v7.7.0
> > VMLINUX: /tmp/vmlinux
> > PERF_BIN: not set
> > PERF_SRC_DIR: not set
> >
> > 1: dwarves_emit.c: _Atomic type compile emission. : Ok
> > 2: _Atomic type display and --skip_emitting_atomic_typedefs. : Ok
> > 3: Auxiliary tools: pglobal, prefcnt, dtagnames. : Ok
> > 4: Bitfield layout and data member filtering. : Ok
> > 5: dwarf_loader coverage: bitfields, templates, enums, inlines. : Ok
> > 6: Bitfield typedef recoding (tag__recode_dwarf_bitfield). : Ok
> > 7: BTF arena type tag encoding for kfuncs. : Ok
> > 8: BTF multi-dimensional array encoding and round-trip. : Ok
> > 9: BTF bitfield encoding and loading round-trip. : Ok
> > 10: BTF VAR and DATASEC encoding for global variables. : Ok
> > 11: BTF deduplication of array types across CUs. : Ok
> > 12: Distilled base BTF generation. : Ok
> > 13: BTF encoding options. : Ok
> > 14: BTF encoder coverage. : Ok
> > 15: Validation of BTF encoding of functions. : Ok
> > 16: BTF_KIND_FWD encoding and loading round-trip. : Ok
> > 17: BTF encoding with invalid symbol names. : Ok
> > 18: Split BTF encoding (vmlinux base + kernel module). : Skip (no loaded module with debuginfod-available debuginfo found)
> > 19: Check BTF type tag order. : Ok
> > 20: BTF FLOAT and ENUM64 type encoding. : Ok
> > 21: BTF encoder verbose logging coverage. : Ok
> > 22: BTF encoding into ELF via pahole -J (btf_encoder__write_elf). : Ok
> > 23: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
> > 24: BTF true_signature: clang optimized aggregate parameters (2 structs: Ok
> > 25: BTF true_signature: clang optimized with large union parameter : Ok
> > 26: BTF true_signature: clang optimized parameters (scalar) : Ok
> > 27: BTF true_signature: clang optimized with stack parameters (9 params: Ok
> > 28: BTF true_signature: clang optimized with stack parameters (non-stat: Ok
> > 29: Class name list from file. : Ok
> > 30: CLI display and filtering options. : Ok
> > 31: codiff struct comparison. : Ok
> > 32: codiff coverage: terse, functions, verbose, multi-CU. : Ok
> > 33: codiff member change coverage. : Ok
> > 34: codiff multi-CU: __cus__find_cu_by_name coverage. : Ok
> > 35: Compilable output and type filtering. : Ok
> > 36: Type containment and pointer search. : Ok
> > 37: C++ namespace and using-declaration display. : Ok
> > 38: C++ variadic template parameter pack handling. : Ok
> > 39: Class name filtering. : Ok
> > 40: Default BTF on a system without BTF. : Ok
> > 41: Display format options. : Ok
> > 42: dwarf_loader.c edge cases: bitfield recode, inlining, call sites. : Ok
> > 43: dwarves.c core API coverage. : Ok
> > 44: DWZ alternate debug file type resolution. : Ok
> > 45: Atomic typedef emission. : Ok
> > 46: Legacy atomic_ base type emission via --compile (hand-crafted DWARF: Ok
> > 47: dwarves_emit.c / dwarves_reorganize.c coverage. : Ok
> > 48: Enumerator search. : Ok
> > 49: Expand pointers option. : Ok
> > 50: Type expansion and anonymous struct options. : Ok
> > 51: Flexible arrays accounting. : Ok
> > 52: Validation of GCC optimized parameters in default BTF. : Ok
> > 53: Validation of BTF encoding of true_signatures. : Skip (no optimizations applied.)
> > 54: Multi-file loading (cus__load_files). : Ok
> > 55: Version output. : Ok
> > 56: Header, range and seek_bytes prettify paths. : Ok
> > 57: pahole.c scattered option paths. : Ok
> > 58: pdwtags DWARF tag display. : Ok
> > 59: Check that pfunct can print btf_decl_tags read from BTF. : Ok
> > 60: pfunct coverage: symtab, class, expand_types, compile. : Ok
> > 61: pfunct function statistics. : Ok
> > 62: pglobal basic: variables, functions, static exclusion. : Ok
> > 63: prefcnt.c: reference counting coverage. : Ok
> > 64: Cacheline boundary display with large structs. : Ok
> > 65: Sizes and holes display. : Ok
> > 66: Small files coverage: elf_symtab, gobuffer, dutil, pglobal. : Ok
> > 67: Sort output and separator. : Ok
> > 68: Typedef chain display. : Ok
> > 70: Compare parallel vs merged CU loading for inter-CU type references.: Ok
> > 71: Pretty printing of files using DWARF type information. : Ok
> > 72: Prototype expression parsing and prettify. : Ok
> > 73: Parallel reproducible DWARF Loading/Serial BTF encoding. : Ok
> > 69: BTF encoding on vmlinux. : Ok
> > Saved timing data to .test-times (73 tests)
> >
> > toolsbuilder@five:~$
> >
> >
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: RFT: coverage analysis and lots more regression tests in the 'next' branch. Was: Re: [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files
2026-08-31 0:18 ` Arnaldo Carvalho de Melo
@ 2026-08-31 0:48 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-08-31 0:48 UTC (permalink / raw)
To: Alan Maguire
Cc: Jiri Olsa, Clark Williams, dwarves, Arnaldo Carvalho de Melo, bpf,
Yonghong Song, Andrii Nakryiko
On Sun, Aug 30, 2026 at 09:18:50PM -0300, Arnaldo Carvalho de Melo wrote:
> On Sun, Aug 30, 2026 at 06:08:49PM +0100, Alan Maguire wrote:
> > On 29/08/2026 21:21, Arnaldo Carvalho de Melo wrote:
> > > Got all of the coverage analysis and new regression tests on the next
> > > branch, and run the 'build-and-test.sh' script on my set of containers,
> > > where all the test ran on:
>
> > This looks fantastic! I ran it thru CI:
>
> Yeah, lots of tests, some simple, some more involved, and several
> scripts to help in development, like the one for test build after a
> range of patches :-)
>
> > https://github.com/alan-maguire/dwarves/actions/runs/33255515875
>
> > Let's get the coverage report enabled as part of CI too..
>
> Right, taking a look at it before after patches seems like a good way to
> notice the need for further tests to cover new code or further test
> existing one.
And BTW, here is an example of the output of coverage analysis, after
installing the the llvm-profdata package (on Fedora), starting from the
most compact part, that is the coverage table, and after it what is
needed first to then generate the table:
⬢ [acme@toolbx pahole]$ make -C build coverage-table
make: Entering directory '/home/acme/git/pahole/build'
[100%] Display ASCII coverage table from existing profdata
┌──────────────────────┬───────┬──────┬──────┬─────────────────────────────┬─────────┬─────────┐
│ File │ Lines │ Hit │ Miss │ Line Coverage │ Funcs │ FuncCov │
├──────────────────────┼───────┼──────┼──────┼─────────────────────────────┼─────────┼─────────┤
│ prefcnt.c │ 99 │ 89 │ 10 │ █████████████████░░░ 89.9% │ 11/11 │ 100.00% │
│ btf_loader.c │ 553 │ 470 │ 83 │ ████████████████░░░░ 85.0% │ 36/37 │ 97.30% │
│ pfunct.c │ 474 │ 391 │ 83 │ ████████████████░░░░ 82.5% │ 28/29 │ 96.55% │
│ pglobal.c │ 193 │ 159 │ 34 │ ████████████████░░░░ 82.4% │ 17/18 │ 94.44% │
│ pdwtags.c │ 101 │ 83 │ 18 │ ████████████████░░░░ 82.2% │ 5/5 │ 100.00% │
│ codiff.c │ 580 │ 472 │ 108 │ ████████████████░░░░ 81.4% │ 20/20 │ 100.00% │
│ dtagnames.c │ 30 │ 24 │ 6 │ ████████████████░░░░ 80.0% │ 4/4 │ 100.00% │
│ dutil.c │ 210 │ 152 │ 58 │ ██████████████░░░░░░ 72.4% │ 13/15 │ 86.67% │
│ dwarves_fprintf.c │ 1598 │ 1154 │ 444 │ ██████████████░░░░░░ 72.2% │ 40/47 │ 85.11% │
│ dwarves_emit.c │ 326 │ 231 │ 95 │ ██████████████░░░░░░ 70.9% │ 15/18 │ 83.33% │
│ gobuffer.c │ 40 │ 27 │ 13 │ █████████████░░░░░░░ 67.5% │ 3/5 │ 60.00% │
│ dwarf_loader.c │ 3376 │ 2214 │ 1162 │ █████████████░░░░░░░ 65.6% │ 146/178 │ 82.02% │
│ btf_encoder.c │ 2188 │ 1401 │ 787 │ ████████████░░░░░░░░ 64.0% │ 72/83 │ 86.75% │
│ dwarves.c │ 1872 │ 1118 │ 754 │ ███████████░░░░░░░░░ 59.7% │ 118/161 │ 73.29% │
│ dwarves_reorganize.c │ 275 │ 162 │ 113 │ ███████████░░░░░░░░░ 58.9% │ 6/8 │ 75.00% │
│ pahole.c │ 2132 │ 1083 │ 1049 │ ██████████░░░░░░░░░░ 50.8% │ 60/90 │ 66.67% │
│ elf_symtab.c │ 55 │ 27 │ 28 │ █████████░░░░░░░░░░░ 49.1% │ 2/2 │ 100.00% │
│ rbtree.c │ 286 │ 137 │ 149 │ █████████░░░░░░░░░░░ 47.9% │ 7/10 │ 70.00% │
│ libctf.c │ 353 │ 28 │ 325 │ █░░░░░░░░░░░░░░░░░░░ 7.9% │ 1/29 │ 3.45% │
│ ctf_loader.c │ 502 │ 5 │ 497 │ ░░░░░░░░░░░░░░░░░░░░ 1.0% │ 1/29 │ 3.45% │
│ ctracer.c │ 521 │ 0 │ 521 │ ░░░░░░░░░░░░░░░░░░░░ 0.0% │ 0/31 │ 0.00% │
│ elfcreator.c │ 201 │ 0 │ 201 │ ░░░░░░░░░░░░░░░░░░░░ 0.0% │ 0/12 │ 0.00% │
│ scncopy.c │ 12 │ 0 │ 12 │ ░░░░░░░░░░░░░░░░░░░░ 0.0% │ 0/1 │ 0.00% │
│ syscse.c │ 83 │ 0 │ 83 │ ░░░░░░░░░░░░░░░░░░░░ 0.0% │ 0/6 │ 0.00% │
├──────────────────────┼───────┼──────┼──────┼─────────────────────────────┼─────────┼─────────┤
│ TOTAL │ 16060 │ 9427 │ 6633 │ ███████████░░░░░░░░░ 58.7% │ 605/849 │ 71.3% │
└──────────────────────┴───────┴──────┴──────┴─────────────────────────────┴─────────┴─────────┘
[100%] Built target coverage-table
make: Leaving directory '/home/acme/git/pahole/build'
⬢ [acme@toolbx pahole]$
⬢ [acme@toolbx pahole]$ make -C build list-targets
make: Entering directory '/home/acme/git/pahole/build'
pahole build targets:
make - Build all binaries and libraries
make install - Install to CMAKE_INSTALL_PREFIX
Testing:
make check - Run the test suite
Coverage:
make coverage - Full coverage: rebuild, run tests, report diff
make coverage-report - Regenerate report from existing profdata
make coverage-table - Display ASCII coverage table
Coverage uses build-coverage/ as the instrumented build directory.
Set BUILD_DIR to override.
Tarball generation:
make tarxz-pkg - Create .tar.xz release tarball
make targz-pkg - Create .tar.gz release tarball
make tarbz2-pkg - Create .tar.bz2 release tarball
make tar-pkg - Create .tar release tarball
Tarballs created in current directory by default. Set TARBALL_DIR to override.
See README.tarball for details.
Built target list-targets
make: Leaving directory '/home/acme/git/pahole/build'
⬢ [acme@toolbx pahole]$ make -C build coverage
make: Entering directory '/home/acme/git/pahole/build'
[100%] Full coverage: rebuild with instrumentation, run tests, report
=== Step 1: Rebuilding with coverage instrumentation ===
[100%] Built target pahole
=== Step 2: Running tests with coverage ===
73: Typedef chain display. : Ok
Saved timing data to .test-times (73 tests)
=== Merging 1025 profile files ===
=== Step 3: Generating coverage report ===
File Previous Current Delta
-------------------------- -------- -------- ---------
btf_encoder.c 0.00% 64.03% +64.03%
btf_loader.c 0.00% 84.99% +84.99%
codiff.c 0.00% 81.38% +81.38%
ctf_loader.c 0.00% 1.00% +1.00%
ctracer.c 0.00% 0.00% +0.00%
dtagnames.c 0.00% 80.00% +80.00%
dutil.c 0.00% 72.38% +72.38%
dutil.h 0.00% 10.00% +10.00%
dwarf_loader.c 0.00% 65.58% +65.58%
dwarves.c 0.21% 59.72% +59.51%
dwarves.h 0.00% 81.54% +81.54%
dwarves_emit.c 0.00% 70.86% +70.86%
dwarves_fprintf.c 0.00% 72.22% +72.22%
dwarves_reorganize.c 0.00% 58.91% +58.91%
elf_symtab.c 0.00% 49.09% +49.09%
elf_symtab.h 0.00% 55.81% +55.81%
elfcreator.c 0.00% 0.00% +0.00%
gobuffer.c 0.00% 67.50% +67.50%
gobuffer.h 0.00% 33.33% +33.33%
hash.h 0.00% 100.00% +100.00%
libctf.c 0.00% 7.93% +7.93%
list.h 0.00% 32.58% +32.58%
pahole.c 1.45% 50.80% +49.35%
pdwtags.c 0.00% 82.18% +82.18%
pfunct.c 0.00% 82.49% +82.49%
pglobal.c 0.00% 82.38% +82.38%
prefcnt.c 0.00% 89.90% +89.90%
rbtree.c 0.00% 47.90% +47.90%
rbtree.h 0.00% 66.67% +66.67%
scncopy.c 0.00% 0.00% +0.00%
syscse.c 0.00% 0.00% +0.00%
=== All .c files by coverage (ascending) ===
File Lines % Missed
-------------------------- -------- ------
ctracer.c 0.00% 521
elfcreator.c 0.00% 201
scncopy.c 0.00% 12
syscse.c 0.00% 83
ctf_loader.c 1.00% 497
libctf.c 7.93% 325
rbtree.c 47.90% 149
elf_symtab.c 49.09% 28
pahole.c 50.80% 1049
dwarves_reorganize.c 58.91% 113
dwarves.c 59.72% 754
btf_encoder.c 64.03% 787
dwarf_loader.c 65.58% 1162
gobuffer.c 67.50% 13
dwarves_emit.c 70.86% 95
dwarves_fprintf.c 72.22% 444
dutil.c 72.38% 58
dtagnames.c 80.00% 6
codiff.c 81.38% 108
pdwtags.c 82.18% 18
pglobal.c 82.38% 34
pfunct.c 82.49% 83
btf_loader.c 84.99% 83
prefcnt.c 89.90% 10
Baseline updated: /home/acme/git/pahole/coverage-report.txt
Previous baseline saved: /home/acme/git/pahole/coverage-report.txt.prev
=== Generating HTML report ===
HTML report: /home/acme/git/pahole/build-coverage/coverage/html/index.html
Done.
[100%] Built target coverage
make: Leaving directory '/home/acme/git/pahole/build'
⬢ [acme@toolbx pahole]$
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-08-31 0:48 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-21 21:34 [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 1/9] dwarf_loader: Initial support for DW_TAG_variant_part Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 2/9] dwarf_loader: Initial support for DW_TAG_subprogram in DW_TAG_enumeration Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 3/9] dwarf_loader: Allow forcing the merge of CUs for solving inter CU tag references Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 4/9] dwarf_loader: Support DW_TAG_imported_unit for same-file partial units Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 5/9] dwarf_loader: Fix cus__merging_cu failing to detect DW_FORM_ref_addr Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 6/9] tests: Add inter-CU type reference comparison test Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 7/9] dwarf_loader: Add cu parameter to tag__set_spec() and dwarf_tag__set_attr_type() Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 8/9] dwarf_loader: Support DW_FORM_GNU_ref_alt references to dwz alternate debug files Arnaldo Carvalho de Melo
2026-08-21 21:35 ` [PATCH v2 9/9] scripts: Add vmlinux_comparison.py for DWARF/BTF analysis Arnaldo Carvalho de Melo
2026-08-26 0:52 ` [PATCH v2 0/9] pahole: Support cross-CU type references and dwz alternate debug files Arnaldo Carvalho de Melo
2026-08-26 11:43 ` Alan Maguire
2026-08-26 13:10 ` Arnaldo Carvalho de Melo
2026-08-28 14:05 ` Alan Maguire
2026-08-28 22:59 ` Arnaldo Carvalho de Melo
2026-08-29 20:21 ` RFT: coverage analysis and lots more regression tests in the 'next' branch. Was: " Arnaldo Carvalho de Melo
2026-08-30 17:08 ` Alan Maguire
2026-08-31 0:18 ` Arnaldo Carvalho de Melo
2026-08-31 0:48 ` Arnaldo Carvalho de Melo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).