[PATCH 3/5] perf symbol: Grow the Rust demangle buffer geometrically

From: Michal Pluta

Date: Sun Sep 27 2026 - 21:03:28 EST


When a demangled Rust name doesn't fit in the provided buffer,
dso__demangle_sym() adds 32 bytes to it and formats the name again from
the start. The number of attempts grows with the length of the output,
so the total work is quadratic. Names with deeply nested generic types
need many attempts, leading to noticeable slowdowns in larger programs.

Double the buffer instead, reaching the maximum buffer limit exactly
rather than stopping 32 bytes early. The demangled names are
unchanged.

Add a test for a symbol whose expansion exceeds the bound.

Signed-off-by: Michal Pluta <michalpl2003@xxxxxxxxx>
---
tools/perf/tests/demangle-rust-v0-test.c | 12 ++++++++++++
tools/perf/util/symbol.c | 10 ++++++++--
2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/tools/perf/tests/demangle-rust-v0-test.c b/tools/perf/tests/demangle-rust-v0-test.c
index ee4ddb61174b..d1e0636d73bc 100644
--- a/tools/perf/tests/demangle-rust-v0-test.c
+++ b/tools/perf/tests/demangle-rust-v0-test.c
@@ -69,6 +69,18 @@ static int test__demangle_rust(struct test_suite *test __maybe_unused, int subte
free(buf);
}

+ /*
+ * A symbol with more lifetimes bound than fit in the largest buffer
+ * must fail to demangle rather than give a truncated name.
+ */
+ buf = dso__demangle_sym(/*dso=*/NULL, /*kmodule=*/0, "_RINvC1a1fFGZZZZZZ_EuE");
+ if (buf) {
+ pr_debug("FAILED: symbol larger than the buffer limit demangled to %zu bytes\n",
+ strlen(buf));
+ ret = TEST_FAIL;
+ free(buf);
+ }
+
return ret;
}

diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 3cb42805a82f..1a52bc2980a0 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2737,29 +2737,35 @@ char *cxx_demangle_sym(const char *str __maybe_unused, bool params __maybe_unuse
}
#endif /* !HAVE_CXA_DEMANGLE_SUPPORT */

+/* Buffer limit for a demangled Rust symbol name. */
+#define RUST_DEMANGLE_MAX_LEN (1024 * 1024)
+
char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
{
struct demangle rust_demangle = {
.style = DemangleStyleUnknown,
};
char *demangled = NULL;
+ size_t buf_len;

/*
* We need to figure out if the object was created from C++ sources
* DWARF DW_compile_unit has this, but we don't always have access
* to it...
*/
if (!want_demangle((dso && dso__kernel(dso)) || kmodule))
return demangled;

rust_demangle_demangle(elf_name, &rust_demangle);
if (rust_demangle_is_known(&rust_demangle)) {
/* A rust mangled name. */
if (rust_demangle.mangled_len == 0)
return demangled;

- for (size_t buf_len = roundup_pow_of_two(rust_demangle.mangled_len * 2);
- buf_len < 1024 * 1024; buf_len += 32) {
+ for (buf_len = min_t(size_t, roundup_pow_of_two(rust_demangle.mangled_len * 2),
+ RUST_DEMANGLE_MAX_LEN);
+ buf_len <= RUST_DEMANGLE_MAX_LEN;
+ buf_len *= 2) {
char *tmp = realloc(demangled, buf_len);

if (!tmp)
--
2.43.0