Add an internal wrapper for `abi::__cxa_demangle()`.

PiperOrigin-RevId: 568652465
Change-Id: I9f72a11cb514eaf694dae589a19dc139891e7af2
diff --git a/absl/base/config.h b/absl/base/config.h
index a8425ba..52d42f0 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -864,6 +864,19 @@
 #define ABSL_INTERNAL_HAS_RTTI 1
 #endif
 
+// `ABSL_INTERNAL_HAS_CXA_DEMANGLE` determines whether `abi::__cxa_demangle` is
+// available.
+#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#error ABSL_INTERNAL_HAS_CXA_DEMANGLE cannot be directly set
+#elif defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__))
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 0
+#elif (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \
+    !defined(__mips__)
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1
+#elif defined(__clang__) && !defined(_MSC_VER)
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1
+#endif
+
 // ABSL_INTERNAL_HAVE_SSE is used for compile-time detection of SSE support.
 // See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of
 // which architectures support the various x86 instruction sets.
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index 7462b12..69413ff 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -731,6 +731,7 @@
     deps = [
         "//absl/base:config",
         "//absl/base:core_headers",
+        "//absl/debugging:demangle_internal",
         "//absl/meta:type_traits",
         "//absl/strings",
         "//absl/types:span",
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index a163351..116ddab 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -777,6 +777,7 @@
   DEPS
     absl::config
     absl::core_headers
+    absl::debugging_internal
     absl::meta
     absl::strings
     absl::span
diff --git a/absl/container/internal/layout.h b/absl/container/internal/layout.h
index a59a243..05de05c 100644
--- a/absl/container/internal/layout.h
+++ b/absl/container/internal/layout.h
@@ -172,6 +172,7 @@
 #include <utility>
 
 #include "absl/base/config.h"
+#include "absl/debugging/internal/demangle.h"
 #include "absl/meta/type_traits.h"
 #include "absl/strings/str_cat.h"
 #include "absl/types/span.h"
@@ -181,14 +182,6 @@
 #include <sanitizer/asan_interface.h>
 #endif
 
-#if defined(__GXX_RTTI)
-#define ABSL_INTERNAL_HAS_CXA_DEMANGLE
-#endif
-
-#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
-#include <cxxabi.h>
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
@@ -294,19 +287,9 @@
 template <class T>
 std::string TypeName() {
   std::string out;
-  int status = 0;
-  char* demangled = nullptr;
-#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
-  demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
-#endif
-  if (status == 0 && demangled != nullptr) {  // Demangling succeeded.
-    absl::StrAppend(&out, "<", demangled, ">");
-    free(demangled);
-  } else {
-#if defined(__GXX_RTTI) || defined(_CPPRTTI)
-    absl::StrAppend(&out, "<", typeid(T).name(), ">");
-#endif
-  }
+  absl::StrAppend(&out, "<",
+                  absl::debugging_internal::DemangleString(typeid(T).name()),
+                  ">");
   return out;
 }
 
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index 42124bf..f50c73f 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -217,7 +217,10 @@
     hdrs = ["internal/demangle.h"],
     copts = ABSL_DEFAULT_COPTS,
     linkopts = ABSL_DEFAULT_LINKOPTS,
-    visibility = ["//visibility:private"],
+    visibility = [
+        "//absl/container:__pkg__",
+        "//absl/debugging:__pkg__",
+    ],
     deps = [
         "//absl/base",
         "//absl/base:config",
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc
index f283291..381a2b5 100644
--- a/absl/debugging/internal/demangle.cc
+++ b/absl/debugging/internal/demangle.cc
@@ -21,7 +21,15 @@
 
 #include <cstdint>
 #include <cstdio>
+#include <cstdlib>
 #include <limits>
+#include <string>
+
+#include "absl/base/config.h"
+
+#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#include <cxxabi.h>
+#endif
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -1983,6 +1991,22 @@
          state.parse_state.out_cur_idx > 0;
 }
 
+std::string DemangleString(const char* mangled) {
+  std::string out;
+  int status = 0;
+  char* demangled = nullptr;
+#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
+  demangled = abi::__cxa_demangle(mangled, nullptr, nullptr, &status);
+#endif
+  if (status == 0 && demangled != nullptr) {
+    out.append(demangled);
+    free(demangled);
+  } else {
+    out.append(mangled);
+  }
+  return out;
+}
+
 }  // namespace debugging_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h
index e1f1569..146d115 100644
--- a/absl/debugging/internal/demangle.h
+++ b/absl/debugging/internal/demangle.h
@@ -12,13 +12,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// An async-signal-safe and thread-safe demangler for Itanium C++ ABI
-// (aka G++ V3 ABI).
+#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
+#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
+
+#include <string>
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace debugging_internal {
+
+// Demangle `mangled`.  On success, return true and write the
+// demangled symbol name to `out`.  Otherwise, return false.
+// `out` is modified even if demangling is unsuccessful.
 //
-// The demangler is implemented to be used in async signal handlers to
-// symbolize stack traces.  We cannot use libstdc++'s
-// abi::__cxa_demangle() in such signal handlers since it's not async
-// signal safe (it uses malloc() internally).
+// This function provides an alternative to libstdc++'s abi::__cxa_demangle,
+// which is not async signal safe (it uses malloc internally).  It's intended to
+// be used in async signal handlers to symbolize stack traces.
 //
 // Note that this demangler doesn't support full demangling.  More
 // specifically, it doesn't print types of function parameters and
@@ -30,40 +40,32 @@
 //
 // Example:
 //
-// | Mangled Name  | The Demangler | abi::__cxa_demangle()
-// |---------------|---------------|-----------------------
-// | _Z1fv         | f()           | f()
-// | _Z1fi         | f()           | f(int)
-// | _Z3foo3bar    | foo()         | foo(bar)
-// | _Z1fIiEvi     | f<>()         | void f<int>(int)
-// | _ZN1N1fE      | N::f          | N::f
-// | _ZN3Foo3BarEv | Foo::Bar()    | Foo::Bar()
-// | _Zrm1XS_"     | operator%()   | operator%(X, X)
-// | _ZN3FooC1Ev   | Foo::Foo()    | Foo::Foo()
-// | _Z1fSs        | f()           | f(std::basic_string<char,
-// |               |               |   std::char_traits<char>,
-// |               |               |   std::allocator<char> >)
+// | Mangled Name  | Demangle    | DemangleString
+// |---------------|-------------|-----------------------
+// | _Z1fv         | f()         | f()
+// | _Z1fi         | f()         | f(int)
+// | _Z3foo3bar    | foo()       | foo(bar)
+// | _Z1fIiEvi     | f<>()       | void f<int>(int)
+// | _ZN1N1fE      | N::f        | N::f
+// | _ZN3Foo3BarEv | Foo::Bar()  | Foo::Bar()
+// | _Zrm1XS_"     | operator%() | operator%(X, X)
+// | _ZN3FooC1Ev   | Foo::Foo()  | Foo::Foo()
+// | _Z1fSs        | f()         | f(std::basic_string<char,
+// |               |             |   std::char_traits<char>,
+// |               |             |   std::allocator<char> >)
 //
 // See the unit test for more examples.
 //
 // Note: we might want to write demanglers for ABIs other than Itanium
 // C++ ABI in the future.
-//
-
-#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
-#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Demangle `mangled`.  On success, return true and write the
-// demangled symbol name to `out`.  Otherwise, return false.
-// `out` is modified even if demangling is unsuccessful.
 bool Demangle(const char* mangled, char* out, size_t out_size);
 
+// A wrapper around `abi::__cxa_demangle()`.  On success, returns the demangled
+// name.  On failure, returns the input mangled name.
+//
+// This function is not async-signal-safe.
+std::string DemangleString(const char* mangled);
+
 }  // namespace debugging_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc
index faec72b..26ed9ce 100644
--- a/absl/debugging/internal/demangle_test.cc
+++ b/absl/debugging/internal/demangle_test.cc
@@ -17,6 +17,7 @@
 #include <cstdlib>
 #include <string>
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/base/config.h"
 #include "absl/debugging/internal/stack_consumption.h"
@@ -28,6 +29,8 @@
 namespace debugging_internal {
 namespace {
 
+using ::testing::ContainsRegex;
+
 // A wrapper function for Demangle() to make the unit test simple.
 static const char *DemangleIt(const char * const mangled) {
   static char demangled[4096];
@@ -237,6 +240,25 @@
   TestOnInput(data.c_str());
 }
 
+struct Base {
+  virtual ~Base() = default;
+};
+
+struct Derived : public Base {};
+
+TEST(DemangleStringTest, SupportsSymbolNameReturnedByTypeId) {
+  EXPECT_EQ(DemangleString(typeid(int).name()), "int");
+  // We want to test that `DemangleString` can demangle the symbol names
+  // returned by `typeid`, but without hard-coding the actual demangled values
+  // (because they are platform-specific).
+  EXPECT_THAT(
+      DemangleString(typeid(Base).name()),
+      ContainsRegex("absl.*debugging_internal.*anonymous namespace.*::Base"));
+  EXPECT_THAT(DemangleString(typeid(Derived).name()),
+              ContainsRegex(
+                  "absl.*debugging_internal.*anonymous namespace.*::Derived"));
+}
+
 }  // namespace
 }  // namespace debugging_internal
 ABSL_NAMESPACE_END