[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250129193037.573431-1-irogers@google.com>
Date: Wed, 29 Jan 2025 11:30:37 -0800
From: Ian Rogers <irogers@...gle.com>
To: Peter Zijlstra <peterz@...radead.org>, Ingo Molnar <mingo@...hat.com>,
Arnaldo Carvalho de Melo <acme@...nel.org>, Namhyung Kim <namhyung@...nel.org>,
Mark Rutland <mark.rutland@....com>,
Alexander Shishkin <alexander.shishkin@...ux.intel.com>, Jiri Olsa <jolsa@...nel.org>,
Ian Rogers <irogers@...gle.com>, Adrian Hunter <adrian.hunter@...el.com>,
Kan Liang <kan.liang@...ux.intel.com>, Miguel Ojeda <ojeda@...nel.org>,
Alex Gaynor <alex.gaynor@...il.com>, Boqun Feng <boqun.feng@...il.com>,
Gary Guo <gary@...yguo.net>,
"Björn Roy Baron" <bjorn3_gh@...tonmail.com>, Benno Lossin <benno.lossin@...ton.me>,
Andreas Hindborg <a.hindborg@...nel.org>, Alice Ryhl <aliceryhl@...gle.com>,
Trevor Gross <tmgross@...ch.edu>, James Clark <james.clark@...aro.org>,
Howard Chu <howardchu95@...il.com>, Ravi Bangoria <ravi.bangoria@....com>,
"Masami Hiramatsu (Google)" <mhiramat@...nel.org>, linux-kernel@...r.kernel.org,
linux-perf-users@...r.kernel.org, rust-for-linux@...r.kernel.org,
Daniel Xu <dxu@...uu.xyz>
Subject: [PATCH v1] perf symbol: Add rust v0 demangling support
Implement symbol demangling based on:
https://doc.rust-lang.org/rustc/symbol-mangling/index.html
Ensure recommended demangling examples match the expectations in the
documentation.
This was requested by Daniel Xu <dxu@...uu.xyz> in:
https://lore.kernel.org/lkml/jgxfnphfo3nzlfipnuuzdlfc4ehbr2tnh2evz3mdhynd6wvrsu@fcz6vrvepybb/
Signed-off-by: Ian Rogers <irogers@...gle.com>
---
This change is on top of the 18 patch v3 series:
https://lore.kernel.org/lkml/20250122174308.350350-1-irogers@google.com/
---
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/demangle-rust-v0-test.c | 82 +++
tools/perf/tests/tests.h | 1 +
tools/perf/util/Build | 1 +
tools/perf/util/demangle-rust-v0.c | 661 +++++++++++++++++++++++
tools/perf/util/demangle-rust-v0.h | 7 +
tools/perf/util/symbol-elf.c | 31 +-
8 files changed, 771 insertions(+), 14 deletions(-)
create mode 100644 tools/perf/tests/demangle-rust-v0-test.c
create mode 100644 tools/perf/util/demangle-rust-v0.c
create mode 100644 tools/perf/util/demangle-rust-v0.h
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 165ba84dc93f..d59d88abf0da 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -56,6 +56,7 @@ perf-test-y += genelf.o
perf-test-y += api-io.o
perf-test-y += demangle-java-test.o
perf-test-y += demangle-ocaml-test.o
+perf-test-y += demangle-rust-v0-test.o
perf-test-y += pfm.o
perf-test-y += parse-metric.o
perf-test-y += expand-cgroup.o
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index e77bf446e821..16937e7e313a 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -126,6 +126,7 @@ static struct test_suite *generic_tests[] = {
&suite__maps__merge_in,
&suite__demangle_java,
&suite__demangle_ocaml,
+ &suite__demangle_rust,
&suite__parse_metric,
&suite__expand_cgroup_events,
&suite__perf_time_to_tsc,
diff --git a/tools/perf/tests/demangle-rust-v0-test.c b/tools/perf/tests/demangle-rust-v0-test.c
new file mode 100644
index 000000000000..ab6613e02c94
--- /dev/null
+++ b/tools/perf/tests/demangle-rust-v0-test.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+#include "tests.h"
+#include "debug.h"
+#include "demangle-rust-v0.h"
+#include <linux/kernel.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int test__demangle_rust(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
+{
+ int ret = TEST_OK;
+ char *buf = NULL;
+ size_t i;
+
+ struct {
+ const char *mangled, *demangled;
+ } test_cases[] = {
+ { "_RNvMsr_NtCs3ssYzQotkvD_3std4pathNtB5_7PathBuf3newCs15kBYyAo9fc_7mycrate",
+ "<std::path::PathBuf>::new" },
+ { "_RNvCs15kBYyAo9fc_7mycrate7example",
+ "mycrate::example" },
+ { "_RNvMs_Cs4Cv8Wi1oAIB_7mycrateNtB4_7Example3foo",
+ "<mycrate::Example>::foo" },
+ { "_RNvXCs15kBYyAo9fc_7mycrateNtB2_7ExampleNtB2_5Trait3foo",
+ "<mycrate::Example as mycrate::Trait>::foo" },
+ { "_RNvMCs7qp2U7fqm6G_7mycrateNtB2_7Example3foo",
+ "<mycrate::Example>::foo" },
+ { "_RNvMs_Cs7qp2U7fqm6G_7mycrateNtB4_7Example3bar",
+ "<mycrate::Example>::bar" },
+ { "_RNvYNtCs15kBYyAo9fc_7mycrate7ExampleNtB4_5Trait7exampleB4_",
+ "<mycrate::Example as mycrate::Trait>::example" },
+ { "_RNCNvCsgStHSCytQ6I_7mycrate4main0B3_",
+ "mycrate::main::{closure#0}" },
+ { "_RNCNvCsgStHSCytQ6I_7mycrate4mains_0B3_",
+ "mycrate::main::{closure#1}" },
+ { "_RINvCsgStHSCytQ6I_7mycrate7examplelKj1_EB2_",
+ "mycrate::example::<i32, 1>" },
+ { "_RINvCs7qp2U7fqm6G_7mycrate7exampleFG0_RL1_hRL0_tEuEB2_",
+ "mycrate::example::<(&'_2 u8, &'_1 u16) -> ()>",
+ /*
+ * TODO: the recommended demangling is:
+ * mycrate::example::<for<'a, 'b> fn(&'a u8, &'b u16)>
+ */
+ },
+ { "_RINvCs7qp2U7fqm6G_7mycrate7exampleKy12345678_EB2_",
+ "mycrate::example::<305419896>" },
+ { "_RNvNvMCsd9PVOYlP1UU_7mycrateINtB4_7ExamplepKpE3foo14EXAMPLE_STATIC",
+ "<mycrate::Example::<_, _>>::foo::EXAMPLE_STATIC"
+ /*
+ * TODO: the recommended demangling is (the :: is optional):
+ * <mycrate::Example<_, _>>::foo::EXAMPLE_STATIC
+ */
+ },
+ { "_RINvCs7qp2U7fqm6G_7mycrate7exampleAtj8_EB2_",
+ "mycrate::example::<[u16; 8]>" },
+ { "_RINvCs7qp2U7fqm6G_7mycrate7exampleNtB2_7ExampleBw_EB2_",
+ "mycrate::example::<mycrate::Example, mycrate::Example>" },
+ { "_RINvMsY_NtCseXNvpPnDBDp_3std4pathNtB6_4Path3neweECs7qp2U7fqm6G_7mycrate",
+ "<std::path::Path>::new::<str>" },
+ { "_RNvNvNvCs7qp2U7fqm6G_7mycrate7EXAMPLE7___getit5___KEY",
+ "mycrate::EXAMPLE::__getit::__KEY" },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
+ buf = rust_v0_demangle_sym(test_cases[i].mangled);
+ if (!buf) {
+ pr_debug("FAILED to demangle: \"%s\"\n \"%s\"\n", test_cases[i].mangled,
+ test_cases[i].demangled);
+ continue;
+ }
+ if (strcmp(buf, test_cases[i].demangled)) {
+ pr_debug("FAILED: %s: %s != %s\n", test_cases[i].mangled,
+ buf, test_cases[i].demangled);
+ ret = TEST_FAIL;
+ }
+ free(buf);
+ }
+
+ return ret;
+}
+
+DEFINE_SUITE("Demangle Rust", demangle_rust);
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 751c8489059a..e1e846c9da4e 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -157,6 +157,7 @@ DECLARE_SUITE(jit_write_elf);
DECLARE_SUITE(api_io);
DECLARE_SUITE(demangle_java);
DECLARE_SUITE(demangle_ocaml);
+DECLARE_SUITE(demangle_rust);
DECLARE_SUITE(pfm);
DECLARE_SUITE(parse_metric);
DECLARE_SUITE(expand_cgroup_events);
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 43408d2de4a2..d1c82d9ab4d7 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -240,6 +240,7 @@ perf-util-$(CONFIG_CXX_DEMANGLE) += demangle-cxx.o
perf-util-y += demangle-ocaml.o
perf-util-y += demangle-java.o
perf-util-y += demangle-rust.o
+perf-util-y += demangle-rust-v0.o
ifdef CONFIG_JITDUMP
perf-util-$(CONFIG_LIBELF) += jitdump.o
diff --git a/tools/perf/util/demangle-rust-v0.c b/tools/perf/util/demangle-rust-v0.c
new file mode 100644
index 000000000000..8839cf2e63a0
--- /dev/null
+++ b/tools/perf/util/demangle-rust-v0.c
@@ -0,0 +1,661 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+#include "demangle-rust-v0.h"
+#include "debug.h"
+#include "strbuf.h"
+
+static bool show_crate_root_disambiguator = false;
+
+struct parse_state {
+ /*
+ * The string itself after the initial "_R" from which all back
+ * references are decoded from.
+ */
+ const char *str;
+ /* Length of str. */
+ size_t len;
+ /* Offset within the string. */
+ size_t offset;
+};
+
+static char parse_state__peek(const struct parse_state *state)
+{
+ pr_debug3("Peek '%c'\n", state->str[state->offset]);
+ if (state->offset >= state->len)
+ return '\0';
+ return state->str[state->offset];
+}
+
+static char parse_state__read(struct parse_state *state)
+{
+ pr_debug3("Read '%c'\n", state->str[state->offset]);
+ if (state->offset >= state->len)
+ return '\0';
+ return state->str[state->offset++];
+}
+
+static bool read_path(struct parse_state *state, struct strbuf* buf);
+static bool read_type(struct parse_state *state, struct strbuf* buf);
+
+static bool read_decimal(struct parse_state *state, size_t *res)
+{
+ size_t orig_offset = state->offset;
+
+ *res = 0;
+ while (true) {
+ char ch = parse_state__read(state);
+
+ if (ch >= '0' && ch <= '9') {
+ *res = (*res * 10) + ch - '0';
+ continue;
+ }
+ state->offset--;
+ break;
+ }
+
+ pr_debug3("Read decimal: %zu\n", *res);
+ /* Something was read if the offset changed. */
+ return orig_offset != state->offset;
+}
+
+static bool read_hexadecimal(struct parse_state *state, size_t *res)
+{
+ size_t orig_offset = state->offset;
+
+ *res = 0;
+ while (true) {
+ char ch = parse_state__read(state);
+
+ if (ch >= '0' && ch <= '9') {
+ *res = (*res * 16) + ch - '0';
+ continue;
+ }
+ if (ch >= 'a' && ch <= 'f') {
+ *res = (*res * 16) + ch - 'a';
+ continue;
+ }
+ if (ch >= 'A' && ch <= 'F') {
+ *res = (*res * 16) + ch - 'A';
+ continue;
+ }
+ state->offset--;
+ break;
+ }
+
+ pr_debug3("Read hexadecimal: %zu\n", *res);
+ /* Something was read if the offset changed. */
+ return orig_offset != state->offset;
+}
+
+static bool read_base62(struct parse_state *state, size_t *res)
+{
+ char ch;
+
+ *res = 0;
+ ch = parse_state__read(state);
+ if (ch == '_') {
+ pr_debug3("Read base62: %zu\n", *res);
+ return true;
+ }
+ do {
+ if (ch >= '0' && ch <= '9') {
+ *res = (*res * 62) + ch - '0';
+ } else if (ch >= 'a' && ch <= 'z') {
+ *res = (*res * 62) + ch - 'a' + 10;
+ } else if (ch >= 'A' && ch <= 'Z') {
+ *res = (*res * 62) + ch - 'A' + 36;
+ } else {
+ pr_debug3("Unexpected base62 character '%c'\n", ch);
+ return false;
+ }
+ ch = parse_state__read(state);
+ } while (ch != '_');
+
+ /* The empty string encodes 0, 0_ encodes 1, etc. so adjust up. */
+ (*res)++;
+
+ /* Something was read if the offset changed. */
+ pr_debug3("Read base62: %zu\n", *res);
+ return true;
+}
+
+static bool read_optional_base62(struct parse_state *state, char opt_char,
+ size_t *res, bool *has_res)
+{
+ *has_res = parse_state__peek(state) == opt_char;
+ if (!*has_res)
+ return true;
+
+ state->offset++;
+ return read_base62(state, res);
+}
+
+struct identifier {
+ const char *bytes;
+ size_t len;
+ size_t disambiguator;
+ bool has_disambiguator;
+};
+
+static bool read_identifier(struct parse_state *state, struct identifier *identifier)
+{
+ size_t num;
+
+ pr_debug3("Reading identifier\n");
+ if (!read_optional_base62(state, 's', &identifier->disambiguator,
+ &identifier->has_disambiguator))
+ return false;
+
+ if (parse_state__peek(state) == 'u') {
+ pr_warning("Punycode encoding ignored\n");
+ state->offset++;
+ }
+ if (!read_decimal(state, &num)) {
+ pr_debug3("Bad undisambiguated-identifier decimal-number\n");
+ return false;
+ }
+ if (parse_state__peek(state) == '_') {
+ /* Optional separator. */
+ state->offset++;
+ }
+ identifier->bytes = &state->str[state->offset];
+ identifier->len = num;
+ if (state->offset + num > state->len) {
+ pr_debug3("Identifier beyond end of input\n");
+ return false;
+ }
+ state->offset += num;
+ pr_debug3("Read identifer '%.*s'\n", (int)identifier->len, identifier->bytes);
+ return true;
+}
+
+static bool read_crate_root(struct parse_state *state, struct strbuf* buf)
+{
+ struct identifier identifier;
+
+ pr_debug3("Reading crate-root\n");
+ if (!read_identifier(state, &identifier))
+ return false;
+
+ if (strbuf_addf(buf, "%.*s", (int)identifier.len, identifier.bytes))
+ return false;
+ if (show_crate_root_disambiguator && identifier.has_disambiguator) {
+ /* Ignore failure. */
+ strbuf_addf(buf, "[%zu]", identifier.disambiguator);
+ }
+ return true;
+}
+
+static bool read_impl_path(struct parse_state *state, struct strbuf* buf)
+{
+ size_t disambiguator;
+ bool has_disambiguator;
+
+ pr_debug3("Reading impl-path\n");
+
+ /* Ignore impl-path. */
+ if (!read_optional_base62(state, 's', &disambiguator, &has_disambiguator))
+ return false;
+ return read_path(state, buf);
+}
+
+static bool read_tuple(struct parse_state *state, struct strbuf* buf)
+{
+ bool first = true;
+
+ pr_debug3("Reading tuple\n");
+
+ if (strbuf_addch(buf, '('))
+ return false;
+ while (parse_state__peek(state) != 'E') {
+ if (!first && strbuf_addstr(buf, ", "))
+ return false;
+ first = false;
+ if (!read_type(state, buf))
+ return false;
+ }
+ state->offset++;
+ return strbuf_addch(buf, ')') == 0;
+}
+
+static bool read_backref(struct parse_state *state, size_t *new_offset)
+{
+ size_t backref_offset = state->offset - 1;
+
+ if (!read_base62(state, new_offset))
+ return false;
+
+ if (*new_offset >= backref_offset) {
+ pr_debug3("Backref offset (%zu) should be before current offset (%zu)\n",
+ *new_offset, backref_offset);
+ return false;
+ }
+ pr_debug3("Read backref to %zu\n", *new_offset);
+ return true;
+}
+
+static bool read_const(struct parse_state *state, struct strbuf* buf)
+{
+ size_t const_data;
+ char ch = parse_state__read(state);
+ bool negative = false, bracket;
+
+ pr_debug3("Reading const\n");
+
+ if (ch == 'p')
+ return strbuf_addch(buf, '_') == 0;
+
+ if (ch == 'B') {
+ size_t backref_off, saved_off;
+
+ if (!read_backref(state, &backref_off))
+ return false;
+ saved_off = state->offset;
+ state->offset = backref_off;
+ if (!read_const(state, buf))
+ return false;
+ state->offset = saved_off;
+ return true;
+ }
+
+ bracket = !(ch >= 'a' && ch <= 'z');
+
+ if (bracket) {
+ state->offset--;
+ if (!read_type(state, buf))
+ return false;
+ }
+ if (parse_state__peek(state) == 'n') {
+ state->offset++;
+ negative = true;
+ }
+
+ if (!read_hexadecimal(state, &const_data))
+ return false;
+
+ if (parse_state__read(state) != '_') {
+ pr_debug3("Missing const-data terminator\n");
+ return false;
+ }
+ if (bracket && strbuf_addstr(buf, "::<"))
+ return false;
+ if (ch == 'b') {
+ if (strbuf_addstr(buf, const_data ? "true" : "false"))
+ return false;
+ } else if (ch == 'c') {
+ if (strbuf_addch(buf, const_data))
+ return false;
+ } else {
+ if (negative) {
+ if (strbuf_addch(buf, '-'))
+ return false;
+ }
+ if (strbuf_addf(buf, "%zu", const_data))
+ return false;
+ }
+ return !bracket || strbuf_addch(buf, '>') == 0;
+}
+
+
+static bool read_type(struct parse_state *state, struct strbuf* buf)
+{
+ char ch = parse_state__read(state);
+
+ pr_debug3("Reading type '%c'\n", ch);
+
+ switch (ch) {
+ /* Basic types. */
+ case 'a':
+ return strbuf_addstr(buf, "i8") == 0;
+ case 'b':
+ return strbuf_addstr(buf, "bool") == 0;
+ case 'c':
+ return strbuf_addstr(buf, "char") == 0;
+ case 'd':
+ return strbuf_addstr(buf, "f64") == 0;
+ case 'e':
+ return strbuf_addstr(buf, "str") == 0;
+ case 'f':
+ return strbuf_addstr(buf, "f32") == 0;
+ case 'h':
+ return strbuf_addstr(buf, "u8") == 0;
+ case 'i':
+ return strbuf_addstr(buf, "isize") == 0;
+ case 'j':
+ return strbuf_addstr(buf, "usize") == 0;
+ case 'l':
+ return strbuf_addstr(buf, "i32") == 0;
+ case 'm':
+ return strbuf_addstr(buf, "u32") == 0;
+ case 'n':
+ return strbuf_addstr(buf, "i128") == 0;
+ case 'o':
+ return strbuf_addstr(buf, "u128") == 0;
+ case 's':
+ return strbuf_addstr(buf, "i16") == 0;
+ case 't':
+ return strbuf_addstr(buf, "u16") == 0;
+ case 'u':
+ return strbuf_addstr(buf, "()") == 0;
+ case 'v':
+ return strbuf_addstr(buf, "...") == 0;
+ case 'x':
+ return strbuf_addstr(buf, "i64") == 0;
+ case 'y':
+ return strbuf_addstr(buf, "u64") == 0;
+ case 'z':
+ return strbuf_addstr(buf, "!") == 0;
+ case 'p':
+ return strbuf_addstr(buf, "_") == 0;
+ /* Array type. */
+ case 'A':
+ if (strbuf_addch(buf, '['))
+ return false;
+ if (!read_type(state, buf))
+ return false;
+ if (strbuf_addstr(buf, "; "))
+ return false;
+ if (!read_const(state, buf))
+ return false;
+ return strbuf_addch(buf, ']') == 0;
+ /* Slice. */
+ case 'S':
+ if (strbuf_addch(buf, '['))
+ return false;
+ if (!read_type(state, buf))
+ return false;
+ return strbuf_addch(buf, ']') == 0;
+ /* Tuple. */
+ case 'T':
+ return read_tuple(state, buf);
+ /* Mutable reference/reference. */
+ case 'Q':
+ case 'R': {
+ size_t lifetime;
+ bool has_lifetime;
+
+ if (strbuf_addstr(buf, ch == 'Q' ? "&mut " : "&"))
+ return false;
+ if (!read_optional_base62(state, 'L', &lifetime, &has_lifetime))
+ return false;
+ if (has_lifetime) {
+ /* TODO: proper lifetime naming. */
+ if (strbuf_addf(buf, "'_%zu ", lifetime))
+ return false;
+ }
+ return read_type(state, buf);
+ }
+ /* Constant raw pointer. */
+ case 'P':
+ if (strbuf_addstr(buf, "*const "))
+ return false;
+ return read_type(state, buf);
+ /* Mutable raw pointer. */
+ case 'O':
+ if (strbuf_addstr(buf, "*mut "))
+ return false;
+ return read_type(state, buf);
+ /* Function pointer. */
+ case 'F': {
+ size_t binder;
+ bool has_binder;
+
+ if (!read_optional_base62(state, 'G', &binder, &has_binder))
+ return false;
+ if (parse_state__peek(state) == 'U') {
+ /* Unsafe, ignored. */
+ state->offset++;
+ }
+ if (parse_state__peek(state) == 'K') {
+ /* ABI. */
+ state->offset++;
+ if (parse_state__peek(state) == 'C') {
+ state->offset++;
+ } else {
+ struct identifier abi; /* Ignored. */
+
+ if (!read_identifier(state, &abi))
+ return false;
+ }
+ }
+ if (!read_tuple(state, buf))
+ return false;
+ if (strbuf_addstr(buf, " -> "))
+ return false;
+ return read_type(state, buf);
+ }
+ /* Trait object. */
+ case 'D':
+ pr_err("Todo: rust trait demangling\n");
+ return false;
+ /* Path. */
+ case 'C':
+ case 'M':
+ case 'X':
+ case 'Y':
+ case 'N':
+ case 'I':
+ state->offset--;
+ return read_path(state, buf);
+ /* Backref. */
+ case 'B': {
+ size_t backref_off, saved_off;
+
+ if (!read_backref(state, &backref_off))
+ return false;
+ saved_off = state->offset;
+ state->offset = backref_off;
+ if (!read_type(state, buf))
+ return false;
+ state->offset = saved_off;
+ return true;
+ }
+ default:
+ pr_err("Unexpected type character '%c'\n", ch);
+ return false;
+ }
+}
+
+static bool read_inherent_impl(struct parse_state *state, struct strbuf* buf)
+{
+ struct strbuf impl_path_buf = STRBUF_INIT; /* Ignored. */
+
+ pr_debug3("Reading inherent-impl\n");
+
+ if (!read_impl_path(state, &impl_path_buf))
+ goto err_out;
+ if (strbuf_addch(buf, '<'))
+ goto err_out;
+ if (!read_type(state, buf))
+ goto err_out;
+ return strbuf_addch(buf, '>') == 0;
+err_out:
+ strbuf_release(&impl_path_buf);
+ return false;
+}
+
+static bool read_trait_impl(struct parse_state *state __maybe_unused, struct strbuf* buf __maybe_unused)
+{
+ struct strbuf impl_path_buf = STRBUF_INIT; /* Ignored. */
+
+ pr_debug3("Reading trait-impl\n");
+
+ if (!read_impl_path(state, &impl_path_buf))
+ goto err_out;
+ if (strbuf_addch(buf, '<'))
+ goto err_out;
+ if (!read_type(state, buf))
+ goto err_out;
+ if (strbuf_addstr(buf, " as "))
+ goto err_out;
+ if (!read_path(state, buf))
+ goto err_out;
+ return strbuf_addch(buf, '>') == 0;
+err_out:
+ strbuf_release(&impl_path_buf);
+ return false;
+}
+
+static bool read_trait_def(struct parse_state *state __maybe_unused, struct strbuf* buf __maybe_unused)
+{
+ pr_debug3("Reading trait-def\n");
+
+ if (strbuf_addch(buf, '<'))
+ return false;
+ if (!read_type(state, buf))
+ return false;
+ if (strbuf_addstr(buf, " as "))
+ return false;
+ if (!read_path(state, buf))
+ return false;
+ return strbuf_addch(buf, '>') == 0;
+}
+
+static bool read_nested_path(struct parse_state *state __maybe_unused, struct strbuf* buf __maybe_unused)
+{
+ struct identifier identifier;
+ size_t orig_buf_len = buf->len;
+ char namespace = parse_state__read(state);
+
+ pr_debug3("Reading nested-path\n");
+
+ if (!read_path(state, buf))
+ return false;
+ if (strbuf_addstr(buf, "::"))
+ return false;
+ if (!read_identifier(state, &identifier))
+ return false;
+ if (strbuf_addf(buf, "%.*s", (int)identifier.len, identifier.bytes))
+ return false;
+ if (namespace == 'C' || namespace == 'S') {
+ if (strbuf_addstr(buf, namespace == 'C' ? "{closure#" : "{shim#"))
+ return false;
+ if (identifier.has_disambiguator) {
+ if (strbuf_addf(buf, "%zu}", identifier.disambiguator + 1))
+ return false;
+ } else {
+ if (strbuf_addstr(buf, "0}"))
+ return false;
+ }
+ } else if (namespace >= 'a' && namespace <= 'z') {
+ /* Internal namespace character, don't show. */
+ } else if (namespace >= 'A' && namespace <= 'Z') {
+ if (strbuf_addch(buf, namespace))
+ return false;
+ } else {
+ pr_debug3("Bad namespace '%c'\n", namespace);
+ return false;
+ }
+
+ pr_debug3("Read nested-path '%.*s'\n", (int)(buf->len - orig_buf_len), &buf->buf[orig_buf_len]);
+ return true;
+}
+
+static bool read_generic_args(struct parse_state *state __maybe_unused, struct strbuf* buf __maybe_unused)
+{
+ bool first = true;
+ char ch;
+
+ pr_debug3("Reading generic-args\n");
+
+ if (!read_path(state, buf))
+ return false;
+ if (strbuf_addstr(buf, "::<"))
+ return false;
+
+ ch = parse_state__read(state);
+ while (ch != 'E') {
+ if (!first && strbuf_addstr(buf, ", "))
+ return false;
+ first = false;
+ if (ch == 'L') {
+ size_t lifetime;
+
+ if (!read_base62(state, &lifetime))
+ return false;
+ /* TODO: proper lifetime naming. */
+ if (strbuf_addf(buf, "'_%zu ", lifetime))
+ return false;
+ } else if (ch == 'K') {
+ if (!read_const(state, buf))
+ return false;
+ } else {
+ state->offset--;
+ if (!read_type(state, buf))
+ return false;
+ }
+ ch = parse_state__read(state);
+ }
+ return strbuf_addch(buf, '>') == 0;
+}
+
+static bool read_path(struct parse_state *state, struct strbuf* buf)
+{
+ char ch = parse_state__read(state);
+
+ switch (ch) {
+ case 'C':
+ return read_crate_root(state, buf);
+ case 'M':
+ return read_inherent_impl(state, buf);
+ case 'X':
+ return read_trait_impl(state, buf);
+ case 'Y':
+ return read_trait_def(state, buf);
+ case 'N':
+ return read_nested_path(state, buf);
+ case 'I':
+ return read_generic_args(state, buf);
+ case 'B': {
+ size_t backref_off, saved_off;
+
+ if (!read_backref(state, &backref_off))
+ return false;
+ saved_off = state->offset;
+ state->offset = backref_off;
+ if (!read_path(state, buf))
+ return false;
+ state->offset = saved_off;
+ return true;
+ }
+ default:
+ pr_err("Unexpected path character '%c'\n", ch);
+ return false;
+ }
+}
+
+char *rust_v0_bdemangle_sym(const char *str)
+{
+ struct parse_state state = {
+ .str = str,
+ .len = strlen(str),
+ .offset = 0,
+ };
+ struct strbuf buf = STRBUF_INIT;
+ size_t encoding_version;
+
+ pr_debug3("Rust parsing of '%s'\n", str);
+
+ /* Check "_R" prefix is present. */
+ if (parse_state__read(&state) != '_' || parse_state__read(&state) != 'R')
+ goto err_out;
+
+ /* Strip initial "_R" to make backrefs easier to decode. */
+ state.str += 2;
+ state.len -= 2;
+ state.offset = 0;
+
+ /* Read optional encoding version. */
+ if (read_decimal(&state, &encoding_version))
+ pr_debug("Rust encoding version %zu\n", encoding_version);
+
+ if (!read_path(&state, &buf))
+ goto err_out;
+
+ /* Ignore optional instantiating create. */
+ /* Ignore optional vendor specific suffix. */
+
+ return strbuf_detach(&buf, NULL);
+err_out:
+ strbuf_release(&buf);
+ return NULL;
+}
diff --git a/tools/perf/util/demangle-rust-v0.h b/tools/perf/util/demangle-rust-v0.h
new file mode 100644
index 000000000000..d342c5319748
--- /dev/null
+++ b/tools/perf/util/demangle-rust-v0.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __PERF_DEMANGLE_RUST_V0
+#define __PERF_DEMANGLE_RUST_V0 1
+
+char *rust_v0_demangle_sym(const char *str);
+
+#endif /* __PERF_DEMANGLE_RUST_V0 */
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 121db55b9709..41707425fc1a 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -16,6 +16,7 @@
#include "demangle-ocaml.h"
#include "demangle-java.h"
#include "demangle-rust.h"
+#include "demangle-rust-v0.h"
#include "machine.h"
#include "vdso.h"
#include "debug.h"
@@ -285,7 +286,7 @@ char *cxx_demangle_sym(const char *str __maybe_unused, bool params __maybe_unuse
static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
{
- char *demangled = NULL;
+ char *demangled;
/*
* We need to figure out if the object was created from C++ sources
@@ -293,23 +294,25 @@ static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
* to it...
*/
if (!want_demangle(dso__kernel(dso) || kmodule))
- return demangled;
+ return NULL;
demangled = cxx_demangle_sym(elf_name, verbose > 0, verbose > 0);
- if (demangled == NULL) {
- demangled = ocaml_demangle_sym(elf_name);
- if (demangled == NULL) {
- demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET);
- }
+ if (demangled) {
+ /* Legacy rust demangling input is already C++ demangled. */
+ if (rust_is_mangled(demangled))
+ rust_demangle_sym(demangled);
+ return demangled;
}
- else if (rust_is_mangled(demangled))
- /*
- * Input to Rust demangling is the BFD-demangled
- * name which it Rust-demangles in place.
- */
- rust_demangle_sym(demangled);
- return demangled;
+ demangled = rust_v0_demangle_sym(elf_name);
+ if (demangled)
+ return demangled;
+
+ demangled = ocaml_demangle_sym(elf_name);
+ if (demangled)
+ return demangled;
+
+ return java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET);
}
struct rel_info {
--
2.48.1.262.g85cc9f2d1e-goog
Powered by blists - more mailing lists