Re: [PATCH 3/5] perf symbol: Grow the Rust demangle buffer geometrically
From: Ian Rogers
Date: Mon Sep 28 2026 - 17:43:44 EST
On Sun, Sep 27, 2026 at 6:02 PM Michal Pluta <michalpl2003@xxxxxxxxx> wrote:
>
> 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;
It seems changing the scope of this variable is unnecessary.
>
> /*
> * 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) {
Thanks for digging into this problem and exploring a fix! Previously,
we guessed the demangled length was twice the mangled length, then
added 32 bytes for each retry. These were numbers I pulled out of thin
air, so I'm glad you've found them to be wrong :-). Could we estimate
the initial demangled size better? Could you get data from Bevy and
Typst? I'm a little concerned that a demangled symbol of say just over
2KB might require 4KB with this change, instead of 2KB + 32bytes.
Thanks!
Ian
> char *tmp = realloc(demangled, buf_len);
>
> if (!tmp)
> --
> 2.43.0
>