[PATCH v3 4/7] perf annotate-data: Bound the member nesting recursion
From: Arnaldo Carvalho de Melo
Date: Tue Sep 22 2026 - 08:27:47 EST
From: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Members are added recursively, and the same kind of broken DIE can make
a member's type point back at one of its own ancestors, recursing until
the stack is gone; nothing usable comes out of nesting members 32 deep
anyway, so stop there, marking the member as truncated (reported by the
JSON exporter added in a later series) and giving up on member types
that don't resolve.
The limit is checked where the children of an aggregate would be
expanded, so only struct/union members get marked as truncated: a
primitive member that merely lands on the limit has no children to
expand and is reported as complete.
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
---
tools/perf/util/annotate-data.c | 36 +++++++++++++++++++++++++++------
tools/perf/util/annotate-data.h | 3 +++
2 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index 2ad6d012e069c522..a0f63a91d45614cb 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -221,6 +221,13 @@ static bool data_type_less(struct rb_node *node_a, const struct rb_node *node_b)
return strcmp(a->self.type_name, b->self.type_name) < 0;
}
+/*
+ * Members are added recursively; bound the nesting so that a broken
+ * type that points back at one of its own ancestors doesn't recurse
+ * until the stack is gone.
+ */
+#define MAX_MEMBER_DEPTH 32
+
/* Recursively add new members for struct/union */
static int __add_member_cb(Dwarf_Die *die, void *arg)
{
@@ -235,6 +242,16 @@ static int __add_member_cb(Dwarf_Die *die, void *arg)
if (dwarf_tag(die) != DW_TAG_member)
return DIE_FIND_CB_SIBLING;
+ if (__die_get_real_type(die, &member_type) == NULL)
+ return DIE_FIND_CB_SIBLING;
+
+ if (dwarf_tag(&member_type) == DW_TAG_typedef) {
+ if (die_get_real_type(&member_type, &die_mem) == NULL)
+ return DIE_FIND_CB_SIBLING;
+ } else {
+ die_mem = member_type;
+ }
+
member = zalloc(sizeof(*member));
if (member == NULL)
return DIE_FIND_CB_END;
@@ -242,12 +259,6 @@ static int __add_member_cb(Dwarf_Die *die, void *arg)
strbuf_init(&sb, 32);
die_get_typename(die, &sb);
- __die_get_real_type(die, &member_type);
- if (dwarf_tag(&member_type) == DW_TAG_typedef)
- die_get_real_type(&member_type, &die_mem);
- else
- die_mem = member_type;
-
if (dwarf_aggregate_size(&die_mem, &size) < 0)
size = 0;
@@ -289,6 +300,7 @@ static int __add_member_cb(Dwarf_Die *die, void *arg)
}
member->size = size;
member->offset = loc + parent->offset;
+ member->depth = parent->depth + 1;
INIT_LIST_HEAD(&member->children);
list_add_tail(&member->node, &parent->children);
@@ -296,6 +308,18 @@ static int __add_member_cb(Dwarf_Die *die, void *arg)
switch (tag) {
case DW_TAG_structure_type:
case DW_TAG_union_type:
+ /*
+ * Only aggregates have children to expand: a primitive
+ * member that happens to land on the limit is complete,
+ * not truncated.
+ */
+ if (member->depth >= MAX_MEMBER_DEPTH) {
+ /* Reported by the JSON exporter so consumers can tell a truncated tree. */
+ member->truncated = true;
+ pr_debug_dtp("member nesting limit reached at %s\n",
+ member->type_name ?: "(unknown type)");
+ break;
+ }
die_find_child(&die_mem, __add_member_cb, member, &die_mem);
break;
default:
diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h
index ca2096a9ee62cbfe..cc576232f55b5fb0 100644
--- a/tools/perf/util/annotate-data.h
+++ b/tools/perf/util/annotate-data.h
@@ -57,6 +57,9 @@ struct annotated_member {
char *var_name;
int offset;
int size;
+ unsigned int depth;
+ /* Children not expanded because the nesting limit was reached */
+ bool truncated;
};
/**
--
2.55.0