* [PATCH 1/5] perf test demangle: Fail when demangling fails
2026-09-28 1:02 [PATCH 0/5] perf symbol: Speed up demangling of long Rust symbols Michal Pluta
@ 2026-09-28 1:02 ` Michal Pluta
2026-09-28 1:02 ` [PATCH 2/5] perf symbol: Don't return an unterminated Rust demangle buffer Michal Pluta
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Michal Pluta @ 2026-09-28 1:02 UTC (permalink / raw)
To: acme, namhyung
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
The Rust and Java demangle tests only log a NULL result from
dso__demangle_sym() and carry on, so a symbol that fails to demangle
doesn't fail the test. Set TEST_FAIL.
Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
tools/perf/tests/demangle-java-test.c | 1 +
tools/perf/tests/demangle-rust-v0-test.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/tools/perf/tests/demangle-java-test.c b/tools/perf/tests/demangle-java-test.c
index 0fb3e5a4a0ed..a8eb0f4effad 100644
--- a/tools/perf/tests/demangle-java-test.c
+++ b/tools/perf/tests/demangle-java-test.c
@@ -33,6 +33,7 @@ static int test__demangle_java(struct test_suite *test __maybe_unused, int subte
if (!buf) {
pr_debug("FAILED to demangle: \"%s\"\n \"%s\"\n", test_cases[i].mangled,
test_cases[i].demangled);
+ ret = TEST_FAIL;
continue;
}
if (strcmp(buf, test_cases[i].demangled)) {
diff --git a/tools/perf/tests/demangle-rust-v0-test.c b/tools/perf/tests/demangle-rust-v0-test.c
index 904f966c65d7..ee4ddb61174b 100644
--- a/tools/perf/tests/demangle-rust-v0-test.c
+++ b/tools/perf/tests/demangle-rust-v0-test.c
@@ -58,6 +58,7 @@ static int test__demangle_rust(struct test_suite *test __maybe_unused, int subte
if (!buf) {
pr_debug("FAILED to demangle: \"%s\"\n \"%s\"\n", test_cases[i].mangled,
test_cases[i].demangled);
+ ret = TEST_FAIL;
continue;
}
if (strcmp(buf, test_cases[i].demangled)) {
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 2/5] perf symbol: Don't return an unterminated Rust demangle buffer
2026-09-28 1:02 [PATCH 0/5] perf symbol: Speed up demangling of long Rust symbols Michal Pluta
2026-09-28 1:02 ` [PATCH 1/5] perf test demangle: Fail when demangling fails Michal Pluta
@ 2026-09-28 1:02 ` Michal Pluta
2026-09-28 1:02 ` [PATCH 3/5] perf symbol: Grow the Rust demangle buffer geometrically Michal Pluta
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Michal Pluta @ 2026-09-28 1:02 UTC (permalink / raw)
To: acme, namhyung
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
When dso__demangle_sym() runs out of retries or fails to grow the
output buffer for a Rust v0 symbol, it returns the buffer and callers
blindly use it as the demangled symbol name. However,
rust_demangle_display_demangle() doesn't NUL terminate its output when
it reports OverflowOverflow, so the buffer isn't a string.
Free the buffer and return NULL in both cases. Callers already fall back
to the mangled name when no demangled name is returned.
Fixes: e20848c317b5 ("perf symbol-elf: Integrate rust-v0 demangling")
Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
tools/perf/util/symbol.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 163652f071c6..3cb42805a82f 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2762,17 +2762,16 @@ char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
buf_len < 1024 * 1024; buf_len += 32) {
char *tmp = realloc(demangled, buf_len);
- if (!tmp) {
- /* Failure to grow output buffer, return what is there. */
- return demangled;
- }
+ if (!tmp)
+ break;
demangled = tmp;
if (rust_demangle_display_demangle(&rust_demangle, demangled, buf_len,
/*alternate=*/true) == OverflowOk)
return demangled;
}
- /* Buffer exceeded sensible bounds, return what is there. */
- return demangled;
+ /* Failure to grow output buffer or buffer exceeded sensible bounds. */
+ free(demangled);
+ return NULL;
}
demangled = cxx_demangle_sym(elf_name, verbose > 0, verbose > 0);
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 3/5] perf symbol: Grow the Rust demangle buffer geometrically
2026-09-28 1:02 [PATCH 0/5] perf symbol: Speed up demangling of long Rust symbols Michal Pluta
2026-09-28 1:02 ` [PATCH 1/5] perf test demangle: Fail when demangling fails Michal Pluta
2026-09-28 1:02 ` [PATCH 2/5] perf symbol: Don't return an unterminated Rust demangle buffer Michal Pluta
@ 2026-09-28 1:02 ` Michal Pluta
2026-09-28 1:02 ` [PATCH 4/5] perf symbol: Shrink the demangled Rust buffer to fit Michal Pluta
2026-09-28 1:02 ` [PATCH 5/5] perf symbol: Move Rust demangling into its own function Michal Pluta
4 siblings, 0 replies; 6+ messages in thread
From: Michal Pluta @ 2026-09-28 1:02 UTC (permalink / raw)
To: acme, namhyung
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
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@gmail.com>
---
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
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 4/5] perf symbol: Shrink the demangled Rust buffer to fit
2026-09-28 1:02 [PATCH 0/5] perf symbol: Speed up demangling of long Rust symbols Michal Pluta
` (2 preceding siblings ...)
2026-09-28 1:02 ` [PATCH 3/5] perf symbol: Grow the Rust demangle buffer geometrically Michal Pluta
@ 2026-09-28 1:02 ` Michal Pluta
2026-09-28 1:02 ` [PATCH 5/5] perf symbol: Move Rust demangling into its own function Michal Pluta
4 siblings, 0 replies; 6+ messages in thread
From: Michal Pluta @ 2026-09-28 1:02 UTC (permalink / raw)
To: acme, namhyung
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
The buffer a demangled Rust name is returned in is sized to whatever
attempt succeeded, which is usually larger than the string it holds.
Shrink it to the string's exact length before returning it.
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
tools/perf/util/symbol.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 1a52bc2980a0..858a14a91f79 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2772,8 +2772,10 @@ char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
break;
demangled = tmp;
if (rust_demangle_display_demangle(&rust_demangle, demangled, buf_len,
- /*alternate=*/true) == OverflowOk)
- return demangled;
+ /*alternate=*/true) == OverflowOk) {
+ tmp = realloc(demangled, strlen(demangled) + 1);
+ return tmp ?: demangled;
+ }
}
/* Failure to grow output buffer or buffer exceeded sensible bounds. */
free(demangled);
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 5/5] perf symbol: Move Rust demangling into its own function
2026-09-28 1:02 [PATCH 0/5] perf symbol: Speed up demangling of long Rust symbols Michal Pluta
` (3 preceding siblings ...)
2026-09-28 1:02 ` [PATCH 4/5] perf symbol: Shrink the demangled Rust buffer to fit Michal Pluta
@ 2026-09-28 1:02 ` Michal Pluta
4 siblings, 0 replies; 6+ messages in thread
From: Michal Pluta @ 2026-09-28 1:02 UTC (permalink / raw)
To: acme, namhyung
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
dso__demangle_sym() is the only place that implements demangling logic
inline. The other three are one call each to a helper. Move the Rust
parsing and buffer growth into rust_demangle_sym(), with the same
signature as the other three.
A Rust name that fails to demangle now falls through to the other
demanglers, matching how the other demanglers chain into each other.
A v0 name can't be misread by any of them, since none accepts its "_R"
prefix. A legacy name can only fail here on allocation failure or
hitting the output bound, neither of which is language-specific.
Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
tools/perf/util/symbol.c | 61 +++++++++++++++++++++++-----------------
1 file changed, 35 insertions(+), 26 deletions(-)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 858a14a91f79..89db9a670833 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2740,47 +2740,56 @@ char *cxx_demangle_sym(const char *str __maybe_unused, bool params __maybe_unuse
/* 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)
+static char *rust_demangle_sym(const char *elf_name)
{
struct demangle rust_demangle = {
.style = DemangleStyleUnknown,
};
char *demangled = NULL;
size_t buf_len;
+ rust_demangle_demangle(elf_name, &rust_demangle);
+ if (!rust_demangle_is_known(&rust_demangle))
+ return NULL;
+
+ if (rust_demangle.mangled_len == 0)
+ return NULL;
+
+ 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)
+ break;
+ demangled = tmp;
+ if (rust_demangle_display_demangle(&rust_demangle, demangled, buf_len,
+ /*alternate=*/true) == OverflowOk) {
+ tmp = realloc(demangled, strlen(demangled) + 1);
+ return tmp ?: demangled;
+ }
+ }
+ /* Failure to grow output buffer or buffer exceeded sensible bounds. */
+ free(demangled);
+ return NULL;
+}
+
+char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
+{
+ char *demangled = NULL;
+
/*
* 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 (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)
- break;
- demangled = tmp;
- if (rust_demangle_display_demangle(&rust_demangle, demangled, buf_len,
- /*alternate=*/true) == OverflowOk) {
- tmp = realloc(demangled, strlen(demangled) + 1);
- return tmp ?: demangled;
- }
- }
- /* Failure to grow output buffer or buffer exceeded sensible bounds. */
- free(demangled);
- return NULL;
- }
+ demangled = rust_demangle_sym(elf_name);
+ if (demangled)
+ return demangled;
demangled = cxx_demangle_sym(elf_name, verbose > 0, verbose > 0);
if (demangled)
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread