[PATCH 07/31] dwarves: Don't search for holes before member byte sizes are cached

Arnaldo Carvalho de Melo <[email protected]> Wed, 29 Jul 2026 16:07:07 -0300
Newsgroups org.kernel.vger.dwarves,org.kernel.vger.bpf
Message-ID <[email protected]>
From: Arnaldo Carvalho de Melo <[email protected]>

cus__add() called cu__find_class_holes() which ran class__find_holes()
on all structs before cu__finalize() had cached member byte_sizes via
class_member__cache_byte_size(). In the merge path, cus__add() is
called during the CU-processing loop, but cu__finalize() runs after
the loop completes. With uncached byte_sizes (still 0), every member
appeared zero-sized, producing phantom holes between contiguous members.
The holes_searched flag then prevented re-computation when
class__find_holes() was called again during printing.

This caused 36 false "BRAIN FART ALERT!" warnings and phantom "XXX 8
bytes hole, try to pack" messages when processing Firefox debug info,
all on Rust vtable/trait types where members are contiguous at 8-byte
intervals.

Before (Firefox, 152.0-1.fc44.x86_64):

  $ pahole -F dwarf firefox-*.debug 2>&1 | grep -c "BRAIN FART"
  36

After:

  $ pahole -F dwarf firefox-*.debug 2>&1 | grep -c "BRAIN FART"
  0

All consumers already call class__find_holes() lazily, guarded by the
holes_searched flag, so the eager call in cus__add() was redundant.

Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
 codiff.c  |  3 +++
 ctracer.c |  1 +
 dwarves.c | 11 -----------
 3 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/codiff.c b/codiff.c
index 9e5c56546c314231..72a2c4d720f7ac78 100644
--- a/codiff.c
+++ b/codiff.c
@@ -286,6 +286,9 @@ static void diff_struct(const struct cu *new_cu, struct class *structure,
 
 	assert(class__is_struct(new_structure));
 
+	class__find_holes(structure);
+	class__find_holes(new_structure);
+
 	diff = class__size(structure) != class__size(new_structure) ||
 	       class__nr_members(structure) != class__nr_members(new_structure) ||
 	       check_print_members_changes(structure, cu,
diff --git a/ctracer.c b/ctracer.c
index 6894766abc236b45..f3f7f937bb981a58 100644
--- a/ctracer.c
+++ b/ctracer.c
@@ -354,6 +354,7 @@ static struct class *class__clone_base_types(const struct tag *tag,
 	if (clone == NULL)
 		return NULL;
 
+	class__find_holes(clone);
 	type__for_each_data_member_safe(&clone->type, pos, next) {
 		struct tag *member_type = cu__type(cu, pos->tag.type);
 
diff --git a/dwarves.c b/dwarves.c
index 95a8dcd66f861be9..57a4bd063cc9413d 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -506,15 +506,6 @@ reevaluate:
 	return result;
 }
 
-static void cu__find_class_holes(struct cu *cu)
-{
-	uint32_t id;
-	struct class *pos;
-
-	cu__for_each_struct(cu, id, pos)
-		class__find_holes(pos);
-}
-
 struct cus {
 	uint32_t	 nr_entries;
 	struct list_head cus;
@@ -567,8 +558,6 @@ void cus__add(struct cus *cus, struct cu *cu)
 	cus__lock(cus);
 	__cus__add(cus, cu);
 	cus__unlock(cus);
-
-	cu__find_class_holes(cu);
 }
 
 static void ptr_table__init(struct ptr_table *pt)
-- 
2.55.0