Commit a28ee5b5 by Chris Mihelich Committed by Copybara-Service

Recognize some simple Rust mangled names in Demangle.

PiperOrigin-RevId: 631018414
Change-Id: Ice8efa0af4cb1f72b5d62fbbea4cb12cbead8634
parent c1e1b47d
...@@ -124,6 +124,8 @@ set(ABSL_INTERNAL_DLL_FILES ...@@ -124,6 +124,8 @@ set(ABSL_INTERNAL_DLL_FILES
"debugging/internal/address_is_readable.h" "debugging/internal/address_is_readable.h"
"debugging/internal/demangle.cc" "debugging/internal/demangle.cc"
"debugging/internal/demangle.h" "debugging/internal/demangle.h"
"debugging/internal/demangle_rust.cc"
"debugging/internal/demangle_rust.h"
"debugging/internal/elf_mem_image.cc" "debugging/internal/elf_mem_image.cc"
"debugging/internal/elf_mem_image.h" "debugging/internal/elf_mem_image.h"
"debugging/internal/examine_stack.cc" "debugging/internal/examine_stack.cc"
......
...@@ -219,8 +219,14 @@ cc_library( ...@@ -219,8 +219,14 @@ cc_library(
cc_library( cc_library(
name = "demangle_internal", name = "demangle_internal",
srcs = ["internal/demangle.cc"], srcs = [
hdrs = ["internal/demangle.h"], "internal/demangle.cc",
"internal/demangle_rust.cc",
],
hdrs = [
"internal/demangle.h",
"internal/demangle_rust.h",
],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [ visibility = [
...@@ -235,6 +241,20 @@ cc_library( ...@@ -235,6 +241,20 @@ cc_library(
) )
cc_test( cc_test(
name = "demangle_rust_test",
srcs = ["internal/demangle_rust_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":demangle_internal",
"//absl/base:config",
"//absl/base:core_headers",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "demangle_test", name = "demangle_test",
srcs = ["internal/demangle_test.cc"], srcs = ["internal/demangle_test.cc"],
copts = ABSL_TEST_COPTS, copts = ABSL_TEST_COPTS,
......
...@@ -196,8 +196,10 @@ absl_cc_library( ...@@ -196,8 +196,10 @@ absl_cc_library(
demangle_internal demangle_internal
HDRS HDRS
"internal/demangle.h" "internal/demangle.h"
"internal/demangle_rust.h"
SRCS SRCS
"internal/demangle.cc" "internal/demangle.cc"
"internal/demangle_rust.cc"
COPTS COPTS
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
DEPS DEPS
...@@ -208,6 +210,19 @@ absl_cc_library( ...@@ -208,6 +210,19 @@ absl_cc_library(
absl_cc_test( absl_cc_test(
NAME NAME
demangle_rust_test
SRCS
"internal/demangle_rust_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::demangle_internal
absl::config
GTest::gmock_main
)
absl_cc_test(
NAME
demangle_test demangle_test
SRCS SRCS
"internal/demangle_test.cc" "internal/demangle_test.cc"
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "absl/debugging/internal/demangle.h" #include "absl/debugging/internal/demangle.h"
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
#include <string> #include <string>
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/debugging/internal/demangle_rust.h"
#if ABSL_INTERNAL_HAS_CXA_DEMANGLE #if ABSL_INTERNAL_HAS_CXA_DEMANGLE
#include <cxxabi.h> #include <cxxabi.h>
...@@ -2110,6 +2112,10 @@ static bool Overflowed(const State *state) { ...@@ -2110,6 +2112,10 @@ static bool Overflowed(const State *state) {
// The demangler entry point. // The demangler entry point.
bool Demangle(const char* mangled, char* out, size_t out_size) { bool Demangle(const char* mangled, char* out, size_t out_size) {
if (mangled[0] == '_' && mangled[1] == 'R') {
return DemangleRustSymbolEncoding(mangled, out, out_size);
}
State state; State state;
InitState(&state, mangled, out, out_size); InitState(&state, mangled, out, out_size);
return ParseTopLevelMangledName(&state) && !Overflowed(&state) && return ParseTopLevelMangledName(&state) && !Overflowed(&state) &&
......
...@@ -56,6 +56,8 @@ namespace debugging_internal { ...@@ -56,6 +56,8 @@ namespace debugging_internal {
// //
// See the unit test for more examples. // See the unit test for more examples.
// //
// Support for Rust mangled names is in development; see demangle_rust.h.
//
// Note: we might want to write demanglers for ABIs other than Itanium // Note: we might want to write demanglers for ABIs other than Itanium
// C++ ABI in the future. // C++ ABI in the future.
bool Demangle(const char* mangled, char* out, size_t out_size); bool Demangle(const char* mangled, char* out, size_t out_size);
......
// Copyright 2024 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_RUST_H_
#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_RUST_H_
#include <cstddef>
#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// Demangle the Rust encoding `mangled`. On success, return true and write the
// demangled symbol name to `out`. Otherwise, return false, leaving unspecified
// contents in `out`. For example, calling DemangleRustSymbolEncoding with
// `mangled = "_RNvC8my_crate7my_func"` will yield `my_crate::my_func` in `out`,
// provided `out_size` is large enough for that value and its trailing NUL.
//
// DemangleRustSymbolEncoding is async-signal-safe and runs in bounded C++
// call-stack space. It is suitable for symbolizing stack traces in a signal
// handler.
//
// The demangling logic is under development. In this version of Abseil,
// DemangleRustSymbolEncoding parses a few simple kinds of symbol names, but
// nothing having backreferences in the input or angle brackets in the
// demangling, and it emits raw Punycode instead of the UTF-8 represented by it.
bool DemangleRustSymbolEncoding(const char* mangled, char* out,
std::size_t out_size);
} // namespace debugging_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_RUST_H_
// Copyright 2024 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/debugging/internal/demangle_rust.h"
#include <cstddef>
#include <string>
#include "gtest/gtest.h"
#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
namespace {
// If DemangleRustSymbolEncoding(mangled, <buffer with room for buffer_size
// chars>, buffer_size) returns true and seems not to have overrun its output
// buffer, returns the string written by DemangleRustSymbolEncoding; otherwise
// returns an error message.
std::string ResultOfDemangling(const char* mangled, std::size_t buffer_size) {
// Fill the buffer with something other than NUL so we test whether Demangle
// appends trailing NUL as expected.
std::string buffer(buffer_size + 1, '~');
constexpr char kCanaryCharacter = 0x7f; // arbitrary unlikely value
buffer[buffer_size] = kCanaryCharacter;
if (!DemangleRustSymbolEncoding(mangled, &buffer[0], buffer_size)) {
return "Failed parse";
}
if (buffer[buffer_size] != kCanaryCharacter) {
return "Buffer overrun by output: " + buffer.substr(0, buffer_size + 1)
+ "...";
}
return buffer.data(); // Not buffer itself: this trims trailing padding.
}
// Tests that DemangleRustSymbolEncoding converts mangled into plaintext given
// enough output buffer space but returns false and avoids overrunning a buffer
// that is one byte too short.
//
// The lambda wrapping allows ASSERT_EQ to branch out the first time an
// expectation is not satisfied, preventing redundant errors for the same bug.
//
// We test first with excess space so that if the algorithm just computes the
// wrong answer, it will be clear from the error log that the bounds checks are
// unlikely to be the code at fault.
#define EXPECT_DEMANGLING(mangled, plaintext) \
do { \
[] { \
constexpr std::size_t plenty_of_space = sizeof(plaintext) + 128; \
constexpr std::size_t just_enough_space = sizeof(plaintext); \
constexpr std::size_t one_byte_too_few = sizeof(plaintext) - 1; \
const char* expected_plaintext = plaintext; \
const char* expected_error = "Failed parse"; \
ASSERT_EQ(ResultOfDemangling(mangled, plenty_of_space), \
expected_plaintext); \
ASSERT_EQ(ResultOfDemangling(mangled, just_enough_space), \
expected_plaintext); \
ASSERT_EQ(ResultOfDemangling(mangled, one_byte_too_few), \
expected_error); \
}(); \
} while (0)
// Tests that DemangleRustSymbolEncoding rejects the given input (typically, a
// truncation of a real Rust symbol name).
#define EXPECT_DEMANGLING_FAILS(mangled) \
do { \
constexpr std::size_t plenty_of_space = 1024; \
const char* expected_error = "Failed parse"; \
EXPECT_EQ(ResultOfDemangling(mangled, plenty_of_space), expected_error); \
} while (0)
// Piping grep -C 1 _R demangle_test.cc into your favorite c++filt
// implementation allows you to verify that the goldens below are reasonable.
TEST(DemangleRust, EmptyDemangling) {
EXPECT_TRUE(DemangleRustSymbolEncoding("_RC0", nullptr, 0));
}
TEST(DemangleRust, FunctionAtCrateLevel) {
EXPECT_DEMANGLING("_RNvC10crate_name9func_name", "crate_name::func_name");
EXPECT_DEMANGLING(
"_RNvCs09azAZ_10crate_name9func_name", "crate_name::func_name");
}
TEST(DemangleRust, TruncationsOfFunctionAtCrateLevel) {
EXPECT_DEMANGLING_FAILS("_R");
EXPECT_DEMANGLING_FAILS("_RN");
EXPECT_DEMANGLING_FAILS("_RNvC");
EXPECT_DEMANGLING_FAILS("_RNvC10");
EXPECT_DEMANGLING_FAILS("_RNvC10crate_nam");
EXPECT_DEMANGLING_FAILS("_RNvC10crate_name");
EXPECT_DEMANGLING_FAILS("_RNvC10crate_name9");
EXPECT_DEMANGLING_FAILS("_RNvC10crate_name9func_nam");
EXPECT_DEMANGLING_FAILS("_RNvCs");
EXPECT_DEMANGLING_FAILS("_RNvCs09azAZ");
EXPECT_DEMANGLING_FAILS("_RNvCs09azAZ_");
}
TEST(DemangleRust, VendorSuffixes) {
EXPECT_DEMANGLING("_RNvC10crate_name9func_name.!@#", "crate_name::func_name");
EXPECT_DEMANGLING("_RNvC10crate_name9func_name$!@#", "crate_name::func_name");
}
TEST(DemangleRust, UnicodeIdentifiers) {
EXPECT_DEMANGLING("_RNvC7ice_cap17Eyjafjallajökull",
"ice_cap::Eyjafjallajökull");
EXPECT_DEMANGLING("_RNvC7ice_caps_u19Eyjafjallajkull_jtb",
"ice_cap::{Punycode Eyjafjallajkull_jtb}");
}
TEST(DemangleRust, FunctionInModule) {
EXPECT_DEMANGLING("_RNvNtCs09azAZ_10crate_name11module_name9func_name",
"crate_name::module_name::func_name");
}
TEST(DemangleRust, FunctionInFunction) {
EXPECT_DEMANGLING(
"_RNvNvCs09azAZ_10crate_name15outer_func_name15inner_func_name",
"crate_name::outer_func_name::inner_func_name");
}
TEST(DemangleRust, ClosureInFunction) {
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_name0",
"crate_name::func_name::{closure#0}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_name0Cs123_12client_crate",
"crate_name::func_name::{closure#0}");
}
TEST(DemangleRust, ClosureNumbering) {
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names_0Cs123_12client_crate",
"crate_name::func_name::{closure#1}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names0_0Cs123_12client_crate",
"crate_name::func_name::{closure#2}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names9_0Cs123_12client_crate",
"crate_name::func_name::{closure#11}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesa_0Cs123_12client_crate",
"crate_name::func_name::{closure#12}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesz_0Cs123_12client_crate",
"crate_name::func_name::{closure#37}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesA_0Cs123_12client_crate",
"crate_name::func_name::{closure#38}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesZ_0Cs123_12client_crate",
"crate_name::func_name::{closure#63}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names10_0Cs123_12client_crate",
"crate_name::func_name::{closure#64}");
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_namesg6_0Cs123_12client_crate",
"crate_name::func_name::{closure#1000}");
}
TEST(DemangleRust, ClosureNumberOverflowingInt) {
EXPECT_DEMANGLING(
"_RNCNvCs09azAZ_10crate_name9func_names1234567_0Cs123_12client_crate",
"crate_name::func_name::{closure#?}");
}
TEST(DemangleRust, UnexpectedlyNamedClosure) {
EXPECT_DEMANGLING(
"_RNCNvCs123_10crate_name9func_name12closure_nameCs456_12client_crate",
"crate_name::func_name::{closure:closure_name#0}");
EXPECT_DEMANGLING(
"_RNCNvCs123_10crate_name9func_names2_12closure_nameCs456_12client_crate",
"crate_name::func_name::{closure:closure_name#4}");
}
TEST(DemangleRust, ItemNestedInsideClosure) {
EXPECT_DEMANGLING(
"_RNvNCNvCs123_10crate_name9func_name015inner_func_nameCs_12client_crate",
"crate_name::func_name::{closure#0}::inner_func_name");
}
TEST(DemangleRust, Shim) {
EXPECT_DEMANGLING(
"_RNSNvCs123_10crate_name9func_name6vtableCs456_12client_crate",
"crate_name::func_name::{shim:vtable#0}");
}
TEST(DemangleRust, UnknownUppercaseNamespace) {
EXPECT_DEMANGLING(
"_RNXNvCs123_10crate_name9func_name14mystery_objectCs456_12client_crate",
"crate_name::func_name::{X:mystery_object#0}");
}
TEST(DemangleRust, NestedUppercaseNamespaces) {
EXPECT_DEMANGLING(
"_RNCNXNYCs123_10crate_names0_1ys1_1xs2_0Cs456_12client_crate",
"crate_name::{Y:y#2}::{X:x#3}::{closure#4}");
}
} // namespace
} // namespace debugging_internal
ABSL_NAMESPACE_END
} // namespace absl
...@@ -300,6 +300,15 @@ TEST(Demangle, AbiTags) { ...@@ -300,6 +300,15 @@ TEST(Demangle, AbiTags) {
EXPECT_STREQ("C[abi:bar][abi:foo]()", tmp); EXPECT_STREQ("C[abi:bar][abi:foo]()", tmp);
} }
// Test one Rust symbol to exercise Demangle's delegation path. Rust demangling
// itself is more thoroughly tested in demangle_rust_test.cc.
TEST(Demangle, DelegatesToDemangleRustSymbolEncoding) {
char tmp[80];
EXPECT_TRUE(Demangle("_RNvC8my_crate7my_func", tmp, sizeof(tmp)));
EXPECT_STREQ("my_crate::my_func", tmp);
}
// Tests that verify that Demangle footprint is within some limit. // Tests that verify that Demangle footprint is within some limit.
// They are not to be run under sanitizers as the sanitizers increase // They are not to be run under sanitizers as the sanitizers increase
// stack consumption by about 4x. // stack consumption by about 4x.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment