[libcxx] [llvm] #105131: P0881R7: Add C++23 stacktrace (PR #136528)

via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 20 19:09:31 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Steve O'Brien (elsteveogrande)

<details>
<summary>Changes</summary>

First pass at `<stacktrace>` implementation.

Some remaining TODOs, which I marked with `TODO(stacktrace23)`:

* Some questions inline regarding tests and `select_on_container_copy_construction` / `select_on_container_swap`
* Still needs support for `formatter`
* A few other questions


---

Patch is 203.60 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/136528.diff


78 Files Affected:

- (modified) libcxx/CMakeLists.txt (+6) 
- (modified) libcxx/docs/UserDocumentation.rst (+1) 
- (modified) libcxx/docs/VendorDocumentation.rst (+8) 
- (modified) libcxx/include/CMakeLists.txt (+7) 
- (modified) libcxx/include/__config (+27) 
- (modified) libcxx/include/__config_site.in (+1) 
- (modified) libcxx/include/__ostream/basic_ostream.h (+1-1) 
- (added) libcxx/include/experimental/__stacktrace/basic_stacktrace.h (+306) 
- (added) libcxx/include/experimental/__stacktrace/detail/alloc.h (+103) 
- (added) libcxx/include/experimental/__stacktrace/detail/context.h (+53) 
- (added) libcxx/include/experimental/__stacktrace/detail/entry.h (+44) 
- (added) libcxx/include/experimental/__stacktrace/detail/to_string.h (+46) 
- (added) libcxx/include/experimental/__stacktrace/stacktrace_entry.h (+111) 
- (added) libcxx/include/experimental/stacktrace (+191) 
- (modified) libcxx/include/module.modulemap.in (+10) 
- (modified) libcxx/modules/std/stacktrace.inc (+5-2) 
- (modified) libcxx/src/CMakeLists.txt (+19) 
- (added) libcxx/src/experimental/stacktrace/alloc.cpp (+20) 
- (added) libcxx/src/experimental/stacktrace/common/config.h (+41) 
- (added) libcxx/src/experimental/stacktrace/common/debug.cpp (+19) 
- (added) libcxx/src/experimental/stacktrace/common/debug.h (+55) 
- (added) libcxx/src/experimental/stacktrace/common/failed.h (+29) 
- (added) libcxx/src/experimental/stacktrace/common/fd.cpp (+31) 
- (added) libcxx/src/experimental/stacktrace/common/fd.h (+122) 
- (added) libcxx/src/experimental/stacktrace/common/images.h (+33) 
- (added) libcxx/src/experimental/stacktrace/context.cpp (+104) 
- (added) libcxx/src/experimental/stacktrace/linux/elf.h (+315) 
- (added) libcxx/src/experimental/stacktrace/linux/linux-dl.cpp (+58) 
- (added) libcxx/src/experimental/stacktrace/linux/linux-elf.cpp (+53) 
- (added) libcxx/src/experimental/stacktrace/linux/linux-sym.cpp (+61) 
- (added) libcxx/src/experimental/stacktrace/linux/linux.h (+98) 
- (added) libcxx/src/experimental/stacktrace/osx/osx.cpp (+111) 
- (added) libcxx/src/experimental/stacktrace/osx/osx.h (+31) 
- (added) libcxx/src/experimental/stacktrace/stacktrace.cpp (+94) 
- (added) libcxx/src/experimental/stacktrace/tools/addr2line.cpp (+100) 
- (added) libcxx/src/experimental/stacktrace/tools/atos.cpp (+115) 
- (added) libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp (+112) 
- (added) libcxx/src/experimental/stacktrace/tools/pspawn.h (+163) 
- (added) libcxx/src/experimental/stacktrace/tools/tools.h (+65) 
- (added) libcxx/src/experimental/stacktrace/tools/toolspawner.cpp (+185) 
- (added) libcxx/src/experimental/stacktrace/unwind/unwind.cpp (+60) 
- (added) libcxx/src/experimental/stacktrace/unwind/unwind.h (+30) 
- (added) libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp (+56) 
- (added) libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h (+71) 
- (added) libcxx/src/experimental/stacktrace/windows/dll.cpp (+40) 
- (added) libcxx/src/experimental/stacktrace/windows/dll.h (+68) 
- (added) libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp (+48) 
- (added) libcxx/src/experimental/stacktrace/windows/psapi_dll.h (+55) 
- (added) libcxx/src/experimental/stacktrace/windows/win_impl.cpp (+204) 
- (added) libcxx/src/experimental/stacktrace/windows/win_impl.h (+38) 
- (added) libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp (+32) 
- (added) libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp (+29) 
- (added) libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp (+29) 
- (added) libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp (+32) 
- (added) libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp (+29) 
- (added) libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp (+29) 
- (modified) libcxx/test/libcxx/transitive_includes/cxx03.csv (+5) 
- (modified) libcxx/test/libcxx/transitive_includes/cxx11.csv (+5) 
- (modified) libcxx/test/libcxx/transitive_includes/cxx14.csv (+5) 
- (modified) libcxx/test/libcxx/transitive_includes/cxx17.csv (+5) 
- (modified) libcxx/test/libcxx/transitive_includes/cxx23.csv (+29) 
- (modified) libcxx/test/libcxx/transitive_includes/cxx26.csv (+29) 
- (added) libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp (+106) 
- (added) libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp (+308) 
- (added) libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp (+44) 
- (added) libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp (+40) 
- (added) libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp (+91) 
- (added) libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp (+156) 
- (added) libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp (+154) 
- (added) libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp (+65) 
- (added) libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp (+63) 
- (added) libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp (+51) 
- (added) libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp (+66) 
- (added) libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp (+85) 
- (added) libcxx/test/std/diagnostics/stacktrace/format.pass.cpp (+53) 
- (added) libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp (+147) 
- (modified) libcxx/utils/libcxx/header_information.py (+3-1) 
- (modified) llvm/utils/gn/secondary/libcxx/src/BUILD.gn (+34) 


``````````diff
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index ebaa6e9fd0e97..b12bf2ead76e5 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -131,6 +131,11 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
    the shared library they shipped should turn this on and see `include/__configuration/availability.h`
    for more details." OFF)
 
+option(LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
+   "For C++23 <stacktrace>: whether to allow invocation of `addr2line`, `llvm-addr2line` or `atos`
+   at runtime (if it's available in PATH) to resolve call-chain addresses in the stacktrace
+   into source locations, if other methods are not available." ON)
+
 if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
   set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
 elseif(MINGW)
@@ -757,6 +762,7 @@ config_define(${LIBCXX_ENABLE_UNICODE} _LIBCPP_HAS_UNICODE)
 config_define(${LIBCXX_ENABLE_WIDE_CHARACTERS} _LIBCPP_HAS_WIDE_CHARACTERS)
 config_define(${LIBCXX_ENABLE_TIME_ZONE_DATABASE} _LIBCPP_HAS_TIME_ZONE_DATABASE)
 config_define(${LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS} _LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS)
+config_define(${LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME} _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME)
 
 # TODO: Remove in LLVM 21. We're leaving an error to make this fail explicitly.
 if (LIBCXX_ENABLE_ASSERTIONS)
diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 4a11a10224ae9..649b7551eb746 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -70,6 +70,7 @@ when ``-fexperimental-library`` is passed:
 
 * The parallel algorithms library (``<execution>`` and the associated algorithms)
 * ``std::chrono::tzdb`` and related time zone functionality
+* ``<stacktrace>``
 * ``<syncstream>``
 
 .. note::
diff --git a/libcxx/docs/VendorDocumentation.rst b/libcxx/docs/VendorDocumentation.rst
index 959a28607d75d..b05d494db4f9b 100644
--- a/libcxx/docs/VendorDocumentation.rst
+++ b/libcxx/docs/VendorDocumentation.rst
@@ -185,6 +185,14 @@ General purpose options
    ship the IANA time zone database. When time zones are not supported,
    time zone support in <chrono> will be disabled.
 
+.. option:: LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME:BOOL
+
+   **Default**: ``OFF``
+
+   For C++23 <stacktrace>: whether to allow invocation of ``addr2line`` or ``llvm-addr2line``
+   at runtime (if it's available in PATH) to resolve call-chain addresses in the stacktrace
+   into source locations, if other methods are not available.
+
 .. option:: LIBCXX_INSTALL_LIBRARY_DIR:PATH
 
   **Default**: ``lib${LIBCXX_LIBDIR_SUFFIX}``
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index f1bdf684a8549..ef091e40b9ec8 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -991,10 +991,17 @@ set(files
   experimental/__simd/traits.h
   experimental/__simd/utility.h
   experimental/__simd/vec_ext.h
+  experimental/__stacktrace/basic_stacktrace.h
+  experimental/__stacktrace/detail/alloc.h
+  experimental/__stacktrace/detail/context.h
+  experimental/__stacktrace/detail/entry.h
+  experimental/__stacktrace/detail/to_string.h
+  experimental/__stacktrace/stacktrace_entry.h
   experimental/iterator
   experimental/memory
   experimental/propagate_const
   experimental/simd
+  experimental/stacktrace
   experimental/type_traits
   experimental/utility
   ext/__hash
diff --git a/libcxx/include/__config b/libcxx/include/__config
index e14632f65b877..071961cf73438 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -977,6 +977,33 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_NOINLINE
 #  endif
 
+// Some functions, e.g. std::stacktrace::current, need to avoid being
+// tail-called by (and tail-calling other) functions, for proper enumeration of
+// call-stack frames.
+// clang-format off
+
+// Disables tail-call optimization for "outbound" calls
+// performed in the function annotated with this attribute.
+#  if __has_cpp_attribute(_Clang::__disable_tail_calls__)
+#    define _LIBCPP_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
+#  elif __has_cpp_attribute(__gnu__::__optimize__)
+#    define _LIBCPP_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
+#  else
+#    define _LIBCPP_NO_TAIL_CALLS_OUT
+#  endif
+
+// Disables tail-call optimization for "inbound" calls -- that is,
+// calls from some other function calling the one having this attribute.
+#  if __has_cpp_attribute(_Clang::__not_tail_called__)
+#    define _LIBCPP_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
+#  else
+#    define _LIBCPP_NO_TAIL_CALLS_IN
+#  endif
+
+// Disable TCO for calls into, and out from, the annotated function.
+#  define _LIBCPP_NO_TAIL_CALLS _LIBCPP_NO_TAIL_CALLS_IN _LIBCPP_NO_TAIL_CALLS_OUT
+// clang-format on
+
 // We often repeat things just for handling wide characters in the library.
 // When wide characters are disabled, it can be useful to have a quick way of
 // disabling it without having to resort to #if-#endif, which has a larger
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index fc01aaf2d8746..4e25ed040c9d0 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -33,6 +33,7 @@
 #cmakedefine _LIBCPP_HAS_NO_STD_MODULES
 #cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
 #cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
+#cmakedefine01 _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
 
 // PSTL backends
 #cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h
index f7473a36d8ccc..891dddbf79c42 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -570,7 +570,7 @@ _LIBCPP_HIDE_FROM_ABI _Stream&& operator<<(_Stream&& __os, const _Tp& __x) {
 }
 
 template <class _CharT, class _Traits, class _Allocator>
-basic_ostream<_CharT, _Traits>&
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
 operator<<(basic_ostream<_CharT, _Traits>& __os, const basic_string<_CharT, _Traits, _Allocator>& __str) {
   return std::__put_character_sequence(__os, __str.data(), __str.size());
 }
diff --git a/libcxx/include/experimental/__stacktrace/basic_stacktrace.h b/libcxx/include/experimental/__stacktrace/basic_stacktrace.h
new file mode 100644
index 0000000000000..8ac1de82b1c63
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/basic_stacktrace.h
@@ -0,0 +1,306 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
+#define _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
+
+#include <experimental/__stacktrace/detail/entry.h>
+#include <experimental/__stacktrace/stacktrace_entry.h>
+
+#include <__config>
+#include <__format/formatter.h>
+#include <__functional/function.h>
+#include <__functional/hash.h>
+#include <__fwd/format.h>
+#include <__fwd/sstream.h>
+#include <__iterator/iterator.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/reverse_access.h>
+#include <__iterator/reverse_iterator.h>
+#include <__memory/allocator.h>
+#include <__memory/allocator_traits.h>
+#include <__memory_resource/memory_resource.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__ostream/basic_ostream.h>
+#include <__utility/move.h>
+#include <__vector/pmr.h>
+#include <__vector/swap.h>
+#include <__vector/vector.h>
+#include <cstddef>
+#include <list>
+#include <memory>
+#include <string>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/to_string.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// (19.6.4)
+// Class template basic_stacktrace [stacktrace.basic]
+
+class stacktrace_entry;
+
+template <class _Allocator>
+class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
+  friend struct hash<basic_stacktrace<_Allocator>>;
+  friend struct __stacktrace::__to_string;
+
+  using _ATraits _LIBCPP_NODEBUG               = allocator_traits<_Allocator>;
+  constexpr static bool __kPropOnCopy          = _ATraits::propagate_on_container_copy_assignment::value;
+  constexpr static bool __kPropOnMove          = _ATraits::propagate_on_container_move_assignment::value;
+  constexpr static bool __kPropOnSwap          = _ATraits::propagate_on_container_swap::value;
+  constexpr static bool __kAlwaysEqual         = _ATraits::is_always_equal::value;
+  constexpr static bool __kNoThrowDflConstruct = is_nothrow_default_constructible_v<_Allocator>;
+  constexpr static bool __kNoThrowAlloc =
+      noexcept(noexcept(_Allocator().allocate(1)) && noexcept(_Allocator().allocate_at_least(1)));
+
+  using __entry_vec _LIBCPP_NODEBUG = vector<stacktrace_entry, _Allocator>;
+
+  [[no_unique_address]] _Allocator __alloc_;
+  __entry_vec __entries_;
+
+  _LIBCPP_HIDE_FROM_ABI basic_stacktrace(const _Allocator& __alloc, std::pmr::list<__stacktrace::entry>&& __vec)
+      : __alloc_(__alloc), __entries_(__alloc) {
+    __entries_.reserve(__vec.size());
+    for (auto& __entry : __vec) {
+      __entries_.emplace_back(std::move(__entry));
+    }
+  }
+
+public:
+  // (19.6.4.1)
+  // Overview [stacktrace.basic.overview]
+
+  using value_type      = stacktrace_entry;
+  using const_reference = const value_type&;
+  using reference       = value_type&;
+  using difference_type = ptrdiff_t;
+  using size_type       = size_t;
+  using allocator_type  = _Allocator;
+  using const_iterator  = decltype(__entries_)::const_iterator;
+  using iterator        = const_iterator;
+
+  using reverse_iterator       = std::reverse_iterator<basic_stacktrace::iterator>;
+  using const_reverse_iterator = std::reverse_iterator<basic_stacktrace::const_iterator>;
+
+  // (19.6.4.2)
+  // Creation and assignment [stacktrace.basic.cons]
+
+  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
+  current(const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+    __stacktrace::alloc __alloc(__caller_alloc);
+    __stacktrace::context __tr{__alloc};
+    __tr.do_stacktrace(1, /* infinite max_depth */ ~0);
+    return {__caller_alloc, std::move(__tr.__entries_)};
+  }
+
+  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
+  current(size_type __skip, const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+    __stacktrace::alloc __alloc(__caller_alloc);
+    __stacktrace::context __tr{__alloc};
+    __tr.do_stacktrace(__skip + 1, /* infinite max_depth */ ~0);
+    return {__caller_alloc, std::move(__tr.__entries_)};
+  }
+
+  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
+  current(size_type __skip,
+          size_type __max_depth,
+          const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+    __stacktrace::alloc __alloc(__caller_alloc);
+    __stacktrace::context __tr{__alloc};
+    __tr.do_stacktrace(__skip + 1, __max_depth);
+    return {__caller_alloc, std::move(__tr.__entries_)};
+  }
+
+  _LIBCPP_EXPORTED_FROM_ABI constexpr ~basic_stacktrace() = default;
+
+  _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace() noexcept(__kNoThrowDflConstruct) : basic_stacktrace(allocator_type()) {}
+
+  _LIBCPP_EXPORTED_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc) noexcept
+      : __alloc_(__alloc), __entries_(__alloc_) {}
+
+  _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) = default;
+
+  _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept = default;
+
+  _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
+      : __alloc_(__alloc), __entries_(__other.__entries_, __alloc) {}
+
+  _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
+      : __alloc_(__alloc) {
+    if (__kAlwaysEqual || __alloc_ == __other.__alloc_) {
+      __entries_ = std::move(__other.__entries_);
+    } else {
+      // "moving" from a container with a different allocator; we're forced to copy items instead
+      for (auto const& __entry : __other.__entries_) {
+        __entries_.push_back(__entry);
+      }
+    }
+  }
+
+  _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other) {
+    if (this == std::addressof(__other)) {
+      return *this;
+    }
+    if (__kPropOnCopy) {
+      __alloc_ = __other.__alloc_;
+    }
+    __entries_ = {__other.__entries_, __alloc_};
+    return *this;
+  }
+
+  _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace&
+  operator=(basic_stacktrace&& __other) noexcept(__kPropOnMove || __kAlwaysEqual) {
+    if (this == std::addressof(__other)) {
+      return *this;
+    }
+    if (__kPropOnMove) {
+      __alloc_   = __other.__alloc_;
+      __entries_ = std::move(__other.__entries_);
+    } else {
+      auto __allocs_eq = __kAlwaysEqual || __alloc_ == __other.__alloc_;
+      if (__allocs_eq) {
+        __entries_ = std::move(__other.__entries_);
+      } else {
+        // "moving" from a container with a different allocator;
+        // we're forced to copy items instead
+        for (auto const& __entry : __other.__entries_) {
+          __entries_.push_back(__entry);
+        }
+      }
+    }
+    return *this;
+  }
+
+  // clang-format on
+
+  // (19.6.4.3)
+  // [stacktrace.basic.obs], observers
+
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI allocator_type get_allocator() const noexcept { return __alloc_; }
+
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator begin() const noexcept { return __entries_.begin(); }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator end() const noexcept { return __entries_.end(); }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator rbegin() const noexcept { return __entries_.rbegin(); }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator rend() const noexcept { return __entries_.rend(); }
+
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator cbegin() const noexcept { return __entries_.cbegin(); }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator cend() const noexcept { return __entries_.cend(); }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator crbegin() const noexcept {
+    return __entries_.crbegin();
+  }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator crend() const noexcept { return __entries_.crend(); }
+
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI bool empty() const noexcept { return __entries_.empty(); }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI size_type size() const noexcept { return __entries_.size(); }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI size_type max_size() const noexcept { return __entries_.max_size(); }
+
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reference operator[](size_type __i) const { return __entries_[__i]; }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reference at(size_type __i) const { return __entries_.at(__i); }
+
+  // (19.6.4.4)
+  // [stacktrace.basic.cmp], comparisons
+
+  template <class _Allocator2>
+  _LIBCPP_EXPORTED_FROM_ABI friend bool
+  operator==(const basic_stacktrace& __x, const basic_stacktrace<_Allocator2>& __y) noexcept {
+    if (__x.size() != __y.size()) {
+      return false;
+    }
+    auto __xi = __x.begin();
+    auto __yi = __y.begin();
+    auto __xe = __x.end();
+    while (__xi != __xe) {
+      if (*__xi++ != *__yi++) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  template <class _Allocator2>
+  _LIBCPP_EXPORTED_FROM_ABI friend strong_ordering
+  operator<=>(const basic_stacktrace& __x, const basic_stacktrace<_Allocator2>& __y) noexcept {
+    auto __ret = __x.size() <=> __y.size();
+    if (__ret != std::strong_ordering::equal) {
+      return __ret;
+    }
+    auto __xi = __x.begin();
+    auto __yi = __y.begin();
+    auto __xe = __x.end();
+    while ((__ret == std::strong_ordering::equal) && __xi != __xe) {
+      __ret = *__xi++ <=> *__yi++;
+    }
+    return __ret;
+  }
+
+  // (19.6.4.5)
+  // [stacktrace.basic.mod], modifiers
+
+  _LIBCPP_EXPORTED_FROM_ABI void swap(basic_stacktrace<_Allocator>& __other) noexcept {
+    std::swap(__entries_, __other.__entries_);
+    if (__kPropOnSwap) {
+      std::swap(__alloc_, __other.__alloc_);
+    }
+  }
+};
+
+using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+
+namespace pmr {
+using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
+} // namespace pmr
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI inline void
+swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) noexcept(noexcept(__a.swap(__b))) {
+  __a.swap(__b);
+}
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI inline string to_string(const basic_stacktrace<_Allocator>& __stacktrace) {
+  return __stacktrace::__to_string()(__stacktrace);
+}
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI inline ostream& operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __stacktrace) {
+  auto __str = __stacktrace::__to_string()(__stacktrace);
+  return __os << __str;
+}
+
+// (19.6.5)
+// Formatting support [stacktrace.format]
+
+// TODO(stacktrace23): needs `formatter`
+template <class _Allocator>
+struct _LIBCPP_EXPORTED_FROM_ABI formatter<basic_stacktrace<_Allocator>>;
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <class _Allocator>
+struct _LIBCPP_EXPORTED_FROM_ABI hash<basic_stacktrace<_Allocator>> {
+  [[nodiscard]] size_t operator()(basic_stacktrace<_Allocator> const& __context) const noexcept {
+    size_t __ret = 1;
+    for (auto const& __entry : __context.__entries_) {
+      __ret += hash<uintptr_t>()(__entry.native_handle());
+      __ret *= 3001;
+    }
+    return __ret;
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
diff --git a/libcxx/include/experimental/__stacktrace/detail/alloc.h b/libcxx/include/experimental/__stacktrace/detail/alloc.h
new file mode 100644
index 0000000000000..a0949b5751899
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/alloc.h
@@ -0,0 +1,103 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+
+#include <__config>
+#include <__functional/function.h>
+#include <__memory/allocator_traits.h>
+#include <__memory_resource/memory_resource.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <cstddef>
+#include <list>
+#include <memory>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class stacktrace_entry;
+
+namespace __stacktrace {
+
+/** Per-stacktrace-invocation allocator which wraps a caller-provided allocator of any type.
+This is intended to be used with `std::pmr::` containers and strings throughout the stacktrace
+creation process. */
+struct alloc final : std::pmr::memory_resource {
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI explicit alloc(_Allocator const& __a) {
+    // Take the given allocator type, and rebind with a new type having <byte> as the template arg
+    using _AT       = std::allocator_traits<_Allocator>;
+    using _BA       = typename _AT::template rebind_alloc<std::byte>;
+    auto __ba       = _BA(__a);
+    __alloc_func_   = [__ba](size_t __sz) mutable { return __ba.allocate(__sz); };
+    __dealloc_func_ = [__ba](void* __ptr, size_t __sz) mutable { return __ba.deallocate((std::byte*)__ptr, __sz); };
+    __alloc_opaque_ = std::addressof(__a);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL ~alloc() override = default;
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL vi...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/136528


More information about the llvm-commits mailing list