[libcxx-commits] [libcxx] [llvm] Add C++23 stacktrace (P0881R7) (PR #136528)

Steve O'Brien via libcxx-commits libcxx-commits at lists.llvm.org
Mon May 19 14:42:52 PDT 2025


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

>From d8afc1c104a58740166f9553dc2ad94de92f9449 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 16 Jan 2025 18:32:09 -0500
Subject: [PATCH 01/37] Add C++23 stacktrace

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

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index dffdd7a3c70a6..163d1db597a2c 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)
@@ -758,6 +763,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 dbacb44735d08..2a8c9600124c9 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 8cb05857ea8d7..eda5c5e840787 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 57223e4f1db18..7b292b1521bdc 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -987,6 +987,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 09ad401a48d60..0a6e4360201ba 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -571,7 +571,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 virtual void _anchor_vfunc();
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t __align) override {
+    // Avoiding "assert" in a system header, but we expect this to hold:
+    // assert(__align <= alignof(std::max_align_t));
+    (void)__align;
+    return __alloc_func_(__size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t __align) override {
+    (void)__align;
+    __dealloc_func_((std::byte*)__ptr, __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
+    auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
+    return __rhs_ba && (__rhs_ba->__alloc_opaque_ == __alloc_opaque_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
+    std::pmr::string __ret{this};
+    if (__size) {
+      __ret.reserve(__size);
+      __ret[0] = 0;
+    }
+    return __ret;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::string hex_string(uintptr_t __addr) {
+    char __ret[19]; // "0x" + 16 digits + NUL
+    auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
+    return {__ret, size_t(__size), this};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::string u64_string(uintptr_t __val) {
+    char __ret[21]; // 20 digits max + NUL
+    auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
+    return {__ret, size_t(__size), this};
+  }
+
+  template <typename _Tp>
+  _LIBCPP_HIDE_FROM_ABI std::pmr::list<_Tp> new_list() {
+    return std::pmr::list<_Tp>{this};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::list<std::pmr::string> new_string_list() { return new_list<std::pmr::string>(); }
+
+private:
+  std::function<std::byte*(size_t)> __alloc_func_;
+  std::function<void(std::byte*, size_t)> __dealloc_func_;
+  /** Only used for equality */
+  void const* __alloc_opaque_;
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
diff --git a/libcxx/include/experimental/__stacktrace/detail/context.h b/libcxx/include/experimental/__stacktrace/detail/context.h
new file mode 100644
index 0000000000000..df791b37b386d
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/context.h
@@ -0,0 +1,53 @@
+// -*- 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_CONTEXT
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+
+#include <__config>
+#include <__memory_resource/memory_resource.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__vector/pmr.h>
+#include <__vector/vector.h>
+#include <cstddef>
+#include <string>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct _LIBCPP_HIDE_FROM_ABI alloc;
+struct _LIBCPP_HIDE_FROM_ABI entry;
+
+namespace __stacktrace {
+
+/** Represents the state of the current in-progress stacktrace operation.  This includes
+the working list of `entry` objects, as well as the caller-provided allocator (wrapped
+in `polymorphic_allocator`) and a handful of utility functions involving that allocator. */
+struct _LIBCPP_HIDE_FROM_ABI context {
+  /** Encapsulates and type-removes the caller's allocator. */
+  alloc& __alloc_;
+
+  /** The working set of entry data; these will later be copied to their final `stacktrace_entry` objects. */
+  std::pmr::list<entry> __entries_;
+
+  /** Path to this process's main executable. */
+  std::pmr::string __main_prog_path_;
+
+  _LIBCPP_HIDE_FROM_ABI explicit context(alloc& __byte_alloc)
+      : __alloc_(__byte_alloc), __entries_(&__alloc_), __main_prog_path_(__alloc_.new_string()) {}
+
+  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void do_stacktrace(size_t __skip, size_t __max_depth);
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
diff --git a/libcxx/include/experimental/__stacktrace/detail/entry.h b/libcxx/include/experimental/__stacktrace/detail/entry.h
new file mode 100644
index 0000000000000..2bab14228b508
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/entry.h
@@ -0,0 +1,44 @@
+// -*- 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_DETAIL_ENTRY
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+
+#include <__config>
+#include <cstdint>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+/** Contains fields which will be used to generate the final `std::stacktrace_entry`.
+This is an intermediate object which owns strings allocated via the caller-provided allocator,
+which are later freed back to that allocator and converted to plain `std::string`s. */
+struct _LIBCPP_HIDE_FROM_ABI entry {
+  /** Caller's / faulting insn's address, including ASLR/slide */
+  uintptr_t __addr_{};
+
+  /** the address minus its image's slide offset */
+  uintptr_t __addr_unslid_{};
+
+  /** entry's description (symbol name) */
+  std::pmr::string __desc_{};
+
+  /** source file name */
+  std::pmr::string __file_{};
+
+  /** line number in source file */
+  uint32_t __line_{};
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
diff --git a/libcxx/include/experimental/__stacktrace/detail/to_string.h b/libcxx/include/experimental/__stacktrace/detail/to_string.h
new file mode 100644
index 0000000000000..47feddf07faf9
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/to_string.h
@@ -0,0 +1,46 @@
+// -*- 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_TO_STRING
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+
+#include <__config>
+#include <__fwd/sstream.h>
+#include <__ostream/basic_ostream.h>
+#include <cstddef>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Allocator>
+class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace;
+
+class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry;
+
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI __to_string {
+  _LIBCPP_HIDE_FROM_ABI string operator()(stacktrace_entry const& __entry);
+
+  _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
+
+  _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
+
+  string operator()(std::stacktrace_entry const* __entries, size_t __count);
+
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
+    return (*this)(__st.__entries_.data(), __st.__entries_.size());
+  }
+};
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
diff --git a/libcxx/include/experimental/__stacktrace/stacktrace_entry.h b/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
new file mode 100644
index 0000000000000..be8abb3ea91b2
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
@@ -0,0 +1,111 @@
+// -*- 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_ENTRY
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
+
+#include <__cstddef/byte.h>
+#include <__cstddef/ptrdiff_t.h>
+#include <__cstddef/size_t.h>
+#include <__format/formatter.h>
+#include <__functional/function.h>
+#include <__functional/hash.h>
+#include <__fwd/format.h>
+#include <__fwd/ostream.h>
+#include <__fwd/sstream.h>
+#include <__fwd/vector.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 <__utility/move.h>
+#include <__vector/pmr.h>
+#include <__vector/swap.h>
+#include <__vector/vector.h>
+#include <cstdint>
+#include <string>
+
+#include "experimental/__stacktrace/detail/entry.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
+public:
+  // (19.6.3.1) Overview [stacktrace.entry.overview]
+  using native_handle_type = uintptr_t;
+
+  // (19.6.3.2) [stacktrace.entry.cons], constructors
+  _LIBCPP_EXPORTED_FROM_ABI ~stacktrace_entry() noexcept                                            = default;
+  _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry() noexcept                                   = default;
+  _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry(const stacktrace_entry&) noexcept            = default;
+  _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry& operator=(const stacktrace_entry&) noexcept = default;
+
+  // (19.6.3.3) [stacktrace.entry.obs], observers
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr native_handle_type native_handle() const noexcept {
+    return __addr_;
+  }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr explicit operator bool() const noexcept {
+    return native_handle() != 0;
+  }
+
+  // (19.6.3.4) [stacktrace.entry.query], query
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string description() const { return __desc_; }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string source_file() const { return __file_; }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI uint_least32_t source_line() const { return __line_; }
+
+  // (19.6.3.5) [stacktrace.entry.cmp], comparison
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr bool
+  operator==(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+    return __x.native_handle() == __y.native_handle();
+  }
+
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr strong_ordering
+  operator<=>(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+    return __x.native_handle() <=> __y.native_handle();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI explicit stacktrace_entry(__stacktrace::entry&& __e)
+      : __addr_(__e.__addr_), __desc_(std::move(__e.__desc_)), __file_(std::move(__e.__file_)), __line_(__e.__line_) {}
+
+private:
+  uintptr_t __addr_{};
+  std::string __desc_{};
+  std::string __file_{};
+  uint_least32_t __line_{};
+};
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry);
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry);
+
+// (19.6.5)
+// Formatting support [stacktrace.format]
+
+// TODO(stacktrace23): needs `formatter`
+template <>
+struct _LIBCPP_EXPORTED_FROM_ABI formatter<stacktrace_entry>;
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <>
+struct _LIBCPP_EXPORTED_FROM_ABI hash<stacktrace_entry> {
+  [[nodiscard]] size_t operator()(stacktrace_entry const& __entry) const noexcept {
+    auto __addr = __entry.native_handle();
+    return hash<uintptr_t>()(__addr);
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
diff --git a/libcxx/include/experimental/stacktrace b/libcxx/include/experimental/stacktrace
new file mode 100644
index 0000000000000..9e1f7259ca3da
--- /dev/null
+++ b/libcxx/include/experimental/stacktrace
@@ -0,0 +1,191 @@
+// -*- 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
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE
+
+/*
+  Header <stacktrace> synopsis
+  (19.6.2)
+
+#include <compare>  // see [compare.syn]
+
+namespace std {
+  // [stacktrace.entry], class stacktrace_entry
+  class stacktrace_entry;
+
+  // [stacktrace.basic], class template basic_stacktrace
+  template<class Allocator>
+    class basic_stacktrace;
+
+  // basic_stacktrace typedef-names
+  using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+
+  // [stacktrace.basic.nonmem], non-member functions
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+      noexcept(noexcept(a.swap(b)));
+
+  string to_string(const stacktrace_entry& f);
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+
+  // [stacktrace.format], formatting support
+  template<> struct formatter<stacktrace_entry>;
+  template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+
+  namespace pmr {
+    using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
+  }
+
+  // [stacktrace.basic.hash], hash support
+  template<class T> struct hash;
+  template<> struct hash<stacktrace_entry>;
+  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;
+}
+
+// [stacktrace.entry]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    using native_handle_type = implementation-defined;
+
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry() noexcept;
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+
+    ~stacktrace_entry();
+
+    // [stacktrace.entry.obs], observers
+    constexpr native_handle_type native_handle() const noexcept;
+    constexpr explicit operator bool() const noexcept;
+
+    // [stacktrace.entry.query], query
+    string description() const;
+    string source_file() const;
+    uint_least32_t source_line() const;
+
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr bool operator==(const stacktrace_entry& x,
+                                     const stacktrace_entry& y) noexcept;
+    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+                                                 const stacktrace_entry& y) noexcept;
+  };
+}
+
+// [stacktrace.basic]
+
+namespace std {
+  template<class Allocator>
+  class basic_stacktrace {
+  public:
+    using value_type = stacktrace_entry;
+    using const_reference = const value_type&;
+    using reference = value_type&;
+    using const_iterator = implementation-defined;  // see [stacktrace.basic.obs]
+    using iterator = const_iterator;
+    using reverse_iterator = reverse_iterator<iterator>;
+    using const_reverse_iterator = reverse_iterator<const_iterator>;
+    using difference_type = implementation-defined;
+    using size_type = implementation-defined;
+    using allocator_type = Allocator;
+
+    // [stacktrace.basic.cons], creation and assignment
+    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip, size_type max_depth,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+
+    basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+    explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+
+    basic_stacktrace(const basic_stacktrace& other);
+    basic_stacktrace(basic_stacktrace&& other) noexcept;
+    basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+    basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+    basic_stacktrace& operator=(const basic_stacktrace& other);
+    basic_stacktrace& operator=(basic_stacktrace&& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+    ~basic_stacktrace();
+
+    // [stacktrace.basic.obs], observers
+    allocator_type get_allocator() const noexcept;
+
+    const_iterator begin() const noexcept;
+    const_iterator end() const noexcept;
+    const_reverse_iterator rbegin() const noexcept;
+    const_reverse_iterator rend() const noexcept;
+
+    const_iterator cbegin() const noexcept;
+    const_iterator cend() const noexcept;
+    const_reverse_iterator crbegin() const noexcept;
+    const_reverse_iterator crend() const noexcept;
+
+    bool empty() const noexcept;
+    size_type size() const noexcept;
+    size_type max_size() const noexcept;
+
+    const_reference operator[](size_type) const;
+    const_reference at(size_type) const;
+
+    // [stacktrace.basic.cmp], comparisons
+    template<class Allocator2>
+    friend bool operator==(const basic_stacktrace& x,
+                           const basic_stacktrace<Allocator2>& y) noexcept;
+    template<class Allocator2>
+    friend strong_ordering operator<=>(const basic_stacktrace& x,
+                                       const basic_stacktrace<Allocator2>& y) noexcept;
+
+    // [stacktrace.basic.mod], modifiers
+    void swap(basic_stacktrace& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+  private:
+    vector<value_type, allocator_type> frames_;         // exposition only
+  };
+}
+
+*/
+
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_STD_VER >= 23
+
+#    include <compare> // [stacktrace.syn]
+
+#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#      pragma GCC system_header
+#    endif
+
+_LIBCPP_PUSH_MACROS
+#    include <__undef_macros>
+
+#    include "experimental/__stacktrace/basic_stacktrace.h"
+#    include "experimental/__stacktrace/stacktrace_entry.h"
+
+_LIBCPP_POP_MACROS
+
+#  endif // _LIBCPP_STD_VER >= 23
+#endif   // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b9cb43ebd999a..be5527e52e930 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2225,6 +2225,16 @@ module std [system] {
       header "experimental/simd"
       export *
     }
+    module stacktrace {
+      private header "experimental/__stacktrace/basic_stacktrace.h"
+      private header "experimental/__stacktrace/detail/alloc.h"
+      private header "experimental/__stacktrace/detail/context.h"
+      private header "experimental/__stacktrace/detail/entry.h"
+      private header "experimental/__stacktrace/detail/to_string.h"
+      private header "experimental/__stacktrace/stacktrace_entry.h"
+      header "experimental/stacktrace"
+      export *
+    }
   }
 
   // Implementation detail headers that are private to libc++. These modules
diff --git a/libcxx/modules/std/stacktrace.inc b/libcxx/modules/std/stacktrace.inc
index c1184087c0df4..e92f730cec62c 100644
--- a/libcxx/modules/std/stacktrace.inc
+++ b/libcxx/modules/std/stacktrace.inc
@@ -8,7 +8,9 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
+// TODO(stacktrace23): update this when stacktrace is taken out of experimental
+#if 0 //_LIBCPP_STD_VER >= 23
+
   // [stacktrace.entry], class stacktrace_­entry
   using std::stacktrace_entry;
 
@@ -31,5 +33,6 @@ export namespace std {
 
   // [stacktrace.basic.hash], hash support
   using std::hash;
-#endif
+
+#endif // _LIBCPP_STD_VER >= 23
 } // namespace std
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 4e9bf900af4c5..b24b98da2bf46 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -309,6 +309,24 @@ add_custom_target(cxx DEPENDS ${LIBCXX_BUILD_TARGETS})
 # Build the experimental static library
 set(LIBCXX_EXPERIMENTAL_SOURCES
   experimental/keep.cpp
+  experimental/stacktrace/alloc.cpp
+  experimental/stacktrace/common/debug.cpp
+  experimental/stacktrace/common/fd.cpp
+  experimental/stacktrace/context.cpp
+  experimental/stacktrace/linux/linux-dl.cpp
+  experimental/stacktrace/linux/linux-elf.cpp
+  experimental/stacktrace/linux/linux-sym.cpp
+  experimental/stacktrace/osx/osx.cpp
+  experimental/stacktrace/stacktrace.cpp
+  experimental/stacktrace/tools/addr2line.cpp
+  experimental/stacktrace/tools/atos.cpp
+  experimental/stacktrace/tools/llvm_symbolizer.cpp
+  experimental/stacktrace/tools/toolspawner.cpp
+  experimental/stacktrace/unwind/unwind.cpp
+  experimental/stacktrace/windows/dbghelp_dll.cpp
+  experimental/stacktrace/windows/dll.cpp
+  experimental/stacktrace/windows/psapi_dll.cpp
+  experimental/stacktrace/windows/win_impl.cpp
   )
 
 if (LIBCXX_PSTL_BACKEND STREQUAL "libdispatch")
@@ -334,6 +352,7 @@ endif()
 
 add_library(cxx_experimental STATIC ${LIBCXX_EXPERIMENTAL_SOURCES})
 target_link_libraries(cxx_experimental PUBLIC cxx-headers)
+
 if (LIBCXX_ENABLE_SHARED)
   target_link_libraries(cxx_experimental PRIVATE cxx_shared)
 else()
diff --git a/libcxx/src/experimental/stacktrace/alloc.cpp b/libcxx/src/experimental/stacktrace/alloc.cpp
new file mode 100644
index 0000000000000..956e01b312d3b
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/alloc.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+void alloc::_anchor_vfunc() {}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/config.h b/libcxx/src/experimental/stacktrace/common/config.h
new file mode 100644
index 0000000000000..0915b57463deb
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/config.h
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_CONFIG_H
+#define _LIBCPP_STACKTRACE_CONFIG_H
+
+#include <__config>
+#include <__config_site>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+#if __has_include(<unwind.h>)
+#  define _LIBCPP_STACKTRACE_COLLECT_UNWIND
+#endif
+
+#if defined(__APPLE__)
+#  define _LIBCPP_STACKTRACE_APPLE
+#endif
+
+#if defined(__linux__)
+#  define _LIBCPP_STACKTRACE_LINUX
+#endif
+
+#if __has_include(<spawn.h>) && _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
+#  define _LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS
+#endif
+
+#if __has_include(<windows.h>) && __has_include(<dbghelp.h>) && __has_include(<psapi.h>)
+#  define _LIBCPP_STACKTRACE_WINDOWS
+#endif
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_CONFIG_H
diff --git a/libcxx/src/experimental/stacktrace/common/debug.cpp b/libcxx/src/experimental/stacktrace/common/debug.cpp
new file mode 100644
index 0000000000000..4f113e10c0c23
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/debug.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "debug.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+debug::~debug() = default;
+
+debug::dummy_ostream::~dummy_ostream() = default;
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/debug.h b/libcxx/src/experimental/stacktrace/common/debug.h
new file mode 100644
index 0000000000000..0cee301d42d8b
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/debug.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_DEBUG_H
+#define _LIBCPP_STACKTRACE_DEBUG_H
+
+#include <__config>
+#include <iostream>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/** Debug-message output stream.  If `LIBCXX_STACKTRACE_DEBUG` is defined in the environment
+or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
+otherwise its does nothing by returning a dummy stream. */
+struct debug : std::ostream {
+  virtual ~debug();
+
+  static bool enabled() {
+#if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
+    return true;
+#else
+    static bool ret = [] {
+      auto const* val = getenv("LIBCXX_STACKTRACE_DEBUG");
+      return val && !strncmp(val, "1", 1);
+    }();
+    return ret;
+#endif
+  }
+
+  /** No-op output stream. */
+  struct dummy_ostream final : std::ostream {
+    virtual ~dummy_ostream();
+    friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
+  };
+
+  friend std::ostream& operator<<(debug& dp, auto const& val) {
+    static dummy_ostream kdummy;
+    if (!enabled()) {
+      return kdummy;
+    }
+    std::cerr << val;
+    return std::cerr;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_DEBUG_H
diff --git a/libcxx/src/experimental/stacktrace/common/failed.h b/libcxx/src/experimental/stacktrace/common/failed.h
new file mode 100644
index 0000000000000..e53eb1123aabc
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/failed.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_FAILED_H
+#define _LIBCPP_STACKTRACE_FAILED_H
+
+#include <stdexcept>
+
+// TODO(stacktrace23) Use std::expected instead?
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct failed : std::runtime_error {
+  virtual ~failed() = default;
+  int errno_{0};
+  failed() : std::runtime_error({}) {}
+  failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_FAILED_H
diff --git a/libcxx/src/experimental/stacktrace/common/fd.cpp b/libcxx/src/experimental/stacktrace/common/fd.cpp
new file mode 100644
index 0000000000000..b7728ea7826dd
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/fd.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "fd.h"
+#include "failed.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+fd_streambuf::~fd_streambuf() = default;
+fd_istream::~fd_istream()     = default;
+
+int fd_streambuf::underflow() {
+  int bytesRead = ::read(fd_, buf_, size_);
+  if (bytesRead < 0) {
+    throw failed("I/O error reading from child process", errno);
+  }
+  if (bytesRead == 0) {
+    return traits_type::eof();
+  }
+  setg(buf_, buf_, buf_ + bytesRead);
+  return int(*buf_);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/fd.h b/libcxx/src/experimental/stacktrace/common/fd.h
new file mode 100644
index 0000000000000..ff003e16a6bd8
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/fd.h
@@ -0,0 +1,122 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_FD_H
+#define _LIBCPP_STACKTRACE_FD_H
+
+#include <__config>
+#include <cassert>
+#include <cerrno>
+#include <cstdio>
+#include <iostream>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <utility>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/** Encapsulates a plain old file descriptor `int`.  Avoids copies in order to
+force some component to "own" this, although it's freely convertible back to
+integer form.  Default-constructed, closed, and moved-out-of instances will have
+the invalid fd `-1`. */
+class fd {
+  int fd_{-1};
+
+public:
+  fd() : fd(-1) {}
+  fd(int fdint) : fd_(fdint) {}
+
+  fd(fd const&)            = delete;
+  fd& operator=(fd const&) = delete;
+
+  fd(fd&& rhs) {
+    if (&rhs != this) {
+      std::exchange(fd_, rhs.fd_);
+    }
+  }
+
+  fd& operator=(fd&& rhs) { return *new (this) fd(std::move(rhs)); }
+
+  ~fd() { close(); }
+
+  /** Returns true IFF fd is above zero */
+  bool valid() const { return fd_ > 0; }
+
+  /* implicit */ operator int() const {
+    auto ret = fd_;
+    assert(ret > 0);
+    return ret;
+  }
+
+  void close() {
+    int fd_old = -1;
+    std::exchange(fd_old, fd_);
+    if (fd_old != -1) {
+      ::close(fd_old);
+    }
+  }
+
+  static fd& null_fd() {
+    static fd ret = {::open("/dev/null", O_RDWR)};
+    return ret;
+  }
+
+  static fd open(std::string_view path) {
+    fd ret = {::open(path.data(), O_RDONLY)};
+    return ret;
+  }
+};
+
+/** Wraps a readable fd using the `streambuf` interface.  I/O errors arising
+from reading the provided fd will result in a `Failed` being thrown. */
+struct fd_streambuf final : std::streambuf {
+  fd& fd_;
+  char* buf_;
+  size_t size_;
+  fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
+  virtual ~fd_streambuf();
+  int underflow() override;
+};
+
+/** Wraps an `FDInStreamBuffer` in an `istream` */
+struct fd_istream final : std::istream {
+  fd_streambuf& buf_;
+  virtual ~fd_istream();
+  explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
+};
+
+struct fd_mmap final {
+  fd fd_{};
+  size_t size_{0};
+  std::byte const* addr_{nullptr};
+
+  explicit fd_mmap(std::string_view path) : fd_mmap(fd::open(path)) {}
+
+  explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
+    if (fd_) {
+      if ((size_ = ::lseek(fd, 0, SEEK_END))) {
+        addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
+      }
+    }
+  }
+
+  operator bool() const { return addr_; }
+
+  ~fd_mmap() {
+    if (addr_) {
+      ::munmap(const_cast<void*>((void const*)addr_), size_);
+    }
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_FD_H
diff --git a/libcxx/src/experimental/stacktrace/common/images.h b/libcxx/src/experimental/stacktrace/common/images.h
new file mode 100644
index 0000000000000..cb9eac47cf9dd
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/images.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_IMAGES_H
+#define _LIBCPP_STACKTRACE_IMAGES_H
+
+#include <cstdint>
+#include <string_view>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+constexpr unsigned k_max_images = 256;
+
+struct image {
+  uintptr_t loaded_at_{};
+  intptr_t slide_{};
+  std::string_view name_{};
+  bool is_main_prog_{};
+
+  bool operator<(image const& rhs) const { return loaded_at_ < rhs.loaded_at_; }
+  operator bool() const { return !name_.empty(); }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/experimental/stacktrace/context.cpp b/libcxx/src/experimental/stacktrace/context.cpp
new file mode 100644
index 0000000000000..3d311af4d7a89
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/context.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <list>
+
+#include <experimental/__stacktrace/basic_stacktrace.h>
+#include <experimental/__stacktrace/stacktrace_entry.h>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+#include <experimental/__stacktrace/detail/to_string.h>
+
+#include "common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+#  include "linux/linux.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+#  include "osx/osx.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+#  include "tools/pspawn.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+#  include "unwind/unwind.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+#  include "windows/win_impl.h"
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip, size_t max_depth) {
+  /*
+  Here we declare stacktrace components or "backends" which will handle the different tasks:
+
+  (1) get the addresses from the call stack
+  (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
+  (3) resolve using debug info, and possibly with an external tool on the $PATH
+  (4+) extra passes to get symbols, in case 3 couldn't
+
+  Based on the macros produced by `stacktrace.h`, throw all backends we have available at the task.  Ideally the #ifdef
+  gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
+  symbolizer.  If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
+
+  All these classes do their best to provide any of the requested fields they can: (symbol, filename, source line),
+  substituting if needed with something reasonable.  For example, if the source filename and line are not available
+  then we will at least report that the address and symbol are in the module `foo.exe`.
+
+  These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
+  already-populated data.  We take care not to crash / abort / throw in any of these, and we'll silently fail.  See
+  `common/debug.h` for a debugging logger you can enable at runtime.
+  */
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+  win_impl dbghelp{*this};
+  auto& collector  = dbghelp;
+  auto& mod_ident  = dbghelp;
+  auto& resolver   = dbghelp;
+  auto& symbolizer = dbghelp;
+#endif
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+  unwind unwind{*this};
+  auto& collector = unwind;
+#endif
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+  osx osx{*this};
+  auto& mod_ident  = osx;
+  auto& symbolizer = osx;
+#endif
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+  linux linux{*this};
+  auto& mod_ident  = linux;
+  auto& symbolizer = linux;
+#endif
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+  spawner pspawn{*this};
+  auto& resolver = pspawn;
+#endif
+
+  collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
+  if (__entries_.size()) {                // (Can't proceed if empty)
+    mod_ident.ident_modules();            // Associate addrs with binaries (ELF/MachO/etc.)
+    resolver.resolve_lines();             // Resolve addresses to symbols, filename, linenumber
+    symbolizer.symbolize();               // Populate missing symbols, if any.
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/linux/elf.h b/libcxx/src/experimental/stacktrace/linux/elf.h
new file mode 100644
index 0000000000000..214e8cc5a7819
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/elf.h
@@ -0,0 +1,315 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_ELF_H
+#define _LIBCPP_STACKTRACE_ELF_H
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <functional>
+#include <string_view>
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// Includes ELF constants and structs copied from <elf.h>, with a few changes.
+
+namespace elf {
+
+struct Header32 final {
+  uint8_t ident[16];  /* Magic number and other info */
+  uint16_t type;      /* Object file type */
+  uint16_t machine;   /* Architecture */
+  uint32_t version;   /* Object file version */
+  uint32_t entry;     /* Entry point virtual address */
+  uint32_t phoff;     /* Program header table file offset */
+  uint32_t shoff;     /* Section header table file offset */
+  uint32_t flags;     /* Processor-specific flags */
+  uint16_t ehsize;    /* ELF header size in bytes */
+  uint16_t phentsize; /* Program header table entry size */
+  uint16_t phnum;     /* Program header table entry count */
+  uint16_t shentsize; /* Section header table entry size */
+  uint16_t shnum;     /* Section header table entry count */
+  uint16_t shstrndx;  /* Section header string table index */
+};
+
+struct Section32 final {
+  uint32_t name;      /* Section name (string tbl index) */
+  uint32_t type;      /* Section type */
+  uint32_t flags;     /* Section flags */
+  uint32_t addr;      /* Section virtual addr at execution */
+  uint32_t offset;    /* Section file offset */
+  uint32_t size;      /* Section size in bytes */
+  uint32_t link;      /* Link to another section */
+  uint32_t info;      /* Additional section information */
+  uint32_t addralign; /* Section alignment */
+  uint32_t entsize;   /* Entry size if section holds table */
+};
+
+struct Symbol32 final {
+  uint32_t name;  /* Symbol name (string tbl index) */
+  uint32_t value; /* Symbol value */
+  uint32_t size;  /* Symbol size */
+  uint8_t info;   /* Symbol type and binding */
+  uint8_t other;  /* Symbol visibility */
+  uint16_t shndx; /* Section index */
+};
+
+struct Header64 final {
+  uint8_t ident[16];  /* Magic number and other info */
+  uint16_t type;      /* Object file type */
+  uint16_t machine;   /* Architecture */
+  uint32_t version;   /* Object file version */
+  uint64_t entry;     /* Entry point virtual address */
+  uint64_t phoff;     /* Program header table file offset */
+  uint64_t shoff;     /* Section header table file offset */
+  uint32_t flags;     /* Processor-specific flags */
+  uint16_t ehsize;    /* ELF header size in bytes */
+  uint16_t phentsize; /* Program header table entry size */
+  uint16_t phnum;     /* Program header table entry count */
+  uint16_t shentsize; /* Section header table entry size */
+  uint16_t shnum;     /* Section header table entry count */
+  uint16_t shstrndx;  /* Section header string table index */
+};
+
+struct Section64 final {
+  uint32_t name;      /* Section name (string tbl index) */
+  uint32_t type;      /* Section type */
+  uint64_t flags;     /* Section flags */
+  uint64_t addr;      /* Section virtual addr at execution */
+  uint64_t offset;    /* Section file offset */
+  uint64_t size;      /* Section size in bytes */
+  uint32_t link;      /* Link to another section */
+  uint32_t info;      /* Additional section information */
+  uint64_t addralign; /* Section alignment */
+  uint64_t entsize;   /* Entry size if section holds table */
+};
+
+struct Symbol64 final {
+  uint32_t name;  /* Symbol name (string tbl index) */
+  uint8_t info;   /* Symbol type and binding */
+  uint8_t other;  /* Symbol visibility */
+  uint16_t shndx; /* Section index */
+  uint64_t value; /* Symbol value */
+  uint64_t size;  /* Symbol size */
+};
+
+/** Represents an ELF header.  Supports the minimum needed to navigate an ELF file's sections and get at the symbol and
+ * string tables. */
+struct Header final {
+  std::byte const* ptr_{};
+  uintptr_t shoff_{};
+  size_t shnum_{};
+  size_t shstrndx_{};
+
+  Header()              = default;
+  Header(Header const&) = default;
+  Header& operator=(Header const& rhs) { return *new (this) Header(rhs); }
+
+  operator bool() { return ptr_; }
+
+  template <class H>
+  explicit Header(H* h)
+      : ptr_((std::byte const*)h),
+        shoff_(uintptr_t(h->shoff)),
+        shnum_(size_t(h->shnum)),
+        shstrndx_(size_t(h->shstrndx)) {}
+};
+
+struct ELF;
+struct StringTable;
+
+struct Section final {
+  constexpr static uint32_t kSymTab = 2; // symbol table
+  constexpr static uint32_t kStrTab = 3; // name table for symbols or sections
+
+  ELF* elf_{};
+  std::byte const* ptr_{};
+  uintptr_t nameIndex_{};
+  uint32_t type_{};
+  uintptr_t offset_{};
+  size_t size_{};
+
+  Section() = default;
+
+  template <class S>
+  Section(ELF* elf, S* sec)
+      : elf_(elf),
+        ptr_((std::byte const*)sec),
+        nameIndex_(sec->name),
+        type_(sec->type),
+        offset_(sec->offset),
+        size_(sec->size) {}
+
+  operator bool() const { return ptr_; }
+
+  template <class T = std::byte>
+  T const* data() const {
+    return (T const*)(elfBase() + offset_);
+  }
+
+  std::byte const* elfBase() const;
+  std::string_view name() const;
+};
+
+struct Symbol final {
+  constexpr static uint8_t kFunc = 0x02; // STT_FUNC (code object)
+
+  ELF* elf_{};
+  std::byte const* ptr_{};
+  uintptr_t nameIndex_{};
+  uint32_t type_{};
+  uintptr_t value_{};
+
+  Symbol()              = default;
+  Symbol(Symbol const&) = default;
+  Symbol& operator=(Symbol const& rhs) { return *new (this) Symbol(rhs); }
+
+  operator bool() { return ptr_; }
+
+  bool isCode() const { return type_ == kFunc; }
+
+  template <class S>
+  Symbol(ELF* elf, S* sym)
+      : elf_(elf), ptr_((std::byte const*)sym), nameIndex_(sym->name), type_(0x0f & sym->info), value_(sym->value) {}
+
+  std::byte const* elfBase() const;
+  std::string_view name() const;
+};
+
+/** Represents one of the ELF's `strtab`s.  This is a block of string data, with strings appended one after another, and
+ * NUL-terminated.  Strings are indexed according to their starting offset.  At offset 0 is typically an empty string.
+ */
+struct StringTable {
+  std::string_view data_{};
+
+  StringTable() = default;
+
+  /* implicit */ StringTable(Section const& sec) : data_(sec.data<char>(), sec.size_) {}
+
+  operator bool() { return data_.size(); }
+
+  std::string_view at(size_t index) {
+    auto* ret = data_.data() + index;
+    return {ret, strlen(ret)};
+  }
+};
+
+/** Encapsulates an ELF image specified by byte-address (e.g. from an mmapped file or a program image or shared object
+ * in memory).  If given a supported ELF image, this will test true with `operator bool` to indicate it is supported and
+ * was able to parse some basic information from the header. */
+struct ELF {
+  Header header_{};
+  Section (*makeSection_)(ELF*, std::byte const*){};
+  Symbol (*makeSymbol_)(ELF*, std::byte const*){};
+  size_t secSize_{};
+  size_t symSize_{};
+  StringTable nametab_{};
+  Section symtab_{};
+  StringTable strtab_{};
+  size_t symCount_{};
+
+  static Section makeSection32(ELF* elf, std::byte const* ptr) { return Section(elf, (Section32 const*)ptr); }
+  static Section makeSection64(ELF* elf, std::byte const* ptr) { return Section(elf, (Section64 const*)ptr); }
+  static Symbol makeSymbol32(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol32 const*)ptr); }
+  static Symbol makeSymbol64(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol64 const*)ptr); }
+
+  operator bool() { return header_; }
+
+  explicit ELF(std::byte const* image) {
+    auto* p = (uint8_t const*)image;
+    // Bytes 0..3: magic bytes: 0x7F, 'E', 'L', 'F'
+    if (*p++ == 0x7f && *p++ == 0x45 && *p++ == 0x4c && *p++ == 0x46) {
+      auto klass       = *p++; // Byte 4 (EI_CLASS): ELF class, 32- or 64-bit (0x01 or 0x02)
+      auto dataFormat  = *p++; // Byte 5 (EI_DATA): (0x01) little- or (0x02) big-endian
+      auto fileVersion = *p++; // Byte 6 (EI_VERSION): ELF version: expect 1 (latest ELF version)
+      constexpr static uint16_t kEndianTestWord{0x0201};
+      auto hostEndianness = *(uint8_t const*)&kEndianTestWord;
+      if (dataFormat == hostEndianness && fileVersion == 1) {
+        if (klass == 0x01) {
+          header_      = Header((Header32 const*)image);
+          makeSection_ = makeSection32;
+          makeSymbol_  = makeSymbol32;
+          secSize_     = sizeof(Section32);
+          symSize_     = sizeof(Symbol32);
+        } else if (klass == 0x02) {
+          header_      = Header((Header64 const*)image);
+          makeSection_ = makeSection64;
+          makeSymbol_  = makeSymbol64;
+          secSize_     = sizeof(Section64);
+          symSize_     = sizeof(Symbol64);
+        }
+      }
+    }
+    if (*this) {
+      nametab_ = section(header_.shstrndx_);
+      eachSection([&](auto& sec) mutable -> bool {
+        if (sec.type_ == Section::kSymTab && sec.name() == ".symtab") {
+          symtab_ = sec;
+        } else if (sec.type_ == Section::kStrTab && sec.name() == ".strtab") {
+          strtab_ = sec;
+        }
+        return !symtab_ || !strtab_;
+      });
+    }
+    if (symtab_) {
+      symCount_ = symtab_.size_ / symSize_;
+    }
+  }
+
+  Section section(size_t index) {
+    auto* addr = header_.ptr_ + header_.shoff_ + (index * secSize_);
+    return makeSection_(this, addr);
+  }
+
+  Symbol symbol(size_t index) {
+    auto* addr = symtab_.data() + (index * symSize_);
+    return makeSymbol_(this, addr);
+  }
+
+  template <class T>
+  using CB = std::function<bool(T const&)>;
+
+  void eachSection(CB<Section> cb) {
+    for (size_t i = 0; i < header_.shnum_ && cb(section(i)); i++)
+      ;
+  }
+
+  void eachSymbol(CB<Symbol> cb) {
+    for (size_t i = 0; i < symCount_ && cb(symbol(i)); i++)
+      ;
+  }
+
+  Symbol getSym(uintptr_t addr) {
+    Symbol ret{};
+    eachSymbol([&](auto& sym) -> bool {
+      if (sym.value_ <= addr && sym.value_ > ret.value_) {
+        ret = sym;
+      }
+      return true;
+    });
+    return ret;
+  }
+};
+
+inline std::byte const* Section::elfBase() const { return elf_->header_.ptr_; }
+inline std::byte const* Symbol::elfBase() const { return elf_->header_.ptr_; }
+
+inline std::string_view Section::name() const { return elf_->nametab_.at(nameIndex_); }
+inline std::string_view Symbol::name() const { return elf_->strtab_.at(nameIndex_); }
+
+} // namespace elf
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp b/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
new file mode 100644
index 0000000000000..62cad72b9f124
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+#  include "linux.h"
+
+#  include <cassert>
+#  include <dlfcn.h>
+#  include <link.h>
+#  include <unistd.h>
+
+#  include "../common/images.h"
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+void linux::ident_modules() {
+  auto& images = images::get();
+
+  // Aside from the left/right sentinels in the array (hence the 2),
+  // are there any other real images?
+  if (images.count_ <= 2) {
+    return;
+  }
+
+  auto mainProg = images.mainProg();
+  if (mainProg) {
+    cx_.__main_prog_path_ = mainProg->name_;
+  }
+
+  unsigned index = 1; // Starts at one, and is moved around in this loop
+  for (auto& entry : cx_.__entries_) {
+    while (images[index].loaded_at_ > entry.__addr_) {
+      --index;
+    }
+    while (images[index + 1].loaded_at_ <= entry.__addr_) {
+      ++index;
+    }
+    entry.__addr_unslid_ = entry.__addr_ - images[index].slide_;
+    entry.__file_        = images[index].name_;
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp b/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
new file mode 100644
index 0000000000000..7cf82789ea2ed
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+#  include "linux.h"
+
+#  include <cassert>
+#  include <unistd.h>
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/fd.h"
+#  include "elf.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/**
+When trying to collect a stacktrace under Linux, there are narrow (but still quite common) cases where we will fail
+to resolve symbols.  Linux's `dl` doesn't want to read symbols from the non-exported symbol table at runtime,
+and older versions of `addr2line` or `llvm-symbolizer` will also not resolve these.
+
+This implementation the minimum necessary to resolve symbols.  It can identify this as an ELF (32 or 64 bits), locate
+the symbol and symbol-string table, and fill in any remaining missing symbols.
+*/
+void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
+  // We can statically initialize these, because main_elf_name should be the same every time.
+  static fd_mmap _mm(main_elf_name);
+  if (_mm) {
+    static elf::ELF _this_elf(_mm.addr_);
+    if (_this_elf) {
+      for (auto& entry : cx_.__entries_) {
+        if (entry.__desc_.empty() && entry.__file_ == main_elf_name) {
+          entry.__desc_ = _this_elf.getSym(entry.__addr_unslid_).name();
+        }
+      }
+    }
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp b/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
new file mode 100644
index 0000000000000..fa7489c64c923
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+#  include "linux.h"
+
+#  include <cassert>
+#  include <dlfcn.h>
+#  include <unistd.h>
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+bool symbolize_entry(entry& entry) {
+  bool ret = false;
+  Dl_info info;
+  if (dladdr((void*)entry.__addr_, &info)) {
+    ret = true; // at least partially successful
+    if (info.dli_fname && entry.__file_.empty()) {
+      // provide at least the binary filename in case we cannot lookup source location
+      entry.__file_ = info.dli_fname;
+    }
+    if (info.dli_sname && entry.__desc_.empty()) {
+      // provide at least the mangled name; try to unmangle in a later step
+      entry.__desc_ = info.dli_sname;
+    }
+  }
+  return ret;
+}
+
+// NOTE:  We can use `dlfcn` to resolve addresses to symbols, which works great --
+// except for symbols in the main program.  If addr2line-style tools are enabled, that step
+// might also be able to get symbols directly from the binary's debug info.
+void linux::symbolize() {
+  for (auto& entry : cx_.__entries_) {
+    symbolize_entry(entry);
+  }
+  // Symbols might be missing, because both (1) Linux's `dladdr` won't try to resolve non-exported symbols,
+  // which can be the case for the main program executable; and (2) debug info was not preserved.
+  // As a last resort, this function (see `linux-elf.cpp`) can still access symbol table directly.
+  image* mainELF = images::get().mainProg();
+  if (mainELF && !mainELF->name_.empty()) {
+    resolve_main_elf_syms(mainELF->name_);
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/experimental/stacktrace/linux/linux.h b/libcxx/src/experimental/stacktrace/linux/linux.h
new file mode 100644
index 0000000000000..a8e8c2279f6fc
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux.h
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_LINUX_H
+#define _LIBCPP_STACKTRACE_LINUX_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+#  include <algorithm>
+#  include <array>
+#  include <cassert>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <link.h>
+#  include <string_view>
+#  include <unistd.h>
+
+#  include "../common/images.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct linux {
+  context& cx_;
+  void ident_modules();
+  void symbolize();
+
+private:
+  void resolve_main_elf_syms(std::string_view elf_name);
+};
+
+struct images {
+  // How many images this contains, including the left/right sentinels.
+  unsigned count_{0};
+  std::array<image, k_max_images + 2> images_{};
+
+  int add(dl_phdr_info& info) {
+    assert(count_ < k_max_images);
+    auto isFirst        = (count_ == 0);
+    auto& image         = images_.at(count_++);
+    image.loaded_at_    = info.dlpi_addr;
+    image.slide_        = info.dlpi_addr;
+    image.name_         = info.dlpi_name;
+    image.is_main_prog_ = isFirst; // first one is the main ELF
+    if (image.name_.empty() && isFirst) {
+      static char buffer[PATH_MAX + 1];
+      uint32_t length = sizeof(buffer);
+      if (readlink("/proc/self/exe", buffer, length) > 0) {
+        image.name_ = buffer;
+      }
+    }
+    return count_ == k_max_images; // return nonzero if we're at the limit
+  }
+
+  static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
+
+  images() {
+    dl_iterate_phdr(images::callback, this);
+    images_[count_++] = {0uz, 0};  // sentinel at low end
+    images_[count_++] = {~0uz, 0}; // sentinel at high end
+    std::sort(images_.begin(), images_.begin() + count_ - 1);
+  }
+
+  image& operator[](size_t index) {
+    assert(index < count_);
+    return images_.at(index);
+  }
+
+  image* mainProg() {
+    for (auto& image : images_) {
+      if (image.is_main_prog_) {
+        return ℑ
+      }
+    }
+    return nullptr;
+  }
+
+  static images& get() {
+    static images images;
+    return images;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
+
+#endif // _LIBCPP_STACKTRACE_LINUX_H
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.cpp b/libcxx/src/experimental/stacktrace/osx/osx.cpp
new file mode 100644
index 0000000000000..c00a7ea94e780
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/osx/osx.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+
+#  include <algorithm>
+#  include <array>
+#  include <dlfcn.h>
+#  include <mach-o/dyld.h>
+#  include <mach-o/loader.h>
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "osx.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+constexpr unsigned kMaxImages = 256;
+
+struct Image {
+  uintptr_t loadedAt{};
+  intptr_t slide{};
+  std::string_view name{};
+  bool operator<(Image const& rhs) const { return loadedAt < rhs.loadedAt; }
+  operator bool() const { return !name.empty(); }
+};
+
+void ident_module(entry& entry, unsigned& index, Image* images) {
+  if (entry.__addr_) {
+    while (images[index].loadedAt > entry.__addr_) {
+      --index;
+    }
+    while (images[index + 1].loadedAt <= entry.__addr_) {
+      ++index;
+    }
+    auto& image = images[index];
+    if (image) {
+      entry.__addr_unslid_ = entry.__addr_ - images[index].slide;
+      entry.__file_        = images[index].name;
+    }
+  }
+}
+
+bool enum_modules(unsigned& count, auto& images) {
+  count = std::min(kMaxImages, _dyld_image_count());
+  for (size_t i = 0; i < count; i++) {
+    auto& image    = images[i];
+    image.slide    = _dyld_get_image_vmaddr_slide(i);
+    image.loadedAt = uintptr_t(_dyld_get_image_header(i));
+    image.name     = _dyld_get_image_name(i);
+  }
+  images[count++] = {0uz, 0};  // sentinel at low end
+  images[count++] = {~0uz, 0}; // sentinel at high end
+  std::sort(images.begin(), images.begin() + count - 1);
+  return true;
+}
+
+void osx::ident_modules() {
+  static unsigned imageCount;
+  static std::array<Image, kMaxImages + 2> images;
+  static bool atomicInitialized = enum_modules(imageCount, images);
+  (void)atomicInitialized;
+
+  // Aside from the left/right sentinels in the array (hence the 2),
+  // are there any other real images?
+  if (imageCount <= 2) {
+    return;
+  }
+
+  // First image (the main program) is at index 1
+  cx_.__main_prog_path_ = images.at(1).name;
+
+  unsigned index = 1; // Starts at one, and is moved by 'ident_module'
+  for (auto& entry : cx_.__entries_) {
+    ident_module(entry, index, images.data());
+  }
+}
+
+void symbolize_entry(entry& entry) {
+  Dl_info info;
+  if (dladdr((void*)entry.__addr_, &info)) {
+    if (info.dli_fname && entry.__file_.empty()) {
+      // provide at least the binary filename in case we cannot lookup source location
+      entry.__file_ = info.dli_fname;
+    }
+    if (info.dli_sname && entry.__desc_.empty()) {
+      // provide at least the mangled name; try to unmangle in a later step
+      entry.__desc_ = info.dli_sname;
+    }
+  }
+}
+
+void osx::symbolize() {
+  for (auto& entry : cx_.__entries_) {
+    symbolize_entry(entry);
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_APPLE
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.h b/libcxx/src/experimental/stacktrace/osx/osx.h
new file mode 100644
index 0000000000000..ca0890929453e
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/osx/osx.h
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_OSX_H
+#define _LIBCPP_STACKTRACE_OSX_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct osx {
+  context& cx_;
+  void ident_modules();
+  void symbolize();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_OSX_H
diff --git a/libcxx/src/experimental/stacktrace/stacktrace.cpp b/libcxx/src/experimental/stacktrace/stacktrace.cpp
new file mode 100644
index 0000000000000..e33b94205a8ea
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/stacktrace.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <experimental/__stacktrace/basic_stacktrace.h>
+#include <experimental/__stacktrace/stacktrace_entry.h>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+#include <experimental/__stacktrace/detail/to_string.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/*
+ * `to_string` Helpers
+ */
+
+void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
+  // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
+  constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
+
+  __os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << entry.native_handle();
+  if (!entry.description().empty()) {
+    __os << ": " << entry.description();
+  }
+  if (!entry.source_file().empty()) {
+    __os << ": " << entry.source_file();
+  }
+  if (entry.source_line()) {
+    __os << ":" << std::dec << entry.source_line();
+  }
+}
+
+void __to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
+  /*
+   * Print each entry as a line, as per `operator()`, with additional whitespace
+   * at the start of the line, and only a newline added at the end:
+   *
+   *   frame   1: 0xbeefbeefbeef: _symbol_name: /path/to/file.cc:123
+   */
+  if (!__count) {
+    __os << "(empty stacktrace)";
+  } else {
+    for (size_t __i = 0; __i < __count; __i++) {
+      if (__i) {
+        // Insert newlines between entries (but not before the first or after the last)
+        __os << std::endl;
+      }
+      __os << "  frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": ";
+      (*this)(__os, __entries[__i]);
+    }
+  }
+}
+
+string __to_string::operator()(std::stacktrace_entry const& entry) {
+  stringstream __ss;
+  (*this)(__ss, entry);
+  return __ss.str();
+}
+
+string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
+  stringstream __ss;
+  (*this)(__ss, __entries, __count);
+  return __ss.str();
+}
+
+} // namespace __stacktrace
+
+// `to_string`-related non-member functions
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
+  return __stacktrace::__to_string()(__entry);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
+  __stacktrace::__to_string()(__os, __entry);
+  return __os;
+}
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/addr2line.cpp b/libcxx/src/experimental/stacktrace/tools/addr2line.cpp
new file mode 100644
index 0000000000000..310e99699569b
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/addr2line.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <iostream>
+
+#include "tools.h"
+
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+std::pmr::list<std::pmr::string> addr2line::buildArgs(context& cx) const {
+  auto& alloc = cx.__alloc_;
+  auto ret    = alloc.new_string_list();
+  if (cx.__main_prog_path_.empty()) {
+    // Should not have reached here but be graceful anyway
+    ret.push_back("/bin/false");
+    return ret;
+  }
+
+  ret.push_back(progName_);
+  ret.push_back("--functions");
+  ret.push_back("--demangle");
+  ret.push_back("--basenames");
+  ret.push_back("--pretty-print"); // This "human-readable form" is easier to parse
+  ret.push_back("-e");
+  ret.push_back(cx.__main_prog_path_);
+  for (auto& entry : cx.__entries_) {
+    ret.push_back(alloc.hex_string(entry.__addr_unslid_));
+  }
+  return ret;
+}
+
+void addr2line::parseOutput(context& trace, entry& entry, std::istream& output) const {
+  // clang-format off
+/*
+Example:
+--
+llvm-addr2line -e foo --functions --demangle --basenames --pretty-print --no-inlines 0x11a0 0x1120 0x3d58 0x1284
+
+Output: (1 line per input address)
+--
+main at foo.cc:15
+register_tm_clones at crtstuff.c:0
+GCC_except_table2 at foo.cc:0
+test::Foo::Foo(int) at foo.cc:11
+*/
+  // clang-format on
+
+  std::pmr::string line{&trace.__alloc_};
+  std::getline(output, line);
+  while (isspace(line.back())) {
+    line.pop_back();
+  }
+  if (line.empty()) {
+    return;
+  }
+  // Split at the sequence " at ".  Barring weird symbols
+  // having " at " in them, this should work.
+  auto sepIndex = line.find(" at ");
+  if (sepIndex == std::string::npos) {
+    return;
+  }
+  if (sepIndex > 0) {
+    entry.__desc_ = line.substr(0, sepIndex);
+  }
+  auto fileBegin = sepIndex + 4;
+  if (fileBegin >= line.size()) {
+    return;
+  }
+  auto fileline = line.substr(fileBegin);
+  auto colon    = fileline.find_last_of(":");
+  if (colon > 0 && !fileline.starts_with("?")) {
+    entry.__file_ = fileline.substr(0, colon);
+  }
+
+  if (colon == std::string::npos) {
+    return;
+  }
+  uint32_t lineno = 0;
+  auto pos        = colon;
+  while (isdigit(fileline[++pos])) {
+    lineno = lineno * 10 + (fileline[pos] - '0');
+  }
+  entry.__line_ = lineno;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/atos.cpp b/libcxx/src/experimental/stacktrace/tools/atos.cpp
new file mode 100644
index 0000000000000..569883a4b12bc
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/atos.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <istream>
+#include <string>
+#include <unistd.h>
+
+#include "tools.h"
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+std::pmr::list<std::pmr::string> atos::buildArgs(context& cx) const {
+  auto& alloc = cx.__alloc_;
+  auto ret    = alloc.new_string_list();
+  ret.push_back(progName_);
+  ret.push_back("-p");
+  ret.push_back(alloc.u64_string(getpid()));
+  // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
+  // ret.push_back("--fullPath");
+  for (auto& entry : cx.__entries_) {
+    ret.push_back(alloc.hex_string(entry.__addr_));
+  }
+  return ret;
+}
+
+void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
+  // Simple example:
+  //
+  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+  //
+  // Assuming this is always atos's format (except when it returns empty lines)
+  // we can split the string like so:
+  //
+  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+  //   ^^^^-----^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^-
+  //   sym      module     filename                            line
+  //
+  // Note that very strange filenames or module names can confuse this.
+  // We'll do the best we can for a decent result, while definitely ensuring safety
+  // (i.e. careful with our bound-checking).
+  //
+  // Another more interesting example (with an added newline for legibility):
+  //
+  //   std::__1::basic_ios<char, std::__1::char_traits<char>>::fill[abi:ne190107]() const (in testprog)
+  //   (/opt/homebrew/Cellar/llvm/19.1.7_1/bin/../include/c++/v1/ios:0
+  //
+  // If this more or less fits our expected format we'll take these data,
+  // even if the line number is 0.
+
+  auto line = cx.__alloc_.new_string(256);
+  std::getline(output, line);
+  while (isspace(line.back())) {
+    line.pop_back();
+  }
+  if (line.empty()) {
+    return;
+  }
+  auto buf  = line.data();
+  auto size = line.size();
+
+  auto* end    = buf + size;
+  auto* symEnd = strstr(buf, " (in ");
+  if (!symEnd) {
+    return;
+  }
+  auto* modBegin = symEnd + 5;
+  auto* modEnd   = strstr(modBegin, ") (");
+  if (!modEnd) {
+    return;
+  }
+  auto* fileBegin = modEnd + 3; // filename starts just after that
+  if (fileBegin >= end) {
+    return;
+  }
+  auto const* lastColon = fileBegin; // we'll search for last colon after filename
+  char const* nextColon;
+  while ((nextColon = strstr(lastColon + 1, ":"))) { // skip colons in filename (e.g. in "C:\foo.cpp")
+    lastColon = nextColon;
+  }
+
+  std::string_view sym{buf, size_t(symEnd - buf)};
+  // In case a previous step could not obtain the symbol name,
+  // we have the name provided by atos; only use that if we have no symbol
+  // (no need to copy more strings otherwise).
+  if (entry.__desc_.empty() && !sym.empty()) {
+    entry.__desc_ = sym;
+  }
+
+  std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
+  if (file != "?" && file != "??" && !file.empty()) {
+    entry.__file_ = file;
+  }
+
+  unsigned lineno = 0;
+  for (auto* digit = lastColon + 1; digit < end && isdigit(*digit); ++digit) {
+    lineno = (lineno * 10) + unsigned(*digit - '0');
+  }
+  entry.__line_ = lineno;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp b/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
new file mode 100644
index 0000000000000..0a9907978dcf2
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <iostream>
+#include <string_view>
+
+#include "../common/debug.h"
+#include "tools.h"
+
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// TODO(stacktrace23): possible to link against `libLLVMSymbolize.a`, or some shared obj at runtime (does that exist?)
+
+std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
+  auto& alloc = cx.__alloc_;
+  auto ret    = alloc.new_string_list();
+  ret.push_back(progName_);
+  ret.push_back("--demangle");
+  ret.push_back("--no-inlines");
+  ret.push_back("--verbose");
+  ret.push_back("--relativenames");
+  ret.push_back("--functions=short");
+  for (auto& entry : cx.__entries_) {
+    auto addr_string = alloc.hex_string(entry.__addr_unslid_);
+    debug() << "@@@ " << addr_string << " " << entry.__file_ << " " << entry.__file_.empty() << '\n';
+    if (!entry.__file_.empty()) {
+      auto arg = alloc.new_string(entry.__file_.size() + 40);
+      arg += "FILE:";
+      arg += entry.__file_;
+      arg += " ";
+      arg += addr_string;
+      ret.push_back(arg);
+    } else {
+      ret.push_back(addr_string);
+    }
+  }
+  return ret;
+}
+
+void llvm_symbolizer::parseOutput(context& cx, entry& entry, std::istream& output) const {
+  // clang-format off
+/*
+With "--verbose", parsing is a little easier, or at least, more reliable;
+probably the best solution (until we have a JSON parser).
+Example output, verbatim, between the '---' lines:
+---
+test1<test_alloc<std::__1::stacktrace_entry> >
+  Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+  Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+  Function start line: 114
+  Function start address: 0x8dd0
+  Line: 116
+  Column: 14
+
+---
+Note that this includes an extra empty line as a terminator.
+*/
+  // clang-format on
+
+  auto& alloc = cx.__alloc_;
+  auto line   = alloc.new_string(256);
+  std::string_view tmp;
+  while (true) {
+    std::getline(output, line);
+    while (isspace(line.back())) {
+      line.pop_back();
+    }
+    if (line.empty()) {
+      return;
+    }
+    if (!line.starts_with("  ")) {
+      // The symbol has no leading whitespace, while the other
+      // lines with "fields" like line, column, filename, etc.
+      // start with two spaces.
+      if (line != "??") {
+        entry.__desc_ = line;
+      }
+    } else if (line.starts_with("  Filename:")) {
+      tmp = line;
+      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+      if (tmp != "??") {
+        entry.__file_ = tmp;
+      }
+    } else if (line.starts_with("  Line:")) {
+      tmp = line;
+      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+      if (tmp != "??" && tmp != "0") {
+        uint32_t lineno = 0;
+        auto pos        = 0;
+        while (isdigit(tmp[pos])) {
+          lineno = lineno * 10 + (tmp[pos++] - '0');
+        }
+        entry.__line_ = lineno;
+      }
+    }
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/pspawn.h b/libcxx/src/experimental/stacktrace/tools/pspawn.h
new file mode 100644
index 0000000000000..906a504441368
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/pspawn.h
@@ -0,0 +1,163 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_PSPAWN_H
+#define _LIBCPP_STACKTRACE_PSPAWN_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+
+#  include <__config>
+#  include <__config_site>
+#  include <cassert>
+#  include <cerrno>
+#  include <csignal>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <list>
+#  include <spawn.h>
+#  include <string>
+#  include <sys/fcntl.h>
+#  include <sys/types.h>
+#  include <sys/wait.h>
+#  include <unistd.h>
+#  include <vector>
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/debug.h"
+#  include "../common/failed.h"
+#  include "../common/fd.h"
+#  include "tools.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct file_actions {
+  posix_spawn_file_actions_t fa_;
+
+  file_actions() {
+    if (posix_spawn_file_actions_init(&fa_)) {
+      throw failed("posix_spawn_file_actions_init", errno);
+    }
+  }
+
+  ~file_actions() { posix_spawn_file_actions_destroy(&fa_); }
+
+  void addClose(int fd) {
+    if (posix_spawn_file_actions_addclose(&fa_, fd)) {
+      throw failed("posix_spawn_file_actions_addclose", errno);
+    }
+  }
+  void addDup2(int fd, int stdfd) {
+    if (posix_spawn_file_actions_adddup2(&fa_, fd, stdfd)) {
+      throw failed("posix_spawn_file_actions_adddup2", errno);
+    }
+  }
+
+  fd redirectOutFD() {
+    int fds[2];
+    if (::pipe(fds)) {
+      throw failed("pipe", errno);
+    }
+    addClose(fds[0]);
+    addDup2(fds[1], 1);
+    return {fds[0]};
+  }
+
+  void redirectInNull() { addDup2(fd::null_fd(), 0); }
+  void redirectOutNull() { addDup2(fd::null_fd(), 1); }
+  void redirectErrNull() { addDup2(fd::null_fd(), 2); }
+};
+
+struct pspawn {
+  tool const& tool_;
+  pid_t pid_{0};
+  file_actions fa_{};
+
+  // TODO(stacktrace23): ignore SIGCHLD for spawned subprocess
+
+  ~pspawn() {
+    if (pid_) {
+      kill(pid_, SIGTERM);
+      wait();
+    }
+  }
+
+  void spawn(std::pmr::list<std::pmr::string> const& argStrings) {
+    std::pmr::vector<char const*> argv{argStrings.get_allocator()};
+    argv.reserve(argStrings.size() + 1);
+    for (auto const& str : argStrings) {
+      argv.push_back(str.data());
+    }
+    argv.push_back(nullptr);
+    int err;
+    if ((err = posix_spawnp(&pid_, argv[0], &fa_.fa_, nullptr, const_cast<char**>(argv.data()), nullptr))) {
+      throw failed("posix_spawnp", err);
+    }
+  }
+
+  int wait() {
+    int status;
+    waitpid(pid_, &status, 0);
+    return status;
+  }
+};
+
+struct pspawn_tool : pspawn {
+  context& cx_;
+  fd fd_;
+  fd_streambuf buf_;
+  fd_istream stream_;
+
+  pspawn_tool(tool const& a2l, context& cx, char* buf, size_t size)
+      : pspawn{a2l}, cx_(cx), fd_(fa_.redirectOutFD()), buf_(fd_, buf, size), stream_(buf_) {
+    if (!debug::enabled()) {
+      fa_.redirectErrNull();
+    }
+    fa_.redirectInNull();
+  }
+
+  void run() {
+    // Cannot run "addr2line" or similar without addresses, since we
+    // provide them in argv, and if there are none passed in argv, the
+    // tool will try to read from stdin and hang.
+    if (cx_.__entries_.empty()) {
+      return;
+    }
+
+    auto argStrings = tool_.buildArgs(cx_);
+    if (debug::enabled()) {
+      debug() << "Trying to get stacktrace using:";
+      for (auto& str : argStrings) {
+        debug() << " \"" << str << '"';
+      }
+      debug() << '\n';
+    }
+
+    spawn(argStrings);
+
+    for (auto& entry : cx_.__entries_) {
+      tool_.parseOutput(cx_, entry, stream_);
+    }
+  }
+};
+
+struct spawner {
+  context& cx_;
+  void resolve_lines();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif // _LIBCPP_STACKTRACE_PSPAWN_H
diff --git a/libcxx/src/experimental/stacktrace/tools/tools.h b/libcxx/src/experimental/stacktrace/tools/tools.h
new file mode 100644
index 0000000000000..d832974e69cbb
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/tools.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_TOOLS_H
+#define _LIBCPP_STACKTRACE_TOOLS_H
+
+#include <__config>
+#include <__config_site>
+#include <cassert>
+#include <cstddef>
+#include <list>
+#include <string>
+
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct tool {
+  char const* progName_;
+  explicit tool(char const* progName) : progName_(progName) {}
+  constexpr virtual ~tool() = default;
+
+  /** Construct complete `argv` for the spawned process.
+  Includes the program name at argv[0], followed by flags */
+  virtual std::pmr::list<std::pmr::string> buildArgs(context& trace) const = 0;
+
+  /** Parse line(s) output by the tool, and modify `entry`. */
+  virtual void parseOutput(context& trace, entry& e, std::istream& output) const = 0;
+};
+
+struct llvm_symbolizer : tool {
+  constexpr virtual ~llvm_symbolizer() = default;
+  llvm_symbolizer() : llvm_symbolizer("llvm-symbolizer") {}
+  explicit llvm_symbolizer(char const* progName) : tool{progName} {}
+  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
+struct addr2line : tool {
+  constexpr virtual ~addr2line() = default;
+  addr2line() : addr2line("addr2line") {}
+  explicit addr2line(char const* progName) : tool{progName} {}
+  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+  void parseOutput(context& trace, entry& e, std::istream& stream) const override;
+};
+
+struct atos : tool {
+  constexpr virtual ~atos() = default;
+  atos() : atos("atos") {}
+  explicit atos(char const* progName) : tool{progName} {}
+  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_TOOLS_H
diff --git a/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp b/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
new file mode 100644
index 0000000000000..05a2f8e6c2eb4
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
@@ -0,0 +1,185 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+
+#  include <__config>
+#  include <__config_site>
+#  include <cassert>
+#  include <cerrno>
+#  include <csignal>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <spawn.h>
+#  include <string>
+#  include <sys/fcntl.h>
+#  include <sys/types.h>
+#  include <sys/wait.h>
+#  include <unistd.h>
+
+#  include "../common/failed.h"
+#  include "pspawn.h"
+
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/**
+Returns a list of `tool` objects that can be tried during the first stacktrace operation.
+The result is an array of `tool*` pointers with a final `nullptr` terminator.
+
+The returned tool array pointer is saved during `resolve_lines`'s static init,
+with the first working tool being used from that point forward.
+
+This will first add any tools specified by these defines (in order):
+- LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH
+- LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH
+- LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH
+
+Then any tools specified by the environment variables of the same names (and added in the same order).
+
+Finally, this adds the tools `llvm-symbolizer`, `addr2line`, and `atos`, in that order.
+These tools won't have absolute paths, so $PATH will be searched for these.
+
+Called by `findWorkingTool` during its static initialization, therefore this is used in a threadsafe way.
+*/
+tool const** toolList() {
+  constexpr static size_t kMax = 20;
+  static tool const* array_[kMax];
+  size_t count = 0;
+
+  auto add = [&](tool* t) {
+    assert(count < kMax - 1);
+    array_[count++] = t;
+  };
+
+#  define STRINGIFY0(x) #x
+#  define STRINGIFY(x) STRINGIFY0(x)
+
+#  if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
+  {
+    static llvm_symbolizer t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)};
+    add(&t);
+  }
+#  endif
+#  if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
+  {
+    static addr2line t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)};
+    add(&t);
+  }
+#  endif
+#  if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
+  {
+    static atos t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)};
+    add(&t);
+  }
+#  endif
+
+  if (getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")) {
+    static llvm_symbolizer t{getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")};
+    add(&t);
+  }
+  if (getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")) {
+    static addr2line t{getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")};
+    add(&t);
+  }
+  if (getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")) {
+    static atos t{getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")};
+    add(&t);
+  }
+
+  {
+    static llvm_symbolizer t;
+    add(&t);
+  }
+  {
+    static addr2line t;
+    add(&t);
+  }
+  {
+    static atos t;
+    add(&t);
+  }
+
+  array_[count] = nullptr; // terminator
+  return array_;
+}
+
+// Try a handful of different addr2line-esque tools, returning the first discovered, or else nullptr
+tool const* findWorkingTool(auto& trace) {
+  // Try each of these programs and stop at the first one that works.
+  // There's no synchronization so this is subject to races on setting `prog`,
+  // but as long as one thread ends up setting it to something that works, we're good.
+  // Programs to try ($PATH is searched):
+  auto* it = toolList();
+  tool const* prog;
+  while ((prog = *it++)) {
+    pspawn test{*prog}; // Just try to run with "--help"
+    try {
+      std::pmr::list<std::pmr::string> testArgs{&trace.__alloc_};
+      testArgs.push_back(prog->progName_);
+      testArgs.push_back("--help");
+      test.fa_.redirectInNull();
+      test.fa_.redirectOutNull();
+      test.fa_.redirectErrNull();
+      test.spawn(testArgs);
+      if (test.wait() == 0) {
+        // Success
+        return prog;
+      }
+    } catch (failed const&) {
+      /* ignore during probe attempt */
+    }
+  }
+  return nullptr;
+}
+
+} // namespace
+
+void spawner::resolve_lines() {
+  // The address-to-line tool that worked (after lazy-setting, below)
+  static tool const* prog{nullptr};
+  // Whether we should not attempt because tool detection previously failed.
+  static bool fail{false};
+
+  // If this previously failed, don't try again.
+  if (fail) {
+    return;
+  }
+
+  if (!prog) {
+    prog = findWorkingTool(cx_);
+    if (!prog) {
+      fail = true;
+      return;
+    }
+  }
+  assert(prog);
+
+  char buf[256];
+  pspawn_tool proc(*prog, cx_, buf, sizeof(buf));
+  try {
+    proc.run();
+  } catch (failed const& failed) {
+    debug() << failed.what();
+    if (failed.errno_) {
+      debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
+    }
+    debug() << '\n';
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.cpp b/libcxx/src/experimental/stacktrace/unwind/unwind.cpp
new file mode 100644
index 0000000000000..a316306bc43eb
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/unwind/unwind.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+
+#  include "./unwind.h"
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+#  include <unwind.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct unwind_backtrace {
+  context& cx_;
+  size_t skip_;
+  size_t maxDepth_;
+
+  _Unwind_Reason_Code callback(_Unwind_Context* ucx) {
+    if (skip_) {
+      --skip_;
+      return _Unwind_Reason_Code::_URC_NO_REASON;
+    }
+    if (!maxDepth_) {
+      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+    }
+    --maxDepth_;
+    int ipBefore;
+    auto ip = _Unwind_GetIPInfo(ucx, &ipBefore);
+    if (!ip) {
+      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+    }
+    auto& entry          = cx_.__entries_.emplace_back();
+    entry.__addr_        = (ipBefore ? ip : ip - 1);
+    entry.__addr_unslid_ = entry.__addr_; // in case we can't un-slide
+    return _Unwind_Reason_Code::_URC_NO_REASON;
+  }
+
+  static _Unwind_Reason_Code callback(_Unwind_Context* cx, void* self) {
+    return ((unwind_backtrace*)self)->callback(cx);
+  }
+};
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void unwind::collect(size_t skip, size_t max_depth) {
+  unwind_backtrace bt{cx_, skip + 1, max_depth}; // skip this call as well
+  _Unwind_Backtrace(unwind_backtrace::callback, &bt);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.h b/libcxx/src/experimental/stacktrace/unwind/unwind.h
new file mode 100644
index 0000000000000..1b099e3a0b922
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/unwind/unwind.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_UNWIND_H
+#define _LIBCPP_STACKTRACE_UNWIND_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct unwind {
+  context& cx_;
+  void collect(size_t skip, size_t max_depth);
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_UNWIND_H
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
new file mode 100644
index 0000000000000..274c9ecb2b314
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include "dll.h"
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+dbghelp_dll::~dbghelp_dll() = default;
+
+dbghelp_dll& dbghelp_dll::get() {
+  dbghelp_dll ret;
+  return ret;
+}
+
+dbghelp_dll::dbghelp_dll() : dll("dbghelp.dll") {
+  // clang-format off
+if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+  if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+  if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+  if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+  if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+  if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+  if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+  if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+  if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+  if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+  if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+  valid_ = true;
+  // clang-format on
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
new file mode 100644
index 0000000000000..920391fb9ba52
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_WINDOWS_DBGHELP_DLL_H
+#define _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/debug.h"
+#  include "dll.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// clang-format off
+
+struct dbghelp_dll final : dll {
+  virtual ~dbghelp_dll() = default;
+  static dbghelp_dll& get() { static dbghelp_dll ret; return ret; }
+
+  IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
+  bool    (*StackWalk64)        (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
+  bool    (*SymCleanup)         (HANDLE);
+  void*   (*SymFunctionTableAccess64)(HANDLE, DWORD64);
+  bool    (*SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
+  DWORD64 (*SymGetModuleBase64) (HANDLE, DWORD64);
+  DWORD   (*SymGetOptions)      ();
+  bool    (*SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
+  bool    (*SymInitialize)      (HANDLE, char const*, bool);
+  DWORD64 (*SymLoadModule64)    (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
+  DWORD   (*SymSetOptions)      (DWORD);
+
+  dbghelp_dll() : dll("dbghelp.dll") {
+    if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+    if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+    if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+    if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+    if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+    if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+    if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+    if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+    if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+    if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+    if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+    valid_ = true;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WINDOWS
+#endif // _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.cpp b/libcxx/src/experimental/stacktrace/windows/dll.cpp
new file mode 100644
index 0000000000000..55fd729a9caf4
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dll.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include "dll.h"
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+dll::~dll() { FreeLibrary(module_); }
+
+dll::dll(char const* name) : name_(name), module_(LoadLibrary(name)) {
+  if (!module_) {
+    debug() << "LoadLibrary failed: " << name_ << ": " << GetLastError() << '\n';
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.h b/libcxx/src/experimental/stacktrace/windows/dll.h
new file mode 100644
index 0000000000000..ab363ce2c2561
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dll.h
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_WINDOWS_DLL_H
+#define _LIBCPP_STACKTRACE_WINDOWS_DLL_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/debug.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// clang-format off
+
+struct dll {
+  char const* name_;
+  HMODULE module_;
+  /** Set to true in subclass's ctor if initialized successfully. */
+  bool valid_{false};
+
+  operator bool() const { return valid_; }
+
+  explicit dll(char const* name)
+      : name_(name), module_(LoadLibrary(name)) {
+    if (!module_) {
+      debug() << "LoadLibrary failed: "
+              << name_ << ": " << GetLastError() << '\n';
+    }
+  }
+
+  virtual ~dll() { FreeLibrary(module_); }
+
+  template <typename F>
+  bool get_func(F* func, char const* name) {
+    if (!(*func = (F)GetProcAddress(module_, name))) {
+      debug() << "GetProcAddress failed: "
+              << name << "' (" << name_ << "): "
+              << GetLastError() << "\n";
+      return false;
+    }
+    return true;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WINDOWS
+#endif // _LIBCPP_STACKTRACE_WINDOWS_DLL_H
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp b/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
new file mode 100644
index 0000000000000..ab28df8d3fbd7
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include "dll.h"
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+psapi_dll::~psapi_dll() = default;
+
+psapi_dll& psapi_dll::get() {
+  psapi_dll ret;
+  return ret;
+}
+
+psapi_dll() : dll("psapi.dll") {
+  // clang-format off
+if (!getFunc(&EnumProcessModules, "EnumProcessModules")) { return; }
+  if (!getFunc(&GetModuleInformation, "GetModuleInformation")) { return; }
+  if (!getFunc(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+  valid_ = true;
+  // clang-format on
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.h b/libcxx/src/experimental/stacktrace/windows/psapi_dll.h
new file mode 100644
index 0000000000000..4e6a192f3d790
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/psapi_dll.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_WINDOWS_PSAPI_DLL_H
+#define _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/debug.h"
+#  include "dll.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// clang-format off
+
+struct psapi_dll final : dll {
+  virtual ~psapi_dll() = default;
+  static psapi_dll& get() { static psapi_dll ret; return ret; }
+
+  bool  (*EnumProcessModules)   (HANDLE, HMODULE*, DWORD, DWORD*);
+  bool  (*GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
+  DWORD (*GetModuleBaseName)    (HANDLE, HMODULE, char**, DWORD);
+
+  psapi_dll() : dll("psapi.dll") {
+    if (!get_func(&EnumProcessModules, "EnumProcessModules")) { return; }
+    if (!get_func(&GetModuleInformation, "GetModuleInformation")) { return; }
+    if (!get_func(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+    valid_ = true;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WINDOWS
+#endif // _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.cpp b/libcxx/src/experimental/stacktrace/windows/win_impl.cpp
new file mode 100644
index 0000000000000..7773d0d872816
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/win_impl.cpp
@@ -0,0 +1,204 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "win_impl.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include "dll.h"
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/*
+Global objects, shared among all threads and among all stacktrace operations.
+The `dbghelp` APIs are not safe to call concurrently (according to their docs)
+so we claim a lock in the `WinDebugAPIs` constructor.
+*/
+
+// Statically-initialized
+std::mutex gWindowsAPILock;
+DbgHelpDLL dbg;
+PSAPIDLL ps;
+
+// Initialized once, in first WinDebugAPIs construction;
+// protected by the above mutex.
+HANDLE proc;
+HMODULE exe;
+IMAGE_NT_HEADERS* ntHeaders;
+bool globalInitialized{false};
+
+// Globals used across invocations of the functions below.
+// Also guarded by the above mutex.
+bool symsInitialized{false};
+HMODULE moduleHandles[1024];
+size_t moduleCount; // 0 IFF module enumeration failed
+
+} // namespace
+
+win_impl::WinDebugAPIs(context& trace) : trace_(trace), guard_(gWindowsAPILock) {
+  if (!globalInitialized) {
+    // Cannot proceed without these DLLs:
+    if (!dbg) {
+      return;
+    }
+    if (!ps) {
+      return;
+    }
+    proc = GetCurrentProcess();
+    if (!(exe = GetModuleHandle(nullptr))) {
+      return;
+    }
+    if (!(ntHeaders = (*dbg.ImageNtHeader)(exe))) {
+      return;
+    }
+
+    globalInitialized = true;
+  }
+
+  // Initialize symbol machinery.
+  // Presumably the symbols in this process's space can change between
+  // stacktraces, so we'll do this each time we take a trace.
+  // The final `true` means we want the runtime to enumerate all this
+  // process's modules' symbol tables.
+  symsInitialized  = (*dbg.SymInitialize)(proc, nullptr, true);
+  DWORD symOptions = (*dbg.SymGetOptions)();
+  symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
+  (*dbg.SymSetOptions)(symOptions);
+}
+
+win_impl::~WinDebugAPIs() {
+  if (symsInitialized) {
+    (*dbg.SymCleanup)(proc);
+    symsInitialized = false;
+  }
+}
+
+void win_impl::ident_modules() {
+  if (!globalInitialized) {
+    return;
+  }
+  DWORD needBytes;
+  auto enumMods = (*ps.EnumProcessModules)(proc, moduleHandles, sizeof(moduleHandles), LPDWORD(&needBytes));
+  if (enumMods) {
+    moduleCount = needBytes / sizeof(HMODULE);
+  } else {
+    moduleCount = 0;
+    Debug() << "EnumProcessModules failed: " << GetLastError() << '\n';
+  }
+}
+
+void win_impl::symbolize() {
+  // Very long symbols longer than this amount will be truncated.
+  static constexpr size_t kMaxSymName = 256;
+  if (!globalInitialized) {
+    return;
+  }
+
+  for (auto& entry : cx_.__entries_) {
+    char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName];
+    auto* sym          = (IMAGEHLP_SYMBOL64*)space;
+    sym->SizeOfStruct  = sizeof(IMAGEHLP_SYMBOL64);
+    sym->MaxNameLength = kMaxSymName;
+    uint64_t disp{0};
+    if ((*dbg.SymGetSymFromAddr64)(proc, entry.__addr_, &disp, sym)) {
+      // Copy chars into the destination string which uses the caller-provided allocator.
+      entry.__desc_ = {sym->Name};
+    } else {
+      Debug() << "SymGetSymFromAddr64 failed: " << GetLastError() << '\n';
+    }
+  }
+}
+
+void win_impl::resolve_lines() {
+  if (!globalInitialized) {
+    return;
+  }
+
+  for (auto& entry : cx_.__entries_) {
+    DWORD disp{0};
+    IMAGEHLP_LINE64 line;
+    line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+    if ((*dbg.SymGetLineFromAddr64)(proc, entry.__addr_, &disp, &line)) {
+      // Copy chars into the destination string which uses the caller-provided allocator.
+      entry.__file_ = line.FileName;
+      entry.__line_ = line.LineNumber;
+    } else {
+      Debug() << "SymGetLineFromAddr64 failed: " << GetLastError() << '\n';
+    }
+  }
+}
+
+/*
+Inlining is disabled from here on;
+this is to ensure `collect` below doesn't get merged into its caller
+and mess around with the top of the stack (making `skip` inaccurate).
+*/
+#  pragma auto_inline(off)
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_t max_depth) {
+  if (!globalInitialized) {
+    return;
+  }
+
+  auto thread  = GetCurrentThread();
+  auto machine = ntHeaders->FileHeader.Machine;
+
+  CONTEXT ccx;
+  RtlCaptureContext(&ccx);
+
+  STACKFRAME64 frame;
+  memset(&frame, 0, sizeof(frame));
+  frame.AddrPC.Mode      = AddrModeFlat;
+  frame.AddrStack.Mode   = AddrModeFlat;
+  frame.AddrFrame.Mode   = AddrModeFlat;
+  frame.AddrPC.Offset    = ccx.Rip;
+  frame.AddrStack.Offset = ccx.Rsp;
+  frame.AddrFrame.Offset = ccx.Rbp;
+
+  while (max_depth &&
+         (*dbg.StackWalk64)(
+             machine,
+             proc,
+             thread,
+             &frame,
+             &ccx,
+             nullptr,
+             dbg.SymFunctionTableAccess64,
+             dbg.SymGetModuleBase64,
+             nullptr)) {
+    if (skip) {
+      --skip;
+      continue;
+    }
+    --max_depth;
+    auto& entry = cx_.__entries_.emplace_back();
+    // We don't need to compute the un-slid addr; windbg only needs the actual addresses.
+    // Assume address is of the instruction after a call instruction, since we can't
+    // differentiate between a signal, SEH exception handler, or a normal function call.
+    entry.__addr_ = frame.AddrPC.Offset - 1; // Back up 1 byte to get into prev insn range
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.h b/libcxx/src/experimental/stacktrace/windows/win_impl.h
new file mode 100644
index 0000000000000..459347ad9a00f
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/win_impl.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_WIN_IMPL_H
+#define _LIBCPP_STACKTRACE_WIN_IMPL_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+#include <mutex>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct win_impl {
+  context& cx_;
+  std::lock_guard<std::mutex> guard_;
+  win_impl(context& trace);
+  ~win_impl();
+
+  void collect(size_t skip, size_t max_depth);
+  void ident_modules();
+  void symbolize();
+  void resolve_lines();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WIN_IMPL_H
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
new file mode 100644
index 0000000000000..6c4f3ee6008a2
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g0
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+  // Get the current trace.
+  // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+  auto trace = std::stacktrace::current();
+  std::cout << trace << std::endl;
+  // First entry of this should be `main`.
+  auto entry = trace.at(0);
+  assert(entry);
+  assert(entry.native_handle());
+  assert(entry.description() == "main" || entry.description() == "_main");
+  // This is 'nodebug', so we cannot get the source file and line:
+  // assert(entry.source_file().ends_with(".pass.cpp"));
+  // assert(entry.source_line() == line_number);
+  // But this should at least produce the executable filename
+  assert(entry.source_file().contains("simple.o0.nodebug.pass.cpp"));
+  return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
new file mode 100644
index 0000000000000..58d3c8fed5c5e
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+  // Get the current trace.
+  //   uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+  auto trace = std::stacktrace::current();
+  std::cout << trace << std::endl;
+  // First entry of this should be `main`.
+  auto entry = trace.at(0);
+  assert(entry);
+  assert(entry.native_handle());
+  assert(entry.description() == "main" || entry.description() == "_main");
+  //   assert(entry.source_file().ends_with(".pass.cpp"));
+  //   assert(entry.source_line() == line_number);
+  return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
new file mode 100644
index 0000000000000..a00866181a0e6
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
+
+#include <cassert>
+#include <iostream>
+#include <experimental/stacktrace>
+
+int main(int, char**) {
+  // Get the current trace.
+  //   uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+  auto trace = std::stacktrace::current();
+  std::cerr << trace << '\n';
+  // First entry of this should be `main`.
+  auto entry = trace.at(0);
+  assert(entry);
+  assert(entry.native_handle());
+  assert(entry.description() == "main" || entry.description() == "_main");
+  //   assert(entry.source_file().ends_with(".pass.cpp"));
+  //   assert(entry.source_line() == line_number);
+  return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
new file mode 100644
index 0000000000000..e8387bfebb14f
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O3 -g0
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+  // Get the current trace.
+  // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+  auto trace = std::stacktrace::current();
+  std::cout << trace << std::endl;
+  // First entry of this should be `main`.
+  auto entry = trace.at(0);
+  assert(entry);
+  assert(entry.native_handle());
+  assert(entry.description() == "main" || entry.description() == "_main");
+  // This is 'nodebug', so we cannot get the source file and line:
+  // assert(entry.source_file().ends_with(".pass.cpp"));
+  // assert(entry.source_line() == line_number);
+  // But this should at least produce the executable filename
+  assert(entry.source_file().contains("simple.o3.nodebug.pass.cpp"));
+  return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
new file mode 100644
index 0000000000000..e3796c2bc8add
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O3 -g
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+  // Get the current trace.
+  //   uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+  auto trace = std::stacktrace::current();
+  std::cout << trace << std::endl;
+  // First entry of this should be `main`.
+  auto entry = trace.at(0);
+  assert(entry);
+  assert(entry.native_handle());
+  assert(entry.description() == "main" || entry.description() == "_main");
+  //   assert(entry.source_file().ends_with(".pass.cpp"));
+  //   assert(entry.source_line() == line_number);
+  return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
new file mode 100644
index 0000000000000..7b58f9accf7c9
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O3 -g -gsplit-dwarf
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+  // Get the current trace.
+  //   uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+  auto trace = std::stacktrace::current();
+  std::cout << trace << std::endl;
+  // First entry of this should be `main`.
+  auto entry = trace.at(0);
+  assert(entry);
+  assert(entry.native_handle());
+  assert(entry.description() == "main" || entry.description() == "_main");
+  //   assert(entry.source_file().ends_with(".pass.cpp"));
+  //   assert(entry.source_line() == line_number);
+  return 0;
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2032,6 +2036,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2032,6 +2036,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..cfcf7bc7c4c19 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2072,6 +2076,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..534776e6f0526 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2085,6 +2089,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index cb23c7a98de1b..fbfa8152b8070 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -295,6 +295,35 @@ experimental/propagate_const version
 experimental/simd cstdint
 experimental/simd limits
 experimental/simd version
+experimental/stacktrace bitset
+experimental/stacktrace cctype
+experimental/stacktrace cerrno
+experimental/stacktrace climits
+experimental/stacktrace clocale
+experimental/stacktrace compare
+experimental/stacktrace cstddef
+experimental/stacktrace cstdint
+experimental/stacktrace cstdio
+experimental/stacktrace cstdlib
+experimental/stacktrace cstring
+experimental/stacktrace ctime
+experimental/stacktrace cwchar
+experimental/stacktrace cwctype
+experimental/stacktrace initializer_list
+experimental/stacktrace ios
+experimental/stacktrace iosfwd
+experimental/stacktrace limits
+experimental/stacktrace list
+experimental/stacktrace locale
+experimental/stacktrace memory
+experimental/stacktrace ratio
+experimental/stacktrace stdexcept
+experimental/stacktrace streambuf
+experimental/stacktrace string
+experimental/stacktrace string_view
+experimental/stacktrace tuple
+experimental/stacktrace typeinfo
+experimental/stacktrace version
 experimental/type_traits cstdint
 experimental/type_traits initializer_list
 experimental/type_traits type_traits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index ce8f0261f2b27..06dd30b07537e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -295,6 +295,35 @@ experimental/propagate_const version
 experimental/simd cstdint
 experimental/simd limits
 experimental/simd version
+experimental/stacktrace bitset
+experimental/stacktrace cctype
+experimental/stacktrace cerrno
+experimental/stacktrace climits
+experimental/stacktrace clocale
+experimental/stacktrace compare
+experimental/stacktrace cstddef
+experimental/stacktrace cstdint
+experimental/stacktrace cstdio
+experimental/stacktrace cstdlib
+experimental/stacktrace cstring
+experimental/stacktrace ctime
+experimental/stacktrace cwchar
+experimental/stacktrace cwctype
+experimental/stacktrace initializer_list
+experimental/stacktrace ios
+experimental/stacktrace iosfwd
+experimental/stacktrace limits
+experimental/stacktrace list
+experimental/stacktrace locale
+experimental/stacktrace memory
+experimental/stacktrace ratio
+experimental/stacktrace stdexcept
+experimental/stacktrace streambuf
+experimental/stacktrace string
+experimental/stacktrace string_view
+experimental/stacktrace tuple
+experimental/stacktrace typeinfo
+experimental/stacktrace version
 experimental/type_traits cstdint
 experimental/type_traits initializer_list
 experimental/type_traits type_traits
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
new file mode 100644
index 0000000000000..495abe79da20f
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+  (19.6.4.4) Comparisons [stacktrace.basic.cmp]
+
+  template<class Allocator2>
+  friend bool operator==(const basic_stacktrace& x,
+                          const basic_stacktrace<Allocator2>& y) noexcept;
+
+  template<class Allocator2>
+  friend strong_ordering operator<=>(const basic_stacktrace& x,
+                                      const basic_stacktrace<Allocator2>& y) noexcept;
+*/
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2a() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2b() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  /*    
+    template<class Allocator2>
+    friend bool operator==(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
+    Returns: equal(x.begin(), x.end(), y.begin(), y.end()).
+  */
+  auto st1a = test1(); // [test1, main, ...]
+  assert(st1a == st1a);
+
+  auto st1b = st1a;
+  assert(st1a == st1b);
+
+  auto st2a = test2a(); // [test1, test2a, main, ...]
+  assert(st1a != st2a);
+
+  std::stacktrace empty; // []
+  assert(st1a != empty);
+  assert(st2a != empty);
+
+  assert(st2a.size() > st1a.size());
+  assert(st1a.size() > empty.size());
+
+  auto st2b = test2b(); // [test1, test2b, main, ...]
+  assert(st2a.size() == st2b.size());
+  assert(st2a != st2b);
+
+  /*    
+    template<class Allocator2>
+    friend strong_ordering
+    operator<=>(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
+
+    Returns: x.size() <=> y.size() if x.size() != y.size();
+    lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end()) otherwise.
+  */
+
+  // empty:  []
+  // st1a:   [test1, main, ...]
+  // st1b:   [test1, main, ...] (copy of st1a)
+  // st2a:   [test1, test2a, main:X, ...]
+  // st2b:   [test1, test2b, main:Y, ...], Y > X
+
+  assert(std::strong_ordering::equal == empty <=> empty);
+  assert(std::strong_ordering::less == empty <=> st1a);
+  assert(std::strong_ordering::less == empty <=> st1b);
+  assert(std::strong_ordering::less == empty <=> st2a);
+  assert(std::strong_ordering::less == empty <=> st2b);
+
+  assert(std::strong_ordering::greater == st1a <=> empty);
+  assert(std::strong_ordering::equal == st1a <=> st1a);
+  assert(std::strong_ordering::equal == st1a <=> st1b);
+  assert(std::strong_ordering::less == st1a <=> st2a);
+  assert(std::strong_ordering::less == st1a <=> st2b);
+
+  assert(std::strong_ordering::greater == st1b <=> empty);
+  assert(std::strong_ordering::equal == st1b <=> st1a);
+  assert(std::strong_ordering::equal == st1b <=> st1b);
+  assert(std::strong_ordering::less == st1b <=> st2a);
+  assert(std::strong_ordering::less == st1b <=> st2b);
+
+  assert(std::strong_ordering::greater == st2a <=> empty);
+  assert(std::strong_ordering::greater == st2a <=> st1a);
+  assert(std::strong_ordering::greater == st2a <=> st1b);
+  assert(std::strong_ordering::equal == st2a <=> st2a);
+  assert(std::strong_ordering::less == st2a <=> st2b);
+
+  assert(std::strong_ordering::greater == st2b <=> empty);
+  assert(std::strong_ordering::greater == st2b <=> st1a);
+  assert(std::strong_ordering::greater == st2b <=> st1b);
+  assert(std::strong_ordering::greater == st2b <=> st2a);
+  assert(std::strong_ordering::equal == st2b <=> st2b);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
new file mode 100644
index 0000000000000..64e54fdf5793d
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
@@ -0,0 +1,308 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O1 -g
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <iostream>
+
+/**
+ * This file includes tests which ensure any allocations performed by `basic_stacktrace`
+ * are done via the user-provided allocator, and not via bare `malloc` / `operator new`s.
+ * Our overridden `malloc` below will fail if `malloc_disabled` is `true`.
+ * The program's startup code may need to malloc, so we'll allow malloc initially.
+ * This is only activated during the "test_no_malloc_or_new_ex_allocator" test,
+ * during which it should be using a `test_alloc` we'll provide (which knows how to
+ * unblock `malloc` temporarily).
+ */
+bool malloc_disabled{false};
+
+void* malloc(size_t size) {
+  // If flag has not been temporarily disabled by our allocator, abort
+  if (malloc_disabled) {
+    abort();
+  }
+  // Since we overrode malloc with this function, and there's no way to call up
+  // to the "real malloc", allocate a different way.  Assumes nothing actually uses `calloc`.
+  return calloc(1, size);
+}
+
+// All the various ways to monkey around with heap memory without an allocator:
+// we'll simply redirect these through our gate-keeping `malloc` above.
+void* operator new(size_t size) { return malloc(size); }
+void* operator new[](size_t size) { return malloc(size); }
+void operator delete(void* ptr) noexcept { free(ptr); }
+void operator delete(void* ptr, size_t) noexcept { free(ptr); }
+void operator delete[](void* ptr) noexcept { free(ptr); }
+void operator delete[](void* ptr, size_t) noexcept { free(ptr); }
+
+/** RAII-style scope object to temporarily permit heap allocations, used by `test_alloc`.*/
+struct scope_enable_malloc {
+  bool prev_malloc_disabled_;
+  scope_enable_malloc() : prev_malloc_disabled_(malloc_disabled) { malloc_disabled = false; }
+  ~scope_enable_malloc() { malloc_disabled = true; }
+};
+
+template <typename T>
+struct test_alloc {
+  using size_type     = size_t;
+  using value_type    = T;
+  using pointer       = T*;
+  using const_pointer = T const*;
+
+  template <typename U>
+  struct rebind {
+    using other = test_alloc<U>;
+  };
+
+  std::allocator<T> wrapped_{};
+
+  test_alloc() = default;
+
+  template <typename U>
+  test_alloc(test_alloc<U> const& rhs) : wrapped_(rhs.wrapped_) {}
+
+  bool operator==(auto const& rhs) const { return &rhs == this; }
+  bool operator==(test_alloc const&) const { return true; }
+
+  T* allocate(size_t n) { return wrapped_.allocate(n); }
+
+  auto allocate_at_least(size_t n) { return wrapped_.allocate_at_least(n); }
+
+  void deallocate(T* ptr, size_t n) { return wrapped_.deallocate(ptr, n); }
+};
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;   [1]
+  static basic_stacktrace current(size_type skip,
+                                  const allocator_type& alloc = allocator_type()) noexcept;   [2]
+  static basic_stacktrace current(size_type skip, size_type max_depth,
+                                  const allocator_type& alloc = allocator_type()) noexcept;   [3]
+
+  basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);            [4]
+  explicit basic_stacktrace(const allocator_type& alloc) noexcept;                            [5]
+
+  basic_stacktrace(const basic_stacktrace& other);                                            [6]
+  basic_stacktrace(basic_stacktrace&& other) noexcept;                                        [7]
+  basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);               [8]
+  basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);                    [9]
+  basic_stacktrace& operator=(const basic_stacktrace& other);                                 [10]
+  basic_stacktrace& operator=(basic_stacktrace&& other)
+    noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+      allocator_traits<Allocator>::is_always_equal::value);                                   [11]
+
+  ~basic_stacktrace();                                                                        [12]
+*/
+
+// clang-format off
+uint32_t test1_line;
+uint32_t test2_line;
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test1(A& alloc) {
+  test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret = std::basic_stacktrace<A>::current(alloc);
+  return ret;
+}
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test2(A& alloc) {
+  test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret = test1(alloc);
+  return ret;
+}
+// clang-format on
+
+/*
+    [1]
+    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+
+    Returns: A basic_stacktrace object with frames_ storing the stacktrace of the current evaluation
+    in the current thread of execution, or an empty basic_stacktrace object if the initialization of
+    frames_ failed. alloc is passed to the constructor of the frames_ object.
+    [Note 1: If the stacktrace was successfully obtained, then frames_.front() is the stacktrace_entry
+    representing approximately the current evaluation, and frames_.back() is the stacktrace_entry
+    representing approximately the initial function of the current thread of execution. — end note]
+  */
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
+  test_alloc<std::stacktrace_entry> alloc;
+  uint32_t main_line = __LINE__ + 1;
+  auto st            = test2(alloc);
+
+  std::cerr << "*** Stacktrace obtained at line " << main_line << '\n' << st << '\n';
+
+  assert(st.size() >= 3);
+  assert(st[0]);
+  assert(st[0].native_handle());
+  assert(st[0].description().contains("test1"));
+  assert(st[0].source_file().contains("basic.cons.pass.cpp"));
+  assert(st[1]);
+  assert(st[1].native_handle());
+  assert(st[1].description().contains("test2"));
+  assert(st[1].source_file().contains("basic.cons.pass.cpp"));
+  assert(st[2]);
+  assert(st[2].native_handle());
+  assert(st[2].description().contains("test_current"));
+  assert(st[2].source_file().contains("basic.cons.pass.cpp"));
+
+  // We unfortunately cannot guarantee the following; in CI, and possibly on users' build machines,
+  // there may not be an up-to-date version of e.g. `addr2line`.
+  // assert(st[0].source_file().ends_with("basic.cons.pass.cpp"));
+  // assert(st[0].source_line() == test1_line);
+  // assert(st[1].source_file().ends_with("basic.cons.pass.cpp"));
+  // assert(st[1].source_line() == test2_line);
+  // assert(st[2].source_file().ends_with("basic.cons.pass.cpp"));
+  // assert(st[2].source_line() == main_line);
+}
+
+/*
+  [2]
+  static basic_stacktrace current(size_type skip,
+                              const allocator_type& alloc = allocator_type()) noexcept;
+  Let t be a stacktrace as-if obtained via basic_stacktrace​::​current(alloc). Let n be t.size().
+  Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
+  t.begin() + min(n, skip), t.end(), and alloc, or an empty basic_stacktrace object if the
+  initialization of frames_ failed.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
+  // Use default allocator for simplicity; alloc is covered above
+  auto st_skip0 = std::stacktrace::current();
+  std::cerr << "*** st_skip0:\n" << st_skip0 << '\n';
+  assert(st_skip0.size() >= 2);
+  auto st_skip1 = std::stacktrace::current(1);
+  std::cerr << "*** st_skip1:\n" << st_skip1 << '\n';
+  assert(st_skip1.size() >= 1);
+  assert(st_skip0.size() == st_skip1.size() + 1);
+  assert(st_skip0[1] == st_skip1[0]);
+  auto st_skip_many = std::stacktrace::current(1 << 20);
+  assert(st_skip_many.empty());
+}
+
+/*
+  [3]
+  static basic_stacktrace current(size_type skip, size_type max_depth,
+                              const allocator_type& alloc = allocator_type()) noexcept;
+  Let t be a stacktrace as-if obtained via basic_stacktrace​::​current(alloc). Let n be t.size().
+  Preconditions: skip <= skip + max_depth is true.
+  Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
+  t.begin() + min(n, skip), t.begin() + min(n, skip + max_depth), and alloc, or an empty
+  basic_stacktrace object if the initialization of frames_ failed.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
+  // current stack is: [this function, main, (possibly something else, e.g. `_start` from libc)]
+  // so it's probably 3 functions deep -- but certainly at least 2 deep.
+  auto st = std::stacktrace::current();
+  assert(st.size() >= 2);
+  auto it     = st.begin();
+  auto entry1 = *(it++); // represents this function
+  auto entry2 = *(it++); // represents our caller, `main`
+
+  // get current trace again, but skip the 1st
+  st = std::stacktrace::current(1, 1);
+  assert(st.size() >= 1);
+  assert(*st.begin() == entry2);
+}
+
+/*
+  [4]
+  basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+  Postconditions: empty() is true.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_default_construct() {
+  std::stacktrace st;
+  assert(st.empty());
+}
+
+/*
+  [5]
+  explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+  Effects: alloc is passed to the frames_ constructor.
+  Postconditions: empty() is true.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_construct_with_allocator() {
+  test_alloc<std::stacktrace_entry> alloc;
+  std::basic_stacktrace<decltype(alloc)> st(alloc);
+  assert(st.empty());
+
+  st = std::basic_stacktrace<decltype(alloc)>::current(alloc);
+  assert(!st.empty());
+}
+
+/*
+  [6] basic_stacktrace(const basic_stacktrace& other);
+  [7] basic_stacktrace(basic_stacktrace&& other) noexcept;
+  [8] basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+  [9] basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+  [10] basic_stacktrace& operator=(const basic_stacktrace& other);
+  [11] basic_stacktrace& operator=(basic_stacktrace&& other)
+    noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+    allocator_traits<Allocator>::is_always_equal::value);
+  
+  Remarks: Implementations may strengthen the exception specification for these functions
+  ([res.on.exception.handling]) by ensuring that empty() is true on failed allocation.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_copy_move_ctors() {
+  using A = std::allocator<std::stacktrace_entry>;
+  A alloc;
+  auto st = std::basic_stacktrace<A>::current(alloc);
+
+  auto copy_constr = std::basic_stacktrace<A>(st);
+  assert(st == copy_constr);
+
+  std::basic_stacktrace<A> copy_assign;
+  copy_assign = std::basic_stacktrace<A>(st);
+  assert(st == copy_assign);
+
+  auto st2 = test2(alloc);
+  assert(st2.size());
+  std::basic_stacktrace<A> move_constr(std::move(st2));
+  assert(move_constr.size());
+  assert(!st2.size());
+
+  auto st3 = test2(alloc);
+  assert(st3.size());
+  std::basic_stacktrace<A> move_assign;
+  move_assign = std::move(st3);
+  assert(move_assign.size());
+  assert(!st3.size());
+
+  // TODO(stacktrace23): should we add test cases with `select_on_container_copy_construction`?
+}
+
+/** Ensure all allocations take place through a given allocator, and that none
+ * are sneaking around it by accidentally using malloc or operator new. */
+void test_no_malloc_or_new_ex_allocator() {
+  // A generic allocator we'll use for everything
+  using A = test_alloc<std::stacktrace_entry>;
+  A alloc;
+  // After this point all stacktrace operations must properly use `alloc`
+  malloc_disabled = true;
+  // Try all the stack trace operations
+  auto st = std::basic_stacktrace<A>(alloc);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_current();
+  test_current_with_skip();
+  test_current_with_skip_depth();
+  test_default_construct();
+  test_construct_with_allocator();
+  test_copy_move_ctors();
+  test_no_malloc_or_new_ex_allocator();
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
new file mode 100644
index 0000000000000..db33a8fabb41a
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+  (19.6.6) Hash Support
+
+  template<> struct hash<stacktrace_entry>;                                 [1]
+  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;       [2]
+
+  The specializations are enabled ([unord.hash]).
+*/
+
+int main(int, char**) {
+  /*
+  [1]
+  template<> struct hash<stacktrace_entry>;
+  */
+  std::stacktrace_entry empty_entry;
+  assert(std::hash<std::stacktrace_entry>()(empty_entry) == 0);
+
+  /*
+  [2]
+  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;
+  */
+  std::stacktrace trace; // initially empty
+  auto hash_val_empty    = std::hash<std::stacktrace>()(trace);
+  trace                  = /* reassign */ std::stacktrace::current();
+  auto hash_val_nonempty = std::hash<std::stacktrace>()(trace);
+
+  assert(hash_val_empty != hash_val_nonempty);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
new file mode 100644
index 0000000000000..5929cd8f8d0f9
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+  (19.6.4.5) Modifiers [stacktrace.basic.mod]
+
+  void swap(basic_stacktrace& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+      allocator_traits<Allocator>::is_always_equal::value);
+
+  Effects: Exchanges the contents of *this and other.
+*/
+int main(int, char**) {
+  std::stacktrace empty;
+  auto current = std::stacktrace::current();
+
+  std::stacktrace a(empty);
+  std::stacktrace b(current);
+  assert(a == empty);
+  assert(b == current);
+
+  a.swap(b);
+  assert(a == current);
+  assert(b == empty);
+
+  // TODO(stacktrace23): should we also test swap w/ `select_on_container_swap` case
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
new file mode 100644
index 0000000000000..665fb475f56e9
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <sstream>
+
+/*
+  (19.6.4.6) Non-member functions
+
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+      noexcept(noexcept(a.swap(b)));
+
+  string to_string(const stacktrace_entry& f);
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+*/
+int main(int, char**) {
+  /*
+  template<class Allocator>
+  void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+    noexcept(noexcept(a.swap(b)));
+  Effects: Equivalent to a.swap(b).
+  */
+  std::stacktrace empty;
+  auto current = std::stacktrace::current();
+
+  std::stacktrace a(empty);
+  std::stacktrace b(current);
+  assert(a == empty);
+  assert(b == current);
+
+  std::swap(a, b);
+  assert(a == current);
+  assert(b == empty);
+
+  /*
+  string to_string(const stacktrace_entry& f);
+  Returns: A string with a description of f.
+  Recommended practice: The description should provide information about the contained evaluation,
+  including information from f.source_file() and f.source_line().
+  */
+
+  assert(std::to_string(a[0]).contains("main"));
+  assert(std::to_string(a[0]).contains("basic.nonmem.pass"));
+
+  /*
+  template<class Allocator>
+  string to_string(const basic_stacktrace<Allocator>& st);
+  Returns: A string with a description of st.
+  [Note 1: The number of lines is not guaranteed to be equal to st.size(). — end note]
+  */
+
+  assert(std::to_string(a).contains("main"));
+  assert(std::to_string(a).contains("basic.nonmem.pass"));
+
+  /*
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  Effects: Equivalent to: return os << to_string(f);
+  */
+
+  std::stringstream entry_os;
+  entry_os << a[0];
+  assert(entry_os.str() == std::to_string(a[0]));
+
+  /*
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+  Effects: Equivalent to: return os << to_string(st);
+  */
+
+  std::stringstream trace_os;
+  trace_os << a;
+  assert(trace_os.str() == std::to_string(a));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
new file mode 100644
index 0000000000000..b5c6cf77dde71
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+#include <iostream>
+#include <iterator>
+#include <memory>
+
+#include <cassert>
+#include <stdexcept>
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  // [stacktrace.basic.obs], observers
+  allocator_type get_allocator() const noexcept;      [1]
+
+  const_iterator begin() const noexcept;              [2]
+  const_iterator end() const noexcept;                [3]
+  const_reverse_iterator rbegin() const noexcept;     [4]
+  const_reverse_iterator rend() const noexcept;       [5]
+
+  const_iterator cbegin() const noexcept;             [6]
+  const_iterator cend() const noexcept;               [7]
+  const_reverse_iterator crbegin() const noexcept;    [8]
+  const_reverse_iterator crend() const noexcept;      [9]
+
+  bool empty() const noexcept;                        [10]
+  size_type size() const noexcept;                    [11]
+  size_type max_size() const noexcept;                [12]
+
+  const_reference operator[](size_type) const;        [13]
+  const_reference at(size_type) const;                [14]
+*/
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+
+  /*
+    using const_iterator = implementation-defined;
+    The type models random_access_iterator ([iterator.concept.random.access]) and meets the
+    Cpp17RandomAccessIterator requirements ([random.access.iterators]).
+  */
+  static_assert(std::random_access_iterator<decltype(st.begin())>);
+  static_assert(std::random_access_iterator<decltype(st.rbegin())>);
+  static_assert(std::random_access_iterator<decltype(st.cbegin())>);
+  static_assert(std::random_access_iterator<decltype(st.crbegin())>);
+
+  /*
+    allocator_type get_allocator() const noexcept;
+    Returns: frames_.get_allocator().
+  */
+  static_assert(std::same_as<decltype(st.get_allocator()), std::stacktrace::allocator_type>);
+
+  /*
+    const_iterator begin() const noexcept;
+    const_iterator cbegin() const noexcept;
+    Returns: An iterator referring to the first element in frames_. If empty() is true,
+    then it returns the same value as end().
+
+    const_iterator end() const noexcept;
+    const_iterator cend() const noexcept;
+    Returns: The end iterator.
+
+    const_reverse_iterator rbegin() const noexcept;
+    const_reverse_iterator crbegin() const noexcept;
+    Returns: reverse_iterator(cend()).
+
+    const_reverse_iterator rend() const noexcept;
+    const_reverse_iterator crend() const noexcept;
+    Returns: reverse_iterator(cbegin()).
+  */
+
+  // st is initially empty:
+  assert(st.begin() == st.end());
+  assert(st.cbegin() == st.cend());
+  assert(st.rbegin() == st.rend());
+  assert(st.crbegin() == st.crend());
+
+  /*
+    bool empty() const noexcept;
+    Returns: frames_.empty().
+  */
+  assert(st.empty());
+  assert(st.size() == 0);
+
+  // no longer empty:
+  st = test3();
+  assert(!st.empty());
+  assert(st.begin() != st.end());
+  assert(st.cbegin() != st.cend());
+
+  /*
+    size_type size() const noexcept;
+    Returns: frames_.size().
+
+    size_type max_size() const noexcept;
+    Returns: frames_.max_size().
+  */
+  std::cout << st << std::endl;
+  assert(st.size() >= 4);
+  assert(st.max_size() == (std::vector<std::stacktrace_entry, std::allocator<std::stacktrace_entry>>().max_size()));
+
+  /*
+    const_reference operator[](size_type frame_no) const;
+    Preconditions: frame_no < size() is true.
+    Returns: frames_[frame_no].
+    Throws: Nothing.
+  */
+  assert(st[0]);
+  assert(st[1]);
+  assert(st[2]);
+  assert(st[3]);
+
+  /*
+    const_reference at(size_type frame_no) const;
+    Returns: frames_[frame_no].
+    Throws: out_of_range if frame_no >= size().
+  */
+  assert(st.at(0));
+  assert(st.at(1));
+  assert(st.at(2));
+  assert(st.at(3));
+  try {
+    (void)st.at(42);
+    assert(false && "'at' should have thrown range_error");
+  } catch (std::out_of_range const&) {
+    // ok
+  }
+
+  auto f0 = st[0];
+  auto f1 = st[1];
+  auto f2 = st[2];
+  auto f3 = st[3];
+
+  auto fit = st.begin();
+  assert(*fit++ == f0);
+  assert(fit != st.end());
+
+  auto rit = st.rbegin();
+  assert(rit != st.rend());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
new file mode 100644
index 0000000000000..ce502626a45ea
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+#include <iterator>
+
+#include <cassert>
+#include <type_traits>
+
+/*
+  (19.6.4) Class template basic_stacktrace [stacktrace.basic]
+  (19.6.4.1) Overview                      [stacktrace.basic.overview]
+
+namespace std {
+  template<class Allocator>
+  class basic_stacktrace {
+  public:
+    using value_type = stacktrace_entry;
+    using const_reference = const value_type&;
+    using reference = value_type&;
+    using const_iterator = implementation-defined;  // see [stacktrace.basic.obs]
+    using iterator = const_iterator;
+    using reverse_iterator = std::reverse_iterator<iterator>;
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+    using difference_type = implementation-defined;
+    using size_type = implementation-defined;
+    using allocator_type = Allocator;
+
+    // (19.6.4.2)
+    // [stacktrace.basic.cons], creation and assignment
+    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip, size_type max_depth,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+
+    basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+    explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+
+    basic_stacktrace(const basic_stacktrace& other);
+    basic_stacktrace(basic_stacktrace&& other) noexcept;
+    basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+    basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+    basic_stacktrace& operator=(const basic_stacktrace& other);
+    basic_stacktrace& operator=(basic_stacktrace&& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+    ~basic_stacktrace();
+
+    // (19.6.4.3)
+    // [stacktrace.basic.obs], observers
+    allocator_type get_allocator() const noexcept;
+
+    const_iterator begin() const noexcept;
+    const_iterator end() const noexcept;
+    const_reverse_iterator rbegin() const noexcept;
+    const_reverse_iterator rend() const noexcept;
+
+    const_iterator cbegin() const noexcept;
+    const_iterator cend() const noexcept;
+    const_reverse_iterator crbegin() const noexcept;
+    const_reverse_iterator crend() const noexcept;
+
+    bool empty() const noexcept;
+    size_type size() const noexcept;
+    size_type max_size() const noexcept;
+
+    const_reference operator[](size_type) const;
+    const_reference at(size_type) const;
+    
+    // (19.6.4.4)
+    // [stacktrace.basic.cmp], comparisons
+    template<class Allocator2>
+    friend bool operator==(const basic_stacktrace& x,
+                           const basic_stacktrace<Allocator2>& y) noexcept;
+    template<class Allocator2>
+    friend strong_ordering operator<=>(const basic_stacktrace& x,
+                                       const basic_stacktrace<Allocator2>& y) noexcept;
+
+    // (19.6.4.5)
+    // [stacktrace.basic.mod], modifiers
+    void swap(basic_stacktrace& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+  private:
+    vector<value_type, allocator_type> frames_;         // exposition only
+  };
+
+  // (19.6.4.6)
+  // [stacktrace.basic.nonmem], non-member functions
+
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+      noexcept(noexcept(a.swap(b)));
+
+  string to_string(const stacktrace_entry& f);
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+}
+
+*/
+
+int main(int, char**) {
+  // using value_type = stacktrace_entry;
+  // using const_reference = const value_type&;
+  // using reference = value_type&;
+  // using const_iterator = implementation-defined;  // see [stacktrace.basic.obs]
+  // using iterator = const_iterator;
+  // using reverse_iterator = std::reverse_iterator<iterator>;
+  // using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+  // using difference_type = implementation-defined;
+  // using size_type = implementation-defined;
+  // using allocator_type = Allocator;
+
+  // This test will only verify these member types exist and are
+  // defined as expected; their actual behavior is tested in another .cpp.
+
+  using A = std::allocator<std::stacktrace_entry>;
+  using S = std::basic_stacktrace<A>;
+
+  static_assert(std::is_same_v<S::value_type, std::stacktrace_entry>);
+  static_assert(std::is_same_v<S::const_reference, std::stacktrace_entry const&>);
+  static_assert(std::is_same_v<S::reference, std::stacktrace_entry&>);
+
+  static_assert(std::forward_iterator<S::const_iterator>);
+  static_assert(std::forward_iterator<S::iterator>);
+  using CRI = S::const_reverse_iterator;
+  static_assert(std::is_same_v<CRI, decltype(S(A()).crbegin())>);
+  using RI = S::reverse_iterator;
+  static_assert(std::is_same_v<RI, decltype(S(A()).rbegin())>);
+
+  using IterT = S::iterator;
+  using DiffT = S::difference_type;
+  static_assert(std::is_same_v<IterT, decltype(IterT() + DiffT())>);
+
+  static_assert(std::is_integral_v<S::size_type>);
+  static_assert(std::is_same_v<S::allocator_type, A>);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
new file mode 100644
index 0000000000000..4c6531319fc09
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+  (19.6.3.5) Comparison [stacktrace.entry.cmp]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr bool operator==(const stacktrace_entry& x,
+                                     const stacktrace_entry& y) noexcept;           // [T11]
+    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+                                                 const stacktrace_entry& y) noexcept;  // [T12]
+
+    [. . .]
+  };
+}
+*/
+
+int main(int, char**) {
+  // Two empty entries
+
+  std::stacktrace_entry a;
+  assert(a.native_handle() == 0);
+  assert(!a);
+
+  std::stacktrace_entry b;
+  assert(b.native_handle() == 0);
+  assert(!b);
+
+  // A non-empty entry.
+  std::stacktrace_entry c;
+  ((std::__stacktrace::entry*)(&c))->__addr_ = (uintptr_t)&main;
+  assert(c);
+
+  // [T11]
+  // friend constexpr bool operator==(const stacktrace_entry& x,
+  //                                  const stacktrace_entry& y) noexcept;
+  // "Returns: true if and only if x and y represent the same
+  // stacktrace entry or both x and y are empty."
+  // (We represent "empty" with a native_handle of zero.)
+  assert(a == b);
+  assert(a != c);
+
+  // [T12]
+  // friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+  //                                              const stacktrace_entry& y) noexcept;
+  assert(std::strong_ordering::equal == (a <=> b));
+  assert(std::strong_ordering::equivalent == (a <=> b));
+  assert(std::strong_ordering::greater == (c <=> a));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
new file mode 100644
index 0000000000000..944b4c519f0fd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <type_traits>
+
+/*
+  (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry() noexcept;                                          // [T2]
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;             // [T3]
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;  // [T4]
+    ~stacktrace_entry();                                                            // [T5]
+
+    [. . .]
+  };
+}
+*/
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  // [T2]
+  // constexpr stacktrace_entry() noexcept;
+  // "Postconditions: *this is empty."
+  static_assert(std::is_default_constructible_v<std::stacktrace_entry>);
+  static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
+  std::stacktrace_entry entry_t2;
+  assert(!entry_t2);
+
+  // [T3]
+  // constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+  static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
+  std::stacktrace_entry entry_t3(entry_t2);
+
+  // [T4]
+  // constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+  static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
+  std::stacktrace_entry entry_t4;
+  entry_t4 = entry_t2;
+
+  // [T5]
+  // ~stacktrace_entry();
+  std::stacktrace_entry* entry_ptr{nullptr};
+  delete entry_ptr;
+  {
+    auto entry_t5(entry_t4); /* construct and immediately let it go out of scope */
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
new file mode 100644
index 0000000000000..da6e3ccec92b6
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+  (19.6.3.3) Observers [stacktrace.entry.obs]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.obs], observers
+    constexpr native_handle_type native_handle() const noexcept;                    // [T6]
+    constexpr explicit operator bool() const noexcept;                              // [T7]
+    
+    [. . .]
+  };
+}
+*/
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {}
+_LIBCPP_END_NAMESPACE_STD
+
+int main(int, char**) {
+  // [T6]
+  std::stacktrace_entry entry_t6;
+
+  // constexpr native_handle_type native_handle() const noexcept;
+  assert(entry_t6.native_handle() == 0);
+
+  // [T7]
+  // constexpr explicit operator bool() const noexcept;
+  // "Returns: false if and only if *this is empty."
+  assert(!entry_t6);
+
+  // Now set addr to something nonzero
+  ((std::__stacktrace::entry*)(&entry_t6))->__addr_ = (uintptr_t)&main;
+  assert(entry_t6.native_handle() == (uintptr_t)&main);
+  assert(entry_t6);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
new file mode 100644
index 0000000000000..b55bdcde966d3
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <concepts>
+#include <cstddef>
+
+/*
+  (19.6.3) Class stacktrace_entry         [stacktrace.entry]
+  (19.6.3.1) Overview                     [stacktrace.entry.overview]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    using native_handle_type = implementation-defined;                              // [T1] [entry.pass.cpp]
+
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry() noexcept;                                          // [T2] [entry.cons.pass.cpp]
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;             // [T3]
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;  // [T4]
+    ~stacktrace_entry();                                                            // [T5]
+
+    // [stacktrace.entry.obs], observers
+    constexpr native_handle_type native_handle() const noexcept;                    // [T6] [entry.obs.pass.cpp]
+    constexpr explicit operator bool() const noexcept;                              // [T7]
+
+    // [stacktrace.entry.query], query
+    string description() const;                                                     // [T8] [entry.query.pass.cpp]
+    string source_file() const;                                                     // [T9]
+    uint_least32_t source_line() const;                                             // [T10]
+
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr bool operator==(const stacktrace_entry& x,
+                                     const stacktrace_entry& y) noexcept;           // [T11] [entry.cmp.pass.cpp]
+    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+                                                 const stacktrace_entry& y) noexcept;  // [T12]
+  };
+}
+*/
+
+int main(int, char**) {
+  // [stacktrace.entry.overview]
+  // "The class stacktrace_entry models regular ([concepts.object])
+  // and three_way_comparable<strong_ordering> ([cmp.concept])."
+  static_assert(std::regular<std::stacktrace_entry>);
+  static_assert(std::three_way_comparable<std::stacktrace_entry, std::strong_ordering>);
+
+  // [T1]
+  // using native_handle_type = implementation-defined;
+  //
+  // [Implementation note: For our purposes anything that unique identifies this stacktrace_entry
+  // would be good enough.  We'll use a pointer-sized numeric type (to represent location of the
+  // calling instruction).  Internally this is defined as `uintptr_t`.]
+  static_assert(sizeof(std::stacktrace_entry::native_handle_type) >= sizeof(void*));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
new file mode 100644
index 0000000000000..bfdb54e4df668
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <iostream>
+#include <string>
+
+/*
+    (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.query], query
+    string description() const;                                                     // [T8]
+    string source_file() const;                                                     // [T9]
+    uint_least32_t source_line() const;                                             // [T10]
+
+    [. . .]
+  };
+}
+*/
+
+int main(int, char**) {
+  // empty trace entry
+  std::stacktrace_entry e;
+
+  // [T8]
+  // string description() const;
+  auto desc = e.description();
+  assert(desc.c_str()[0] == 0);
+  assert(desc.size() == 0);
+
+  // [T9]
+  // string source_file() const;
+  auto src = e.source_file();
+  assert(src.c_str()[0] == 0);
+  assert(src.size() == 0);
+
+  // [T10]
+  // uint_least32_t source_line() const;
+  assert(e.source_line() == 0);
+
+  // Get the current trace.
+  uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+  auto trace           = std::stacktrace::current();
+
+  // First entry of this should be `main`.
+  e = trace.at(0);
+  assert(e);
+
+  std::cout << "main is at:      " << (void*)&main << std::endl;
+  std::cout << "e.native_handle: " << (void*)e.native_handle() << std::endl;
+  std::cout << "e.description:   " << e.description() << std::endl;
+  std::cout << "e.source_file:   " << e.source_file() << std::endl;
+  std::cout << "e.source_line:   " << e.source_line() << std::endl;
+
+  std::cout << trace << std::endl;
+
+  assert(e.native_handle());
+  assert(e.native_handle() >= (uintptr_t)&main);
+  assert(e.description() == "main" || e.description() == "_main");
+  // Even if we cannot get the debug info or call out to llvm-addr2line,
+  // we should at least get the executable filename, e.g. `entry.query.pass.cpp.dir/t.exe`
+  assert(!e.source_file().empty());
+  assert(e.source_file().contains("entry.query.pass.cpp"));
+
+  // These might not work at testing time, so disable.
+  // Complete tests checking the filename:line part are in `std/diagnostics/stacktrace`
+  //   assert(e.source_file().ends_with("entry.query.pass.cpp"));
+  //   assert(e.source_line() == line_number);
+  (void)line_number;
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
new file mode 100644
index 0000000000000..b37681659d43c
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <experimental/stacktrace>
+
+/*
+  (19.6.5) Formatting support [stacktrace.format]
+
+  template<> struct formatter<stacktrace_entry>;                            [1]
+  template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;  [2]
+*/
+
+int main(int, char**) {
+  /*
+    [1]
+    template<> struct formatter<stacktrace_entry>;
+
+    formatter<stacktrace_entry> interprets format-spec as a stacktrace-entry-format-spec.
+    The syntax of format specifications is as follows:
+
+      stacktrace-entry-format-spec :
+        fill-and-align_[opt] width_[opt]
+
+    [Note 1: The productions fill-and-align and width are described in [format.string.std]. — end note]
+
+    A stacktrace_entry object se is formatted as if by copying to_string(se) through the output iterator
+    of the context with additional padding and adjustments as specified by the format specifiers.
+  */
+
+  // TODO(stacktrace23): needs `formatter`
+
+  /*
+    [2]
+    template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+    
+    For formatter<basic_stacktrace<Allocator>>, format-spec is empty.
+    
+    A basic_stacktrace<Allocator> object s is formatted as if by copying to_string(s) through the
+    output iterator of the context.
+  */
+
+  // TODO(stacktrace23): needs `formatter`
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
new file mode 100644
index 0000000000000..3c92016b2c900
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <experimental/stacktrace>
+
+#include <memory>
+#include <type_traits>
+
+/*
+
+// (19.6.2) Header <stacktrace> synopsis [stacktrace.syn]
+
+namespace std {
+
+  // [stacktrace.entry], class stacktrace_entry
+  class stacktrace_entry;                                               [1]
+
+  // [stacktrace.basic], class template basic_stacktrace
+  template<class Allocator>
+    class basic_stacktrace;                                             [2]
+
+  // basic_stacktrace typedef-names
+  using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;     [3]
+
+  // [stacktrace.basic.nonmem], non-member functions
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a,
+              basic_stacktrace<Allocator>& b)
+                noexcept(noexcept(a.swap(b)));                          [4]
+
+  string to_string(const stacktrace_entry& f);                          [5]
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);            [6]
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);          [7]
+  template<class Allocator>
+    ostream& operator<<(ostream& os,
+                        const basic_stacktrace<Allocator>& st);         [8]
+
+  // [stacktrace.format], formatting support
+  template<> struct formatter<stacktrace_entry>;                        [9]
+  template<class Allocator>
+    struct formatter<basic_stacktrace<Allocator>>;                      [10]
+
+  namespace pmr {
+    using stacktrace =
+        basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;      [11]
+  }
+
+  // [stacktrace.basic.hash], hash support
+  template<> struct hash<stacktrace_entry>;                             [12]
+  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;   [13]
+}
+*/
+
+// static_assert(__cpp_lib_stacktrace == 202011L);
+
+int main(int, char**) {
+  // Very basic tests to ensure the required things are declared.
+  // Only checking for types' and functions' existence, parameter and return types,
+  // and type-completeness.  Functional tests exist in the other .cpp's in this directory.
+
+  using Alloc = std::allocator<std::stacktrace_entry>;
+
+  // [1]
+  // [stacktrace.entry], class stacktrace_entry
+  // class stacktrace_entry;
+  using T1 = std::stacktrace_entry;
+  assert(std::is_constructible_v<T1>);
+
+  // [2]
+  // [stacktrace.basic], class template basic_stacktrace
+  // template<class Allocator>
+  //   class basic_stacktrace;
+  using T2 = std::basic_stacktrace<Alloc>;
+  assert(std::is_constructible_v<T2>);
+
+  // [3]
+  // basic_stacktrace typedef-names
+  // using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+  using T3 = std::stacktrace;
+  static_assert(std::is_same_v<T3, std::basic_stacktrace<std::allocator<std::stacktrace_entry>>>);
+
+  // [4]
+  // [stacktrace.basic.nonmem], non-member functions
+  // template<class Allocator>
+  //   void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+  //     noexcept(noexcept(a.swap(b)));
+  std::basic_stacktrace<Alloc> a;
+  std::basic_stacktrace<Alloc> b;
+  std::swap(a, b);
+
+  // [5]
+  // string to_string(const stacktrace_entry& f);
+  using T5 = decltype(std::to_string(std::stacktrace_entry()));
+  static_assert(std::is_same_v<std::string, T5>);
+
+  // [6]
+  // template<class Allocator>
+  // string to_string(const basic_stacktrace<Allocator>& st);
+  using T6 = decltype(std::to_string(std::basic_stacktrace<Alloc>()));
+  static_assert(std::is_same_v<std::string, T6>);
+
+  // [7]
+  // ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  std::ostream* os;
+  using T7 = decltype(operator<<(*os, std::stacktrace_entry()));
+  static_assert(std::is_same_v<std::ostream&, T7>);
+
+  // [8]
+  // template<class Allocator>
+  //   ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+  using T8 = decltype(operator<<(*os, std::basic_stacktrace<Alloc>()));
+  static_assert(std::is_same_v<std::ostream&, T8>);
+
+  // [9]
+  // template<> struct formatter<stacktrace_entry>;
+  //   using T9 = std::formatter<std::stacktrace_entry>;
+  //   static_assert(std::is_constructible_v<T9>);
+
+  // TODO(stacktrace23): needs `formatter`
+
+  // [10]
+  // template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+  //   using T10 = std::formatter<std::basic_stacktrace<Alloc>>;
+  //   static_assert(std::is_constructible_v<T10>);
+
+  // TODO(stacktrace23): needs `formatter`
+
+  // [11]
+  // namespace pmr { using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>; }
+  using AllocPMR = std::pmr::polymorphic_allocator<std::stacktrace_entry>;
+  using BasicPMR = std::basic_stacktrace<AllocPMR>;
+  using T11      = std::pmr::stacktrace;
+  static_assert(std::is_same_v<T11, BasicPMR>);
+
+  return 0;
+}
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index a505d37b65b81..6d69cf29af308 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -170,7 +170,7 @@ def __hash__(self) -> int:
     "linalg",
     "rcu",
     "spanstream",
-    "stacktrace",
+    "stacktrace",  # TODO(stacktrace23): remove this when stacktrace is taken out of experimental
     "stdfloat",
     "text_encoding",
 ]))
@@ -208,6 +208,7 @@ def __hash__(self) -> int:
     "experimental/iterator": "// UNSUPPORTED: c++03",
     "experimental/propagate_const": "// UNSUPPORTED: c++03",
     "experimental/simd": "// UNSUPPORTED: c++03",
+    "experimental/stacktrace": "// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20",
     "experimental/type_traits": "// UNSUPPORTED: c++03",
     "experimental/utility": "// UNSUPPORTED: c++03",
     "filesystem": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14",
@@ -275,6 +276,7 @@ def __hash__(self) -> int:
     "regex": ["compare", "initializer_list"],
     "set": ["compare", "initializer_list"],
     "stack": ["compare", "initializer_list"],
+    "stacktrace": ["compare"],
     "string_view": ["compare"],
     "string": ["compare", "initializer_list"],
     "syncstream": ["ostream"],
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index b9e8d0717f7df..41755a276145b 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -318,6 +318,40 @@ if (libcxx_enable_experimental) {
     output_dir = runtimes_dir
     output_name = "c++experimental"
     sources = [ "experimental/keep.cpp" ]
+    sources += [
+      "experimental/stacktrace/alloc.cpp",
+      "experimental/stacktrace/common/config.h",
+      "experimental/stacktrace/common/debug.cpp",
+      "experimental/stacktrace/common/debug.h",
+      "experimental/stacktrace/common/failed.h",
+      "experimental/stacktrace/common/fd.cpp",
+      "experimental/stacktrace/common/fd.h",
+      "experimental/stacktrace/common/images.h",
+      "experimental/stacktrace/context.cpp",
+      "experimental/stacktrace/linux/linux-dl.cpp",
+      "experimental/stacktrace/linux/linux-elf.cpp",
+      "experimental/stacktrace/linux/linux-sym.cpp",
+      "experimental/stacktrace/linux/linux.h",
+      "experimental/stacktrace/osx/osx.cpp",
+      "experimental/stacktrace/osx/osx.h",
+      "experimental/stacktrace/stacktrace.cpp",
+      "experimental/stacktrace/tools/addr2line.cpp",
+      "experimental/stacktrace/tools/atos.cpp",
+      "experimental/stacktrace/tools/llvm_symbolizer.cpp",
+      "experimental/stacktrace/tools/pspawn.h",
+      "experimental/stacktrace/tools/tools.h",
+      "experimental/stacktrace/tools/toolspawner.cpp",
+      "experimental/stacktrace/unwind/unwind.cpp",
+      "experimental/stacktrace/unwind/unwind.h",
+      "experimental/stacktrace/windows/dbghelp_dll.cpp",
+      "experimental/stacktrace/windows/dbghelp_dll.h",
+      "experimental/stacktrace/windows/dll.cpp",
+      "experimental/stacktrace/windows/dll.h",
+      "experimental/stacktrace/windows/psapi_dll.cpp",
+      "experimental/stacktrace/windows/psapi_dll.h",
+      "experimental/stacktrace/windows/win_impl.cpp",
+      "experimental/stacktrace/windows/win_impl.h",
+    ]
     if (libcxx_enable_filesystem && libcxx_enable_time_zone_database) {
       sources += [
         # TODO TZDB The exception could be moved in chrono once the TZDB library

>From 1bd21b7898ff0af05604e92c3155598fdefb0d0a Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sun, 20 Apr 2025 23:48:58 -0400
Subject: [PATCH 02/37] Remove unrelated change

---
 libcxx/include/__ostream/basic_ostream.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h
index 0a6e4360201ba..09ad401a48d60 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -571,7 +571,7 @@ _LIBCPP_HIDE_FROM_ABI _Stream&& operator<<(_Stream&& __os, const _Tp& __x) {
 }
 
 template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
+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());
 }

>From be604f2b6a703e4d5485b4dc825a245e943c3207 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 09:10:49 -0400
Subject: [PATCH 03/37] fix cflag option

---
 libcxx/CMakeLists.txt               | 8 ++++----
 libcxx/docs/VendorDocumentation.rst | 6 +++---
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 163d1db597a2c..f4e63c0f93e79 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -131,9 +131,9 @@ 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
+option(LIBCXX_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")
@@ -763,7 +763,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)
+config_define(${LIBCXX_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/VendorDocumentation.rst b/libcxx/docs/VendorDocumentation.rst
index b05d494db4f9b..11d67458b4df9 100644
--- a/libcxx/docs/VendorDocumentation.rst
+++ b/libcxx/docs/VendorDocumentation.rst
@@ -185,12 +185,12 @@ 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
+.. option:: LIBCXX_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
+   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.
 
 .. option:: LIBCXX_INSTALL_LIBRARY_DIR:PATH

>From 0e0285e9e8e719c202387d525d6f4bca57d23b76 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 21 Apr 2025 19:26:15 -0400
Subject: [PATCH 04/37] Move stacktrace headers out of experimental

---
 libcxx/include/CMakeLists.txt                 | 14 ++--
 .../__stacktrace/basic_stacktrace.h           |  6 +-
 .../__stacktrace/detail/alloc.h               |  0
 .../__stacktrace/detail/context.h             |  0
 .../__stacktrace/detail/entry.h               |  0
 .../__stacktrace/detail/to_string.h           |  0
 .../__stacktrace/stacktrace_entry.h           |  2 +-
 libcxx/include/module.modulemap.in            | 21 +++---
 libcxx/include/{experimental => }/stacktrace  |  4 +-
 libcxx/modules/std.compat.cppm.in             |  5 +-
 libcxx/modules/std.cppm.in                    |  3 -
 llvm/utils/gn/secondary/libcxx/src/BUILD.gn   | 66 +++++++++----------
 12 files changed, 57 insertions(+), 64 deletions(-)
 rename libcxx/include/{experimental => }/__stacktrace/basic_stacktrace.h (98%)
 rename libcxx/include/{experimental => }/__stacktrace/detail/alloc.h (100%)
 rename libcxx/include/{experimental => }/__stacktrace/detail/context.h (100%)
 rename libcxx/include/{experimental => }/__stacktrace/detail/entry.h (100%)
 rename libcxx/include/{experimental => }/__stacktrace/detail/to_string.h (100%)
 rename libcxx/include/{experimental => }/__stacktrace/stacktrace_entry.h (98%)
 rename libcxx/include/{experimental => }/stacktrace (98%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index eda5c5e840787..7a50044c129b8 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -727,6 +727,12 @@ set(files
   __ranges/views.h
   __ranges/zip_view.h
   __split_buffer
+  __stacktrace/basic_stacktrace.h
+  __stacktrace/detail/alloc.h
+  __stacktrace/detail/context.h
+  __stacktrace/detail/entry.h
+  __stacktrace/detail/to_string.h
+  __stacktrace/stacktrace_entry.h
   __std_mbstate_t.h
   __stop_token/atomic_unique_lock.h
   __stop_token/intrusive_list_view.h
@@ -991,17 +997,10 @@ 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
@@ -1054,6 +1053,7 @@ set(files
   span
   sstream
   stack
+  stacktrace
   stdatomic.h
   stdbool.h
   stddef.h
diff --git a/libcxx/include/experimental/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
similarity index 98%
rename from libcxx/include/experimental/__stacktrace/basic_stacktrace.h
rename to libcxx/include/__stacktrace/basic_stacktrace.h
index 8ac1de82b1c63..cad3571d559bc 100644
--- a/libcxx/include/experimental/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -37,9 +37,9 @@
 #include <memory>
 #include <string>
 
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/to_string.h>
+#include <__stacktrace/detail/alloc.h>
+#include <__stacktrace/detail/context.h>
+#include <__stacktrace/detail/to_string.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
diff --git a/libcxx/include/experimental/__stacktrace/detail/alloc.h b/libcxx/include/__stacktrace/detail/alloc.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/alloc.h
rename to libcxx/include/__stacktrace/detail/alloc.h
diff --git a/libcxx/include/experimental/__stacktrace/detail/context.h b/libcxx/include/__stacktrace/detail/context.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/context.h
rename to libcxx/include/__stacktrace/detail/context.h
diff --git a/libcxx/include/experimental/__stacktrace/detail/entry.h b/libcxx/include/__stacktrace/detail/entry.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/entry.h
rename to libcxx/include/__stacktrace/detail/entry.h
diff --git a/libcxx/include/experimental/__stacktrace/detail/to_string.h b/libcxx/include/__stacktrace/detail/to_string.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/to_string.h
rename to libcxx/include/__stacktrace/detail/to_string.h
diff --git a/libcxx/include/experimental/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
similarity index 98%
rename from libcxx/include/experimental/__stacktrace/stacktrace_entry.h
rename to libcxx/include/__stacktrace/stacktrace_entry.h
index be8abb3ea91b2..f06fb0b6d4cb1 100644
--- a/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -33,7 +33,7 @@
 #include <cstdint>
 #include <string>
 
-#include "experimental/__stacktrace/detail/entry.h"
+#include <__stacktrace/detail/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index be5527e52e930..7017f5eec55f6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1985,6 +1985,17 @@ module std [system] {
     export *
   }
 
+  module stacktrace {
+    module basic_stacktrace { header "__stacktrace/basic_stacktrace.h" }
+    module alloc { header "__stacktrace/detail/alloc.h" }
+    module context { header "__stacktrace/detail/context.h" }
+    module entry { header "__stacktrace/detail/entry.h" }
+    module to_string { header "__stacktrace/detail/to_string.h" }
+    module stacktrace_entry { header "__stacktrace/stacktrace_entry.h" }
+    header "stacktrace"
+    export *
+  }
+
   module stdexcept {
     header "stdexcept"
     export *
@@ -2225,16 +2236,6 @@ module std [system] {
       header "experimental/simd"
       export *
     }
-    module stacktrace {
-      private header "experimental/__stacktrace/basic_stacktrace.h"
-      private header "experimental/__stacktrace/detail/alloc.h"
-      private header "experimental/__stacktrace/detail/context.h"
-      private header "experimental/__stacktrace/detail/entry.h"
-      private header "experimental/__stacktrace/detail/to_string.h"
-      private header "experimental/__stacktrace/stacktrace_entry.h"
-      header "experimental/stacktrace"
-      export *
-    }
   }
 
   // Implementation detail headers that are private to libc++. These modules
diff --git a/libcxx/include/experimental/stacktrace b/libcxx/include/stacktrace
similarity index 98%
rename from libcxx/include/experimental/stacktrace
rename to libcxx/include/stacktrace
index 9e1f7259ca3da..33415533c2eea 100644
--- a/libcxx/include/experimental/stacktrace
+++ b/libcxx/include/stacktrace
@@ -180,8 +180,8 @@ namespace std {
 _LIBCPP_PUSH_MACROS
 #    include <__undef_macros>
 
-#    include "experimental/__stacktrace/basic_stacktrace.h"
-#    include "experimental/__stacktrace/stacktrace_entry.h"
+#    include <__stacktrace/basic_stacktrace.h>
+#    include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_POP_MACROS
 
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index 95931447ccdc6..2ee56c6b810c9 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -71,9 +71,6 @@ module;
 #  if __has_include(<spanstream>)
 #    error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<spanstream>)
-#  if __has_include(<stacktrace>)
-#    error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<stacktrace>)
 #  if __has_include(<stdfloat>)
 #    error "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<stdfloat>)
@@ -86,4 +83,4 @@ export module std.compat;
 export import std;
 
 
- at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
\ No newline at end of file
+ at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 5c523691bff4e..dc40af57e573a 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -181,9 +181,6 @@ module;
 #  if __has_include(<spanstream>)
 #    error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<spanstream>)
-#  if __has_include(<stacktrace>)
-#    error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<stacktrace>)
 #  if __has_include(<stdfloat>)
 #    error "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<stdfloat>)
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index 41755a276145b..3e9b6b678a847 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -168,6 +168,38 @@ cxx_sources = [
   "ryu/d2s.cpp",
   "ryu/f2s.cpp",
   "shared_mutex.cpp",
+  # "stacktrace/alloc.cpp",
+  # "stacktrace/common/config.h",
+  # "stacktrace/common/debug.cpp",
+  # "stacktrace/common/debug.h",
+  # "stacktrace/common/failed.h",
+  # "stacktrace/common/fd.cpp",
+  # "stacktrace/common/fd.h",
+  # "stacktrace/common/images.h",
+  # "stacktrace/context.cpp",
+  # "stacktrace/linux/linux-dl.cpp",
+  # "stacktrace/linux/linux-elf.cpp",
+  # "stacktrace/linux/linux-sym.cpp",
+  # "stacktrace/linux/linux.h",
+  # "stacktrace/osx/osx.cpp",
+  # "stacktrace/osx/osx.h",
+  # "stacktrace/stacktrace.cpp",
+  # "stacktrace/tools/addr2line.cpp",
+  # "stacktrace/tools/atos.cpp",
+  # "stacktrace/tools/llvm_symbolizer.cpp",
+  # "stacktrace/tools/pspawn.h",
+  # "stacktrace/tools/tools.h",
+  # "stacktrace/tools/toolspawner.cpp",
+  # "stacktrace/unwind/unwind.cpp",
+  # "stacktrace/unwind/unwind.h",
+  # "stacktrace/windows/dbghelp_dll.cpp",
+  # "stacktrace/windows/dbghelp_dll.h",
+  # "stacktrace/windows/dll.cpp",
+  # "stacktrace/windows/dll.h",
+  # "stacktrace/windows/psapi_dll.cpp",
+  # "stacktrace/windows/psapi_dll.h",
+  # "stacktrace/windows/win_impl.cpp",
+  # "stacktrace/windows/win_impl.h",
   "stdexcept.cpp",
   "string.cpp",
   "strstream.cpp",
@@ -318,40 +350,6 @@ if (libcxx_enable_experimental) {
     output_dir = runtimes_dir
     output_name = "c++experimental"
     sources = [ "experimental/keep.cpp" ]
-    sources += [
-      "experimental/stacktrace/alloc.cpp",
-      "experimental/stacktrace/common/config.h",
-      "experimental/stacktrace/common/debug.cpp",
-      "experimental/stacktrace/common/debug.h",
-      "experimental/stacktrace/common/failed.h",
-      "experimental/stacktrace/common/fd.cpp",
-      "experimental/stacktrace/common/fd.h",
-      "experimental/stacktrace/common/images.h",
-      "experimental/stacktrace/context.cpp",
-      "experimental/stacktrace/linux/linux-dl.cpp",
-      "experimental/stacktrace/linux/linux-elf.cpp",
-      "experimental/stacktrace/linux/linux-sym.cpp",
-      "experimental/stacktrace/linux/linux.h",
-      "experimental/stacktrace/osx/osx.cpp",
-      "experimental/stacktrace/osx/osx.h",
-      "experimental/stacktrace/stacktrace.cpp",
-      "experimental/stacktrace/tools/addr2line.cpp",
-      "experimental/stacktrace/tools/atos.cpp",
-      "experimental/stacktrace/tools/llvm_symbolizer.cpp",
-      "experimental/stacktrace/tools/pspawn.h",
-      "experimental/stacktrace/tools/tools.h",
-      "experimental/stacktrace/tools/toolspawner.cpp",
-      "experimental/stacktrace/unwind/unwind.cpp",
-      "experimental/stacktrace/unwind/unwind.h",
-      "experimental/stacktrace/windows/dbghelp_dll.cpp",
-      "experimental/stacktrace/windows/dbghelp_dll.h",
-      "experimental/stacktrace/windows/dll.cpp",
-      "experimental/stacktrace/windows/dll.h",
-      "experimental/stacktrace/windows/psapi_dll.cpp",
-      "experimental/stacktrace/windows/psapi_dll.h",
-      "experimental/stacktrace/windows/win_impl.cpp",
-      "experimental/stacktrace/windows/win_impl.h",
-    ]
     if (libcxx_enable_filesystem && libcxx_enable_time_zone_database) {
       sources += [
         # TODO TZDB The exception could be moved in chrono once the TZDB library

>From 2c7280c29d44dcb04b426379eaf8c73622353ea4 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 21 Apr 2025 22:35:16 -0400
Subject: [PATCH 05/37] Move stacktrace srcs out of experimental

---
 libcxx/docs/UserDocumentation.rst             |  1 -
 libcxx/include/CMakeLists.txt                 |  8 +--
 .../include/__stacktrace/{detail => }/alloc.h | 28 ++++----
 .../include/__stacktrace/basic_stacktrace.h   | 16 ++---
 .../__stacktrace/{detail => }/context.h       | 20 +++---
 .../include/__stacktrace/{detail => }/entry.h |  6 +-
 .../include/__stacktrace/stacktrace_entry.h   |  8 +--
 .../__stacktrace/{detail => }/to_string.h     |  6 +-
 libcxx/include/module.modulemap.in            |  8 +--
 libcxx/include/stacktrace                     |  6 +-
 libcxx/modules/std/stacktrace.inc             |  3 +-
 libcxx/src/CMakeLists.txt                     | 36 +++++------
 libcxx/src/stacktrace/README.md               |  5 ++
 .../{experimental => }/stacktrace/alloc.cpp   |  4 +-
 .../stacktrace/common/config.h                |  0
 .../stacktrace/common/debug.cpp               |  0
 .../stacktrace/common/debug.h                 |  0
 .../stacktrace/common/failed.h                |  0
 .../stacktrace/common/fd.cpp                  |  0
 .../{experimental => }/stacktrace/common/fd.h |  0
 .../stacktrace/common/images.h                |  0
 .../{experimental => }/stacktrace/context.cpp | 14 ++--
 .../{experimental => }/stacktrace/linux/elf.h |  0
 .../stacktrace/linux/linux-dl.cpp             |  4 +-
 .../stacktrace/linux/linux-elf.cpp            |  4 +-
 .../stacktrace/linux/linux-sym.cpp            |  4 +-
 .../stacktrace/linux/linux.h                  |  2 +-
 .../{experimental => }/stacktrace/osx/osx.cpp |  4 +-
 .../{experimental => }/stacktrace/osx/osx.h   |  2 +-
 .../stacktrace/stacktrace.cpp                 | 12 ++--
 .../stacktrace/tools/addr2line.cpp            |  4 +-
 .../stacktrace/tools/atos.cpp                 |  6 +-
 .../stacktrace/tools/llvm_symbolizer.cpp      |  4 +-
 .../stacktrace/tools/pspawn.h                 |  4 +-
 .../stacktrace/tools/tools.h                  |  4 +-
 .../stacktrace/tools/toolspawner.cpp          |  2 +-
 .../stacktrace/unwind/unwind.cpp              |  4 +-
 .../stacktrace/unwind/unwind.h                |  2 +-
 .../stacktrace/windows/dbghelp_dll.cpp        |  2 +-
 .../stacktrace/windows/dbghelp_dll.h          |  2 +-
 .../stacktrace/windows/dll.cpp                |  2 +-
 .../stacktrace/windows/dll.h                  |  2 +-
 .../stacktrace/windows/psapi_dll.cpp          |  2 +-
 .../stacktrace/windows/psapi_dll.h            |  2 +-
 .../stacktrace/windows/win_impl.cpp           |  2 +-
 .../stacktrace/windows/win_impl.h             |  2 +-
 .../stacktrace/simple.o0.nodebug.pass.cpp     |  2 +-
 .../stacktrace/simple.o0.nosplit.pass.cpp     |  2 +-
 .../stacktrace/simple.o0.split.pass.cpp       |  2 +-
 .../stacktrace/simple.o3.nodebug.pass.cpp     |  2 +-
 .../stacktrace/simple.o3.nosplit.pass.cpp     |  2 +-
 .../stacktrace/simple.o3.split.pass.cpp       |  2 +-
 .../test/libcxx/transitive_includes/cxx23.csv | 58 ++++++++---------
 .../test/libcxx/transitive_includes/cxx26.csv | 58 ++++++++---------
 .../diagnostics/stacktrace/basic.cmp.pass.cpp |  2 +-
 .../stacktrace/basic.cons.pass.cpp            |  2 +-
 .../stacktrace/basic.hash.pass.cpp            |  2 +-
 .../diagnostics/stacktrace/basic.mod.pass.cpp |  2 +-
 .../stacktrace/basic.nonmem.pass.cpp          |  2 +-
 .../diagnostics/stacktrace/basic.obs.pass.cpp |  2 +-
 .../std/diagnostics/stacktrace/basic.pass.cpp |  2 +-
 .../diagnostics/stacktrace/entry.cmp.pass.cpp |  2 +-
 .../stacktrace/entry.cons.pass.cpp            |  2 +-
 .../diagnostics/stacktrace/entry.obs.pass.cpp |  2 +-
 .../std/diagnostics/stacktrace/entry.pass.cpp |  2 +-
 .../stacktrace/entry.query.pass.cpp           |  2 +-
 .../diagnostics/stacktrace/format.pass.cpp    |  2 +-
 .../std/diagnostics/stacktrace/syn.pass.cpp   |  2 +-
 libcxx/utils/libcxx/header_information.py     |  3 +-
 llvm/utils/gn/secondary/libcxx/src/BUILD.gn   | 64 +++++++++----------
 70 files changed, 233 insertions(+), 237 deletions(-)
 rename libcxx/include/__stacktrace/{detail => }/alloc.h (80%)
 rename libcxx/include/__stacktrace/{detail => }/context.h (72%)
 rename libcxx/include/__stacktrace/{detail => }/entry.h (88%)
 rename libcxx/include/__stacktrace/{detail => }/to_string.h (89%)
 create mode 100644 libcxx/src/stacktrace/README.md
 rename libcxx/src/{experimental => }/stacktrace/alloc.cpp (86%)
 rename libcxx/src/{experimental => }/stacktrace/common/config.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/debug.cpp (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/debug.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/failed.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/fd.cpp (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/fd.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/images.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/context.cpp (88%)
 rename libcxx/src/{experimental => }/stacktrace/linux/elf.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/linux/linux-dl.cpp (92%)
 rename libcxx/src/{experimental => }/stacktrace/linux/linux-elf.cpp (93%)
 rename libcxx/src/{experimental => }/stacktrace/linux/linux-sym.cpp (94%)
 rename libcxx/src/{experimental => }/stacktrace/linux/linux.h (98%)
 rename libcxx/src/{experimental => }/stacktrace/osx/osx.cpp (96%)
 rename libcxx/src/{experimental => }/stacktrace/osx/osx.h (95%)
 rename libcxx/src/{experimental => }/stacktrace/stacktrace.cpp (88%)
 rename libcxx/src/{experimental => }/stacktrace/tools/addr2line.cpp (96%)
 rename libcxx/src/{experimental => }/stacktrace/tools/atos.cpp (95%)
 rename libcxx/src/{experimental => }/stacktrace/tools/llvm_symbolizer.cpp (96%)
 rename libcxx/src/{experimental => }/stacktrace/tools/pspawn.h (97%)
 rename libcxx/src/{experimental => }/stacktrace/tools/tools.h (95%)
 rename libcxx/src/{experimental => }/stacktrace/tools/toolspawner.cpp (98%)
 rename libcxx/src/{experimental => }/stacktrace/unwind/unwind.cpp (93%)
 rename libcxx/src/{experimental => }/stacktrace/unwind/unwind.h (95%)
 rename libcxx/src/{experimental => }/stacktrace/windows/dbghelp_dll.cpp (97%)
 rename libcxx/src/{experimental => }/stacktrace/windows/dbghelp_dll.h (98%)
 rename libcxx/src/{experimental => }/stacktrace/windows/dll.cpp (95%)
 rename libcxx/src/{experimental => }/stacktrace/windows/dll.h (96%)
 rename libcxx/src/{experimental => }/stacktrace/windows/psapi_dll.cpp (95%)
 rename libcxx/src/{experimental => }/stacktrace/windows/psapi_dll.h (96%)
 rename libcxx/src/{experimental => }/stacktrace/windows/win_impl.cpp (99%)
 rename libcxx/src/{experimental => }/stacktrace/windows/win_impl.h (96%)

diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 2a8c9600124c9..dbacb44735d08 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -70,7 +70,6 @@ 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/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 7a50044c129b8..e659cd368de95 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -728,10 +728,10 @@ set(files
   __ranges/zip_view.h
   __split_buffer
   __stacktrace/basic_stacktrace.h
-  __stacktrace/detail/alloc.h
-  __stacktrace/detail/context.h
-  __stacktrace/detail/entry.h
-  __stacktrace/detail/to_string.h
+  __stacktrace/alloc.h
+  __stacktrace/context.h
+  __stacktrace/entry.h
+  __stacktrace/to_string.h
   __stacktrace/stacktrace_entry.h
   __std_mbstate_t.h
   __stop_token/atomic_unique_lock.h
diff --git a/libcxx/include/__stacktrace/detail/alloc.h b/libcxx/include/__stacktrace/alloc.h
similarity index 80%
rename from libcxx/include/__stacktrace/detail/alloc.h
rename to libcxx/include/__stacktrace/alloc.h
index a0949b5751899..402cf3c371161 100644
--- a/libcxx/include/__stacktrace/detail/alloc.h
+++ b/libcxx/include/__stacktrace/alloc.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+#ifndef _LIBCPP_STACKTRACE_ALLOC
+#define _LIBCPP_STACKTRACE_ALLOC
 
 #include <__config>
 #include <__functional/function.h>
@@ -29,7 +29,7 @@ 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 {
+struct _LIBCPP_HIDDEN 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
@@ -38,28 +38,22 @@ struct alloc final : std::pmr::memory_resource {
     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);
+    __alloc_ptr     = std::addressof(__a);
   }
 
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL ~alloc() override = default;
+  _LIBCPP_HIDE_FROM_ABI ~alloc() override;
 
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual void _anchor_vfunc();
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t __align) override {
-    // Avoiding "assert" in a system header, but we expect this to hold:
-    // assert(__align <= alignof(std::max_align_t));
-    (void)__align;
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t /*__align*/) override {
     return __alloc_func_(__size);
   }
 
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t __align) override {
-    (void)__align;
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t /*__align*/) override {
     __dealloc_func_((std::byte*)__ptr, __size);
   }
 
   _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
     auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
-    return __rhs_ba && (__rhs_ba->__alloc_opaque_ == __alloc_opaque_);
+    return __rhs_ba && (__rhs_ba->__alloc_ptr == __alloc_ptr);
   }
 
   _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
@@ -93,11 +87,11 @@ struct alloc final : std::pmr::memory_resource {
 private:
   std::function<std::byte*(size_t)> __alloc_func_;
   std::function<void(std::byte*, size_t)> __dealloc_func_;
-  /** Only used for equality */
-  void const* __alloc_opaque_;
+  /** Only used for checking equality */
+  void const* __alloc_ptr;
 };
 
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+#endif // _LIBCPP_STACKTRACE_ALLOC
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index cad3571d559bc..5e2ee1ae1248a 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -7,11 +7,11 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
-#define _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
+#ifndef _LIBCPP_BASIC_STACKTRACE
+#define _LIBCPP_BASIC_STACKTRACE
 
-#include <experimental/__stacktrace/detail/entry.h>
-#include <experimental/__stacktrace/stacktrace_entry.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/stacktrace_entry.h>
 
 #include <__config>
 #include <__format/formatter.h>
@@ -37,9 +37,9 @@
 #include <memory>
 #include <string>
 
-#include <__stacktrace/detail/alloc.h>
-#include <__stacktrace/detail/context.h>
-#include <__stacktrace/detail/to_string.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/to_string.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -303,4 +303,4 @@ struct _LIBCPP_EXPORTED_FROM_ABI hash<basic_stacktrace<_Allocator>> {
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
+#endif // _LIBCPP_BASIC_STACKTRACE
diff --git a/libcxx/include/__stacktrace/detail/context.h b/libcxx/include/__stacktrace/context.h
similarity index 72%
rename from libcxx/include/__stacktrace/detail/context.h
rename to libcxx/include/__stacktrace/context.h
index df791b37b386d..2a23798751c60 100644
--- a/libcxx/include/__stacktrace/detail/context.h
+++ b/libcxx/include/__stacktrace/context.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+#ifndef _LIBCPP_STACKTRACE_CONTEXT
+#define _LIBCPP_STACKTRACE_CONTEXT
 
 #include <__config>
 #include <__memory_resource/memory_resource.h>
@@ -18,20 +18,20 @@
 #include <cstddef>
 #include <string>
 
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-struct _LIBCPP_HIDE_FROM_ABI alloc;
-struct _LIBCPP_HIDE_FROM_ABI entry;
+struct _LIBCPP_HIDDEN alloc;
+struct _LIBCPP_HIDDEN entry;
 
 namespace __stacktrace {
 
 /** Represents the state of the current in-progress stacktrace operation.  This includes
 the working list of `entry` objects, as well as the caller-provided allocator (wrapped
 in `polymorphic_allocator`) and a handful of utility functions involving that allocator. */
-struct _LIBCPP_HIDE_FROM_ABI context {
+struct _LIBCPP_HIDDEN context {
   /** Encapsulates and type-removes the caller's allocator. */
   alloc& __alloc_;
 
@@ -41,13 +41,13 @@ struct _LIBCPP_HIDE_FROM_ABI context {
   /** Path to this process's main executable. */
   std::pmr::string __main_prog_path_;
 
-  _LIBCPP_HIDE_FROM_ABI explicit context(alloc& __byte_alloc)
+  _LIBCPP_HIDDEN explicit context(alloc& __byte_alloc)
       : __alloc_(__byte_alloc), __entries_(&__alloc_), __main_prog_path_(__alloc_.new_string()) {}
 
-  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void do_stacktrace(size_t __skip, size_t __max_depth);
+  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void do_stacktrace(size_t __skip, size_t __max_depth);
 };
 
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+#endif // _LIBCPP_STACKTRACE_CONTEXT
diff --git a/libcxx/include/__stacktrace/detail/entry.h b/libcxx/include/__stacktrace/entry.h
similarity index 88%
rename from libcxx/include/__stacktrace/detail/entry.h
rename to libcxx/include/__stacktrace/entry.h
index 2bab14228b508..50853b87cc693 100644
--- a/libcxx/include/__stacktrace/detail/entry.h
+++ b/libcxx/include/__stacktrace/entry.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+#ifndef _LIBCPP_STACKTRACE_DETAIL_ENTRY
+#define _LIBCPP_STACKTRACE_DETAIL_ENTRY
 
 #include <__config>
 #include <cstdint>
@@ -41,4 +41,4 @@ struct _LIBCPP_HIDE_FROM_ABI entry {
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+#endif // _LIBCPP_STACKTRACE_DETAIL_ENTRY
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index f06fb0b6d4cb1..e5071655bd0d4 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
+#ifndef _LIBCPP_STACKTRACE_ENTRY
+#define _LIBCPP_STACKTRACE_ENTRY
 
 #include <__cstddef/byte.h>
 #include <__cstddef/ptrdiff_t.h>
@@ -33,7 +33,7 @@
 #include <cstdint>
 #include <string>
 
-#include <__stacktrace/detail/entry.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -108,4 +108,4 @@ struct _LIBCPP_EXPORTED_FROM_ABI hash<stacktrace_entry> {
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
+#endif // _LIBCPP_STACKTRACE_ENTRY
diff --git a/libcxx/include/__stacktrace/detail/to_string.h b/libcxx/include/__stacktrace/to_string.h
similarity index 89%
rename from libcxx/include/__stacktrace/detail/to_string.h
rename to libcxx/include/__stacktrace/to_string.h
index 47feddf07faf9..1d2c51a7a5c10 100644
--- a/libcxx/include/__stacktrace/detail/to_string.h
+++ b/libcxx/include/__stacktrace/to_string.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+#ifndef _LIBCPP_STACKTRACE_TO_STRING
+#define _LIBCPP_STACKTRACE_TO_STRING
 
 #include <__config>
 #include <__fwd/sstream.h>
@@ -43,4 +43,4 @@ struct _LIBCPP_HIDE_FROM_ABI __to_string {
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 7017f5eec55f6..6a89e279e9eac 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1987,10 +1987,10 @@ module std [system] {
 
   module stacktrace {
     module basic_stacktrace { header "__stacktrace/basic_stacktrace.h" }
-    module alloc { header "__stacktrace/detail/alloc.h" }
-    module context { header "__stacktrace/detail/context.h" }
-    module entry { header "__stacktrace/detail/entry.h" }
-    module to_string { header "__stacktrace/detail/to_string.h" }
+    module alloc { header "__stacktrace/alloc.h" }
+    module context { header "__stacktrace/context.h" }
+    module entry { header "__stacktrace/entry.h" }
+    module to_string { header "__stacktrace/to_string.h" }
     module stacktrace_entry { header "__stacktrace/stacktrace_entry.h" }
     header "stacktrace"
     export *
diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace
index 33415533c2eea..8c7d8f82acbd2 100644
--- a/libcxx/include/stacktrace
+++ b/libcxx/include/stacktrace
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE
+#ifndef _LIBCPP_STACKTRACE
+#define _LIBCPP_STACKTRACE
 
 /*
   Header <stacktrace> synopsis
@@ -188,4 +188,4 @@ _LIBCPP_POP_MACROS
 #  endif // _LIBCPP_STD_VER >= 23
 #endif   // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE
+#endif // _LIBCPP_STACKTRACE
diff --git a/libcxx/modules/std/stacktrace.inc b/libcxx/modules/std/stacktrace.inc
index e92f730cec62c..e7b31fd29b3a6 100644
--- a/libcxx/modules/std/stacktrace.inc
+++ b/libcxx/modules/std/stacktrace.inc
@@ -8,8 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-// TODO(stacktrace23): update this when stacktrace is taken out of experimental
-#if 0 //_LIBCPP_STD_VER >= 23
+#if _LIBCPP_STD_VER >= 23
 
   // [stacktrace.entry], class stacktrace_­entry
   using std::stacktrace_entry;
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index b24b98da2bf46..5076d1b13758f 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,6 +40,24 @@ set(LIBCXX_SOURCES
   ryu/d2fixed.cpp
   ryu/d2s.cpp
   ryu/f2s.cpp
+  stacktrace/alloc.cpp
+  stacktrace/common/debug.cpp
+  stacktrace/common/fd.cpp
+  stacktrace/context.cpp
+  stacktrace/linux/linux-dl.cpp
+  stacktrace/linux/linux-elf.cpp
+  stacktrace/linux/linux-sym.cpp
+  stacktrace/osx/osx.cpp
+  stacktrace/stacktrace.cpp
+  stacktrace/tools/addr2line.cpp
+  stacktrace/tools/atos.cpp
+  stacktrace/tools/llvm_symbolizer.cpp
+  stacktrace/tools/toolspawner.cpp
+  stacktrace/unwind/unwind.cpp
+  stacktrace/windows/dbghelp_dll.cpp
+  stacktrace/windows/dll.cpp
+  stacktrace/windows/psapi_dll.cpp
+  stacktrace/windows/win_impl.cpp
   stdexcept.cpp
   string.cpp
   support/runtime/exception_fallback.ipp
@@ -309,24 +327,6 @@ add_custom_target(cxx DEPENDS ${LIBCXX_BUILD_TARGETS})
 # Build the experimental static library
 set(LIBCXX_EXPERIMENTAL_SOURCES
   experimental/keep.cpp
-  experimental/stacktrace/alloc.cpp
-  experimental/stacktrace/common/debug.cpp
-  experimental/stacktrace/common/fd.cpp
-  experimental/stacktrace/context.cpp
-  experimental/stacktrace/linux/linux-dl.cpp
-  experimental/stacktrace/linux/linux-elf.cpp
-  experimental/stacktrace/linux/linux-sym.cpp
-  experimental/stacktrace/osx/osx.cpp
-  experimental/stacktrace/stacktrace.cpp
-  experimental/stacktrace/tools/addr2line.cpp
-  experimental/stacktrace/tools/atos.cpp
-  experimental/stacktrace/tools/llvm_symbolizer.cpp
-  experimental/stacktrace/tools/toolspawner.cpp
-  experimental/stacktrace/unwind/unwind.cpp
-  experimental/stacktrace/windows/dbghelp_dll.cpp
-  experimental/stacktrace/windows/dll.cpp
-  experimental/stacktrace/windows/psapi_dll.cpp
-  experimental/stacktrace/windows/win_impl.cpp
   )
 
 if (LIBCXX_PSTL_BACKEND STREQUAL "libdispatch")
diff --git a/libcxx/src/stacktrace/README.md b/libcxx/src/stacktrace/README.md
new file mode 100644
index 0000000000000..afdfb9bf2e0f9
--- /dev/null
+++ b/libcxx/src/stacktrace/README.md
@@ -0,0 +1,5 @@
+# C++23 `<stacktrace>`
+
+# References
+
+1. https://eel.is/c++draft/stacktrace
diff --git a/libcxx/src/experimental/stacktrace/alloc.cpp b/libcxx/src/stacktrace/alloc.cpp
similarity index 86%
rename from libcxx/src/experimental/stacktrace/alloc.cpp
rename to libcxx/src/stacktrace/alloc.cpp
index 956e01b312d3b..43842aa1a5b47 100644
--- a/libcxx/src/experimental/stacktrace/alloc.cpp
+++ b/libcxx/src/stacktrace/alloc.cpp
@@ -9,12 +9,12 @@
 #include <__config>
 #include <__config_site>
 
-#include <experimental/__stacktrace/detail/alloc.h>
+#include <__stacktrace/alloc.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-void alloc::_anchor_vfunc() {}
+alloc::~alloc() {}
 
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/config.h b/libcxx/src/stacktrace/common/config.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/config.h
rename to libcxx/src/stacktrace/common/config.h
diff --git a/libcxx/src/experimental/stacktrace/common/debug.cpp b/libcxx/src/stacktrace/common/debug.cpp
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/debug.cpp
rename to libcxx/src/stacktrace/common/debug.cpp
diff --git a/libcxx/src/experimental/stacktrace/common/debug.h b/libcxx/src/stacktrace/common/debug.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/debug.h
rename to libcxx/src/stacktrace/common/debug.h
diff --git a/libcxx/src/experimental/stacktrace/common/failed.h b/libcxx/src/stacktrace/common/failed.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/failed.h
rename to libcxx/src/stacktrace/common/failed.h
diff --git a/libcxx/src/experimental/stacktrace/common/fd.cpp b/libcxx/src/stacktrace/common/fd.cpp
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/fd.cpp
rename to libcxx/src/stacktrace/common/fd.cpp
diff --git a/libcxx/src/experimental/stacktrace/common/fd.h b/libcxx/src/stacktrace/common/fd.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/fd.h
rename to libcxx/src/stacktrace/common/fd.h
diff --git a/libcxx/src/experimental/stacktrace/common/images.h b/libcxx/src/stacktrace/common/images.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/images.h
rename to libcxx/src/stacktrace/common/images.h
diff --git a/libcxx/src/experimental/stacktrace/context.cpp b/libcxx/src/stacktrace/context.cpp
similarity index 88%
rename from libcxx/src/experimental/stacktrace/context.cpp
rename to libcxx/src/stacktrace/context.cpp
index 3d311af4d7a89..e2d1445aa4995 100644
--- a/libcxx/src/experimental/stacktrace/context.cpp
+++ b/libcxx/src/stacktrace/context.cpp
@@ -11,13 +11,13 @@
 
 #include <list>
 
-#include <experimental/__stacktrace/basic_stacktrace.h>
-#include <experimental/__stacktrace/stacktrace_entry.h>
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
 
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
-#include <experimental/__stacktrace/detail/to_string.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/to_string.h>
 
 #include "common/config.h"
 
@@ -44,7 +44,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip, size_t max_depth) {
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void context::do_stacktrace(size_t skip, size_t max_depth) {
   /*
   Here we declare stacktrace components or "backends" which will handle the different tasks:
 
diff --git a/libcxx/src/experimental/stacktrace/linux/elf.h b/libcxx/src/stacktrace/linux/elf.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/linux/elf.h
rename to libcxx/src/stacktrace/linux/elf.h
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp b/libcxx/src/stacktrace/linux/linux-dl.cpp
similarity index 92%
rename from libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
rename to libcxx/src/stacktrace/linux/linux-dl.cpp
index 62cad72b9f124..e15403f7d3a50 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
+++ b/libcxx/src/stacktrace/linux/linux-dl.cpp
@@ -19,8 +19,8 @@
 
 #  include "../common/images.h"
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp b/libcxx/src/stacktrace/linux/linux-elf.cpp
similarity index 93%
rename from libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
rename to libcxx/src/stacktrace/linux/linux-elf.cpp
index 7cf82789ea2ed..2cbeade56534b 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
+++ b/libcxx/src/stacktrace/linux/linux-elf.cpp
@@ -15,8 +15,8 @@
 #  include <cassert>
 #  include <unistd.h>
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/fd.h"
 #  include "elf.h"
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp b/libcxx/src/stacktrace/linux/linux-sym.cpp
similarity index 94%
rename from libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
rename to libcxx/src/stacktrace/linux/linux-sym.cpp
index fa7489c64c923..b9a8c313e668b 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
+++ b/libcxx/src/stacktrace/linux/linux-sym.cpp
@@ -16,8 +16,8 @@
 #  include <dlfcn.h>
 #  include <unistd.h>
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/linux/linux.h b/libcxx/src/stacktrace/linux/linux.h
similarity index 98%
rename from libcxx/src/experimental/stacktrace/linux/linux.h
rename to libcxx/src/stacktrace/linux/linux.h
index a8e8c2279f6fc..52b0377bcb144 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux.h
+++ b/libcxx/src/stacktrace/linux/linux.h
@@ -27,7 +27,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
 
 struct linux {
   context& cx_;
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.cpp b/libcxx/src/stacktrace/osx/osx.cpp
similarity index 96%
rename from libcxx/src/experimental/stacktrace/osx/osx.cpp
rename to libcxx/src/stacktrace/osx/osx.cpp
index c00a7ea94e780..f377c0bded38a 100644
--- a/libcxx/src/experimental/stacktrace/osx/osx.cpp
+++ b/libcxx/src/stacktrace/osx/osx.cpp
@@ -16,8 +16,8 @@
 #  include <mach-o/dyld.h>
 #  include <mach-o/loader.h>
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 #  include "osx.h"
 
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.h b/libcxx/src/stacktrace/osx/osx.h
similarity index 95%
rename from libcxx/src/experimental/stacktrace/osx/osx.h
rename to libcxx/src/stacktrace/osx/osx.h
index ca0890929453e..ce38aa0a378c6 100644
--- a/libcxx/src/experimental/stacktrace/osx/osx.h
+++ b/libcxx/src/stacktrace/osx/osx.h
@@ -17,7 +17,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
 
 struct osx {
   context& cx_;
diff --git a/libcxx/src/experimental/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
similarity index 88%
rename from libcxx/src/experimental/stacktrace/stacktrace.cpp
rename to libcxx/src/stacktrace/stacktrace.cpp
index e33b94205a8ea..98152c0c323f6 100644
--- a/libcxx/src/experimental/stacktrace/stacktrace.cpp
+++ b/libcxx/src/stacktrace/stacktrace.cpp
@@ -14,13 +14,13 @@
 #include <sstream>
 #include <string>
 
-#include <experimental/__stacktrace/basic_stacktrace.h>
-#include <experimental/__stacktrace/stacktrace_entry.h>
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
 
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
-#include <experimental/__stacktrace/detail/to_string.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/to_string.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/addr2line.cpp b/libcxx/src/stacktrace/tools/addr2line.cpp
similarity index 96%
rename from libcxx/src/experimental/stacktrace/tools/addr2line.cpp
rename to libcxx/src/stacktrace/tools/addr2line.cpp
index 310e99699569b..c1d36e83f13a6 100644
--- a/libcxx/src/experimental/stacktrace/tools/addr2line.cpp
+++ b/libcxx/src/stacktrace/tools/addr2line.cpp
@@ -13,8 +13,8 @@
 
 #include "tools.h"
 
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
 #include <string>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/atos.cpp b/libcxx/src/stacktrace/tools/atos.cpp
similarity index 95%
rename from libcxx/src/experimental/stacktrace/tools/atos.cpp
rename to libcxx/src/stacktrace/tools/atos.cpp
index 569883a4b12bc..f6e6458aaae0d 100644
--- a/libcxx/src/experimental/stacktrace/tools/atos.cpp
+++ b/libcxx/src/stacktrace/tools/atos.cpp
@@ -15,9 +15,9 @@
 
 #include "tools.h"
 
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp b/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
similarity index 96%
rename from libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
rename to libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
index 0a9907978dcf2..18b493c97a040 100644
--- a/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
+++ b/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
@@ -15,8 +15,8 @@
 #include "../common/debug.h"
 #include "tools.h"
 
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/pspawn.h b/libcxx/src/stacktrace/tools/pspawn.h
similarity index 97%
rename from libcxx/src/experimental/stacktrace/tools/pspawn.h
rename to libcxx/src/stacktrace/tools/pspawn.h
index 906a504441368..4e2fa381083ad 100644
--- a/libcxx/src/experimental/stacktrace/tools/pspawn.h
+++ b/libcxx/src/stacktrace/tools/pspawn.h
@@ -29,8 +29,8 @@
 #  include <unistd.h>
 #  include <vector>
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/debug.h"
 #  include "../common/failed.h"
diff --git a/libcxx/src/experimental/stacktrace/tools/tools.h b/libcxx/src/stacktrace/tools/tools.h
similarity index 95%
rename from libcxx/src/experimental/stacktrace/tools/tools.h
rename to libcxx/src/stacktrace/tools/tools.h
index d832974e69cbb..70374cf075ebf 100644
--- a/libcxx/src/experimental/stacktrace/tools/tools.h
+++ b/libcxx/src/stacktrace/tools/tools.h
@@ -16,8 +16,8 @@
 #include <list>
 #include <string>
 
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp b/libcxx/src/stacktrace/tools/toolspawner.cpp
similarity index 98%
rename from libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
rename to libcxx/src/stacktrace/tools/toolspawner.cpp
index 05a2f8e6c2eb4..54ddaf156fc64 100644
--- a/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
+++ b/libcxx/src/stacktrace/tools/toolspawner.cpp
@@ -27,7 +27,7 @@
 #  include "../common/failed.h"
 #  include "pspawn.h"
 
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.cpp b/libcxx/src/stacktrace/unwind/unwind.cpp
similarity index 93%
rename from libcxx/src/experimental/stacktrace/unwind/unwind.cpp
rename to libcxx/src/stacktrace/unwind/unwind.cpp
index a316306bc43eb..51689c5f1c15d 100644
--- a/libcxx/src/experimental/stacktrace/unwind/unwind.cpp
+++ b/libcxx/src/stacktrace/unwind/unwind.cpp
@@ -12,8 +12,8 @@
 
 #  include "./unwind.h"
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 #  include <unwind.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.h b/libcxx/src/stacktrace/unwind/unwind.h
similarity index 95%
rename from libcxx/src/experimental/stacktrace/unwind/unwind.h
rename to libcxx/src/stacktrace/unwind/unwind.h
index 1b099e3a0b922..a258a32f50c5d 100644
--- a/libcxx/src/experimental/stacktrace/unwind/unwind.h
+++ b/libcxx/src/stacktrace/unwind/unwind.h
@@ -17,7 +17,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
 
 struct unwind {
   context& cx_;
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp b/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
similarity index 97%
rename from libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
rename to libcxx/src/stacktrace/windows/dbghelp_dll.cpp
index 274c9ecb2b314..7753ad77d3d26 100644
--- a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
+++ b/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
@@ -21,7 +21,7 @@
 #  include <mutex>
 
 #  include "dll.h"
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h b/libcxx/src/stacktrace/windows/dbghelp_dll.h
similarity index 98%
rename from libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
rename to libcxx/src/stacktrace/windows/dbghelp_dll.h
index 920391fb9ba52..b1204a369a3f1 100644
--- a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
+++ b/libcxx/src/stacktrace/windows/dbghelp_dll.h
@@ -22,7 +22,7 @@
 #  include <cstdlib>
 #  include <mutex>
 
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/debug.h"
 #  include "dll.h"
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.cpp b/libcxx/src/stacktrace/windows/dll.cpp
similarity index 95%
rename from libcxx/src/experimental/stacktrace/windows/dll.cpp
rename to libcxx/src/stacktrace/windows/dll.cpp
index 55fd729a9caf4..b0ea2609b55cd 100644
--- a/libcxx/src/experimental/stacktrace/windows/dll.cpp
+++ b/libcxx/src/stacktrace/windows/dll.cpp
@@ -21,7 +21,7 @@
 #  include <mutex>
 
 #  include "dll.h"
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.h b/libcxx/src/stacktrace/windows/dll.h
similarity index 96%
rename from libcxx/src/experimental/stacktrace/windows/dll.h
rename to libcxx/src/stacktrace/windows/dll.h
index ab363ce2c2561..4ef8e7c5df190 100644
--- a/libcxx/src/experimental/stacktrace/windows/dll.h
+++ b/libcxx/src/stacktrace/windows/dll.h
@@ -22,7 +22,7 @@
 #  include <cstdlib>
 #  include <mutex>
 
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/debug.h"
 
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp b/libcxx/src/stacktrace/windows/psapi_dll.cpp
similarity index 95%
rename from libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
rename to libcxx/src/stacktrace/windows/psapi_dll.cpp
index ab28df8d3fbd7..d4f7fdc006341 100644
--- a/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
+++ b/libcxx/src/stacktrace/windows/psapi_dll.cpp
@@ -21,7 +21,7 @@
 #  include <mutex>
 
 #  include "dll.h"
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.h b/libcxx/src/stacktrace/windows/psapi_dll.h
similarity index 96%
rename from libcxx/src/experimental/stacktrace/windows/psapi_dll.h
rename to libcxx/src/stacktrace/windows/psapi_dll.h
index 4e6a192f3d790..bcad1bfb72d0a 100644
--- a/libcxx/src/experimental/stacktrace/windows/psapi_dll.h
+++ b/libcxx/src/stacktrace/windows/psapi_dll.h
@@ -22,7 +22,7 @@
 #  include <cstdlib>
 #  include <mutex>
 
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/debug.h"
 #  include "dll.h"
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.cpp b/libcxx/src/stacktrace/windows/win_impl.cpp
similarity index 99%
rename from libcxx/src/experimental/stacktrace/windows/win_impl.cpp
rename to libcxx/src/stacktrace/windows/win_impl.cpp
index 7773d0d872816..44d6052706b85 100644
--- a/libcxx/src/experimental/stacktrace/windows/win_impl.cpp
+++ b/libcxx/src/stacktrace/windows/win_impl.cpp
@@ -21,7 +21,7 @@
 #  include <mutex>
 
 #  include "dll.h"
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.h b/libcxx/src/stacktrace/windows/win_impl.h
similarity index 96%
rename from libcxx/src/experimental/stacktrace/windows/win_impl.h
rename to libcxx/src/stacktrace/windows/win_impl.h
index 459347ad9a00f..c065987120425 100644
--- a/libcxx/src/experimental/stacktrace/windows/win_impl.h
+++ b/libcxx/src/stacktrace/windows/win_impl.h
@@ -18,7 +18,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
 
 struct win_impl {
   context& cx_;
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
index 6c4f3ee6008a2..460d61d58a915 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
@@ -10,7 +10,7 @@
 // ADDITIONAL_COMPILE_FLAGS: -O0 -g0
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <iostream>
 
 int main(int, char**) {
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
index 58d3c8fed5c5e..bdf5ba22bc46b 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
@@ -10,7 +10,7 @@
 // ADDITIONAL_COMPILE_FLAGS: -O0 -g
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <iostream>
 
 int main(int, char**) {
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
index a00866181a0e6..022f27f5e569e 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
@@ -11,7 +11,7 @@
 
 #include <cassert>
 #include <iostream>
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 int main(int, char**) {
   // Get the current trace.
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
index e8387bfebb14f..35a30eb078212 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
@@ -10,7 +10,7 @@
 // ADDITIONAL_COMPILE_FLAGS: -O3 -g0
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <iostream>
 
 int main(int, char**) {
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
index e3796c2bc8add..8d0fcf5806884 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
@@ -10,7 +10,7 @@
 // ADDITIONAL_COMPILE_FLAGS: -O3 -g
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <iostream>
 
 int main(int, char**) {
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
index 7b58f9accf7c9..cea9c79c87a77 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
@@ -10,7 +10,7 @@
 // ADDITIONAL_COMPILE_FLAGS: -O3 -g -gsplit-dwarf
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <iostream>
 
 int main(int, char**) {
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index fbfa8152b8070..29b49f2ae7e84 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -295,35 +295,6 @@ experimental/propagate_const version
 experimental/simd cstdint
 experimental/simd limits
 experimental/simd version
-experimental/stacktrace bitset
-experimental/stacktrace cctype
-experimental/stacktrace cerrno
-experimental/stacktrace climits
-experimental/stacktrace clocale
-experimental/stacktrace compare
-experimental/stacktrace cstddef
-experimental/stacktrace cstdint
-experimental/stacktrace cstdio
-experimental/stacktrace cstdlib
-experimental/stacktrace cstring
-experimental/stacktrace ctime
-experimental/stacktrace cwchar
-experimental/stacktrace cwctype
-experimental/stacktrace initializer_list
-experimental/stacktrace ios
-experimental/stacktrace iosfwd
-experimental/stacktrace limits
-experimental/stacktrace list
-experimental/stacktrace locale
-experimental/stacktrace memory
-experimental/stacktrace ratio
-experimental/stacktrace stdexcept
-experimental/stacktrace streambuf
-experimental/stacktrace string
-experimental/stacktrace string_view
-experimental/stacktrace tuple
-experimental/stacktrace typeinfo
-experimental/stacktrace version
 experimental/type_traits cstdint
 experimental/type_traits initializer_list
 experimental/type_traits type_traits
@@ -955,6 +926,35 @@ stack limits
 stack stdexcept
 stack tuple
 stack version
+stacktrace bitset
+stacktrace cctype
+stacktrace cerrno
+stacktrace climits
+stacktrace clocale
+stacktrace compare
+stacktrace cstddef
+stacktrace cstdint
+stacktrace cstdio
+stacktrace cstdlib
+stacktrace cstring
+stacktrace ctime
+stacktrace cwchar
+stacktrace cwctype
+stacktrace initializer_list
+stacktrace ios
+stacktrace iosfwd
+stacktrace limits
+stacktrace list
+stacktrace locale
+stacktrace memory
+stacktrace ratio
+stacktrace stdexcept
+stacktrace streambuf
+stacktrace string
+stacktrace string_view
+stacktrace tuple
+stacktrace typeinfo
+stacktrace version
 stop_token atomic
 stop_token climits
 stop_token cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 06dd30b07537e..5f7b5401f081a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -295,35 +295,6 @@ experimental/propagate_const version
 experimental/simd cstdint
 experimental/simd limits
 experimental/simd version
-experimental/stacktrace bitset
-experimental/stacktrace cctype
-experimental/stacktrace cerrno
-experimental/stacktrace climits
-experimental/stacktrace clocale
-experimental/stacktrace compare
-experimental/stacktrace cstddef
-experimental/stacktrace cstdint
-experimental/stacktrace cstdio
-experimental/stacktrace cstdlib
-experimental/stacktrace cstring
-experimental/stacktrace ctime
-experimental/stacktrace cwchar
-experimental/stacktrace cwctype
-experimental/stacktrace initializer_list
-experimental/stacktrace ios
-experimental/stacktrace iosfwd
-experimental/stacktrace limits
-experimental/stacktrace list
-experimental/stacktrace locale
-experimental/stacktrace memory
-experimental/stacktrace ratio
-experimental/stacktrace stdexcept
-experimental/stacktrace streambuf
-experimental/stacktrace string
-experimental/stacktrace string_view
-experimental/stacktrace tuple
-experimental/stacktrace typeinfo
-experimental/stacktrace version
 experimental/type_traits cstdint
 experimental/type_traits initializer_list
 experimental/type_traits type_traits
@@ -955,6 +926,35 @@ stack limits
 stack stdexcept
 stack tuple
 stack version
+stacktrace bitset
+stacktrace cctype
+stacktrace cerrno
+stacktrace climits
+stacktrace clocale
+stacktrace compare
+stacktrace cstddef
+stacktrace cstdint
+stacktrace cstdio
+stacktrace cstdlib
+stacktrace cstring
+stacktrace ctime
+stacktrace cwchar
+stacktrace cwctype
+stacktrace initializer_list
+stacktrace ios
+stacktrace iosfwd
+stacktrace limits
+stacktrace list
+stacktrace locale
+stacktrace memory
+stacktrace ratio
+stacktrace stdexcept
+stacktrace streambuf
+stacktrace string
+stacktrace string_view
+stacktrace tuple
+stacktrace typeinfo
+stacktrace version
 stop_token atomic
 stop_token climits
 stop_token cstdint
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
index 495abe79da20f..e8b5c36d05ba1 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
index 64e54fdf5793d..edf07510687dc 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // ADDITIONAL_COMPILE_FLAGS: -O1 -g
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 #include <iostream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
index db33a8fabb41a..976b7ef05ccf2 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
index 5929cd8f8d0f9..9d473202d492f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
index 665fb475f56e9..d0b327e738c6c 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 #include <sstream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
index b5c6cf77dde71..1f4102bd1ca61 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <iostream>
 #include <iterator>
 #include <memory>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
index ce502626a45ea..8260b0a8028d5 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <iterator>
 
 #include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
index 4c6531319fc09..6da4d40c5e08a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
index 944b4c519f0fd..84472ea91b2aa 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
@@ -10,7 +10,7 @@
 // ADDITIONAL_COMPILE_FLAGS: -g
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <type_traits>
 
 /*
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
index da6e3ccec92b6..f31d7d3e9f8b7 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
index b55bdcde966d3..a77687a5607a1 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 #include <concepts>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
index bfdb54e4df668..0d8fac58f30ee 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 #include <iostream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
index b37681659d43c..42accf6ce1daa 100644
--- a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 /*
   (19.6.5) Formatting support [stacktrace.format]
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
index 3c92016b2c900..9682f6bfc146d 100644
--- a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <memory>
 #include <type_traits>
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index 6d69cf29af308..4f569ffba6325 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -170,7 +170,6 @@ def __hash__(self) -> int:
     "linalg",
     "rcu",
     "spanstream",
-    "stacktrace",  # TODO(stacktrace23): remove this when stacktrace is taken out of experimental
     "stdfloat",
     "text_encoding",
 ]))
@@ -208,7 +207,6 @@ def __hash__(self) -> int:
     "experimental/iterator": "// UNSUPPORTED: c++03",
     "experimental/propagate_const": "// UNSUPPORTED: c++03",
     "experimental/simd": "// UNSUPPORTED: c++03",
-    "experimental/stacktrace": "// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20",
     "experimental/type_traits": "// UNSUPPORTED: c++03",
     "experimental/utility": "// UNSUPPORTED: c++03",
     "filesystem": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14",
@@ -227,6 +225,7 @@ def __hash__(self) -> int:
     "semaphore": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
     "shared_mutex": "// UNSUPPORTED: no-threads, c++03, c++11",
     "sstream": "// UNSUPPORTED: no-localization",
+    "stacktrace": "// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20",
     "stdatomic.h": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17, c++20",
     "stop_token": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
     "streambuf": "// UNSUPPORTED: no-localization",
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index 3e9b6b678a847..f824b25349068 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -168,38 +168,38 @@ cxx_sources = [
   "ryu/d2s.cpp",
   "ryu/f2s.cpp",
   "shared_mutex.cpp",
-  # "stacktrace/alloc.cpp",
-  # "stacktrace/common/config.h",
-  # "stacktrace/common/debug.cpp",
-  # "stacktrace/common/debug.h",
-  # "stacktrace/common/failed.h",
-  # "stacktrace/common/fd.cpp",
-  # "stacktrace/common/fd.h",
-  # "stacktrace/common/images.h",
-  # "stacktrace/context.cpp",
-  # "stacktrace/linux/linux-dl.cpp",
-  # "stacktrace/linux/linux-elf.cpp",
-  # "stacktrace/linux/linux-sym.cpp",
-  # "stacktrace/linux/linux.h",
-  # "stacktrace/osx/osx.cpp",
-  # "stacktrace/osx/osx.h",
-  # "stacktrace/stacktrace.cpp",
-  # "stacktrace/tools/addr2line.cpp",
-  # "stacktrace/tools/atos.cpp",
-  # "stacktrace/tools/llvm_symbolizer.cpp",
-  # "stacktrace/tools/pspawn.h",
-  # "stacktrace/tools/tools.h",
-  # "stacktrace/tools/toolspawner.cpp",
-  # "stacktrace/unwind/unwind.cpp",
-  # "stacktrace/unwind/unwind.h",
-  # "stacktrace/windows/dbghelp_dll.cpp",
-  # "stacktrace/windows/dbghelp_dll.h",
-  # "stacktrace/windows/dll.cpp",
-  # "stacktrace/windows/dll.h",
-  # "stacktrace/windows/psapi_dll.cpp",
-  # "stacktrace/windows/psapi_dll.h",
-  # "stacktrace/windows/win_impl.cpp",
-  # "stacktrace/windows/win_impl.h",
+  "stacktrace/alloc.cpp",
+  "stacktrace/common/config.h",
+  "stacktrace/common/debug.cpp",
+  "stacktrace/common/debug.h",
+  "stacktrace/common/failed.h",
+  "stacktrace/common/fd.cpp",
+  "stacktrace/common/fd.h",
+  "stacktrace/common/images.h",
+  "stacktrace/context.cpp",
+  "stacktrace/linux/linux-dl.cpp",
+  "stacktrace/linux/linux-elf.cpp",
+  "stacktrace/linux/linux-sym.cpp",
+  "stacktrace/linux/linux.h",
+  "stacktrace/osx/osx.cpp",
+  "stacktrace/osx/osx.h",
+  "stacktrace/stacktrace.cpp",
+  "stacktrace/tools/addr2line.cpp",
+  "stacktrace/tools/atos.cpp",
+  "stacktrace/tools/llvm_symbolizer.cpp",
+  "stacktrace/tools/pspawn.h",
+  "stacktrace/tools/tools.h",
+  "stacktrace/tools/toolspawner.cpp",
+  "stacktrace/unwind/unwind.cpp",
+  "stacktrace/unwind/unwind.h",
+  "stacktrace/windows/dbghelp_dll.cpp",
+  "stacktrace/windows/dbghelp_dll.h",
+  "stacktrace/windows/dll.cpp",
+  "stacktrace/windows/dll.h",
+  "stacktrace/windows/psapi_dll.cpp",
+  "stacktrace/windows/psapi_dll.h",
+  "stacktrace/windows/win_impl.cpp",
+  "stacktrace/windows/win_impl.h",
   "stdexcept.cpp",
   "string.cpp",
   "strstream.cpp",

>From b5bbf41546d5104908bd92614ccbae76adfadd90 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 09:50:44 -0400
Subject: [PATCH 06/37] simplify layout of stacktrace files: condense, collapse

---
 libcxx/include/CMakeLists.txt                 |   5 +-
 libcxx/include/__stacktrace/alloc.h           |  97 ----
 .../include/__stacktrace/basic_stacktrace.h   |  36 +-
 libcxx/include/__stacktrace/entry.h           |  44 --
 libcxx/include/__stacktrace/impl.h            | 157 +++++++
 .../include/__stacktrace/stacktrace_entry.h   |  11 +-
 libcxx/include/__stacktrace/to_string.h       |  46 --
 libcxx/include/module.modulemap.in            |  14 +-
 libcxx/include/stacktrace                     |   1 +
 libcxx/modules/std.cppm.in                    |   1 +
 libcxx/src/CMakeLists.txt                     |  22 +-
 libcxx/src/stacktrace/alloc.cpp               |  20 -
 libcxx/src/stacktrace/common/debug.cpp        |  19 -
 libcxx/src/stacktrace/common/debug.h          |  55 ---
 libcxx/src/stacktrace/common/failed.h         |  29 --
 libcxx/src/stacktrace/common/fd.cpp           |  31 --
 libcxx/src/stacktrace/common/fd.h             | 122 -----
 libcxx/src/stacktrace/common/images.h         |  33 --
 libcxx/src/stacktrace/{common => }/config.h   |   0
 libcxx/src/stacktrace/context.cpp             | 104 -----
 .../__stacktrace => src/stacktrace}/context.h |   7 +-
 .../{linux/linux-sym.cpp => linux.cpp}        |  62 ++-
 .../src/stacktrace/{linux/elf.h => linux.h}   |  99 +++-
 libcxx/src/stacktrace/linux/linux-dl.cpp      |  58 ---
 libcxx/src/stacktrace/linux/linux-elf.cpp     |  53 ---
 libcxx/src/stacktrace/linux/linux.h           |  98 ----
 libcxx/src/stacktrace/{osx => }/osx.cpp       |  13 +-
 libcxx/src/stacktrace/{osx => }/osx.h         |   0
 libcxx/src/stacktrace/stacktrace.cpp          | 153 +++++-
 libcxx/src/stacktrace/tools.cpp               | 437 ++++++++++++++++++
 .../stacktrace/{tools/pspawn.h => tools.h}    |  94 ++--
 libcxx/src/stacktrace/tools/addr2line.cpp     | 100 ----
 libcxx/src/stacktrace/tools/atos.cpp          | 115 -----
 .../src/stacktrace/tools/llvm_symbolizer.cpp  | 112 -----
 libcxx/src/stacktrace/tools/tools.h           |  65 ---
 libcxx/src/stacktrace/tools/toolspawner.cpp   | 185 --------
 libcxx/src/stacktrace/{unwind => }/unwind.cpp |  10 +-
 libcxx/src/stacktrace/{unwind => }/unwind.h   |   6 +
 libcxx/src/stacktrace/utils.h                 | 176 +++++++
 .../{windows/win_impl.cpp => windows.cpp}     |  69 ++-
 .../{windows/dbghelp_dll.h => windows.h}      |  86 +++-
 libcxx/src/stacktrace/windows/dbghelp_dll.cpp |  56 ---
 libcxx/src/stacktrace/windows/dll.cpp         |  40 --
 libcxx/src/stacktrace/windows/dll.h           |  68 ---
 libcxx/src/stacktrace/windows/psapi_dll.cpp   |  48 --
 libcxx/src/stacktrace/windows/psapi_dll.h     |  55 ---
 libcxx/src/stacktrace/windows/win_impl.h      |  38 --
 llvm/utils/gn/secondary/libcxx/src/BUILD.gn   |  42 +-
 48 files changed, 1307 insertions(+), 1885 deletions(-)
 delete mode 100644 libcxx/include/__stacktrace/alloc.h
 delete mode 100644 libcxx/include/__stacktrace/entry.h
 create mode 100644 libcxx/include/__stacktrace/impl.h
 delete mode 100644 libcxx/include/__stacktrace/to_string.h
 delete mode 100644 libcxx/src/stacktrace/alloc.cpp
 delete mode 100644 libcxx/src/stacktrace/common/debug.cpp
 delete mode 100644 libcxx/src/stacktrace/common/debug.h
 delete mode 100644 libcxx/src/stacktrace/common/failed.h
 delete mode 100644 libcxx/src/stacktrace/common/fd.cpp
 delete mode 100644 libcxx/src/stacktrace/common/fd.h
 delete mode 100644 libcxx/src/stacktrace/common/images.h
 rename libcxx/src/stacktrace/{common => }/config.h (100%)
 delete mode 100644 libcxx/src/stacktrace/context.cpp
 rename libcxx/{include/__stacktrace => src/stacktrace}/context.h (93%)
 rename libcxx/src/stacktrace/{linux/linux-sym.cpp => linux.cpp} (50%)
 rename libcxx/src/stacktrace/{linux/elf.h => linux.h} (82%)
 delete mode 100644 libcxx/src/stacktrace/linux/linux-dl.cpp
 delete mode 100644 libcxx/src/stacktrace/linux/linux-elf.cpp
 delete mode 100644 libcxx/src/stacktrace/linux/linux.h
 rename libcxx/src/stacktrace/{osx => }/osx.cpp (92%)
 rename libcxx/src/stacktrace/{osx => }/osx.h (100%)
 create mode 100644 libcxx/src/stacktrace/tools.cpp
 rename libcxx/src/stacktrace/{tools/pspawn.h => tools.h} (60%)
 delete mode 100644 libcxx/src/stacktrace/tools/addr2line.cpp
 delete mode 100644 libcxx/src/stacktrace/tools/atos.cpp
 delete mode 100644 libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
 delete mode 100644 libcxx/src/stacktrace/tools/tools.h
 delete mode 100644 libcxx/src/stacktrace/tools/toolspawner.cpp
 rename libcxx/src/stacktrace/{unwind => }/unwind.cpp (91%)
 rename libcxx/src/stacktrace/{unwind => }/unwind.h (81%)
 create mode 100644 libcxx/src/stacktrace/utils.h
 rename libcxx/src/stacktrace/{windows/win_impl.cpp => windows.cpp} (74%)
 rename libcxx/src/stacktrace/{windows/dbghelp_dll.h => windows.h} (55%)
 delete mode 100644 libcxx/src/stacktrace/windows/dbghelp_dll.cpp
 delete mode 100644 libcxx/src/stacktrace/windows/dll.cpp
 delete mode 100644 libcxx/src/stacktrace/windows/dll.h
 delete mode 100644 libcxx/src/stacktrace/windows/psapi_dll.cpp
 delete mode 100644 libcxx/src/stacktrace/windows/psapi_dll.h
 delete mode 100644 libcxx/src/stacktrace/windows/win_impl.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index e659cd368de95..34895f334733c 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -728,10 +728,7 @@ set(files
   __ranges/zip_view.h
   __split_buffer
   __stacktrace/basic_stacktrace.h
-  __stacktrace/alloc.h
-  __stacktrace/context.h
-  __stacktrace/entry.h
-  __stacktrace/to_string.h
+  __stacktrace/impl.h
   __stacktrace/stacktrace_entry.h
   __std_mbstate_t.h
   __stop_token/atomic_unique_lock.h
diff --git a/libcxx/include/__stacktrace/alloc.h b/libcxx/include/__stacktrace/alloc.h
deleted file mode 100644
index 402cf3c371161..0000000000000
--- a/libcxx/include/__stacktrace/alloc.h
+++ /dev/null
@@ -1,97 +0,0 @@
-// -*- 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_STACKTRACE_ALLOC
-#define _LIBCPP_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 _LIBCPP_HIDDEN 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_ptr     = std::addressof(__a);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI ~alloc() override;
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t /*__align*/) override {
-    return __alloc_func_(__size);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t /*__align*/) override {
-    __dealloc_func_((std::byte*)__ptr, __size);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
-    auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
-    return __rhs_ba && (__rhs_ba->__alloc_ptr == __alloc_ptr);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
-    std::pmr::string __ret{this};
-    if (__size) {
-      __ret.reserve(__size);
-      __ret[0] = 0;
-    }
-    return __ret;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::string hex_string(uintptr_t __addr) {
-    char __ret[19]; // "0x" + 16 digits + NUL
-    auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
-    return {__ret, size_t(__size), this};
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::string u64_string(uintptr_t __val) {
-    char __ret[21]; // 20 digits max + NUL
-    auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
-    return {__ret, size_t(__size), this};
-  }
-
-  template <typename _Tp>
-  _LIBCPP_HIDE_FROM_ABI std::pmr::list<_Tp> new_list() {
-    return std::pmr::list<_Tp>{this};
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::list<std::pmr::string> new_string_list() { return new_list<std::pmr::string>(); }
-
-private:
-  std::function<std::byte*(size_t)> __alloc_func_;
-  std::function<void(std::byte*, size_t)> __dealloc_func_;
-  /** Only used for checking equality */
-  void const* __alloc_ptr;
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_ALLOC
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 5e2ee1ae1248a..1fe5cfa1b0cf5 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -10,7 +10,7 @@
 #ifndef _LIBCPP_BASIC_STACKTRACE
 #define _LIBCPP_BASIC_STACKTRACE
 
-#include <__stacktrace/entry.h>
+#include <__stacktrace/impl.h>
 #include <__stacktrace/stacktrace_entry.h>
 
 #include <__config>
@@ -33,13 +33,7 @@
 #include <__vector/swap.h>
 #include <__vector/vector.h>
 #include <cstddef>
-#include <list>
 #include <memory>
-#include <string>
-
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/to_string.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -67,13 +61,7 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
   [[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));
-    }
-  }
+  // _LIBCPP_HIDE_FROM_ABI basic_stacktrace(const _Allocator& __alloc, std::pmr::list<stacktrace_entry>&& __vec);
 
 public:
   // (19.6.4.1)
@@ -96,28 +84,26 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
 
   _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_)};
+    return current(1, /* no __max_depth */ ~0, __caller_alloc);
   }
 
   _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_)};
+    return current(__skip + 1, /* no __max_depth */ ~0, __caller_alloc);
   }
 
   _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) {
+    basic_stacktrace<_Allocator> __ret{__caller_alloc};
     __stacktrace::alloc __alloc(__caller_alloc);
-    __stacktrace::context __tr{__alloc};
-    __tr.do_stacktrace(__skip + 1, __max_depth);
-    return {__caller_alloc, std::move(__tr.__entries_)};
+    auto __resize = [&__ret](size_t __sz) { __ret.__entries_.resize(__sz); };
+    auto __assign = [&__ret](size_t __index, stacktrace_entry&& __entry) {
+      __ret.__entries_.at(__index) = std::move(__entry);
+    };
+    __stacktrace::__impl(__skip + 1, __max_depth, __alloc, __resize, __assign);
+    return __ret;
   }
 
   _LIBCPP_EXPORTED_FROM_ABI constexpr ~basic_stacktrace() = default;
diff --git a/libcxx/include/__stacktrace/entry.h b/libcxx/include/__stacktrace/entry.h
deleted file mode 100644
index 50853b87cc693..0000000000000
--- a/libcxx/include/__stacktrace/entry.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// -*- 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_STACKTRACE_DETAIL_ENTRY
-#define _LIBCPP_STACKTRACE_DETAIL_ENTRY
-
-#include <__config>
-#include <cstdint>
-#include <string>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-namespace __stacktrace {
-
-/** Contains fields which will be used to generate the final `std::stacktrace_entry`.
-This is an intermediate object which owns strings allocated via the caller-provided allocator,
-which are later freed back to that allocator and converted to plain `std::string`s. */
-struct _LIBCPP_HIDE_FROM_ABI entry {
-  /** Caller's / faulting insn's address, including ASLR/slide */
-  uintptr_t __addr_{};
-
-  /** the address minus its image's slide offset */
-  uintptr_t __addr_unslid_{};
-
-  /** entry's description (symbol name) */
-  std::pmr::string __desc_{};
-
-  /** source file name */
-  std::pmr::string __file_{};
-
-  /** line number in source file */
-  uint32_t __line_{};
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_DETAIL_ENTRY
diff --git a/libcxx/include/__stacktrace/impl.h b/libcxx/include/__stacktrace/impl.h
new file mode 100644
index 0000000000000..7aaec10672baa
--- /dev/null
+++ b/libcxx/include/__stacktrace/impl.h
@@ -0,0 +1,157 @@
+// -*- 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_STACKTRACE_IMPL
+#define _LIBCPP_STACKTRACE_IMPL
+
+#include <__cstddef/byte.h>
+#include <__cstddef/ptrdiff_t.h>
+#include <__cstddef/size_t.h>
+#include <__format/formatter.h>
+#include <__functional/function.h>
+#include <__functional/hash.h>
+#include <__fwd/format.h>
+#include <__fwd/ostream.h>
+#include <__fwd/sstream.h>
+#include <__fwd/vector.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 <__utility/move.h>
+#include <__vector/pmr.h>
+#include <__vector/swap.h>
+#include <__vector/vector.h>
+#include <list>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Allocator>
+class basic_stacktrace;
+
+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 _LIBCPP_HIDE_FROM_ABI alloc final : std::pmr::memory_resource {
+  template <class _Allocator>
+  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_ptr_    = std::addressof(__a);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI ~alloc() override = default;
+
+  _LIBCPP_HIDE_FROM_ABI void* do_allocate(size_t __size, size_t /*__align*/) override { return __alloc_func_(__size); }
+
+  _LIBCPP_HIDE_FROM_ABI void do_deallocate(void* __ptr, size_t __size, size_t /*__align*/) override {
+    __dealloc_func_((std::byte*)__ptr, __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
+    auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
+    return __rhs_ba && (__rhs_ba->__alloc_ptr_ == __alloc_ptr_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
+    std::pmr::string __ret{this};
+    if (__size) {
+      __ret.reserve(__size);
+      __ret[0] = 0;
+    }
+    return __ret;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::string hex_string(uintptr_t __addr) {
+    char __ret[19]; // "0x" + 16 digits + NUL
+    auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
+    return {__ret, size_t(__size), this};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::string u64_string(uintptr_t __val) {
+    char __ret[21]; // 20 digits max + NUL
+    auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
+    return {__ret, size_t(__size), this};
+  }
+
+  template <typename _Tp>
+  _LIBCPP_HIDE_FROM_ABI std::pmr::list<_Tp> new_list() {
+    return std::pmr::list<_Tp>{this};
+  }
+
+  std::pmr::list<std::pmr::string> new_string_list() { return new_list<std::pmr::string>(); }
+
+private:
+  std::function<std::byte*(size_t)> __alloc_func_;
+  std::function<void(std::byte*, size_t)> __dealloc_func_;
+  /** Only used for checking equality */
+  void const* __alloc_ptr_;
+};
+
+/** Contains fields which will be used to generate the final `std::stacktrace_entry`.
+This is an intermediate object which owns strings allocated via the caller-provided allocator,
+which are later freed back to that allocator and converted to plain `std::string`s. */
+struct entry {
+  /** Caller's / faulting insn's address, including ASLR/slide */
+  uintptr_t __addr_{};
+
+  /** the address minus its image's slide offset */
+  uintptr_t __addr_unslid_{};
+
+  /** entry's description (symbol name) */
+  std::pmr::string __desc_{};
+
+  /** source file name */
+  std::pmr::string __file_{};
+
+  /** line number in source file */
+  uint32_t __line_{};
+
+  /* implicit */ operator std::stacktrace_entry();
+};
+
+struct __to_string {
+  _LIBCPP_EXPORTED_FROM_ABI string operator()(stacktrace_entry const& __entry);
+
+  _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
+
+  _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
+
+  _LIBCPP_EXPORTED_FROM_ABI string operator()(std::stacktrace_entry const* __entries, size_t __count);
+
+  template <class _Allocator>
+  _LIBCPP_EXPORTED_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
+    return (*this)(__st.__entries_.data(), __st.__entries_.size());
+  }
+};
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+__impl(size_t __skip,
+       size_t __max_depth,
+       alloc& __alloc,
+       std::function<void(size_t)> __resize_func,
+       std::function<void(size_t, std::stacktrace_entry&&)> __assign_func);
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_IMPL
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index e5071655bd0d4..43b10d2a86f65 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -33,10 +33,12 @@
 #include <cstdint>
 #include <string>
 
-#include <__stacktrace/entry.h>
-
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+namespace __stacktrace {
+struct entry;
+} // namespace __stacktrace
+
 class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
 public:
   // (19.6.3.1) Overview [stacktrace.entry.overview]
@@ -72,10 +74,9 @@ class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
     return __x.native_handle() <=> __y.native_handle();
   }
 
-  _LIBCPP_HIDE_FROM_ABI explicit stacktrace_entry(__stacktrace::entry&& __e)
-      : __addr_(__e.__addr_), __desc_(std::move(__e.__desc_)), __file_(std::move(__e.__file_)), __line_(__e.__line_) {}
-
 private:
+  friend struct __stacktrace::entry;
+
   uintptr_t __addr_{};
   std::string __desc_{};
   std::string __file_{};
diff --git a/libcxx/include/__stacktrace/to_string.h b/libcxx/include/__stacktrace/to_string.h
deleted file mode 100644
index 1d2c51a7a5c10..0000000000000
--- a/libcxx/include/__stacktrace/to_string.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// -*- 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_STACKTRACE_TO_STRING
-#define _LIBCPP_STACKTRACE_TO_STRING
-
-#include <__config>
-#include <__fwd/sstream.h>
-#include <__ostream/basic_ostream.h>
-#include <cstddef>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <class _Allocator>
-class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace;
-
-class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry;
-
-namespace __stacktrace {
-
-struct _LIBCPP_HIDE_FROM_ABI __to_string {
-  _LIBCPP_HIDE_FROM_ABI string operator()(stacktrace_entry const& __entry);
-
-  _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
-
-  _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
-
-  string operator()(std::stacktrace_entry const* __entries, size_t __count);
-
-  template <class _Allocator>
-  _LIBCPP_HIDE_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
-    return (*this)(__st.__entries_.data(), __st.__entries_.size());
-  }
-};
-
-} // namespace __stacktrace
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 6a89e279e9eac..9187e49031d70 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1986,12 +1986,14 @@ module std [system] {
   }
 
   module stacktrace {
-    module basic_stacktrace { header "__stacktrace/basic_stacktrace.h" }
-    module alloc { header "__stacktrace/alloc.h" }
-    module context { header "__stacktrace/context.h" }
-    module entry { header "__stacktrace/entry.h" }
-    module to_string { header "__stacktrace/to_string.h" }
-    module stacktrace_entry { header "__stacktrace/stacktrace_entry.h" }
+    module fwd {
+      header "__stacktrace/basic_stacktrace.h"
+      header "__stacktrace/stacktrace_entry.h"
+    }
+    module impl {
+      header "__stacktrace/impl.h"
+    }
+
     header "stacktrace"
     export *
   }
diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace
index 8c7d8f82acbd2..f635208a64d33 100644
--- a/libcxx/include/stacktrace
+++ b/libcxx/include/stacktrace
@@ -181,6 +181,7 @@ _LIBCPP_PUSH_MACROS
 #    include <__undef_macros>
 
 #    include <__stacktrace/basic_stacktrace.h>
+#    include <__stacktrace/impl.h>
 #    include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_POP_MACROS
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index dc40af57e573a..7691f7e860655 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -124,6 +124,7 @@ module;
 #  include <sstream>
 #endif
 #include <stack>
+#include <stacktrace>
 #include <stdexcept>
 #include <stop_token>
 #if _LIBCPP_HAS_LOCALIZATION
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 5076d1b13758f..1d8d32975a15d 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,24 +40,12 @@ set(LIBCXX_SOURCES
   ryu/d2fixed.cpp
   ryu/d2s.cpp
   ryu/f2s.cpp
-  stacktrace/alloc.cpp
-  stacktrace/common/debug.cpp
-  stacktrace/common/fd.cpp
-  stacktrace/context.cpp
-  stacktrace/linux/linux-dl.cpp
-  stacktrace/linux/linux-elf.cpp
-  stacktrace/linux/linux-sym.cpp
-  stacktrace/osx/osx.cpp
+  stacktrace/linux.cpp
+  stacktrace/osx.cpp
   stacktrace/stacktrace.cpp
-  stacktrace/tools/addr2line.cpp
-  stacktrace/tools/atos.cpp
-  stacktrace/tools/llvm_symbolizer.cpp
-  stacktrace/tools/toolspawner.cpp
-  stacktrace/unwind/unwind.cpp
-  stacktrace/windows/dbghelp_dll.cpp
-  stacktrace/windows/dll.cpp
-  stacktrace/windows/psapi_dll.cpp
-  stacktrace/windows/win_impl.cpp
+  stacktrace/tools.cpp
+  stacktrace/unwind.cpp
+  stacktrace/windows.cpp
   stdexcept.cpp
   string.cpp
   support/runtime/exception_fallback.ipp
diff --git a/libcxx/src/stacktrace/alloc.cpp b/libcxx/src/stacktrace/alloc.cpp
deleted file mode 100644
index 43842aa1a5b47..0000000000000
--- a/libcxx/src/stacktrace/alloc.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-
-#include <__stacktrace/alloc.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-alloc::~alloc() {}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/common/debug.cpp b/libcxx/src/stacktrace/common/debug.cpp
deleted file mode 100644
index 4f113e10c0c23..0000000000000
--- a/libcxx/src/stacktrace/common/debug.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "debug.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-debug::~debug() = default;
-
-debug::dummy_ostream::~dummy_ostream() = default;
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/common/debug.h b/libcxx/src/stacktrace/common/debug.h
deleted file mode 100644
index 0cee301d42d8b..0000000000000
--- a/libcxx/src/stacktrace/common/debug.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_DEBUG_H
-#define _LIBCPP_STACKTRACE_DEBUG_H
-
-#include <__config>
-#include <iostream>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/** Debug-message output stream.  If `LIBCXX_STACKTRACE_DEBUG` is defined in the environment
-or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
-otherwise its does nothing by returning a dummy stream. */
-struct debug : std::ostream {
-  virtual ~debug();
-
-  static bool enabled() {
-#if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
-    return true;
-#else
-    static bool ret = [] {
-      auto const* val = getenv("LIBCXX_STACKTRACE_DEBUG");
-      return val && !strncmp(val, "1", 1);
-    }();
-    return ret;
-#endif
-  }
-
-  /** No-op output stream. */
-  struct dummy_ostream final : std::ostream {
-    virtual ~dummy_ostream();
-    friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
-  };
-
-  friend std::ostream& operator<<(debug& dp, auto const& val) {
-    static dummy_ostream kdummy;
-    if (!enabled()) {
-      return kdummy;
-    }
-    std::cerr << val;
-    return std::cerr;
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_DEBUG_H
diff --git a/libcxx/src/stacktrace/common/failed.h b/libcxx/src/stacktrace/common/failed.h
deleted file mode 100644
index e53eb1123aabc..0000000000000
--- a/libcxx/src/stacktrace/common/failed.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_FAILED_H
-#define _LIBCPP_STACKTRACE_FAILED_H
-
-#include <stdexcept>
-
-// TODO(stacktrace23) Use std::expected instead?
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct failed : std::runtime_error {
-  virtual ~failed() = default;
-  int errno_{0};
-  failed() : std::runtime_error({}) {}
-  failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_FAILED_H
diff --git a/libcxx/src/stacktrace/common/fd.cpp b/libcxx/src/stacktrace/common/fd.cpp
deleted file mode 100644
index b7728ea7826dd..0000000000000
--- a/libcxx/src/stacktrace/common/fd.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "fd.h"
-#include "failed.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-fd_streambuf::~fd_streambuf() = default;
-fd_istream::~fd_istream()     = default;
-
-int fd_streambuf::underflow() {
-  int bytesRead = ::read(fd_, buf_, size_);
-  if (bytesRead < 0) {
-    throw failed("I/O error reading from child process", errno);
-  }
-  if (bytesRead == 0) {
-    return traits_type::eof();
-  }
-  setg(buf_, buf_, buf_ + bytesRead);
-  return int(*buf_);
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/common/fd.h b/libcxx/src/stacktrace/common/fd.h
deleted file mode 100644
index ff003e16a6bd8..0000000000000
--- a/libcxx/src/stacktrace/common/fd.h
+++ /dev/null
@@ -1,122 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_FD_H
-#define _LIBCPP_STACKTRACE_FD_H
-
-#include <__config>
-#include <cassert>
-#include <cerrno>
-#include <cstdio>
-#include <iostream>
-#include <sys/fcntl.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include <utility>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/** Encapsulates a plain old file descriptor `int`.  Avoids copies in order to
-force some component to "own" this, although it's freely convertible back to
-integer form.  Default-constructed, closed, and moved-out-of instances will have
-the invalid fd `-1`. */
-class fd {
-  int fd_{-1};
-
-public:
-  fd() : fd(-1) {}
-  fd(int fdint) : fd_(fdint) {}
-
-  fd(fd const&)            = delete;
-  fd& operator=(fd const&) = delete;
-
-  fd(fd&& rhs) {
-    if (&rhs != this) {
-      std::exchange(fd_, rhs.fd_);
-    }
-  }
-
-  fd& operator=(fd&& rhs) { return *new (this) fd(std::move(rhs)); }
-
-  ~fd() { close(); }
-
-  /** Returns true IFF fd is above zero */
-  bool valid() const { return fd_ > 0; }
-
-  /* implicit */ operator int() const {
-    auto ret = fd_;
-    assert(ret > 0);
-    return ret;
-  }
-
-  void close() {
-    int fd_old = -1;
-    std::exchange(fd_old, fd_);
-    if (fd_old != -1) {
-      ::close(fd_old);
-    }
-  }
-
-  static fd& null_fd() {
-    static fd ret = {::open("/dev/null", O_RDWR)};
-    return ret;
-  }
-
-  static fd open(std::string_view path) {
-    fd ret = {::open(path.data(), O_RDONLY)};
-    return ret;
-  }
-};
-
-/** Wraps a readable fd using the `streambuf` interface.  I/O errors arising
-from reading the provided fd will result in a `Failed` being thrown. */
-struct fd_streambuf final : std::streambuf {
-  fd& fd_;
-  char* buf_;
-  size_t size_;
-  fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
-  virtual ~fd_streambuf();
-  int underflow() override;
-};
-
-/** Wraps an `FDInStreamBuffer` in an `istream` */
-struct fd_istream final : std::istream {
-  fd_streambuf& buf_;
-  virtual ~fd_istream();
-  explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
-};
-
-struct fd_mmap final {
-  fd fd_{};
-  size_t size_{0};
-  std::byte const* addr_{nullptr};
-
-  explicit fd_mmap(std::string_view path) : fd_mmap(fd::open(path)) {}
-
-  explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
-    if (fd_) {
-      if ((size_ = ::lseek(fd, 0, SEEK_END))) {
-        addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
-      }
-    }
-  }
-
-  operator bool() const { return addr_; }
-
-  ~fd_mmap() {
-    if (addr_) {
-      ::munmap(const_cast<void*>((void const*)addr_), size_);
-    }
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_FD_H
diff --git a/libcxx/src/stacktrace/common/images.h b/libcxx/src/stacktrace/common/images.h
deleted file mode 100644
index cb9eac47cf9dd..0000000000000
--- a/libcxx/src/stacktrace/common/images.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_IMAGES_H
-#define _LIBCPP_STACKTRACE_IMAGES_H
-
-#include <cstdint>
-#include <string_view>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-constexpr unsigned k_max_images = 256;
-
-struct image {
-  uintptr_t loaded_at_{};
-  intptr_t slide_{};
-  std::string_view name_{};
-  bool is_main_prog_{};
-
-  bool operator<(image const& rhs) const { return loaded_at_ < rhs.loaded_at_; }
-  operator bool() const { return !name_.empty(); }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/stacktrace/common/config.h b/libcxx/src/stacktrace/config.h
similarity index 100%
rename from libcxx/src/stacktrace/common/config.h
rename to libcxx/src/stacktrace/config.h
diff --git a/libcxx/src/stacktrace/context.cpp b/libcxx/src/stacktrace/context.cpp
deleted file mode 100644
index e2d1445aa4995..0000000000000
--- a/libcxx/src/stacktrace/context.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-
-#include <list>
-
-#include <__stacktrace/basic_stacktrace.h>
-#include <__stacktrace/stacktrace_entry.h>
-
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-#include <__stacktrace/to_string.h>
-
-#include "common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-#  include "linux/linux.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_APPLE)
-#  include "osx/osx.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-#  include "tools/pspawn.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
-#  include "unwind/unwind.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
-#  include "windows/win_impl.h"
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void context::do_stacktrace(size_t skip, size_t max_depth) {
-  /*
-  Here we declare stacktrace components or "backends" which will handle the different tasks:
-
-  (1) get the addresses from the call stack
-  (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
-  (3) resolve using debug info, and possibly with an external tool on the $PATH
-  (4+) extra passes to get symbols, in case 3 couldn't
-
-  Based on the macros produced by `stacktrace.h`, throw all backends we have available at the task.  Ideally the #ifdef
-  gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
-  symbolizer.  If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
-
-  All these classes do their best to provide any of the requested fields they can: (symbol, filename, source line),
-  substituting if needed with something reasonable.  For example, if the source filename and line are not available
-  then we will at least report that the address and symbol are in the module `foo.exe`.
-
-  These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
-  already-populated data.  We take care not to crash / abort / throw in any of these, and we'll silently fail.  See
-  `common/debug.h` for a debugging logger you can enable at runtime.
-  */
-
-#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
-  win_impl dbghelp{*this};
-  auto& collector  = dbghelp;
-  auto& mod_ident  = dbghelp;
-  auto& resolver   = dbghelp;
-  auto& symbolizer = dbghelp;
-#endif
-#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
-  unwind unwind{*this};
-  auto& collector = unwind;
-#endif
-#if defined(_LIBCPP_STACKTRACE_APPLE)
-  osx osx{*this};
-  auto& mod_ident  = osx;
-  auto& symbolizer = osx;
-#endif
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-  linux linux{*this};
-  auto& mod_ident  = linux;
-  auto& symbolizer = linux;
-#endif
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-  spawner pspawn{*this};
-  auto& resolver = pspawn;
-#endif
-
-  collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
-  if (__entries_.size()) {                // (Can't proceed if empty)
-    mod_ident.ident_modules();            // Associate addrs with binaries (ELF/MachO/etc.)
-    resolver.resolve_lines();             // Resolve addresses to symbols, filename, linenumber
-    symbolizer.symbolize();               // Populate missing symbols, if any.
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__stacktrace/context.h b/libcxx/src/stacktrace/context.h
similarity index 93%
rename from libcxx/include/__stacktrace/context.h
rename to libcxx/src/stacktrace/context.h
index 2a23798751c60..585d5ff7ff4d1 100644
--- a/libcxx/include/__stacktrace/context.h
+++ b/libcxx/src/stacktrace/context.h
@@ -16,14 +16,15 @@
 #include <__vector/pmr.h>
 #include <__vector/vector.h>
 #include <cstddef>
+#include <list>
 #include <string>
 
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/entry.h>
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-struct _LIBCPP_HIDDEN alloc;
+struct alloc;
 struct _LIBCPP_HIDDEN entry;
 
 namespace __stacktrace {
diff --git a/libcxx/src/stacktrace/linux/linux-sym.cpp b/libcxx/src/stacktrace/linux.cpp
similarity index 50%
rename from libcxx/src/stacktrace/linux/linux-sym.cpp
rename to libcxx/src/stacktrace/linux.cpp
index b9a8c313e668b..0ad3c276f5cb4 100644
--- a/libcxx/src/stacktrace/linux/linux-sym.cpp
+++ b/libcxx/src/stacktrace/linux.cpp
@@ -6,22 +6,74 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "../common/config.h"
+#include "stacktrace/config.h"
 
 #if defined(_LIBCPP_STACKTRACE_LINUX)
 
-#  include "linux.h"
-
 #  include <cassert>
 #  include <dlfcn.h>
+#  include <link.h>
+#  include <stacktrace>
 #  include <unistd.h>
 
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
+#  include "stacktrace/config.h"
+#  include "stacktrace/context.h"
+#  include "stacktrace/linux.h"
+#  include "stacktrace/utils.h"
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
+void linux::ident_modules() {
+  auto& images = images::get();
+
+  // Aside from the left/right sentinels in the array (hence the 2),
+  // are there any other real images?
+  if (images.count_ <= 2) {
+    return;
+  }
+
+  auto mainProg = images.mainProg();
+  if (mainProg) {
+    cx_.__main_prog_path_ = mainProg->name_;
+  }
+
+  unsigned index = 1; // Starts at one, and is moved around in this loop
+  for (auto& entry : cx_.__entries_) {
+    while (images[index].loaded_at_ > entry.__addr_) {
+      --index;
+    }
+    while (images[index + 1].loaded_at_ <= entry.__addr_) {
+      ++index;
+    }
+    entry.__addr_unslid_ = entry.__addr_ - images[index].slide_;
+    entry.__file_        = images[index].name_;
+  }
+}
+
+/**
+When trying to collect a stacktrace under Linux, there are narrow (but still quite common) cases where we will fail
+to resolve symbols.  Linux's `dl` doesn't want to read symbols from the non-exported symbol table at runtime,
+and older versions of `addr2line` or `llvm-symbolizer` will also not resolve these.
+
+This implementation the minimum necessary to resolve symbols.  It can identify this as an ELF (32 or 64 bits), locate
+the symbol and symbol-string table, and fill in any remaining missing symbols.
+*/
+void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
+  // We can statically initialize these, because main_elf_name should be the same every time.
+  static fd_mmap _mm(main_elf_name);
+  if (_mm) {
+    static elf::ELF _this_elf(_mm.addr_);
+    if (_this_elf) {
+      for (auto& entry : cx_.__entries_) {
+        if (entry.__desc_.empty() && entry.__file_ == main_elf_name) {
+          entry.__desc_ = _this_elf.getSym(entry.__addr_unslid_).name();
+        }
+      }
+    }
+  }
+}
+
 bool symbolize_entry(entry& entry) {
   bool ret = false;
   Dl_info info;
diff --git a/libcxx/src/stacktrace/linux/elf.h b/libcxx/src/stacktrace/linux.h
similarity index 82%
rename from libcxx/src/stacktrace/linux/elf.h
rename to libcxx/src/stacktrace/linux.h
index 214e8cc5a7819..fd337c0adc422 100644
--- a/libcxx/src/stacktrace/linux/elf.h
+++ b/libcxx/src/stacktrace/linux.h
@@ -6,21 +6,94 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_STACKTRACE_ELF_H
-#define _LIBCPP_STACKTRACE_ELF_H
-
-#include <cstddef>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <functional>
-#include <string_view>
-#include <sys/fcntl.h>
-#include <unistd.h>
+#ifndef _LIBCPP_STACKTRACE_LINUX_H
+#define _LIBCPP_STACKTRACE_LINUX_H
+
+#include "stacktrace/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+#  include <algorithm>
+#  include <array>
+#  include <cassert>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <functional>
+#  include <link.h>
+#  include <stacktrace>
+#  include <string_view>
+#  include <unistd.h>
+
+#  include "stacktrace/config.h"
+#  include "stacktrace/context.h"
+#  include "stacktrace/utils.h"
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
+struct context;
+
+struct linux {
+  context& cx_;
+  void ident_modules();
+  void symbolize();
+
+private:
+  void resolve_main_elf_syms(std::string_view elf_name);
+};
+
+struct images {
+  // How many images this contains, including the left/right sentinels.
+  unsigned count_{0};
+  std::array<image, k_max_images + 2> images_{};
+
+  int add(dl_phdr_info& info) {
+    assert(count_ < k_max_images);
+    auto isFirst        = (count_ == 0);
+    auto& image         = images_.at(count_++);
+    image.loaded_at_    = info.dlpi_addr;
+    image.slide_        = info.dlpi_addr;
+    image.name_         = info.dlpi_name;
+    image.is_main_prog_ = isFirst; // first one is the main ELF
+    if (image.name_.empty() && isFirst) {
+      static char buffer[PATH_MAX + 1];
+      uint32_t length = sizeof(buffer);
+      if (readlink("/proc/self/exe", buffer, length) > 0) {
+        image.name_ = buffer;
+      }
+    }
+    return count_ == k_max_images; // return nonzero if we're at the limit
+  }
+
+  static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
+
+  images() {
+    dl_iterate_phdr(images::callback, this);
+    images_[count_++] = {0uz, 0};  // sentinel at low end
+    images_[count_++] = {~0uz, 0}; // sentinel at high end
+    std::sort(images_.begin(), images_.begin() + count_ - 1);
+  }
+
+  image& operator[](size_t index) {
+    assert(index < count_);
+    return images_.at(index);
+  }
+
+  image* mainProg() {
+    for (auto& image : images_) {
+      if (image.is_main_prog_) {
+        return ℑ
+      }
+    }
+    return nullptr;
+  }
+
+  static images& get() {
+    static images images;
+    return images;
+  }
+};
+
 // Includes ELF constants and structs copied from <elf.h>, with a few changes.
 
 namespace elf {
@@ -312,4 +385,6 @@ inline std::string_view Symbol::name() const { return elf_->strtab_.at(nameIndex
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif
+#endif // _LIBCPP_STACKTRACE_LINUX
+
+#endif // _LIBCPP_STACKTRACE_LINUX_H
diff --git a/libcxx/src/stacktrace/linux/linux-dl.cpp b/libcxx/src/stacktrace/linux/linux-dl.cpp
deleted file mode 100644
index e15403f7d3a50..0000000000000
--- a/libcxx/src/stacktrace/linux/linux-dl.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-#  include "linux.h"
-
-#  include <cassert>
-#  include <dlfcn.h>
-#  include <link.h>
-#  include <unistd.h>
-
-#  include "../common/images.h"
-
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-void linux::ident_modules() {
-  auto& images = images::get();
-
-  // Aside from the left/right sentinels in the array (hence the 2),
-  // are there any other real images?
-  if (images.count_ <= 2) {
-    return;
-  }
-
-  auto mainProg = images.mainProg();
-  if (mainProg) {
-    cx_.__main_prog_path_ = mainProg->name_;
-  }
-
-  unsigned index = 1; // Starts at one, and is moved around in this loop
-  for (auto& entry : cx_.__entries_) {
-    while (images[index].loaded_at_ > entry.__addr_) {
-      --index;
-    }
-    while (images[index + 1].loaded_at_ <= entry.__addr_) {
-      ++index;
-    }
-    entry.__addr_unslid_ = entry.__addr_ - images[index].slide_;
-    entry.__file_        = images[index].name_;
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/stacktrace/linux/linux-elf.cpp b/libcxx/src/stacktrace/linux/linux-elf.cpp
deleted file mode 100644
index 2cbeade56534b..0000000000000
--- a/libcxx/src/stacktrace/linux/linux-elf.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-#  include "linux.h"
-
-#  include <cassert>
-#  include <unistd.h>
-
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-
-#  include "../common/fd.h"
-#  include "elf.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/**
-When trying to collect a stacktrace under Linux, there are narrow (but still quite common) cases where we will fail
-to resolve symbols.  Linux's `dl` doesn't want to read symbols from the non-exported symbol table at runtime,
-and older versions of `addr2line` or `llvm-symbolizer` will also not resolve these.
-
-This implementation the minimum necessary to resolve symbols.  It can identify this as an ELF (32 or 64 bits), locate
-the symbol and symbol-string table, and fill in any remaining missing symbols.
-*/
-void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
-  // We can statically initialize these, because main_elf_name should be the same every time.
-  static fd_mmap _mm(main_elf_name);
-  if (_mm) {
-    static elf::ELF _this_elf(_mm.addr_);
-    if (_this_elf) {
-      for (auto& entry : cx_.__entries_) {
-        if (entry.__desc_.empty() && entry.__file_ == main_elf_name) {
-          entry.__desc_ = _this_elf.getSym(entry.__addr_unslid_).name();
-        }
-      }
-    }
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/stacktrace/linux/linux.h b/libcxx/src/stacktrace/linux/linux.h
deleted file mode 100644
index 52b0377bcb144..0000000000000
--- a/libcxx/src/stacktrace/linux/linux.h
+++ /dev/null
@@ -1,98 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_LINUX_H
-#define _LIBCPP_STACKTRACE_LINUX_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-#  include <algorithm>
-#  include <array>
-#  include <cassert>
-#  include <cstddef>
-#  include <cstdlib>
-#  include <link.h>
-#  include <string_view>
-#  include <unistd.h>
-
-#  include "../common/images.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct context;
-
-struct linux {
-  context& cx_;
-  void ident_modules();
-  void symbolize();
-
-private:
-  void resolve_main_elf_syms(std::string_view elf_name);
-};
-
-struct images {
-  // How many images this contains, including the left/right sentinels.
-  unsigned count_{0};
-  std::array<image, k_max_images + 2> images_{};
-
-  int add(dl_phdr_info& info) {
-    assert(count_ < k_max_images);
-    auto isFirst        = (count_ == 0);
-    auto& image         = images_.at(count_++);
-    image.loaded_at_    = info.dlpi_addr;
-    image.slide_        = info.dlpi_addr;
-    image.name_         = info.dlpi_name;
-    image.is_main_prog_ = isFirst; // first one is the main ELF
-    if (image.name_.empty() && isFirst) {
-      static char buffer[PATH_MAX + 1];
-      uint32_t length = sizeof(buffer);
-      if (readlink("/proc/self/exe", buffer, length) > 0) {
-        image.name_ = buffer;
-      }
-    }
-    return count_ == k_max_images; // return nonzero if we're at the limit
-  }
-
-  static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
-
-  images() {
-    dl_iterate_phdr(images::callback, this);
-    images_[count_++] = {0uz, 0};  // sentinel at low end
-    images_[count_++] = {~0uz, 0}; // sentinel at high end
-    std::sort(images_.begin(), images_.begin() + count_ - 1);
-  }
-
-  image& operator[](size_t index) {
-    assert(index < count_);
-    return images_.at(index);
-  }
-
-  image* mainProg() {
-    for (auto& image : images_) {
-      if (image.is_main_prog_) {
-        return ℑ
-      }
-    }
-    return nullptr;
-  }
-
-  static images& get() {
-    static images images;
-    return images;
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
-
-#endif // _LIBCPP_STACKTRACE_LINUX_H
diff --git a/libcxx/src/stacktrace/osx/osx.cpp b/libcxx/src/stacktrace/osx.cpp
similarity index 92%
rename from libcxx/src/stacktrace/osx/osx.cpp
rename to libcxx/src/stacktrace/osx.cpp
index f377c0bded38a..39e4d34b3e1c3 100644
--- a/libcxx/src/stacktrace/osx/osx.cpp
+++ b/libcxx/src/stacktrace/osx.cpp
@@ -6,20 +6,23 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "../common/config.h"
+#include "stacktrace/config.h"
 
 #if defined(_LIBCPP_STACKTRACE_APPLE)
 
+#  include "stacktrace/osx.h"
+
 #  include <algorithm>
 #  include <array>
 #  include <dlfcn.h>
 #  include <mach-o/dyld.h>
 #  include <mach-o/loader.h>
 
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-
-#  include "osx.h"
+#  include "stacktrace/config.h"
+#  include "stacktrace/context.h"
+#  include "stacktrace/utils.h"
+#  include <__stacktrace/basic_stacktrace.h>
+#  include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/stacktrace/osx/osx.h b/libcxx/src/stacktrace/osx.h
similarity index 100%
rename from libcxx/src/stacktrace/osx/osx.h
rename to libcxx/src/stacktrace/osx.h
diff --git a/libcxx/src/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
index 98152c0c323f6..e3e2a0e1d73c2 100644
--- a/libcxx/src/stacktrace/stacktrace.cpp
+++ b/libcxx/src/stacktrace/stacktrace.cpp
@@ -10,26 +10,63 @@
 #include <__config_site>
 
 #include <iomanip>
+#include <ios>
 #include <iostream>
 #include <sstream>
+#include <stacktrace>
 #include <string>
 
-#include <__stacktrace/basic_stacktrace.h>
-#include <__stacktrace/stacktrace_entry.h>
+#include "stacktrace/config.h"
+#include "stacktrace/context.h"
+#include "stacktrace/utils.h"
 
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-#include <__stacktrace/to_string.h>
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+#  include "stacktrace/linux.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+#  include "stacktrace/osx.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+#  include "stacktrace/tools.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+#  include "stacktrace/unwind.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+#  include "stacktrace/windows.h"
+#endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+
+// `to_string`-related non-member functions
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
+  return __stacktrace::__to_string()(__entry);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
+  __stacktrace::__to_string()(__os, __entry);
+  return __os;
+}
+
 namespace __stacktrace {
 
+/* Anchors for classes w/ vtables */
+// alloc::~alloc()                        = default;
+debug::~debug()                        = default;
+debug::dummy_ostream::~dummy_ostream() = default;
+fd_streambuf::~fd_streambuf()          = default;
+fd_istream::~fd_istream()              = default;
+
 /*
  * `to_string` Helpers
  */
 
-void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
+_LIBCPP_EXPORTED_FROM_ABI void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
   // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
   constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
 
@@ -45,7 +82,8 @@ void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry)
   }
 }
 
-void __to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
+_LIBCPP_EXPORTED_FROM_ABI void
+__to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
   /*
    * Print each entry as a line, as per `operator()`, with additional whitespace
    * at the start of the line, and only a newline added at the end:
@@ -66,29 +104,110 @@ void __to_string::operator()(ostream& __os, std::stacktrace_entry const* __entri
   }
 }
 
-string __to_string::operator()(std::stacktrace_entry const& entry) {
+_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const& entry) {
   stringstream __ss;
   (*this)(__ss, entry);
   return __ss.str();
 }
 
-string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
+_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
   stringstream __ss;
   (*this)(__ss, __entries, __count);
   return __ss.str();
 }
 
-} // namespace __stacktrace
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void
+__impl(size_t skip,
+       size_t max_depth,
+       alloc& alloc,
+       std::function<void(size_t)> resize_func,
+       std::function<void(size_t, std::stacktrace_entry&&)> assign_func) {
+  context cx{alloc};
+  cx.do_stacktrace(1 + skip, max_depth);
+  resize_func(cx.__entries_.size());
+  size_t i = 0;
+  for (auto& entry : cx.__entries_) {
+    assign_func(i++, entry);
+  }
+}
 
-// `to_string`-related non-member functions
+entry::operator std::stacktrace_entry() {
+  std::stacktrace_entry __ret;
+  __ret.__addr_ = __addr_;
+  __ret.__desc_ = __desc_;
+  __ret.__file_ = __file_;
+  __ret.__line_ = __line_;
+  return __ret;
+}
 
-_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
-  return __stacktrace::__to_string()(__entry);
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip, size_t max_depth) {
+  /*
+  Here we declare stacktrace components or "backends" which will handle the different tasks:
+
+  (1) get the addresses from the call stack
+  (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
+  (3) resolve using debug info, and possibly with an external tool on the $PATH
+  (4+) extra passes to get symbols, in case 3 couldn't
+
+  Based on the macros produced by `stacktrace.h`, throw all backends we have available at the task.  Ideally the #ifdef
+  gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
+  symbolizer.  If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
+
+  All these classes do their best to provide any of the requested fields they can: (symbol, filename, source line),
+  substituting if needed with something reasonable.  For example, if the source filename and line are not available
+  then we will at least report that the address and symbol are in the module `foo.exe`.
+
+  These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
+  already-populated data.  We take care not to crash / abort / throw in any of these, and we'll silently fail.  See
+  `common/debug.h` for a debugging logger you can enable at runtime.
+  */
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+  win_impl dbghelp{*this};
+  auto& collector  = dbghelp;
+  auto& mod_ident  = dbghelp;
+  auto& resolver   = dbghelp;
+  auto& symbolizer = dbghelp;
+#endif
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+  unwind unwind{*this};
+  auto& collector = unwind;
+#endif
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+  osx osx{*this};
+  auto& mod_ident  = osx;
+  auto& symbolizer = osx;
+#endif
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+  linux linux{*this};
+  auto& mod_ident  = linux;
+  auto& symbolizer = linux;
+#endif
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+  spawner pspawn{*this};
+  auto& resolver = pspawn;
+#endif
+
+  collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
+  if (__entries_.size()) {                // (Can't proceed if empty)
+    mod_ident.ident_modules();            // Associate addrs with binaries (ELF/MachO/etc.)
+    resolver.resolve_lines();             // Resolve addresses to symbols, filename, linenumber
+    symbolizer.symbolize();               // Populate missing symbols, if any.
+  }
 }
 
-_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
-  __stacktrace::__to_string()(__os, __entry);
-  return __os;
+int fd_streambuf::underflow() {
+  int bytesRead = ::read(fd_, buf_, size_);
+  if (bytesRead < 0) {
+    throw failed("I/O error reading from child process", errno);
+  }
+  if (bytesRead == 0) {
+    return traits_type::eof();
+  }
+  setg(buf_, buf_, buf_ + bytesRead);
+  return int(*buf_);
 }
 
+} // namespace __stacktrace
+
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools.cpp b/libcxx/src/stacktrace/tools.cpp
new file mode 100644
index 0000000000000..05f14d2f08575
--- /dev/null
+++ b/libcxx/src/stacktrace/tools.cpp
@@ -0,0 +1,437 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "stacktrace/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+
+#  include <__config>
+#  include <__config_site>
+#  include <cassert>
+#  include <cerrno>
+#  include <csignal>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <spawn.h>
+#  include <sys/fcntl.h>
+#  include <sys/types.h>
+#  include <sys/wait.h>
+#  include <unistd.h>
+
+#  include "stacktrace/context.h"
+#  include "stacktrace/tools.h"
+#  include "stacktrace/utils.h"
+#  include <__stacktrace/basic_stacktrace.h>
+#  include <__stacktrace/stacktrace_entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/**
+Returns a list of `tool` objects that can be tried during the first stacktrace operation.
+The result is an array of `tool*` pointers with a final `nullptr` terminator.
+
+The returned tool array pointer is saved during `resolve_lines`'s static init,
+with the first working tool being used from that point forward.
+
+This will first add any tools specified by these defines (in order):
+- LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH
+- LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH
+- LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH
+
+Then any tools specified by the environment variables of the same names (and added in the same order).
+
+Finally, this adds the tools `llvm-symbolizer`, `addr2line`, and `atos`, in that order.
+These tools won't have absolute paths, so $PATH will be searched for these.
+
+Called by `findWorkingTool` during its static initialization, therefore this is used in a threadsafe way.
+*/
+tool const** toolList() {
+  constexpr static size_t kMax = 20;
+  static tool const* array_[kMax];
+  size_t count = 0;
+
+  auto add = [&](tool* t) {
+    assert(count < kMax - 1);
+    array_[count++] = t;
+  };
+
+#  define STRINGIFY0(x) #x
+#  define STRINGIFY(x) STRINGIFY0(x)
+
+#  if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
+  {
+    static llvm_symbolizer t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)};
+    add(&t);
+  }
+#  endif
+#  if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
+  {
+    static addr2line t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)};
+    add(&t);
+  }
+#  endif
+#  if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
+  {
+    static atos t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)};
+    add(&t);
+  }
+#  endif
+
+  if (getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")) {
+    static llvm_symbolizer t{getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")};
+    add(&t);
+  }
+  if (getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")) {
+    static addr2line t{getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")};
+    add(&t);
+  }
+  if (getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")) {
+    static atos t{getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")};
+    add(&t);
+  }
+
+  {
+    static llvm_symbolizer t;
+    add(&t);
+  }
+  {
+    static addr2line t;
+    add(&t);
+  }
+  {
+    static atos t;
+    add(&t);
+  }
+
+  array_[count] = nullptr; // terminator
+  return array_;
+}
+
+// Try a handful of different addr2line-esque tools, returning the first discovered, or else nullptr
+tool const* findWorkingTool(auto& trace) {
+  // Try each of these programs and stop at the first one that works.
+  // There's no synchronization so this is subject to races on setting `prog`,
+  // but as long as one thread ends up setting it to something that works, we're good.
+  // Programs to try ($PATH is searched):
+  auto* it = toolList();
+  tool const* prog;
+  while ((prog = *it++)) {
+    pspawn test{*prog}; // Just try to run with "--help"
+    try {
+      std::pmr::list<std::pmr::string> testArgs{&trace.__alloc_};
+      testArgs.push_back(prog->progName_);
+      testArgs.push_back("--help");
+      test.fa_.redirectInNull();
+      test.fa_.redirectOutNull();
+      test.fa_.redirectErrNull();
+      test.spawn(testArgs);
+      if (test.wait() == 0) {
+        // Success
+        return prog;
+      }
+    } catch (failed const&) {
+      /* ignore during probe attempt */
+    }
+  }
+  return nullptr;
+}
+
+} // namespace
+
+void spawner::resolve_lines() {
+  // The address-to-line tool that worked (after lazy-setting, below)
+  static tool const* prog{nullptr};
+  // Whether we should not attempt because tool detection previously failed.
+  static bool fail{false};
+
+  // If this previously failed, don't try again.
+  if (fail) {
+    return;
+  }
+
+  if (!prog) {
+    prog = findWorkingTool(cx_);
+    if (!prog) {
+      fail = true;
+      return;
+    }
+  }
+  assert(prog);
+
+  char buf[256];
+  pspawn_tool proc(*prog, cx_, buf, sizeof(buf));
+  try {
+    proc.run();
+  } catch (failed const& failed) {
+    debug() << failed.what();
+    if (failed.errno_) {
+      debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
+    }
+    debug() << '\n';
+  }
+}
+
+// TODO(stacktrace23): possible to link against `libLLVMSymbolize.a`, or some shared obj at runtime (does that exist?)
+
+std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
+  auto& alloc = cx.__alloc_;
+  auto ret    = alloc.new_string_list();
+  ret.push_back(progName_);
+  ret.push_back("--demangle");
+  ret.push_back("--no-inlines");
+  ret.push_back("--verbose");
+  ret.push_back("--relativenames");
+  ret.push_back("--functions=short");
+  for (auto& entry : cx.__entries_) {
+    auto addr_string = alloc.hex_string(entry.__addr_unslid_);
+    debug() << "@@@ " << addr_string << " " << entry.__file_ << " " << entry.__file_.empty() << '\n';
+    if (!entry.__file_.empty()) {
+      auto arg = alloc.new_string(entry.__file_.size() + 40);
+      arg += "FILE:";
+      arg += entry.__file_;
+      arg += " ";
+      arg += addr_string;
+      ret.push_back(arg);
+    } else {
+      ret.push_back(addr_string);
+    }
+  }
+  return ret;
+}
+
+void llvm_symbolizer::parseOutput(context& cx, entry& entry, std::istream& output) const {
+  // clang-format off
+/*
+With "--verbose", parsing is a little easier, or at least, more reliable;
+probably the best solution (until we have a JSON parser).
+Example output, verbatim, between the '---' lines:
+---
+test1<test_alloc<std::__1::stacktrace_entry> >
+  Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+  Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+  Function start line: 114
+  Function start address: 0x8dd0
+  Line: 116
+  Column: 14
+
+---
+Note that this includes an extra empty line as a terminator.
+*/
+  // clang-format on
+
+  auto& alloc = cx.__alloc_;
+  auto line   = alloc.new_string(256);
+  std::string_view tmp;
+  while (true) {
+    std::getline(output, line);
+    while (isspace(line.back())) {
+      line.pop_back();
+    }
+    if (line.empty()) {
+      return;
+    }
+    if (!line.starts_with("  ")) {
+      // The symbol has no leading whitespace, while the other
+      // lines with "fields" like line, column, filename, etc.
+      // start with two spaces.
+      if (line != "??") {
+        entry.__desc_ = line;
+      }
+    } else if (line.starts_with("  Filename:")) {
+      tmp = line;
+      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+      if (tmp != "??") {
+        entry.__file_ = tmp;
+      }
+    } else if (line.starts_with("  Line:")) {
+      tmp = line;
+      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+      if (tmp != "??" && tmp != "0") {
+        uint32_t lineno = 0;
+        auto pos        = 0;
+        while (isdigit(tmp[pos])) {
+          lineno = lineno * 10 + (tmp[pos++] - '0');
+        }
+        entry.__line_ = lineno;
+      }
+    }
+  }
+}
+
+std::pmr::list<std::pmr::string> addr2line::buildArgs(context& cx) const {
+  auto& alloc = cx.__alloc_;
+  auto ret    = alloc.new_string_list();
+  if (cx.__main_prog_path_.empty()) {
+    // Should not have reached here but be graceful anyway
+    ret.push_back("/bin/false");
+    return ret;
+  }
+
+  ret.push_back(progName_);
+  ret.push_back("--functions");
+  ret.push_back("--demangle");
+  ret.push_back("--basenames");
+  ret.push_back("--pretty-print"); // This "human-readable form" is easier to parse
+  ret.push_back("-e");
+  ret.push_back(cx.__main_prog_path_);
+  for (auto& entry : cx.__entries_) {
+    ret.push_back(alloc.hex_string(entry.__addr_unslid_));
+  }
+  return ret;
+}
+
+void addr2line::parseOutput(context& trace, entry& entry, std::istream& output) const {
+  // clang-format off
+/*
+Example:
+--
+llvm-addr2line -e foo --functions --demangle --basenames --pretty-print --no-inlines 0x11a0 0x1120 0x3d58 0x1284
+
+Output: (1 line per input address)
+--
+main at foo.cc:15
+register_tm_clones at crtstuff.c:0
+GCC_except_table2 at foo.cc:0
+test::Foo::Foo(int) at foo.cc:11
+*/
+  // clang-format on
+
+  std::pmr::string line{&trace.__alloc_};
+  std::getline(output, line);
+  while (isspace(line.back())) {
+    line.pop_back();
+  }
+  if (line.empty()) {
+    return;
+  }
+  // Split at the sequence " at ".  Barring weird symbols
+  // having " at " in them, this should work.
+  auto sepIndex = line.find(" at ");
+  if (sepIndex == std::string::npos) {
+    return;
+  }
+  if (sepIndex > 0) {
+    entry.__desc_ = line.substr(0, sepIndex);
+  }
+  auto fileBegin = sepIndex + 4;
+  if (fileBegin >= line.size()) {
+    return;
+  }
+  auto fileline = line.substr(fileBegin);
+  auto colon    = fileline.find_last_of(":");
+  if (colon > 0 && !fileline.starts_with("?")) {
+    entry.__file_ = fileline.substr(0, colon);
+  }
+
+  if (colon == std::string::npos) {
+    return;
+  }
+  uint32_t lineno = 0;
+  auto pos        = colon;
+  while (isdigit(fileline[++pos])) {
+    lineno = lineno * 10 + (fileline[pos] - '0');
+  }
+  entry.__line_ = lineno;
+}
+
+std::pmr::list<std::pmr::string> atos::buildArgs(context& cx) const {
+  auto& alloc = cx.__alloc_;
+  auto ret    = alloc.new_string_list();
+  ret.push_back(progName_);
+  ret.push_back("-p");
+  ret.push_back(alloc.u64_string(getpid()));
+  // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
+  // ret.push_back("--fullPath");
+  for (auto& entry : cx.__entries_) {
+    ret.push_back(alloc.hex_string(entry.__addr_));
+  }
+  return ret;
+}
+
+void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
+  // Simple example:
+  //
+  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+  //
+  // Assuming this is always atos's format (except when it returns empty lines)
+  // we can split the string like so:
+  //
+  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+  //   ^^^^-----^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^-
+  //   sym      module     filename                            line
+  //
+  // Note that very strange filenames or module names can confuse this.
+  // We'll do the best we can for a decent result, while definitely ensuring safety
+  // (i.e. careful with our bound-checking).
+  //
+  // Another more interesting example (with an added newline for legibility):
+  //
+  //   std::__1::basic_ios<char, std::__1::char_traits<char>>::fill[abi:ne190107]() const (in testprog)
+  //   (/opt/homebrew/Cellar/llvm/19.1.7_1/bin/../include/c++/v1/ios:0
+  //
+  // If this more or less fits our expected format we'll take these data,
+  // even if the line number is 0.
+
+  auto line = cx.__alloc_.new_string(256);
+  std::getline(output, line);
+  while (isspace(line.back())) {
+    line.pop_back();
+  }
+  if (line.empty()) {
+    return;
+  }
+  auto buf  = line.data();
+  auto size = line.size();
+
+  auto* end    = buf + size;
+  auto* symEnd = strstr(buf, " (in ");
+  if (!symEnd) {
+    return;
+  }
+  auto* modBegin = symEnd + 5;
+  auto* modEnd   = strstr(modBegin, ") (");
+  if (!modEnd) {
+    return;
+  }
+  auto* fileBegin = modEnd + 3; // filename starts just after that
+  if (fileBegin >= end) {
+    return;
+  }
+  auto const* lastColon = fileBegin; // we'll search for last colon after filename
+  char const* nextColon;
+  while ((nextColon = strstr(lastColon + 1, ":"))) { // skip colons in filename (e.g. in "C:\foo.cpp")
+    lastColon = nextColon;
+  }
+
+  std::string_view sym{buf, size_t(symEnd - buf)};
+  // In case a previous step could not obtain the symbol name,
+  // we have the name provided by atos; only use that if we have no symbol
+  // (no need to copy more strings otherwise).
+  if (entry.__desc_.empty() && !sym.empty()) {
+    entry.__desc_ = sym;
+  }
+
+  std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
+  if (file != "?" && file != "??" && !file.empty()) {
+    entry.__file_ = file;
+  }
+
+  unsigned lineno = 0;
+  for (auto* digit = lastColon + 1; digit < end && isdigit(*digit); ++digit) {
+    lineno = (lineno * 10) + unsigned(*digit - '0');
+  }
+  entry.__line_ = lineno;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/stacktrace/tools/pspawn.h b/libcxx/src/stacktrace/tools.h
similarity index 60%
rename from libcxx/src/stacktrace/tools/pspawn.h
rename to libcxx/src/stacktrace/tools.h
index 4e2fa381083ad..b3e6e7ec22c30 100644
--- a/libcxx/src/stacktrace/tools/pspawn.h
+++ b/libcxx/src/stacktrace/tools.h
@@ -6,40 +6,70 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_STACKTRACE_PSPAWN_H
-#define _LIBCPP_STACKTRACE_PSPAWN_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-
-#  include <__config>
-#  include <__config_site>
-#  include <cassert>
-#  include <cerrno>
-#  include <csignal>
-#  include <cstddef>
-#  include <cstdlib>
-#  include <list>
-#  include <spawn.h>
-#  include <string>
-#  include <sys/fcntl.h>
-#  include <sys/types.h>
-#  include <sys/wait.h>
-#  include <unistd.h>
-#  include <vector>
-
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-
-#  include "../common/debug.h"
-#  include "../common/failed.h"
-#  include "../common/fd.h"
-#  include "tools.h"
+#ifndef _LIBCPP_STACKTRACE_TOOLS_H
+#define _LIBCPP_STACKTRACE_TOOLS_H
+
+#include <__config>
+#include <__config_site>
+#include <cassert>
+#include <cerrno>
+#include <csignal>
+#include <cstddef>
+#include <cstdlib>
+#include <list>
+#include <spawn.h>
+#include <string>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <vector>
+
+#include "stacktrace/context.h"
+#include "stacktrace/utils.h"
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
+struct tool {
+  char const* progName_;
+  explicit tool(char const* progName) : progName_(progName) {}
+  constexpr virtual ~tool() = default;
+
+  /** Construct complete `argv` for the spawned process.
+  Includes the program name at argv[0], followed by flags */
+  virtual std::pmr::list<std::pmr::string> buildArgs(context& trace) const = 0;
+
+  /** Parse line(s) output by the tool, and modify `entry`. */
+  virtual void parseOutput(context& trace, entry& e, std::istream& output) const = 0;
+};
+
+struct llvm_symbolizer : tool {
+  constexpr virtual ~llvm_symbolizer() = default;
+  llvm_symbolizer() : llvm_symbolizer("llvm-symbolizer") {}
+  explicit llvm_symbolizer(char const* progName) : tool{progName} {}
+  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
+struct addr2line : tool {
+  constexpr virtual ~addr2line() = default;
+  addr2line() : addr2line("addr2line") {}
+  explicit addr2line(char const* progName) : tool{progName} {}
+  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+  void parseOutput(context& trace, entry& e, std::istream& stream) const override;
+};
+
+struct atos : tool {
+  constexpr virtual ~atos() = default;
+  atos() : atos("atos") {}
+  explicit atos(char const* progName) : tool{progName} {}
+  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
 struct file_actions {
   posix_spawn_file_actions_t fa_;
 
@@ -158,6 +188,4 @@ struct spawner {
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif
-
-#endif // _LIBCPP_STACKTRACE_PSPAWN_H
+#endif // _LIBCPP_STACKTRACE_TOOLS_H
diff --git a/libcxx/src/stacktrace/tools/addr2line.cpp b/libcxx/src/stacktrace/tools/addr2line.cpp
deleted file mode 100644
index c1d36e83f13a6..0000000000000
--- a/libcxx/src/stacktrace/tools/addr2line.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <iostream>
-
-#include "tools.h"
-
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-#include <string>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-std::pmr::list<std::pmr::string> addr2line::buildArgs(context& cx) const {
-  auto& alloc = cx.__alloc_;
-  auto ret    = alloc.new_string_list();
-  if (cx.__main_prog_path_.empty()) {
-    // Should not have reached here but be graceful anyway
-    ret.push_back("/bin/false");
-    return ret;
-  }
-
-  ret.push_back(progName_);
-  ret.push_back("--functions");
-  ret.push_back("--demangle");
-  ret.push_back("--basenames");
-  ret.push_back("--pretty-print"); // This "human-readable form" is easier to parse
-  ret.push_back("-e");
-  ret.push_back(cx.__main_prog_path_);
-  for (auto& entry : cx.__entries_) {
-    ret.push_back(alloc.hex_string(entry.__addr_unslid_));
-  }
-  return ret;
-}
-
-void addr2line::parseOutput(context& trace, entry& entry, std::istream& output) const {
-  // clang-format off
-/*
-Example:
---
-llvm-addr2line -e foo --functions --demangle --basenames --pretty-print --no-inlines 0x11a0 0x1120 0x3d58 0x1284
-
-Output: (1 line per input address)
---
-main at foo.cc:15
-register_tm_clones at crtstuff.c:0
-GCC_except_table2 at foo.cc:0
-test::Foo::Foo(int) at foo.cc:11
-*/
-  // clang-format on
-
-  std::pmr::string line{&trace.__alloc_};
-  std::getline(output, line);
-  while (isspace(line.back())) {
-    line.pop_back();
-  }
-  if (line.empty()) {
-    return;
-  }
-  // Split at the sequence " at ".  Barring weird symbols
-  // having " at " in them, this should work.
-  auto sepIndex = line.find(" at ");
-  if (sepIndex == std::string::npos) {
-    return;
-  }
-  if (sepIndex > 0) {
-    entry.__desc_ = line.substr(0, sepIndex);
-  }
-  auto fileBegin = sepIndex + 4;
-  if (fileBegin >= line.size()) {
-    return;
-  }
-  auto fileline = line.substr(fileBegin);
-  auto colon    = fileline.find_last_of(":");
-  if (colon > 0 && !fileline.starts_with("?")) {
-    entry.__file_ = fileline.substr(0, colon);
-  }
-
-  if (colon == std::string::npos) {
-    return;
-  }
-  uint32_t lineno = 0;
-  auto pos        = colon;
-  while (isdigit(fileline[++pos])) {
-    lineno = lineno * 10 + (fileline[pos] - '0');
-  }
-  entry.__line_ = lineno;
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/atos.cpp b/libcxx/src/stacktrace/tools/atos.cpp
deleted file mode 100644
index f6e6458aaae0d..0000000000000
--- a/libcxx/src/stacktrace/tools/atos.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <istream>
-#include <string>
-#include <unistd.h>
-
-#include "tools.h"
-
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-std::pmr::list<std::pmr::string> atos::buildArgs(context& cx) const {
-  auto& alloc = cx.__alloc_;
-  auto ret    = alloc.new_string_list();
-  ret.push_back(progName_);
-  ret.push_back("-p");
-  ret.push_back(alloc.u64_string(getpid()));
-  // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
-  // ret.push_back("--fullPath");
-  for (auto& entry : cx.__entries_) {
-    ret.push_back(alloc.hex_string(entry.__addr_));
-  }
-  return ret;
-}
-
-void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
-  // Simple example:
-  //
-  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
-  //
-  // Assuming this is always atos's format (except when it returns empty lines)
-  // we can split the string like so:
-  //
-  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
-  //   ^^^^-----^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^-
-  //   sym      module     filename                            line
-  //
-  // Note that very strange filenames or module names can confuse this.
-  // We'll do the best we can for a decent result, while definitely ensuring safety
-  // (i.e. careful with our bound-checking).
-  //
-  // Another more interesting example (with an added newline for legibility):
-  //
-  //   std::__1::basic_ios<char, std::__1::char_traits<char>>::fill[abi:ne190107]() const (in testprog)
-  //   (/opt/homebrew/Cellar/llvm/19.1.7_1/bin/../include/c++/v1/ios:0
-  //
-  // If this more or less fits our expected format we'll take these data,
-  // even if the line number is 0.
-
-  auto line = cx.__alloc_.new_string(256);
-  std::getline(output, line);
-  while (isspace(line.back())) {
-    line.pop_back();
-  }
-  if (line.empty()) {
-    return;
-  }
-  auto buf  = line.data();
-  auto size = line.size();
-
-  auto* end    = buf + size;
-  auto* symEnd = strstr(buf, " (in ");
-  if (!symEnd) {
-    return;
-  }
-  auto* modBegin = symEnd + 5;
-  auto* modEnd   = strstr(modBegin, ") (");
-  if (!modEnd) {
-    return;
-  }
-  auto* fileBegin = modEnd + 3; // filename starts just after that
-  if (fileBegin >= end) {
-    return;
-  }
-  auto const* lastColon = fileBegin; // we'll search for last colon after filename
-  char const* nextColon;
-  while ((nextColon = strstr(lastColon + 1, ":"))) { // skip colons in filename (e.g. in "C:\foo.cpp")
-    lastColon = nextColon;
-  }
-
-  std::string_view sym{buf, size_t(symEnd - buf)};
-  // In case a previous step could not obtain the symbol name,
-  // we have the name provided by atos; only use that if we have no symbol
-  // (no need to copy more strings otherwise).
-  if (entry.__desc_.empty() && !sym.empty()) {
-    entry.__desc_ = sym;
-  }
-
-  std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
-  if (file != "?" && file != "??" && !file.empty()) {
-    entry.__file_ = file;
-  }
-
-  unsigned lineno = 0;
-  for (auto* digit = lastColon + 1; digit < end && isdigit(*digit); ++digit) {
-    lineno = (lineno * 10) + unsigned(*digit - '0');
-  }
-  entry.__line_ = lineno;
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp b/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
deleted file mode 100644
index 18b493c97a040..0000000000000
--- a/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <iostream>
-#include <string_view>
-
-#include "../common/debug.h"
-#include "tools.h"
-
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// TODO(stacktrace23): possible to link against `libLLVMSymbolize.a`, or some shared obj at runtime (does that exist?)
-
-std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
-  auto& alloc = cx.__alloc_;
-  auto ret    = alloc.new_string_list();
-  ret.push_back(progName_);
-  ret.push_back("--demangle");
-  ret.push_back("--no-inlines");
-  ret.push_back("--verbose");
-  ret.push_back("--relativenames");
-  ret.push_back("--functions=short");
-  for (auto& entry : cx.__entries_) {
-    auto addr_string = alloc.hex_string(entry.__addr_unslid_);
-    debug() << "@@@ " << addr_string << " " << entry.__file_ << " " << entry.__file_.empty() << '\n';
-    if (!entry.__file_.empty()) {
-      auto arg = alloc.new_string(entry.__file_.size() + 40);
-      arg += "FILE:";
-      arg += entry.__file_;
-      arg += " ";
-      arg += addr_string;
-      ret.push_back(arg);
-    } else {
-      ret.push_back(addr_string);
-    }
-  }
-  return ret;
-}
-
-void llvm_symbolizer::parseOutput(context& cx, entry& entry, std::istream& output) const {
-  // clang-format off
-/*
-With "--verbose", parsing is a little easier, or at least, more reliable;
-probably the best solution (until we have a JSON parser).
-Example output, verbatim, between the '---' lines:
----
-test1<test_alloc<std::__1::stacktrace_entry> >
-  Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
-  Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
-  Function start line: 114
-  Function start address: 0x8dd0
-  Line: 116
-  Column: 14
-
----
-Note that this includes an extra empty line as a terminator.
-*/
-  // clang-format on
-
-  auto& alloc = cx.__alloc_;
-  auto line   = alloc.new_string(256);
-  std::string_view tmp;
-  while (true) {
-    std::getline(output, line);
-    while (isspace(line.back())) {
-      line.pop_back();
-    }
-    if (line.empty()) {
-      return;
-    }
-    if (!line.starts_with("  ")) {
-      // The symbol has no leading whitespace, while the other
-      // lines with "fields" like line, column, filename, etc.
-      // start with two spaces.
-      if (line != "??") {
-        entry.__desc_ = line;
-      }
-    } else if (line.starts_with("  Filename:")) {
-      tmp = line;
-      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
-      if (tmp != "??") {
-        entry.__file_ = tmp;
-      }
-    } else if (line.starts_with("  Line:")) {
-      tmp = line;
-      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
-      if (tmp != "??" && tmp != "0") {
-        uint32_t lineno = 0;
-        auto pos        = 0;
-        while (isdigit(tmp[pos])) {
-          lineno = lineno * 10 + (tmp[pos++] - '0');
-        }
-        entry.__line_ = lineno;
-      }
-    }
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/tools.h b/libcxx/src/stacktrace/tools/tools.h
deleted file mode 100644
index 70374cf075ebf..0000000000000
--- a/libcxx/src/stacktrace/tools/tools.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_TOOLS_H
-#define _LIBCPP_STACKTRACE_TOOLS_H
-
-#include <__config>
-#include <__config_site>
-#include <cassert>
-#include <cstddef>
-#include <list>
-#include <string>
-
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct tool {
-  char const* progName_;
-  explicit tool(char const* progName) : progName_(progName) {}
-  constexpr virtual ~tool() = default;
-
-  /** Construct complete `argv` for the spawned process.
-  Includes the program name at argv[0], followed by flags */
-  virtual std::pmr::list<std::pmr::string> buildArgs(context& trace) const = 0;
-
-  /** Parse line(s) output by the tool, and modify `entry`. */
-  virtual void parseOutput(context& trace, entry& e, std::istream& output) const = 0;
-};
-
-struct llvm_symbolizer : tool {
-  constexpr virtual ~llvm_symbolizer() = default;
-  llvm_symbolizer() : llvm_symbolizer("llvm-symbolizer") {}
-  explicit llvm_symbolizer(char const* progName) : tool{progName} {}
-  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
-  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
-};
-
-struct addr2line : tool {
-  constexpr virtual ~addr2line() = default;
-  addr2line() : addr2line("addr2line") {}
-  explicit addr2line(char const* progName) : tool{progName} {}
-  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
-  void parseOutput(context& trace, entry& e, std::istream& stream) const override;
-};
-
-struct atos : tool {
-  constexpr virtual ~atos() = default;
-  atos() : atos("atos") {}
-  explicit atos(char const* progName) : tool{progName} {}
-  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
-  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_TOOLS_H
diff --git a/libcxx/src/stacktrace/tools/toolspawner.cpp b/libcxx/src/stacktrace/tools/toolspawner.cpp
deleted file mode 100644
index 54ddaf156fc64..0000000000000
--- a/libcxx/src/stacktrace/tools/toolspawner.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-
-#  include <__config>
-#  include <__config_site>
-#  include <cassert>
-#  include <cerrno>
-#  include <csignal>
-#  include <cstddef>
-#  include <cstdlib>
-#  include <spawn.h>
-#  include <string>
-#  include <sys/fcntl.h>
-#  include <sys/types.h>
-#  include <sys/wait.h>
-#  include <unistd.h>
-
-#  include "../common/failed.h"
-#  include "pspawn.h"
-
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-namespace {
-
-/**
-Returns a list of `tool` objects that can be tried during the first stacktrace operation.
-The result is an array of `tool*` pointers with a final `nullptr` terminator.
-
-The returned tool array pointer is saved during `resolve_lines`'s static init,
-with the first working tool being used from that point forward.
-
-This will first add any tools specified by these defines (in order):
-- LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH
-- LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH
-- LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH
-
-Then any tools specified by the environment variables of the same names (and added in the same order).
-
-Finally, this adds the tools `llvm-symbolizer`, `addr2line`, and `atos`, in that order.
-These tools won't have absolute paths, so $PATH will be searched for these.
-
-Called by `findWorkingTool` during its static initialization, therefore this is used in a threadsafe way.
-*/
-tool const** toolList() {
-  constexpr static size_t kMax = 20;
-  static tool const* array_[kMax];
-  size_t count = 0;
-
-  auto add = [&](tool* t) {
-    assert(count < kMax - 1);
-    array_[count++] = t;
-  };
-
-#  define STRINGIFY0(x) #x
-#  define STRINGIFY(x) STRINGIFY0(x)
-
-#  if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
-  {
-    static llvm_symbolizer t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)};
-    add(&t);
-  }
-#  endif
-#  if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
-  {
-    static addr2line t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)};
-    add(&t);
-  }
-#  endif
-#  if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
-  {
-    static atos t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)};
-    add(&t);
-  }
-#  endif
-
-  if (getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")) {
-    static llvm_symbolizer t{getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")};
-    add(&t);
-  }
-  if (getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")) {
-    static addr2line t{getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")};
-    add(&t);
-  }
-  if (getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")) {
-    static atos t{getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")};
-    add(&t);
-  }
-
-  {
-    static llvm_symbolizer t;
-    add(&t);
-  }
-  {
-    static addr2line t;
-    add(&t);
-  }
-  {
-    static atos t;
-    add(&t);
-  }
-
-  array_[count] = nullptr; // terminator
-  return array_;
-}
-
-// Try a handful of different addr2line-esque tools, returning the first discovered, or else nullptr
-tool const* findWorkingTool(auto& trace) {
-  // Try each of these programs and stop at the first one that works.
-  // There's no synchronization so this is subject to races on setting `prog`,
-  // but as long as one thread ends up setting it to something that works, we're good.
-  // Programs to try ($PATH is searched):
-  auto* it = toolList();
-  tool const* prog;
-  while ((prog = *it++)) {
-    pspawn test{*prog}; // Just try to run with "--help"
-    try {
-      std::pmr::list<std::pmr::string> testArgs{&trace.__alloc_};
-      testArgs.push_back(prog->progName_);
-      testArgs.push_back("--help");
-      test.fa_.redirectInNull();
-      test.fa_.redirectOutNull();
-      test.fa_.redirectErrNull();
-      test.spawn(testArgs);
-      if (test.wait() == 0) {
-        // Success
-        return prog;
-      }
-    } catch (failed const&) {
-      /* ignore during probe attempt */
-    }
-  }
-  return nullptr;
-}
-
-} // namespace
-
-void spawner::resolve_lines() {
-  // The address-to-line tool that worked (after lazy-setting, below)
-  static tool const* prog{nullptr};
-  // Whether we should not attempt because tool detection previously failed.
-  static bool fail{false};
-
-  // If this previously failed, don't try again.
-  if (fail) {
-    return;
-  }
-
-  if (!prog) {
-    prog = findWorkingTool(cx_);
-    if (!prog) {
-      fail = true;
-      return;
-    }
-  }
-  assert(prog);
-
-  char buf[256];
-  pspawn_tool proc(*prog, cx_, buf, sizeof(buf));
-  try {
-    proc.run();
-  } catch (failed const& failed) {
-    debug() << failed.what();
-    if (failed.errno_) {
-      debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
-    }
-    debug() << '\n';
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif
diff --git a/libcxx/src/stacktrace/unwind/unwind.cpp b/libcxx/src/stacktrace/unwind.cpp
similarity index 91%
rename from libcxx/src/stacktrace/unwind/unwind.cpp
rename to libcxx/src/stacktrace/unwind.cpp
index 51689c5f1c15d..ad5c1f7a443a9 100644
--- a/libcxx/src/stacktrace/unwind/unwind.cpp
+++ b/libcxx/src/stacktrace/unwind.cpp
@@ -6,16 +6,16 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "../common/config.h"
+#include "stacktrace/config.h"
 
 #if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
 
-#  include "./unwind.h"
-
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
 #  include <unwind.h>
 
+#  include "stacktrace/unwind.h"
+#  include <__stacktrace/basic_stacktrace.h>
+#  include <__stacktrace/stacktrace_entry.h>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
diff --git a/libcxx/src/stacktrace/unwind/unwind.h b/libcxx/src/stacktrace/unwind.h
similarity index 81%
rename from libcxx/src/stacktrace/unwind/unwind.h
rename to libcxx/src/stacktrace/unwind.h
index a258a32f50c5d..98216e61d6bcf 100644
--- a/libcxx/src/stacktrace/unwind/unwind.h
+++ b/libcxx/src/stacktrace/unwind.h
@@ -14,6 +14,12 @@
 #include <cstddef>
 #include <cstdlib>
 
+#include "stacktrace/config.h"
+#include "stacktrace/context.h"
+#include "stacktrace/utils.h"
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
diff --git a/libcxx/src/stacktrace/utils.h b/libcxx/src/stacktrace/utils.h
new file mode 100644
index 0000000000000..ea0e7cd659e63
--- /dev/null
+++ b/libcxx/src/stacktrace/utils.h
@@ -0,0 +1,176 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_DEBUG_H
+#define _LIBCPP_STACKTRACE_DEBUG_H
+
+#include <__config>
+#include <cassert>
+#include <cerrno>
+#include <cstdio>
+#include <iostream>
+#include <stdexcept>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <utility>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct failed : std::runtime_error {
+  virtual ~failed() = default;
+  int errno_{0};
+  failed() : std::runtime_error({}) {}
+  failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
+};
+
+/** Debug-message output stream.  If `LIBCXX_STACKTRACE_DEBUG` is defined in the environment
+or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
+otherwise its does nothing by returning a dummy stream. */
+struct _LIBCPP_HIDE_FROM_ABI debug : std::ostream {
+  _LIBCPP_HIDE_FROM_ABI virtual ~debug();
+
+  _LIBCPP_HIDE_FROM_ABI static bool enabled() {
+#if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
+    return true;
+#else
+    static bool ret = [] {
+      auto const* val = getenv("LIBCXX_STACKTRACE_DEBUG");
+      return val && !strncmp(val, "1", 1);
+    }();
+    return ret;
+#endif
+  }
+
+  /** No-op output stream. */
+  struct _LIBCPP_HIDE_FROM_ABI dummy_ostream final : std::ostream {
+    _LIBCPP_HIDE_FROM_ABI virtual ~dummy_ostream();
+    friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
+  };
+
+  friend std::ostream& operator<<(debug& dp, auto const& val) {
+    static dummy_ostream kdummy;
+    if (!enabled()) {
+      return kdummy;
+    }
+    std::cerr << val;
+    return std::cerr;
+  }
+};
+
+/** Encapsulates a plain old file descriptor `int`.  Avoids copies in order to
+force some component to "own" this, although it's freely convertible back to
+integer form.  Default-constructed, closed, and moved-out-of instances will have
+the invalid fd `-1`. */
+class _LIBCPP_HIDE_FROM_ABI fd {
+  int fd_{-1};
+
+public:
+  fd() : fd(-1) {}
+  fd(int fdint) : fd_(fdint) {}
+
+  fd(fd const&)            = delete;
+  fd& operator=(fd const&) = delete;
+
+  fd(fd&& rhs) {
+    if (&rhs != this) {
+      std::exchange(fd_, rhs.fd_);
+    }
+  }
+
+  fd& operator=(fd&& rhs) { return *new (this) fd(std::move(rhs)); }
+
+  ~fd() { close(); }
+
+  /** Returns true IFF fd is above zero */
+  bool valid() const { return fd_ > 0; }
+
+  /* implicit */ operator int() const {
+    auto ret = fd_;
+    assert(ret > 0);
+    return ret;
+  }
+
+  void close() {
+    int fd_old = -1;
+    std::exchange(fd_old, fd_);
+    if (fd_old != -1) {
+      ::close(fd_old);
+    }
+  }
+
+  static fd& null_fd() {
+    static fd ret = {::open("/dev/null", O_RDWR)};
+    return ret;
+  }
+
+  static fd open(std::string_view path) {
+    fd ret = {::open(path.data(), O_RDONLY)};
+    return ret;
+  }
+};
+
+/** Wraps a readable fd using the `streambuf` interface.  I/O errors arising
+from reading the provided fd will result in a `Failed` being thrown. */
+struct _LIBCPP_HIDE_FROM_ABI fd_streambuf final : std::streambuf {
+  fd& fd_;
+  char* buf_;
+  size_t size_;
+  _LIBCPP_HIDE_FROM_ABI fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
+  _LIBCPP_HIDE_FROM_ABI virtual ~fd_streambuf();
+  _LIBCPP_HIDE_FROM_ABI int underflow() override;
+};
+
+/** Wraps an `FDInStreamBuffer` in an `istream` */
+struct fd_istream final : std::istream {
+  fd_streambuf& buf_;
+  _LIBCPP_HIDE_FROM_ABI virtual ~fd_istream();
+  _LIBCPP_HIDE_FROM_ABI explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
+};
+
+struct fd_mmap final {
+  fd fd_{};
+  size_t size_{0};
+  std::byte const* addr_{nullptr};
+
+  _LIBCPP_HIDE_FROM_ABI explicit fd_mmap(std::string_view path) : fd_mmap(fd::open(path)) {}
+
+  _LIBCPP_HIDE_FROM_ABI explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
+    if (fd_) {
+      if ((size_ = ::lseek(fd, 0, SEEK_END))) {
+        addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
+      }
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI operator bool() const { return addr_; }
+
+  _LIBCPP_HIDE_FROM_ABI ~fd_mmap() {
+    if (addr_) {
+      ::munmap(const_cast<void*>((void const*)addr_), size_);
+    }
+  }
+};
+
+constexpr unsigned k_max_images = 256;
+
+struct _LIBCPP_HIDE_FROM_ABI image {
+  uintptr_t loaded_at_{};
+  intptr_t slide_{};
+  std::string_view name_{};
+  bool is_main_prog_{};
+
+  _LIBCPP_HIDE_FROM_ABI bool operator<(image const& rhs) const { return loaded_at_ < rhs.loaded_at_; }
+  _LIBCPP_HIDE_FROM_ABI operator bool() const { return !name_.empty(); }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_DEBUG_H
diff --git a/libcxx/src/stacktrace/windows/win_impl.cpp b/libcxx/src/stacktrace/windows.cpp
similarity index 74%
rename from libcxx/src/stacktrace/windows/win_impl.cpp
rename to libcxx/src/stacktrace/windows.cpp
index 44d6052706b85..36de17973a231 100644
--- a/libcxx/src/stacktrace/windows/win_impl.cpp
+++ b/libcxx/src/stacktrace/windows.cpp
@@ -6,22 +6,17 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "win_impl.h"
+#include "stacktrace/config.h"
 
 #if defined(_LIBCPP_STACKTRACE_WINDOWS)
 
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include "dll.h"
-#  include <__stacktrace/entry.h>
+#  include "stacktrace/alloc.h"
+#  include "stacktrace/config.h"
+#  include "stacktrace/context.h"
+#  include "stacktrace/utils.h"
+#  include "stacktrace/windows.h"
+#  include <__stacktrace/basic_stacktrace.h>
+#  include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
@@ -198,6 +193,54 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_
   }
 }
 
+dll::~dll() { FreeLibrary(module_); }
+
+dll::dll(char const* name) : name_(name), module_(LoadLibrary(name)) {
+  if (!module_) {
+    debug() << "LoadLibrary failed: " << name_ << ": " << GetLastError() << '\n';
+  }
+}
+
+dbghelp_dll::~dbghelp_dll() = default;
+
+dbghelp_dll& dbghelp_dll::get() {
+  dbghelp_dll ret;
+  return ret;
+}
+
+dbghelp_dll::dbghelp_dll() : dll("dbghelp.dll") {
+  // clang-format off
+if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+  if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+  if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+  if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+  if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+  if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+  if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+  if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+  if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+  if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+  if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+  valid_ = true;
+  // clang-format on
+}
+
+psapi_dll::~psapi_dll() = default;
+
+psapi_dll& psapi_dll::get() {
+  psapi_dll ret;
+  return ret;
+}
+
+psapi_dll() : dll("psapi.dll") {
+  // clang-format off
+if (!getFunc(&EnumProcessModules, "EnumProcessModules")) { return; }
+  if (!getFunc(&GetModuleInformation, "GetModuleInformation")) { return; }
+  if (!getFunc(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+  valid_ = true;
+  // clang-format on
+}
+
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/src/stacktrace/windows/dbghelp_dll.h b/libcxx/src/stacktrace/windows.h
similarity index 55%
rename from libcxx/src/stacktrace/windows/dbghelp_dll.h
rename to libcxx/src/stacktrace/windows.h
index b1204a369a3f1..d31274448c5be 100644
--- a/libcxx/src/stacktrace/windows/dbghelp_dll.h
+++ b/libcxx/src/stacktrace/windows.h
@@ -6,10 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
-#define _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
-
-#include "../common/config.h"
+#ifndef _LIBCPP_STACKTRACE_WIN_IMPL_H
+#define _LIBCPP_STACKTRACE_WIN_IMPL_H
 
 #if defined(_LIBCPP_STACKTRACE_WINDOWS)
 // windows.h must be first
@@ -18,20 +16,65 @@
 #  include <dbghelp.h>
 #  define PSAPI_VERSION 1
 #  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include <__stacktrace/entry.h>
+#endif
 
-#  include "../common/debug.h"
-#  include "dll.h"
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+#include <mutex>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
+struct context;
+
+struct win_impl {
+  context& cx_;
+  std::lock_guard<std::mutex> guard_;
+  win_impl(context& trace);
+  ~win_impl();
+
+  void collect(size_t skip, size_t max_depth);
+  void ident_modules();
+  void symbolize();
+  void resolve_lines();
+};
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
 // clang-format off
 
+struct dll {
+  char const* name_;
+  HMODULE module_;
+  /** Set to true in subclass's ctor if initialized successfully. */
+  bool valid_{false};
+
+  operator bool() const { return valid_; }
+
+  explicit dll(char const* name)
+      : name_(name), module_(LoadLibrary(name)) {
+    if (!module_) {
+      debug() << "LoadLibrary failed: "
+              << name_ << ": " << GetLastError() << '\n';
+    }
+  }
+
+  virtual ~dll() { FreeLibrary(module_); }
+
+  template <typename F>
+  bool get_func(F* func, char const* name) {
+    if (!(*func = (F)GetProcAddress(module_, name))) {
+      debug() << "GetProcAddress failed: "
+              << name << "' (" << name_ << "): "
+              << GetLastError() << "\n";
+      return false;
+    }
+    return true;
+  }
+};
+
 struct dbghelp_dll final : dll {
   virtual ~dbghelp_dll() = default;
   static dbghelp_dll& get() { static dbghelp_dll ret; return ret; }
@@ -64,8 +107,25 @@ struct dbghelp_dll final : dll {
   }
 };
 
+struct psapi_dll final : dll {
+  virtual ~psapi_dll() = default;
+  static psapi_dll& get() { static psapi_dll ret; return ret; }
+
+  bool  (*EnumProcessModules)   (HANDLE, HMODULE*, DWORD, DWORD*);
+  bool  (*GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
+  DWORD (*GetModuleBaseName)    (HANDLE, HMODULE, char**, DWORD);
+
+  psapi_dll() : dll("psapi.dll") {
+    if (!get_func(&EnumProcessModules, "EnumProcessModules")) { return; }
+    if (!get_func(&GetModuleInformation, "GetModuleInformation")) { return; }
+    if (!get_func(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+    valid_ = true;
+  }
+};
+
+#endif
+
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STACKTRACE_WINDOWS
-#endif // _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
+#endif // _LIBCPP_STACKTRACE_WIN_IMPL_H
diff --git a/libcxx/src/stacktrace/windows/dbghelp_dll.cpp b/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
deleted file mode 100644
index 7753ad77d3d26..0000000000000
--- a/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include "dll.h"
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-dbghelp_dll::~dbghelp_dll() = default;
-
-dbghelp_dll& dbghelp_dll::get() {
-  dbghelp_dll ret;
-  return ret;
-}
-
-dbghelp_dll::dbghelp_dll() : dll("dbghelp.dll") {
-  // clang-format off
-if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
-  if (!get_func(&StackWalk64, "StackWalk64")) { return; }
-  if (!get_func(&SymCleanup, "SymCleanup")) { return; }
-  if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
-  if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
-  if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
-  if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
-  if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
-  if (!get_func(&SymInitialize, "SymInitialize")) { return; }
-  if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
-  if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
-  valid_ = true;
-  // clang-format on
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/dll.cpp b/libcxx/src/stacktrace/windows/dll.cpp
deleted file mode 100644
index b0ea2609b55cd..0000000000000
--- a/libcxx/src/stacktrace/windows/dll.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include "dll.h"
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-dll::~dll() { FreeLibrary(module_); }
-
-dll::dll(char const* name) : name_(name), module_(LoadLibrary(name)) {
-  if (!module_) {
-    debug() << "LoadLibrary failed: " << name_ << ": " << GetLastError() << '\n';
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/dll.h b/libcxx/src/stacktrace/windows/dll.h
deleted file mode 100644
index 4ef8e7c5df190..0000000000000
--- a/libcxx/src/stacktrace/windows/dll.h
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_WINDOWS_DLL_H
-#define _LIBCPP_STACKTRACE_WINDOWS_DLL_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include <__stacktrace/entry.h>
-
-#  include "../common/debug.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// clang-format off
-
-struct dll {
-  char const* name_;
-  HMODULE module_;
-  /** Set to true in subclass's ctor if initialized successfully. */
-  bool valid_{false};
-
-  operator bool() const { return valid_; }
-
-  explicit dll(char const* name)
-      : name_(name), module_(LoadLibrary(name)) {
-    if (!module_) {
-      debug() << "LoadLibrary failed: "
-              << name_ << ": " << GetLastError() << '\n';
-    }
-  }
-
-  virtual ~dll() { FreeLibrary(module_); }
-
-  template <typename F>
-  bool get_func(F* func, char const* name) {
-    if (!(*func = (F)GetProcAddress(module_, name))) {
-      debug() << "GetProcAddress failed: "
-              << name << "' (" << name_ << "): "
-              << GetLastError() << "\n";
-      return false;
-    }
-    return true;
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WINDOWS
-#endif // _LIBCPP_STACKTRACE_WINDOWS_DLL_H
diff --git a/libcxx/src/stacktrace/windows/psapi_dll.cpp b/libcxx/src/stacktrace/windows/psapi_dll.cpp
deleted file mode 100644
index d4f7fdc006341..0000000000000
--- a/libcxx/src/stacktrace/windows/psapi_dll.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include "dll.h"
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-psapi_dll::~psapi_dll() = default;
-
-psapi_dll& psapi_dll::get() {
-  psapi_dll ret;
-  return ret;
-}
-
-psapi_dll() : dll("psapi.dll") {
-  // clang-format off
-if (!getFunc(&EnumProcessModules, "EnumProcessModules")) { return; }
-  if (!getFunc(&GetModuleInformation, "GetModuleInformation")) { return; }
-  if (!getFunc(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
-  valid_ = true;
-  // clang-format on
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/psapi_dll.h b/libcxx/src/stacktrace/windows/psapi_dll.h
deleted file mode 100644
index bcad1bfb72d0a..0000000000000
--- a/libcxx/src/stacktrace/windows/psapi_dll.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_WINDOWS_PSAPI_DLL_H
-#define _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include <__stacktrace/entry.h>
-
-#  include "../common/debug.h"
-#  include "dll.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// clang-format off
-
-struct psapi_dll final : dll {
-  virtual ~psapi_dll() = default;
-  static psapi_dll& get() { static psapi_dll ret; return ret; }
-
-  bool  (*EnumProcessModules)   (HANDLE, HMODULE*, DWORD, DWORD*);
-  bool  (*GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
-  DWORD (*GetModuleBaseName)    (HANDLE, HMODULE, char**, DWORD);
-
-  psapi_dll() : dll("psapi.dll") {
-    if (!get_func(&EnumProcessModules, "EnumProcessModules")) { return; }
-    if (!get_func(&GetModuleInformation, "GetModuleInformation")) { return; }
-    if (!get_func(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
-    valid_ = true;
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WINDOWS
-#endif // _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
diff --git a/libcxx/src/stacktrace/windows/win_impl.h b/libcxx/src/stacktrace/windows/win_impl.h
deleted file mode 100644
index c065987120425..0000000000000
--- a/libcxx/src/stacktrace/windows/win_impl.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_WIN_IMPL_H
-#define _LIBCPP_STACKTRACE_WIN_IMPL_H
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <cstdlib>
-#include <mutex>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct context;
-
-struct win_impl {
-  context& cx_;
-  std::lock_guard<std::mutex> guard_;
-  win_impl(context& trace);
-  ~win_impl();
-
-  void collect(size_t skip, size_t max_depth);
-  void ident_modules();
-  void symbolize();
-  void resolve_lines();
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WIN_IMPL_H
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index f824b25349068..ce6b52fbb0462 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -168,38 +168,18 @@ cxx_sources = [
   "ryu/d2s.cpp",
   "ryu/f2s.cpp",
   "shared_mutex.cpp",
-  "stacktrace/alloc.cpp",
-  "stacktrace/common/config.h",
-  "stacktrace/common/debug.cpp",
-  "stacktrace/common/debug.h",
-  "stacktrace/common/failed.h",
-  "stacktrace/common/fd.cpp",
-  "stacktrace/common/fd.h",
-  "stacktrace/common/images.h",
-  "stacktrace/context.cpp",
-  "stacktrace/linux/linux-dl.cpp",
-  "stacktrace/linux/linux-elf.cpp",
-  "stacktrace/linux/linux-sym.cpp",
-  "stacktrace/linux/linux.h",
-  "stacktrace/osx/osx.cpp",
-  "stacktrace/osx/osx.h",
+  "stacktrace/config.h",
+  "stacktrace/linux.cpp",
+  "stacktrace/linux.h",
+  "stacktrace/osx.cpp",
+  "stacktrace/osx.h",
   "stacktrace/stacktrace.cpp",
-  "stacktrace/tools/addr2line.cpp",
-  "stacktrace/tools/atos.cpp",
-  "stacktrace/tools/llvm_symbolizer.cpp",
-  "stacktrace/tools/pspawn.h",
-  "stacktrace/tools/tools.h",
-  "stacktrace/tools/toolspawner.cpp",
-  "stacktrace/unwind/unwind.cpp",
-  "stacktrace/unwind/unwind.h",
-  "stacktrace/windows/dbghelp_dll.cpp",
-  "stacktrace/windows/dbghelp_dll.h",
-  "stacktrace/windows/dll.cpp",
-  "stacktrace/windows/dll.h",
-  "stacktrace/windows/psapi_dll.cpp",
-  "stacktrace/windows/psapi_dll.h",
-  "stacktrace/windows/win_impl.cpp",
-  "stacktrace/windows/win_impl.h",
+  "stacktrace/tools.cpp",
+  "stacktrace/tools.h",
+  "stacktrace/unwind.cpp",
+  "stacktrace/utils.h",
+  "stacktrace/windows.cpp",
+  "stacktrace/windows.h",
   "stdexcept.cpp",
   "string.cpp",
   "strstream.cpp",

>From 89529f5ccd6b02ab082abb8ae2d5e21bcad2dc27 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 15:38:01 -0400
Subject: [PATCH 07/37] Reference papers and issues

---
 libcxx/docs/ReleaseNotes/21.rst    | 2 ++
 libcxx/docs/Status/Cxx23Issues.csv | 2 +-
 libcxx/docs/Status/Cxx23Papers.csv | 6 +++---
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index 4c4227dfef6a2..117abb255ba01 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -47,6 +47,8 @@ Implemented Papers
 - P1222R4: A Standard ``flat_set`` (`Github <https://github.com/llvm/llvm-project/issues/105193>`__)
 - P2897R7: ``aligned_accessor``: An mdspan accessor expressing pointer over-alignment (`Github <https://github.com/llvm/llvm-project/issues/118372>`__)
 - P3247R2: Deprecate the notion of trivial types (`Github <https://github.com/llvm/llvm-project/issues/118387>`__)
+- P0881R7: A Proposal to add stacktrace library (`Github <https://github.com/llvm/llvm-project/issues/105131>`__)
+- P2301R1: Add a `pmr` alias for `std::stacktrace` (`Github <https://github.com/llvm/llvm-project/issues/105167>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index b5c88611559ef..8ecac9389b342 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -189,7 +189,7 @@
 "`LWG3028 <https://wg21.link/LWG3028>`__","Container requirements tables should distinguish ``const`` and non-``const`` variables","2022-11 (Kona)","","",""
 "`LWG3118 <https://wg21.link/LWG3118>`__","``fpos`` equality comparison unspecified","2022-11 (Kona)","","",""
 "`LWG3177 <https://wg21.link/LWG3177>`__","Limit permission to specialize variable templates to program-defined types","2022-11 (Kona)","|Nothing To Do|","",""
-"`LWG3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized","2022-11 (Kona)","","",""
+"`LWG3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized","2022-11 (Kona)","|Nothing To Do|","",""
 "`LWG3545 <https://wg21.link/LWG3545>`__","``std::pointer_traits`` should be SFINAE-friendly","2022-11 (Kona)","|Complete|","18",""
 "`LWG3569 <https://wg21.link/LWG3569>`__","``join_view`` fails to support ranges of ranges with non-default_initializable iterators","2022-11 (Kona)","","",""
 "`LWG3594 <https://wg21.link/LWG3594>`__","``inout_ptr`` — inconsistent ``release()`` in destructor","2022-11 (Kona)","|Complete|","19",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index c26363bcda796..d98b848665cf8 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -1,5 +1,5 @@
 "Paper #","Paper Name","Meeting","Status","First released version","Notes"
-"`P0881R7 <https://wg21.link/P0881R7>`__","A Proposal to add stacktrace library","2020-11 (Virtual)","","",""
+"`P0881R7 <https://wg21.link/P0881R7>`__","A Proposal to add stacktrace library","2020-11 (Virtual)","|Complete|","21",""
 "`P0943R6 <https://wg21.link/P0943R6>`__","Support C atomics in C++","2020-11 (Virtual)","|Complete|","15",""
 "`P1048R1 <https://wg21.link/P1048R1>`__","A proposal for a type trait to detect scoped enumerations","2020-11 (Virtual)","|Complete|","12",""
 "`P1679R3 <https://wg21.link/P1679R3>`__","string contains function","2020-11 (Virtual)","|Complete|","12",""
@@ -32,7 +32,7 @@
 "`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","",""
 "`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","","",""
 "`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14",""
-"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","",""
+"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","|Complete|","21",""
 "`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|In Progress|","",""
 "`P2340R1 <https://wg21.link/P2340R1>`__","Clarifying the status of the 'C headers'","2021-10 (Virtual)","|Nothing To Do|","",""
 "`P2393R1 <https://wg21.link/P2393R1>`__","Cleaning up ``integer``-class types","2021-10 (Virtual)","","",""
@@ -110,7 +110,7 @@
 "`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","2023-02 (Issaquah)","|Complete|","19",""
 "`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","2023-02 (Issaquah)","|Complete|","17",""
 "`P2572R1 <https://wg21.link/P2572R1>`__","``std::format`` fill character allowances","2023-02 (Issaquah)","|Complete|","17",""
-"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","2023-02 (Issaquah)","|Partial|","","The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is not implemented yet"
+"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","2023-02 (Issaquah)","|Partial|","","The formatter for ``stacktrace`` is not implemented yet"
 "`P2679R2 <https://wg21.link/P2679R2>`__","Fixing ``std::start_lifetime_as`` for arrays","2023-02 (Issaquah)","","",""
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","","",""

>From b67ed62789dbc8ac20ae09958892b77bdf05cb9e Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 15:51:02 -0400
Subject: [PATCH 08/37] Updates per PR feedback

---
 libcxx/include/stacktrace                     | 27 +++++++++----------
 libcxx/src/CMakeLists.txt                     |  2 +-
 libcxx/src/stacktrace/config.h                |  2 +-
 libcxx/src/stacktrace/{osx.cpp => macos.cpp}  | 14 +++++-----
 libcxx/src/stacktrace/{osx.h => macos.h}      |  8 +++---
 libcxx/src/stacktrace/stacktrace.cpp          | 12 ++++-----
 libcxx/src/stacktrace/unwind.h                |  2 --
 libcxx/src/stacktrace/windows.cpp             |  2 --
 libcxx/src/stacktrace/windows.h               |  2 ++
 .../stacktrace/simple.o0.nodebug.pass.cpp     |  2 +-
 .../stacktrace/simple.o0.nosplit.pass.cpp     |  2 +-
 .../stacktrace/simple.o0.split.pass.cpp       |  2 +-
 .../stacktrace/simple.o3.nodebug.pass.cpp     |  2 +-
 .../stacktrace/simple.o3.nosplit.pass.cpp     |  2 +-
 .../stacktrace/simple.o3.split.pass.cpp       |  2 +-
 .../diagnostics/stacktrace/basic.cmp.pass.cpp |  2 +-
 .../stacktrace/basic.cons.pass.cpp            |  4 +--
 .../stacktrace/basic.hash.pass.cpp            |  2 +-
 .../diagnostics/stacktrace/basic.mod.pass.cpp |  2 +-
 .../stacktrace/basic.nonmem.pass.cpp          |  2 +-
 .../diagnostics/stacktrace/basic.obs.pass.cpp |  2 +-
 .../std/diagnostics/stacktrace/basic.pass.cpp |  2 +-
 .../diagnostics/stacktrace/entry.cmp.pass.cpp |  2 +-
 .../stacktrace/entry.cons.pass.cpp            |  2 +-
 .../diagnostics/stacktrace/entry.obs.pass.cpp |  2 +-
 .../std/diagnostics/stacktrace/entry.pass.cpp |  2 +-
 .../stacktrace/entry.query.pass.cpp           |  4 +--
 .../diagnostics/stacktrace/format.pass.cpp    |  2 +-
 .../std/diagnostics/stacktrace/syn.pass.cpp   |  2 +-
 llvm/utils/gn/secondary/libcxx/src/BUILD.gn   | 12 ---------
 30 files changed, 54 insertions(+), 73 deletions(-)
 rename libcxx/src/stacktrace/{osx.cpp => macos.cpp} (93%)
 rename libcxx/src/stacktrace/{osx.h => macos.h} (84%)

diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace
index f635208a64d33..3a680b76b55db 100644
--- a/libcxx/include/stacktrace
+++ b/libcxx/include/stacktrace
@@ -164,29 +164,26 @@ namespace std {
 
 */
 
-#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
-#  include <__cxx03/__config>
-#else
-#  include <__config>
+#include <__config>
 
-#  if _LIBCPP_STD_VER >= 23
+#if _LIBCPP_STD_VER >= 23
 
-#    include <compare> // [stacktrace.syn]
+#  include <compare> // [stacktrace.syn]
 
-#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#      pragma GCC system_header
-#    endif
+#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#    pragma GCC system_header
+#  endif
 
 _LIBCPP_PUSH_MACROS
-#    include <__undef_macros>
+#  include <__undef_macros>
 
-#    include <__stacktrace/basic_stacktrace.h>
-#    include <__stacktrace/impl.h>
-#    include <__stacktrace/stacktrace_entry.h>
+#  include <__stacktrace/basic_stacktrace.h>
+#  include <__stacktrace/stacktrace_entry.h>
+
+#  include <__stacktrace/impl.h>
 
 _LIBCPP_POP_MACROS
 
-#  endif // _LIBCPP_STD_VER >= 23
-#endif   // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#endif // _LIBCPP_STD_VER >= 23
 
 #endif // _LIBCPP_STACKTRACE
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 1d8d32975a15d..45632d95daf45 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -41,7 +41,7 @@ set(LIBCXX_SOURCES
   ryu/d2s.cpp
   ryu/f2s.cpp
   stacktrace/linux.cpp
-  stacktrace/osx.cpp
+  stacktrace/macos.cpp
   stacktrace/stacktrace.cpp
   stacktrace/tools.cpp
   stacktrace/unwind.cpp
diff --git a/libcxx/src/stacktrace/config.h b/libcxx/src/stacktrace/config.h
index 0915b57463deb..62f5a06408c90 100644
--- a/libcxx/src/stacktrace/config.h
+++ b/libcxx/src/stacktrace/config.h
@@ -20,7 +20,7 @@ namespace __stacktrace {
 #endif
 
 #if defined(__APPLE__)
-#  define _LIBCPP_STACKTRACE_APPLE
+#  define _LIBCPP_STACKTRACE_MACOS
 #endif
 
 #if defined(__linux__)
diff --git a/libcxx/src/stacktrace/osx.cpp b/libcxx/src/stacktrace/macos.cpp
similarity index 93%
rename from libcxx/src/stacktrace/osx.cpp
rename to libcxx/src/stacktrace/macos.cpp
index 39e4d34b3e1c3..f3b3194183752 100644
--- a/libcxx/src/stacktrace/osx.cpp
+++ b/libcxx/src/stacktrace/macos.cpp
@@ -8,9 +8,10 @@
 
 #include "stacktrace/config.h"
 
-#if defined(_LIBCPP_STACKTRACE_APPLE)
+#if defined(_LIBCPP_STACKTRACE_MACOS)
 
-#  include "stacktrace/osx.h"
+#  include "stacktrace/context.h"
+#  include "stacktrace/macos.h"
 
 #  include <algorithm>
 #  include <array>
@@ -18,9 +19,6 @@
 #  include <mach-o/dyld.h>
 #  include <mach-o/loader.h>
 
-#  include "stacktrace/config.h"
-#  include "stacktrace/context.h"
-#  include "stacktrace/utils.h"
 #  include <__stacktrace/basic_stacktrace.h>
 #  include <__stacktrace/stacktrace_entry.h>
 
@@ -67,7 +65,7 @@ bool enum_modules(unsigned& count, auto& images) {
   return true;
 }
 
-void osx::ident_modules() {
+void macos::ident_modules() {
   static unsigned imageCount;
   static std::array<Image, kMaxImages + 2> images;
   static bool atomicInitialized = enum_modules(imageCount, images);
@@ -102,7 +100,7 @@ void symbolize_entry(entry& entry) {
   }
 }
 
-void osx::symbolize() {
+void macos::symbolize() {
   for (auto& entry : cx_.__entries_) {
     symbolize_entry(entry);
   }
@@ -111,4 +109,4 @@ void osx::symbolize() {
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STACKTRACE_APPLE
+#endif // _LIBCPP_STACKTRACE_MACOS
diff --git a/libcxx/src/stacktrace/osx.h b/libcxx/src/stacktrace/macos.h
similarity index 84%
rename from libcxx/src/stacktrace/osx.h
rename to libcxx/src/stacktrace/macos.h
index ce38aa0a378c6..98589a8f34494 100644
--- a/libcxx/src/stacktrace/osx.h
+++ b/libcxx/src/stacktrace/macos.h
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_STACKTRACE_OSX_H
-#define _LIBCPP_STACKTRACE_OSX_H
+#ifndef _LIBCPP_STACKTRACE_MACOS_H
+#define _LIBCPP_STACKTRACE_MACOS_H
 
 #include <__config>
 #include <__config_site>
@@ -19,7 +19,7 @@ namespace __stacktrace {
 
 struct context;
 
-struct osx {
+struct macos {
   context& cx_;
   void ident_modules();
   void symbolize();
@@ -28,4 +28,4 @@ struct osx {
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STACKTRACE_OSX_H
+#endif // _LIBCPP_STACKTRACE_MACOS_H
diff --git a/libcxx/src/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
index e3e2a0e1d73c2..aa0e63b9eadc5 100644
--- a/libcxx/src/stacktrace/stacktrace.cpp
+++ b/libcxx/src/stacktrace/stacktrace.cpp
@@ -24,8 +24,8 @@
 #  include "stacktrace/linux.h"
 #endif
 
-#if defined(_LIBCPP_STACKTRACE_APPLE)
-#  include "stacktrace/osx.h"
+#if defined(_LIBCPP_STACKTRACE_MACOS)
+#  include "stacktrace/macos.h"
 #endif
 
 #if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
@@ -173,10 +173,10 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip,
   unwind unwind{*this};
   auto& collector = unwind;
 #endif
-#if defined(_LIBCPP_STACKTRACE_APPLE)
-  osx osx{*this};
-  auto& mod_ident  = osx;
-  auto& symbolizer = osx;
+#if defined(_LIBCPP_STACKTRACE_MACOS)
+  macos macos{*this};
+  auto& mod_ident  = macos;
+  auto& symbolizer = macos;
 #endif
 #if defined(_LIBCPP_STACKTRACE_LINUX)
   linux linux{*this};
diff --git a/libcxx/src/stacktrace/unwind.h b/libcxx/src/stacktrace/unwind.h
index 98216e61d6bcf..e8efd96e03b56 100644
--- a/libcxx/src/stacktrace/unwind.h
+++ b/libcxx/src/stacktrace/unwind.h
@@ -14,9 +14,7 @@
 #include <cstddef>
 #include <cstdlib>
 
-#include "stacktrace/config.h"
 #include "stacktrace/context.h"
-#include "stacktrace/utils.h"
 #include <__stacktrace/basic_stacktrace.h>
 #include <__stacktrace/stacktrace_entry.h>
 
diff --git a/libcxx/src/stacktrace/windows.cpp b/libcxx/src/stacktrace/windows.cpp
index 36de17973a231..2de064020db0e 100644
--- a/libcxx/src/stacktrace/windows.cpp
+++ b/libcxx/src/stacktrace/windows.cpp
@@ -15,8 +15,6 @@
 #  include "stacktrace/context.h"
 #  include "stacktrace/utils.h"
 #  include "stacktrace/windows.h"
-#  include <__stacktrace/basic_stacktrace.h>
-#  include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/stacktrace/windows.h b/libcxx/src/stacktrace/windows.h
index d31274448c5be..91b3739f427c1 100644
--- a/libcxx/src/stacktrace/windows.h
+++ b/libcxx/src/stacktrace/windows.h
@@ -9,6 +9,8 @@
 #ifndef _LIBCPP_STACKTRACE_WIN_IMPL_H
 #define _LIBCPP_STACKTRACE_WIN_IMPL_H
 
+#include "stacktrace/config.h"
+
 #if defined(_LIBCPP_STACKTRACE_WINDOWS)
 // windows.h must be first
 #  include <windows.h>
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
index 460d61d58a915..f40037d7e46e6 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 // ADDITIONAL_COMPILE_FLAGS: -O0 -g0
 
 #include <cassert>
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
index bdf5ba22bc46b..2c7fa9416f31f 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 // ADDITIONAL_COMPILE_FLAGS: -O0 -g
 
 #include <cassert>
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
index 022f27f5e569e..1f6e2bd557460 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 // ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
 
 #include <cassert>
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
index 35a30eb078212..fafb0d618b908 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 // ADDITIONAL_COMPILE_FLAGS: -O3 -g0
 
 #include <cassert>
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
index 8d0fcf5806884..3690499ae7c25 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 // ADDITIONAL_COMPILE_FLAGS: -O3 -g
 
 #include <cassert>
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
index cea9c79c87a77..4b32e33110d1a 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 // ADDITIONAL_COMPILE_FLAGS: -O3 -g -gsplit-dwarf
 
 #include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
index e8b5c36d05ba1..38c0f1fbe3c7a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
index edf07510687dc..f4d0dfbe0f8ed 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// ADDITIONAL_COMPILE_FLAGS: -O1 -g
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
index 976b7ef05ccf2..ecfdf564846f1 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
index 9d473202d492f..e53890a39c2c4 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
index d0b327e738c6c..e1ef4245b83e9 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
index 1f4102bd1ca61..e88480093536b 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 #include <iostream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
index 8260b0a8028d5..089ecccef1196 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 #include <iterator>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
index 6da4d40c5e08a..963926b041f03 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
index 84472ea91b2aa..20f8a24d41b89 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 // ADDITIONAL_COMPILE_FLAGS: -g
 
 #include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
index f31d7d3e9f8b7..229619f9c24a1 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
index a77687a5607a1..359d4d5c2ea70 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
index 0d8fac58f30ee..b8fda6dab0fe3 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
index 42accf6ce1daa..3db4920b3c388 100644
--- a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <cassert>
 #include <stacktrace>
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
index 9682f6bfc146d..173e81286506e 100644
--- a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <cassert>
 #include <stacktrace>
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index ce6b52fbb0462..b9e8d0717f7df 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -168,18 +168,6 @@ cxx_sources = [
   "ryu/d2s.cpp",
   "ryu/f2s.cpp",
   "shared_mutex.cpp",
-  "stacktrace/config.h",
-  "stacktrace/linux.cpp",
-  "stacktrace/linux.h",
-  "stacktrace/osx.cpp",
-  "stacktrace/osx.h",
-  "stacktrace/stacktrace.cpp",
-  "stacktrace/tools.cpp",
-  "stacktrace/tools.h",
-  "stacktrace/unwind.cpp",
-  "stacktrace/utils.h",
-  "stacktrace/windows.cpp",
-  "stacktrace/windows.h",
   "stdexcept.cpp",
   "string.cpp",
   "strstream.cpp",

>From 53191ddf6b217289d8b1ef618dcf9af457223386 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 17:08:25 -0400
Subject: [PATCH 09/37] Remove newline at end of file: std.compat.cppm.in

---
 libcxx/modules/std.compat.cppm.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index 2ee56c6b810c9..55d21030c031b 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -83,4 +83,4 @@ export module std.compat;
 export import std;
 
 
- at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
+ at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
\ No newline at end of file

>From a6678d25a61eb53a542f8e83ca9096c5869b0645 Mon Sep 17 00:00:00 2001
From: "steve at obrien.cc" <Steve O'Brien>
Date: Wed, 23 Apr 2025 18:15:36 -0400
Subject: [PATCH 10/37] Committing regenerated files

---
 ...bcxxabi.v1.stable.exceptions.nonew.abilist | 33 ++++++++++++++++++-
 .../gn/build/sync_source_lists_from_cmake.py  | 12 ++-----
 llvm/utils/gn/build/toolchain/BUILD.gn        | 17 ++++++++++
 3 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 679a0626d3268..7b0e6d3c82a84 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -622,6 +622,11 @@
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclERKNS_16stacktrace_entryE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6__implEmmRNS0_5allocB8ne210000ENS_8functionIFvmEEENS3_IFvmONS_16stacktrace_entryEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD2Ev', 'type': 'FUNC'}
@@ -773,6 +778,7 @@
 {'is_defined': True, 'name': '_ZNSt3__112system_errorD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112system_errorD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__112system_errorD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj', 'type': 'FUNC'}
@@ -953,7 +959,6 @@
 {'is_defined': True, 'name': '_ZNSt3__113shared_futureIvED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__113shared_futureIvED2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__113shared_futureIvEaSERKS1_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__114__num_get_base5__srcE', 'size': 33, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__114__num_put_base12__format_intEPcPKcbj', 'type': 'FUNC'}
@@ -1137,6 +1142,7 @@
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__116generic_categoryEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__116stacktrace_entryD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state10__sub_waitERNS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state12__make_readyEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state13set_exceptionESt13exception_ptr', 'type': 'FUNC'}
@@ -1586,6 +1592,7 @@
 {'is_defined': True, 'name': '_ZNSt3__19strstreamD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19strstreamD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19strstreamD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19to_stringERKNS_16stacktrace_entryE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19to_stringEd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19to_stringEe', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19to_stringEf', 'type': 'FUNC'}
@@ -1595,6 +1602,7 @@
 {'is_defined': True, 'name': '_ZNSt3__19to_stringEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19to_stringEx', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__19to_stringEy', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__1lsERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZSt17__throw_bad_allocv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZSt17current_exceptionv', 'type': 'FUNC'}
@@ -1604,6 +1612,7 @@
 {'is_defined': True, 'name': '_ZSt7nothrow', 'size': 1, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__112__stacktrace10fd_istreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
@@ -1618,6 +1627,8 @@
 {'is_defined': True, 'name': '_ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt12experimental15fundamentals_v112bad_any_castE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt12experimental19bad_optional_accessE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace3elf6SymbolEEEE', 'size': 16, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 16, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__110__time_getE', 'size': 16, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__110__time_putE', 'size': 16, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__110ctype_baseE', 'size': 16, 'type': 'OBJECT'}
@@ -1633,6 +1644,12 @@
 {'is_defined': True, 'name': '_ZTINSt3__111__money_putIcEE', 'size': 16, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'size': 16, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__111regex_errorE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace10fd_istreamE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace15llvm_symbolizerE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace4atosE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace4toolE', 'size': 16, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace6failedE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace9addr2lineE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112bad_weak_ptrE', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'size': 16, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTINSt3__112ctype_bynameIcEE', 'size': 24, 'type': 'OBJECT'}
@@ -1751,6 +1768,8 @@
 {'is_defined': True, 'name': '_ZTISt19bad_optional_access', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt12experimental15fundamentals_v112bad_any_castE', 'size': 50, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt12experimental19bad_optional_accessE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf6SymbolEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 65, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__110__time_getE', 'size': 21, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__110__time_putE', 'size': 21, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__110ctype_baseE', 'size': 21, 'type': 'OBJECT'}
@@ -1766,6 +1785,12 @@
 {'is_defined': True, 'name': '_ZTSNSt3__111__money_putIcEE', 'size': 25, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'size': 25, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__111regex_errorE', 'size': 22, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace10fd_istreamE', 'size': 35, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace15llvm_symbolizerE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace4atosE', 'size': 28, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace4toolE', 'size': 28, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace6failedE', 'size': 30, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace9addr2lineE', 'size': 33, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112bad_weak_ptrE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'size': 23, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTSNSt3__112ctype_bynameIcEE', 'size': 26, 'type': 'OBJECT'}
@@ -1884,6 +1909,7 @@
 {'is_defined': True, 'name': '_ZTSSt19bad_optional_access', 'size': 24, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTTNSt3__110istrstreamE', 'size': 32, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTTNSt3__110ostrstreamE', 'size': 32, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTTNSt3__112__stacktrace10fd_istreamE', 'size': 32, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTTNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 16, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTTNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 16, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTTNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 16, 'type': 'OBJECT'}
@@ -1904,6 +1930,11 @@
 {'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'size': 112, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__110ostrstreamE', 'size': 80, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__111regex_errorE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace10fd_istreamE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace15llvm_symbolizerE', 'size': 48, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace4atosE', 'size': 48, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace6failedE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace9addr2lineE', 'size': 48, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112bad_weak_ptrE', 'size': 40, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112ctype_bynameIcEE', 'size': 104, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZTVNSt3__112ctype_bynameIwEE', 'size': 136, 'type': 'OBJECT'}
diff --git a/llvm/utils/gn/build/sync_source_lists_from_cmake.py b/llvm/utils/gn/build/sync_source_lists_from_cmake.py
index 4cd30aa410864..aada38bebd421 100755
--- a/llvm/utils/gn/build/sync_source_lists_from_cmake.py
+++ b/llvm/utils/gn/build/sync_source_lists_from_cmake.py
@@ -61,13 +61,13 @@ def git_out(args):
     gn_files = git_out(["ls-files", "*BUILD.gn"]).splitlines()
 
     # Matches e.g. |   "foo.cpp",|, captures |foo| in group 1.
-    gn_cpp_re = re.compile(r'^\s*"([^$"]+\.(?:cpp|c|h|mm|S))",$', re.MULTILINE)
+    gn_cpp_re = re.compile(r'^\s*"([^$"]+\.(?:cpp|c|h|S))",$', re.MULTILINE)
     # Matches e.g. |   bar_sources = [ "foo.cpp" ]|, captures |foo| in group 1.
     gn_cpp_re2 = re.compile(
-        r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|mm|S))" ]$', re.MULTILINE
+        r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|S))" ]$', re.MULTILINE
     )
     # Matches e.g. |   foo.cpp|, captures |foo| in group 1.
-    cmake_cpp_re = re.compile(r"^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|mm|S))$", re.MULTILINE)
+    cmake_cpp_re = re.compile(r"^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|S))$", re.MULTILINE)
 
     changes_by_rev = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
 
@@ -108,12 +108,6 @@ def get_sources(source_re, text):
 
         gn_cpp = get_sources(gn_cpp_re, open(gn_file).read())
         gn_cpp |= get_sources(gn_cpp_re2, open(gn_file).read())
-
-        sources_file = os.path.join(os.path.dirname(gn_file), "sources.gni")
-        if os.path.exists(sources_file):
-            gn_cpp |= get_sources(gn_cpp_re, open(sources_file).read())
-            gn_cpp |= get_sources(gn_cpp_re2, open(sources_file).read())
-
         cmake_cpp = get_sources(cmake_cpp_re, open(cmake_file).read())
 
         if gn_cpp == cmake_cpp:
diff --git a/llvm/utils/gn/build/toolchain/BUILD.gn b/llvm/utils/gn/build/toolchain/BUILD.gn
index 00d9df12f5a97..ce2e6df1b69f5 100644
--- a/llvm/utils/gn/build/toolchain/BUILD.gn
+++ b/llvm/utils/gn/build/toolchain/BUILD.gn
@@ -71,6 +71,23 @@ template("unix_toolchain") {
       default_output_dir = "{{root_out_dir}}/lib"
     }
 
+    if (current_os == "ios" || current_os == "mac") {
+      # gn < 1693 (e214b5d35898) doesn't support |frameworks|, requiring
+      # frameworks to be listed in |libs|, but gn >= 1808 (3028c6a426a4) forbids
+      # frameworks from appearing in |libs|. This assertion provides a helpful
+      # cue to upgrade, and is much more user-friendly than the failure that
+      # occurs when an older gn encounters |frameworks|.
+      #
+      # gn_version doesn’t actually exist in gn < 1709 (52cb644a3fb4), and
+      # defined(gn_version) doesn't actually work as expected
+      # (https://crbug.com/gn/183), so 1709 is the true minimum enforced by
+      # this construct, and if gn_version is not available, this line will still
+      # be blamed, making the resolution somewhat discoverable.
+      assert(gn_version >= 1693,
+             "Your GN is too old! " +
+                 "Update it, perhaps by running llvm/utils/gn/get.py")
+    }
+
     # Make these apply to all tools below.
     lib_switch = "-l"
     lib_dir_switch = "-L"

>From 12ed688bd1db4bfd081a68985876a6ae94e42db9 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 18:12:10 -0400
Subject: [PATCH 11/37] Address lints for system headers

---
 libcxx/include/__stacktrace/basic_stacktrace.h |  9 +++++++++
 libcxx/include/__stacktrace/impl.h             | 10 ++++++++++
 libcxx/include/__stacktrace/stacktrace_entry.h |  9 +++++++++
 3 files changed, 28 insertions(+)

diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 1fe5cfa1b0cf5..d90b53247ee27 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -35,6 +35,13 @@
 #include <cstddef>
 #include <memory>
 
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 // (19.6.4)
@@ -289,4 +296,6 @@ struct _LIBCPP_EXPORTED_FROM_ABI hash<basic_stacktrace<_Allocator>> {
 
 _LIBCPP_END_NAMESPACE_STD
 
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP_BASIC_STACKTRACE
diff --git a/libcxx/include/__stacktrace/impl.h b/libcxx/include/__stacktrace/impl.h
index 7aaec10672baa..a131d0940d2ef 100644
--- a/libcxx/include/__stacktrace/impl.h
+++ b/libcxx/include/__stacktrace/impl.h
@@ -32,9 +32,17 @@
 #include <__vector/pmr.h>
 #include <__vector/swap.h>
 #include <__vector/vector.h>
+#include <cstdint>
 #include <list>
 #include <string>
 
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Allocator>
@@ -154,4 +162,6 @@ __impl(size_t __skip,
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP_STACKTRACE_IMPL
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index 43b10d2a86f65..9099b1f849148 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -33,6 +33,13 @@
 #include <cstdint>
 #include <string>
 
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace __stacktrace {
@@ -109,4 +116,6 @@ struct _LIBCPP_EXPORTED_FROM_ABI hash<stacktrace_entry> {
 
 _LIBCPP_END_NAMESPACE_STD
 
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP_STACKTRACE_ENTRY

>From 4ef48ac01836e46ee4a9dc8db6a56eafad744bc4 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 18:25:26 -0400
Subject: [PATCH 12/37] Add generated 'stacktrace.version.compile.pass.cpp'
 test

---
 .../stacktrace.version.compile.pass.cpp       | 120 ++++++++++++++++++
 1 file changed, 120 insertions(+)
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp

diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
new file mode 100644
index 0000000000000..eea8cf66dfbb8
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <stacktrace>
+
+// Test the feature test macros defined by <stacktrace>
+
+// clang-format off
+
+#include <stacktrace>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_formatters
+#    error "__cpp_lib_formatters should not be defined before c++23"
+#  endif
+
+#  ifdef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_formatters
+#    error "__cpp_lib_formatters should not be defined before c++23"
+#  endif
+
+#  ifdef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_formatters
+#    error "__cpp_lib_formatters should not be defined before c++23"
+#  endif
+
+#  ifdef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_formatters
+#    error "__cpp_lib_formatters should not be defined before c++23"
+#  endif
+
+#  ifdef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_formatters
+#      error "__cpp_lib_formatters should be defined in c++23"
+#    endif
+#    if __cpp_lib_formatters != 202302L
+#      error "__cpp_lib_formatters should have the value 202302L in c++23"
+#    endif
+#  else
+#    ifdef __cpp_lib_formatters
+#      error "__cpp_lib_formatters should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_stacktrace
+#      error "__cpp_lib_stacktrace should be defined in c++23"
+#    endif
+#    if __cpp_lib_stacktrace != 202011L
+#      error "__cpp_lib_stacktrace should have the value 202011L in c++23"
+#    endif
+#  else
+#    ifdef __cpp_lib_stacktrace
+#      error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_formatters
+#      error "__cpp_lib_formatters should be defined in c++26"
+#    endif
+#    if __cpp_lib_formatters != 202302L
+#      error "__cpp_lib_formatters should have the value 202302L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_formatters
+#      error "__cpp_lib_formatters should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#  if !defined(_LIBCPP_VERSION)
+#    ifndef __cpp_lib_stacktrace
+#      error "__cpp_lib_stacktrace should be defined in c++26"
+#    endif
+#    if __cpp_lib_stacktrace != 202011L
+#      error "__cpp_lib_stacktrace should have the value 202011L in c++26"
+#    endif
+#  else
+#    ifdef __cpp_lib_stacktrace
+#      error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
+#    endif
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
+

>From 04a11114e251ee5f12b411451e1cb6b4b45aab12 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 18:33:15 -0400
Subject: [PATCH 13/37] Address some errors from gcc regarding inlining

---
 libcxx/src/stacktrace/stacktrace.cpp | 7 -------
 libcxx/src/stacktrace/utils.h        | 8 ++++----
 2 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/libcxx/src/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
index aa0e63b9eadc5..98acec1e92d0b 100644
--- a/libcxx/src/stacktrace/stacktrace.cpp
+++ b/libcxx/src/stacktrace/stacktrace.cpp
@@ -55,13 +55,6 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_en
 
 namespace __stacktrace {
 
-/* Anchors for classes w/ vtables */
-// alloc::~alloc()                        = default;
-debug::~debug()                        = default;
-debug::dummy_ostream::~dummy_ostream() = default;
-fd_streambuf::~fd_streambuf()          = default;
-fd_istream::~fd_istream()              = default;
-
 /*
  * `to_string` Helpers
  */
diff --git a/libcxx/src/stacktrace/utils.h b/libcxx/src/stacktrace/utils.h
index ea0e7cd659e63..9c39e0d184e9f 100644
--- a/libcxx/src/stacktrace/utils.h
+++ b/libcxx/src/stacktrace/utils.h
@@ -34,7 +34,7 @@ struct failed : std::runtime_error {
 or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
 otherwise its does nothing by returning a dummy stream. */
 struct _LIBCPP_HIDE_FROM_ABI debug : std::ostream {
-  _LIBCPP_HIDE_FROM_ABI virtual ~debug();
+  _LIBCPP_HIDE_FROM_ABI virtual ~debug() = default;
 
   _LIBCPP_HIDE_FROM_ABI static bool enabled() {
 #if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
@@ -50,7 +50,7 @@ struct _LIBCPP_HIDE_FROM_ABI debug : std::ostream {
 
   /** No-op output stream. */
   struct _LIBCPP_HIDE_FROM_ABI dummy_ostream final : std::ostream {
-    _LIBCPP_HIDE_FROM_ABI virtual ~dummy_ostream();
+    _LIBCPP_HIDE_FROM_ABI virtual ~dummy_ostream() = default;
     friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
   };
 
@@ -123,14 +123,14 @@ struct _LIBCPP_HIDE_FROM_ABI fd_streambuf final : std::streambuf {
   char* buf_;
   size_t size_;
   _LIBCPP_HIDE_FROM_ABI fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
-  _LIBCPP_HIDE_FROM_ABI virtual ~fd_streambuf();
+  _LIBCPP_HIDE_FROM_ABI virtual ~fd_streambuf() = default;
   _LIBCPP_HIDE_FROM_ABI int underflow() override;
 };
 
 /** Wraps an `FDInStreamBuffer` in an `istream` */
 struct fd_istream final : std::istream {
   fd_streambuf& buf_;
-  _LIBCPP_HIDE_FROM_ABI virtual ~fd_istream();
+  _LIBCPP_HIDE_FROM_ABI virtual ~fd_istream() = default;
   _LIBCPP_HIDE_FROM_ABI explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
 };
 

>From a857aebe5e8da3ea6e5827270fc090d07f446e0d Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 22:51:00 -0400
Subject: [PATCH 14/37] Feature macros + regenerate

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/include/version                        |  2 +-
 .../std/diagnostics/stacktrace/syn.pass.cpp   | 14 ++++----
 .../stacktrace.version.compile.pass.cpp       | 32 ++++++-------------
 .../version.version.compile.pass.cpp          | 32 ++++++-------------
 .../generate_feature_test_macro_components.py |  1 -
 6 files changed, 29 insertions(+), 54 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 9b57b7c8eeb52..9ae573c7aa4a8 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -392,7 +392,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_spanstream``                                   *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_stacktrace``                                   *unimplemented*
+    ``__cpp_lib_stacktrace``                                   ``202011L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_stdatomic_h``                                  ``202011L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/include/version b/libcxx/include/version
index 77d97b93adc6c..e0bae02a84d2d 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -524,7 +524,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 // # define __cpp_lib_ranges_zip                           202110L
 // # define __cpp_lib_reference_from_temporary             202202L
 // # define __cpp_lib_spanstream                           202106L
-// # define __cpp_lib_stacktrace                           202011L
+# define __cpp_lib_stacktrace                           202011L
 # define __cpp_lib_stdatomic_h                          202011L
 # define __cpp_lib_string_contains                      202011L
 # define __cpp_lib_string_resize_and_overwrite          202110L
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
index 173e81286506e..f6c44bf8fe44f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -8,12 +8,6 @@
 
 // REQUIRES: std-at-least-c++23
 
-#include <cassert>
-#include <stacktrace>
-
-#include <memory>
-#include <type_traits>
-
 /*
 
 // (19.6.2) Header <stacktrace> synopsis [stacktrace.syn]
@@ -62,7 +56,13 @@ namespace std {
 }
 */
 
-// static_assert(__cpp_lib_stacktrace == 202011L);
+#include <__config>
+#include <cassert>
+#include <memory>
+#include <stacktrace>
+#include <type_traits>
+
+static_assert(__cpp_lib_stacktrace == 202011L);
 
 int main(int, char**) {
   // Very basic tests to ensure the required things are declared.
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index eea8cf66dfbb8..461087467a270 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -73,17 +73,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should be defined in c++23"
-#    endif
-#    if __cpp_lib_stacktrace != 202011L
-#      error "__cpp_lib_stacktrace should have the value 202011L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should be defined in c++23"
+#  endif
+#  if __cpp_lib_stacktrace != 202011L
+#    error "__cpp_lib_stacktrace should have the value 202011L in c++23"
 #  endif
 
 #elif TEST_STD_VER > 23
@@ -101,17 +95,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should be defined in c++26"
-#    endif
-#    if __cpp_lib_stacktrace != 202011L
-#      error "__cpp_lib_stacktrace should have the value 202011L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should be defined in c++26"
+#  endif
+#  if __cpp_lib_stacktrace != 202011L
+#    error "__cpp_lib_stacktrace should have the value 202011L in c++26"
 #  endif
 
 #endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index aa33a2788f1eb..881764515b9cf 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -5831,17 +5831,11 @@
 #    error "__cpp_lib_sstream_from_string_view should not be defined before c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should be defined in c++23"
-#    endif
-#    if __cpp_lib_stacktrace != 202011L
-#      error "__cpp_lib_stacktrace should have the value 202011L in c++23"
-#    endif
-#  else
-#    ifdef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should be defined in c++23"
+#  endif
+#  if __cpp_lib_stacktrace != 202011L
+#    error "__cpp_lib_stacktrace should have the value 202011L in c++23"
 #  endif
 
 #  ifndef __cpp_lib_starts_ends_with
@@ -7771,17 +7765,11 @@
 #    error "__cpp_lib_sstream_from_string_view should have the value 202306L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should be defined in c++26"
-#    endif
-#    if __cpp_lib_stacktrace != 202011L
-#      error "__cpp_lib_stacktrace should have the value 202011L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_stacktrace
-#      error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_stacktrace
+#    error "__cpp_lib_stacktrace should be defined in c++26"
+#  endif
+#  if __cpp_lib_stacktrace != 202011L
+#    error "__cpp_lib_stacktrace should have the value 202011L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_starts_ends_with
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 2b7f6fa8a48a9..b3d0a573cf8ce 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1288,7 +1288,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_stacktrace",
             "values": {"c++23": 202011},
             "headers": ["stacktrace"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_starts_ends_with",

>From fb739fa555cf4e9149230db08cb4c142e68ce8d5 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 24 Apr 2025 00:16:39 -0400
Subject: [PATCH 15/37] Break up tests

---
 .../stacktrace/basic.cmp/equality.cpp         |  51 ++++++
 .../strong_ordering.cpp}                      |  39 +----
 .../stacktrace/basic.cons/copy_and_move.cpp   |  81 +++++++++
 .../stacktrace/basic.cons/ctor_no_args.cpp    |  48 ++++++
 .../stacktrace/basic.cons/ctor_with_alloc.cpp |  79 +++++++++
 .../stacktrace/basic.cons/current_no_args.cpp |  67 ++++++++
 .../stacktrace/basic.cons/current_skip.cpp    |  45 +++++
 .../basic.cons/current_skip_depth.cpp         |  42 +++++
 .../only_uses_allocator.cpp}                  |  26 ++-
 .../stacktrace/basic.hash.pass.cpp            |  24 +--
 .../swap.pass.cpp}                            |   9 +-
 .../stacktrace/basic.nonmem.pass.cpp          |  91 ----------
 .../basic.nonmem/operator_left_shift.cpp      |  35 ++++
 .../stacktrace/basic.nonmem/swap.pass.cpp     |  36 ++++
 .../stacktrace/basic.nonmem/to_string.cpp     |  33 ++++
 .../diagnostics/stacktrace/basic.obs.pass.cpp | 156 ------------------
 .../stacktrace/basic.obs/at.pass.cpp          |  48 ++++++
 .../stacktrace/basic.obs/begin_end.pass.cpp   |  41 +++++
 .../stacktrace/basic.obs/cbegin_cend.pass.cpp |  36 ++++
 .../basic.obs/crbegin_crend.pass.cpp          |  36 ++++
 .../stacktrace/basic.obs/empty.pass.cpp       |  32 ++++
 .../basic.obs/get_allocator.pass.cpp          |  24 +++
 .../stacktrace/basic.obs/max_size.pass.cpp    |  31 ++++
 .../basic.obs/operator_index.pass.cpp         |  48 ++++++
 .../stacktrace/basic.obs/rbegin_rend.pass.cpp |  37 +++++
 .../stacktrace/basic.obs/size.pass.cpp        |  32 ++++
 .../std/diagnostics/stacktrace/basic.pass.cpp |  22 +--
 .../stacktrace/entry.cmp/equality.pass.cpp    |  41 +++++
 .../strong_ordering.pass.cpp}                 |  51 ++----
 .../stacktrace/entry.cons.pass.cpp            |  63 -------
 .../stacktrace/entry.cons/copy_assign.cpp     |  34 ++++
 .../stacktrace/entry.cons/copy_construct.cpp  |  33 ++++
 .../stacktrace/entry.cons/default.cpp         |  35 ++++
 .../stacktrace/entry.obs/native_handle.cpp    |  28 ++++
 .../operator_bool.pass.cpp}                   |  28 +---
 .../std/diagnostics/stacktrace/entry.pass.cpp |  35 ++--
 .../stacktrace/entry.query.pass.cpp           |  85 ----------
 .../entry.query/description.pass.cpp          |  32 ++++
 .../entry.query/source_file.pass.cpp          |  32 ++++
 .../entry.query/source_line.pass.cpp          |  30 ++++
 .../formatter_basic_stacktrace.pass.cpp       |  31 ++++
 .../formatter_stacktrace_entry.cpp}           |  24 +--
 .../std/diagnostics/stacktrace/syn.pass.cpp   | 147 -----------------
 43 files changed, 1270 insertions(+), 708 deletions(-)
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.cpp
 rename libcxx/test/std/diagnostics/stacktrace/{basic.cmp.pass.cpp => basic.cmp/strong_ordering.cpp} (79%)
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.cpp
 rename libcxx/test/std/diagnostics/stacktrace/{basic.cons.pass.cpp => basic.cons/only_uses_allocator.cpp} (89%)
 rename libcxx/test/std/diagnostics/stacktrace/{basic.mod.pass.cpp => basic.mod/swap.pass.cpp} (97%)
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
 rename libcxx/test/std/diagnostics/stacktrace/{entry.cmp.pass.cpp => entry.cmp/strong_ordering.pass.cpp} (51%)
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons/default.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.cpp
 rename libcxx/test/std/diagnostics/stacktrace/{entry.obs.pass.cpp => entry.obs/operator_bool.pass.cpp} (56%)
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.query/description.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp
 create mode 100644 libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp
 rename libcxx/test/std/diagnostics/stacktrace/{format.pass.cpp => format/formatter_stacktrace_entry.cpp} (65%)
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp

diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.cpp
new file mode 100644
index 0000000000000..5864d241abf35
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.4) Comparisons [stacktrace.basic.cmp]
+
+  template<class Allocator2>
+  friend bool operator==(const basic_stacktrace& x,
+                          const basic_stacktrace<Allocator2>& y) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2a() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2b() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  auto st1a = test1(); // [test1, main, ...]
+  assert(st1a == st1a);
+
+  auto st1b = st1a;
+  assert(st1a == st1b);
+
+  auto st2a = test2a(); // [test1, test2a, main, ...]
+  assert(st1a != st2a);
+
+  std::stacktrace empty; // []
+  assert(st1a != empty);
+  assert(st2a != empty);
+
+  assert(st2a.size() > st1a.size());
+  assert(st1a.size() > empty.size());
+
+  auto st2b = test2b(); // [test1, test2b, main, ...]
+  assert(st2a.size() == st2b.size());
+  assert(st2a != st2b);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.cpp
similarity index 79%
rename from libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
rename to libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.cpp
index 38c0f1fbe3c7a..5664e9bc41db2 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.cpp
@@ -8,22 +8,17 @@
 
 // REQUIRES: std-at-least-c++23
 
-#include <stacktrace>
-
-#include <cassert>
-
 /*
   (19.6.4.4) Comparisons [stacktrace.basic.cmp]
 
-  template<class Allocator2>
-  friend bool operator==(const basic_stacktrace& x,
-                          const basic_stacktrace<Allocator2>& y) noexcept;
-
   template<class Allocator2>
   friend strong_ordering operator<=>(const basic_stacktrace& x,
                                       const basic_stacktrace<Allocator2>& y) noexcept;
 */
 
+#include <cassert>
+#include <stacktrace>
+
 _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(); }
 
 _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2a() { return test1(); }
@@ -33,39 +28,19 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2b() { return test1()
 _LIBCPP_NO_TAIL_CALLS
 int main(int, char**) {
   /*    
-    template<class Allocator2>
-    friend bool operator==(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
-    Returns: equal(x.begin(), x.end(), y.begin(), y.end()).
+    Returns: x.size() <=> y.size() if x.size() != y.size();
+    lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end()) otherwise.
   */
-  auto st1a = test1(); // [test1, main, ...]
-  assert(st1a == st1a);
 
+  auto st1a = test1(); // [test1, main, ...]
   auto st1b = st1a;
   assert(st1a == st1b);
-
   auto st2a = test2a(); // [test1, test2a, main, ...]
   assert(st1a != st2a);
-
   std::stacktrace empty; // []
-  assert(st1a != empty);
-  assert(st2a != empty);
-
-  assert(st2a.size() > st1a.size());
-  assert(st1a.size() > empty.size());
-
-  auto st2b = test2b(); // [test1, test2b, main, ...]
-  assert(st2a.size() == st2b.size());
+  auto st2b = test2b();  // [test1, test2b, main, ...]
   assert(st2a != st2b);
 
-  /*    
-    template<class Allocator2>
-    friend strong_ordering
-    operator<=>(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
-
-    Returns: x.size() <=> y.size() if x.size() != y.size();
-    lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end()) otherwise.
-  */
-
   // empty:  []
   // st1a:   [test1, main, ...]
   // st1b:   [test1, main, ...] (copy of st1a)
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.cpp
new file mode 100644
index 0000000000000..ae37fc11b3089
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  basic_stacktrace(const basic_stacktrace& other);
+  basic_stacktrace(basic_stacktrace&& other) noexcept;                                    
+  basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);           
+  basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);                
+  basic_stacktrace& operator=(const basic_stacktrace& other);                             
+  basic_stacktrace& operator=(basic_stacktrace&& other)
+    noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+      allocator_traits<Allocator>::is_always_equal::value);                               
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+// clang-format off
+uint32_t test1_line;
+uint32_t test2_line;
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test1(A& alloc) {
+  test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret = std::basic_stacktrace<A>::current(alloc);
+  return ret;
+}
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test2(A& alloc) {
+  test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret = test1(alloc);
+  return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_copy_move_ctors() {
+  using A = std::allocator<std::stacktrace_entry>;
+  A alloc;
+  auto st = std::basic_stacktrace<A>::current(alloc);
+
+  auto copy_constr = std::basic_stacktrace<A>(st);
+  assert(st == copy_constr);
+
+  std::basic_stacktrace<A> copy_assign;
+  copy_assign = std::basic_stacktrace<A>(st);
+  assert(st == copy_assign);
+
+  auto st2 = test2(alloc);
+  assert(st2.size());
+  std::basic_stacktrace<A> move_constr(std::move(st2));
+  assert(move_constr.size());
+  assert(!st2.size());
+
+  auto st3 = test2(alloc);
+  assert(st3.size());
+  std::basic_stacktrace<A> move_assign;
+  move_assign = std::move(st3);
+  assert(move_assign.size());
+  assert(!st3.size());
+
+  // TODO(stacktrace23): should we add test cases with `select_on_container_copy_construction`?
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_copy_move_ctors();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.cpp
new file mode 100644
index 0000000000000..3b406f1b16a61
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+uint32_t test1_line;
+uint32_t test2_line;
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test1(A& alloc) {
+  test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret   = std::basic_stacktrace<A>::current(alloc);
+  return ret;
+}
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test2(A& alloc) {
+  test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret   = test1(alloc);
+  return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_default_construct() {
+  std::stacktrace st;
+  assert(st.empty());
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_default_construct();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.cpp
new file mode 100644
index 0000000000000..a49f45245f7d2
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+uint32_t test1_line;
+uint32_t test2_line;
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test1(A& alloc) {
+  test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret   = std::basic_stacktrace<A>::current(alloc);
+  return ret;
+}
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test2(A& alloc) {
+  test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret   = test1(alloc);
+  return ret;
+}
+
+template <typename T>
+struct test_alloc {
+  using size_type     = size_t;
+  using value_type    = T;
+  using pointer       = T*;
+  using const_pointer = T const*;
+
+  template <typename U>
+  struct rebind {
+    using other = test_alloc<U>;
+  };
+
+  std::allocator<T> wrapped_{};
+
+  test_alloc() = default;
+
+  template <typename U>
+  test_alloc(test_alloc<U> const& rhs) : wrapped_(rhs.wrapped_) {}
+
+  bool operator==(auto const& rhs) const { return &rhs == this; }
+  bool operator==(test_alloc const&) const { return true; }
+
+  T* allocate(size_t n) { return wrapped_.allocate(n); }
+  auto allocate_at_least(size_t n) { return wrapped_.allocate_at_least(n); }
+  void deallocate(T* ptr, size_t n) { return wrapped_.deallocate(ptr, n); }
+};
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_construct_with_allocator() {
+  test_alloc<std::stacktrace_entry> alloc;
+  std::basic_stacktrace<decltype(alloc)> st(alloc);
+  assert(st.empty());
+
+  st = std::basic_stacktrace<decltype(alloc)>::current(alloc);
+  assert(!st.empty());
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_construct_with_allocator();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.cpp
new file mode 100644
index 0000000000000..cfd9ebf87c8a7
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+uint32_t test1_line;
+uint32_t test2_line;
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() {
+  test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret   = std::stacktrace::current();
+  return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() {
+  test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret   = test1();
+  return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
+  auto st = test2();
+  assert(st.size() >= 3);
+  assert(st[0]);
+  assert(st[0].native_handle());
+  assert(st[0].description().contains("test1"));
+  assert(st[0].source_file().contains("basic.cons.pass.cpp"));
+  assert(st[1]);
+  assert(st[1].native_handle());
+  assert(st[1].description().contains("test2"));
+  assert(st[1].source_file().contains("basic.cons.pass.cpp"));
+  assert(st[2]);
+  assert(st[2].native_handle());
+  assert(st[2].description().contains("test_current"));
+  assert(st[2].source_file().contains("basic.cons.pass.cpp"));
+
+  // We unfortunately cannot guarantee the following; in CI, and possibly on users' build machines,
+  // there may not be an up-to-date version of e.g. `addr2line`.
+  // assert(st[0].source_file().ends_with("basic.cons.pass.cpp"));
+  // assert(st[0].source_line() == test1_line);
+  // assert(st[1].source_file().ends_with("basic.cons.pass.cpp"));
+  // assert(st[1].source_line() == test2_line);
+  // assert(st[2].source_file().ends_with("basic.cons.pass.cpp"));
+  // assert(st[2].source_line() == main_line);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_current();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.cpp
new file mode 100644
index 0000000000000..ce35227cc89ba
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  static basic_stacktrace current(size_type skip,
+                                  const allocator_type& alloc = allocator_type()) noexcept;   [2]
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+/*
+  Let t be a stacktrace as-if obtained via basic_stacktrace​::​current(alloc). Let n be t.size().
+  Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
+  t.begin() + min(n, skip), t.end(), and alloc, or an empty basic_stacktrace object if the
+  initialization of frames_ failed.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
+  // Use default allocator for simplicity; alloc is covered above
+  auto st_skip0 = std::stacktrace::current();
+  assert(st_skip0.size() >= 2);
+  auto st_skip1 = std::stacktrace::current(1);
+  assert(st_skip1.size() >= 1);
+  assert(st_skip0.size() == st_skip1.size() + 1);
+  assert(st_skip0[1] == st_skip1[0]);
+  auto st_skip_many = std::stacktrace::current(1 << 20);
+  assert(st_skip_many.empty());
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_current_with_skip();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.cpp
new file mode 100644
index 0000000000000..a999845e92e01
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  static basic_stacktrace current(size_type skip, size_type max_depth,
+                                  const allocator_type& alloc = allocator_type()) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
+  // current stack is: [this function, main, (possibly something else, e.g. `_start` from libc)]
+  // so it's probably 3 functions deep -- but certainly at least 2 deep.
+  auto st = std::stacktrace::current();
+  assert(st.size() >= 2);
+  auto it     = st.begin();
+  auto entry1 = *(it++); // represents this function
+  auto entry2 = *(it++); // represents our caller, `main`
+
+  // get current trace again, but skip the 1st
+  st = std::stacktrace::current(1, 1);
+  assert(st.size() >= 1);
+  assert(*st.begin() == entry2);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_current_with_skip_depth();
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.cpp
similarity index 89%
rename from libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
rename to libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.cpp
index f4d0dfbe0f8ed..69da39d13208f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.cpp
@@ -9,10 +9,34 @@
 // REQUIRES: std-at-least-c++23
 // ADDITIONAL_COMPILE_FLAGS: -g
 
-#include <stacktrace>
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;   [1]
+  static basic_stacktrace current(size_type skip,
+                                  const allocator_type& alloc = allocator_type()) noexcept;   [2]
+  static basic_stacktrace current(size_type skip, size_type max_depth,
+                                  const allocator_type& alloc = allocator_type()) noexcept;   [3]
+
+  basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);            [4]
+  explicit basic_stacktrace(const allocator_type& alloc) noexcept;                            [5]
+
+  basic_stacktrace(const basic_stacktrace& other);                                            [6]
+  basic_stacktrace(basic_stacktrace&& other) noexcept;                                        [7]
+  basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);               [8]
+  basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);                    [9]
+  basic_stacktrace& operator=(const basic_stacktrace& other);                                 [10]
+  basic_stacktrace& operator=(basic_stacktrace&& other)
+    noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+      allocator_traits<Allocator>::is_always_equal::value);                                   [11]
+
+  ~basic_stacktrace();                                                                        [12]
+*/
 
 #include <cassert>
 #include <iostream>
+#include <stacktrace>
 
 /**
  * This file includes tests which ensure any allocations performed by `basic_stacktrace`
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
index ecfdf564846f1..843d25aaa2c3e 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
@@ -8,10 +8,6 @@
 
 // REQUIRES: std-at-least-c++23
 
-#include <stacktrace>
-
-#include <cassert>
-
 /*
   (19.6.6) Hash Support
 
@@ -21,24 +17,20 @@
   The specializations are enabled ([unord.hash]).
 */
 
-int main(int, char**) {
-  /*
-  [1]
-  template<> struct hash<stacktrace_entry>;
-  */
-  std::stacktrace_entry empty_entry;
-  assert(std::hash<std::stacktrace_entry>()(empty_entry) == 0);
+#include <cassert>
+#include <stacktrace>
 
-  /*
-  [2]
-  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;
-  */
+int main(int, char**) {
   std::stacktrace trace; // initially empty
   auto hash_val_empty    = std::hash<std::stacktrace>()(trace);
   trace                  = /* reassign */ std::stacktrace::current();
   auto hash_val_nonempty = std::hash<std::stacktrace>()(trace);
-
   assert(hash_val_empty != hash_val_nonempty);
 
+  std::stacktrace_entry empty_entry;
+  assert(std::hash<std::stacktrace_entry>()(empty_entry) == 0);
+  auto nonempty_entry = trace[0];
+  assert(std::hash<std::stacktrace_entry>()(nonempty_entry) != 0);
+
   return 0;
 }
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
similarity index 97%
rename from libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
rename to libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
index e53890a39c2c4..147ce1f65810d 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
@@ -8,19 +8,20 @@
 
 // REQUIRES: std-at-least-c++23
 
-#include <stacktrace>
-
-#include <cassert>
-
 /*
   (19.6.4.5) Modifiers [stacktrace.basic.mod]
 
+  template<class Allocator>
   void swap(basic_stacktrace& other)
       noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
       allocator_traits<Allocator>::is_always_equal::value);
 
   Effects: Exchanges the contents of *this and other.
 */
+
+#include <cassert>
+#include <stacktrace>
+
 int main(int, char**) {
   std::stacktrace empty;
   auto current = std::stacktrace::current();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
deleted file mode 100644
index e1ef4245b83e9..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-#include <stacktrace>
-
-#include <cassert>
-#include <sstream>
-
-/*
-  (19.6.4.6) Non-member functions
-
-  template<class Allocator>
-    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
-      noexcept(noexcept(a.swap(b)));
-
-  string to_string(const stacktrace_entry& f);
-
-  template<class Allocator>
-    string to_string(const basic_stacktrace<Allocator>& st);
-
-  ostream& operator<<(ostream& os, const stacktrace_entry& f);
-  template<class Allocator>
-    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
-*/
-int main(int, char**) {
-  /*
-  template<class Allocator>
-  void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
-    noexcept(noexcept(a.swap(b)));
-  Effects: Equivalent to a.swap(b).
-  */
-  std::stacktrace empty;
-  auto current = std::stacktrace::current();
-
-  std::stacktrace a(empty);
-  std::stacktrace b(current);
-  assert(a == empty);
-  assert(b == current);
-
-  std::swap(a, b);
-  assert(a == current);
-  assert(b == empty);
-
-  /*
-  string to_string(const stacktrace_entry& f);
-  Returns: A string with a description of f.
-  Recommended practice: The description should provide information about the contained evaluation,
-  including information from f.source_file() and f.source_line().
-  */
-
-  assert(std::to_string(a[0]).contains("main"));
-  assert(std::to_string(a[0]).contains("basic.nonmem.pass"));
-
-  /*
-  template<class Allocator>
-  string to_string(const basic_stacktrace<Allocator>& st);
-  Returns: A string with a description of st.
-  [Note 1: The number of lines is not guaranteed to be equal to st.size(). — end note]
-  */
-
-  assert(std::to_string(a).contains("main"));
-  assert(std::to_string(a).contains("basic.nonmem.pass"));
-
-  /*
-  ostream& operator<<(ostream& os, const stacktrace_entry& f);
-  Effects: Equivalent to: return os << to_string(f);
-  */
-
-  std::stringstream entry_os;
-  entry_os << a[0];
-  assert(entry_os.str() == std::to_string(a[0]));
-
-  /*
-  template<class Allocator>
-    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
-  Effects: Equivalent to: return os << to_string(st);
-  */
-
-  std::stringstream trace_os;
-  trace_os << a;
-  assert(trace_os.str() == std::to_string(a));
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.cpp
new file mode 100644
index 0000000000000..def734fa56697
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.6) Non-member functions
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+*/
+
+#include <cassert>
+#include <sstream>
+#include <stacktrace>
+
+int main(int, char**) {
+  auto a = std::stacktrace::current();
+
+  std::stringstream entry_os;
+  entry_os << a[0];
+  assert(entry_os.str() == std::to_string(a[0]));
+
+  std::stringstream trace_os;
+  trace_os << a;
+  assert(trace_os.str() == std::to_string(a));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
new file mode 100644
index 0000000000000..281af1c245245
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.6) Non-member functions
+
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+      noexcept(noexcept(a.swap(b)));
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) {
+  std::stacktrace empty;
+  auto current = std::stacktrace::current();
+
+  std::stacktrace a(empty);
+  std::stacktrace b(current);
+  assert(a == empty);
+  assert(b == current);
+
+  std::swap(a, b);
+  assert(a == current);
+  assert(b == empty);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.cpp
new file mode 100644
index 0000000000000..91bc9dc9990a1
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.6) Non-member functions
+
+  string to_string(const stacktrace_entry& f);
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) {
+  auto a = std::stacktrace::current();
+
+  assert(std::to_string(a[0]).contains("main"));
+  assert(std::to_string(a[0]).contains("basic.nonmem.pass"));
+
+  assert(std::to_string(a).contains("main"));
+  assert(std::to_string(a).contains("basic.nonmem.pass"));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
deleted file mode 100644
index e88480093536b..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-#include <stacktrace>
-#include <iostream>
-#include <iterator>
-#include <memory>
-
-#include <cassert>
-#include <stdexcept>
-
-/*
-  (19.6.4.3) Observers [stacktrace.basic.obs]
-
-  // [stacktrace.basic.obs], observers
-  allocator_type get_allocator() const noexcept;      [1]
-
-  const_iterator begin() const noexcept;              [2]
-  const_iterator end() const noexcept;                [3]
-  const_reverse_iterator rbegin() const noexcept;     [4]
-  const_reverse_iterator rend() const noexcept;       [5]
-
-  const_iterator cbegin() const noexcept;             [6]
-  const_iterator cend() const noexcept;               [7]
-  const_reverse_iterator crbegin() const noexcept;    [8]
-  const_reverse_iterator crend() const noexcept;      [9]
-
-  bool empty() const noexcept;                        [10]
-  size_type size() const noexcept;                    [11]
-  size_type max_size() const noexcept;                [12]
-
-  const_reference operator[](size_type) const;        [13]
-  const_reference at(size_type) const;                [14]
-*/
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
-
-_LIBCPP_NO_TAIL_CALLS
-int main(int, char**) {
-  std::stacktrace st;
-
-  /*
-    using const_iterator = implementation-defined;
-    The type models random_access_iterator ([iterator.concept.random.access]) and meets the
-    Cpp17RandomAccessIterator requirements ([random.access.iterators]).
-  */
-  static_assert(std::random_access_iterator<decltype(st.begin())>);
-  static_assert(std::random_access_iterator<decltype(st.rbegin())>);
-  static_assert(std::random_access_iterator<decltype(st.cbegin())>);
-  static_assert(std::random_access_iterator<decltype(st.crbegin())>);
-
-  /*
-    allocator_type get_allocator() const noexcept;
-    Returns: frames_.get_allocator().
-  */
-  static_assert(std::same_as<decltype(st.get_allocator()), std::stacktrace::allocator_type>);
-
-  /*
-    const_iterator begin() const noexcept;
-    const_iterator cbegin() const noexcept;
-    Returns: An iterator referring to the first element in frames_. If empty() is true,
-    then it returns the same value as end().
-
-    const_iterator end() const noexcept;
-    const_iterator cend() const noexcept;
-    Returns: The end iterator.
-
-    const_reverse_iterator rbegin() const noexcept;
-    const_reverse_iterator crbegin() const noexcept;
-    Returns: reverse_iterator(cend()).
-
-    const_reverse_iterator rend() const noexcept;
-    const_reverse_iterator crend() const noexcept;
-    Returns: reverse_iterator(cbegin()).
-  */
-
-  // st is initially empty:
-  assert(st.begin() == st.end());
-  assert(st.cbegin() == st.cend());
-  assert(st.rbegin() == st.rend());
-  assert(st.crbegin() == st.crend());
-
-  /*
-    bool empty() const noexcept;
-    Returns: frames_.empty().
-  */
-  assert(st.empty());
-  assert(st.size() == 0);
-
-  // no longer empty:
-  st = test3();
-  assert(!st.empty());
-  assert(st.begin() != st.end());
-  assert(st.cbegin() != st.cend());
-
-  /*
-    size_type size() const noexcept;
-    Returns: frames_.size().
-
-    size_type max_size() const noexcept;
-    Returns: frames_.max_size().
-  */
-  std::cout << st << std::endl;
-  assert(st.size() >= 4);
-  assert(st.max_size() == (std::vector<std::stacktrace_entry, std::allocator<std::stacktrace_entry>>().max_size()));
-
-  /*
-    const_reference operator[](size_type frame_no) const;
-    Preconditions: frame_no < size() is true.
-    Returns: frames_[frame_no].
-    Throws: Nothing.
-  */
-  assert(st[0]);
-  assert(st[1]);
-  assert(st[2]);
-  assert(st[3]);
-
-  /*
-    const_reference at(size_type frame_no) const;
-    Returns: frames_[frame_no].
-    Throws: out_of_range if frame_no >= size().
-  */
-  assert(st.at(0));
-  assert(st.at(1));
-  assert(st.at(2));
-  assert(st.at(3));
-  try {
-    (void)st.at(42);
-    assert(false && "'at' should have thrown range_error");
-  } catch (std::out_of_range const&) {
-    // ok
-  }
-
-  auto f0 = st[0];
-  auto f1 = st[1];
-  auto f2 = st[2];
-  auto f3 = st[3];
-
-  auto fit = st.begin();
-  assert(*fit++ == f0);
-  assert(fit != st.end());
-
-  auto rit = st.rbegin();
-  assert(rit != st.rend());
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
new file mode 100644
index 0000000000000..f9604c761b17c
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_reference at(size_type) const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  auto st = test3();
+
+  assert(st.at(0));
+  assert(st.at(1));
+  assert(st.at(2));
+  assert(st.at(3));
+
+  auto f0 = st.at(0);
+  auto f1 = st.at(1);
+  auto f2 = st.at(2);
+  auto f3 = st.at(3);
+
+  auto it = st.begin();
+  assert(*it++ == f0);
+  assert(it != st.end());
+  assert(*it++ == f1);
+  assert(it != st.end());
+  assert(*it++ == f2);
+  assert(it != st.end());
+  assert(*it++ == f3);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
new file mode 100644
index 0000000000000..db3189d7cb007
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_iterator begin() const noexcept;
+  const_iterator end() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(std::random_access_iterator<decltype(st.begin())>);
+  assert(st.begin() == st.end());
+  // no longer empty:
+  st = test3();
+  assert(!st.empty());
+  assert(st.begin() != st.end());
+  auto f0 = st[0];
+  auto it = st.begin();
+  assert(*it == f0);
+  assert(it != st.end());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
new file mode 100644
index 0000000000000..3d084d45ef7b2
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_iterator cbegin() const noexcept;
+  const_iterator cend() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(std::random_access_iterator<decltype(st.cbegin())>);
+  assert(st.cbegin() == st.cend());
+  // no longer empty:
+  st = test3();
+  assert(st.cbegin() != st.cend());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
new file mode 100644
index 0000000000000..5d4d6fc6737f9
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_reverse_iterator crbegin() const noexcept;
+  const_reverse_iterator crend() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(std::random_access_iterator<decltype(st.crbegin())>);
+  assert(st.crbegin() == st.crend());
+  // no longer empty:
+  st = test3();
+  assert(st.crbegin() != st.crend());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
new file mode 100644
index 0000000000000..dc3c6baa8b978
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  bool empty() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  assert(st.empty());
+  st = test3();
+  assert(!st.empty());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
new file mode 100644
index 0000000000000..edea5cc4e3d1a
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  allocator_type get_allocator() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(std::same_as<decltype(st.get_allocator()), std::stacktrace::allocator_type>);
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
new file mode 100644
index 0000000000000..158a9594f7907
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  size_type max_size() const noexcept;
+*/
+
+#include <cassert>
+#include <memory>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  assert(st.max_size() == (std::vector<std::stacktrace_entry, std::allocator<std::stacktrace_entry>>().max_size()));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
new file mode 100644
index 0000000000000..d837434f8d643
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_reference operator[](size_type) const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  auto st = test3();
+
+  assert(st[0]);
+  assert(st[1]);
+  assert(st[2]);
+  assert(st[3]);
+
+  auto f0 = st[0];
+  auto f1 = st[1];
+  auto f2 = st[2];
+  auto f3 = st[3];
+
+  auto it = st.begin();
+  assert(*it++ == f0);
+  assert(it != st.end());
+  assert(*it++ == f1);
+  assert(it != st.end());
+  assert(*it++ == f2);
+  assert(it != st.end());
+  assert(*it++ == f3);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
new file mode 100644
index 0000000000000..f8446d42b5f03
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  const_reverse_iterator rbegin() const noexcept;
+  const_reverse_iterator rend() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  static_assert(std::random_access_iterator<decltype(st.rbegin())>);
+  assert(st.rbegin() == st.rend());
+  // no longer empty:
+  st      = test3();
+  auto it = st.rbegin();
+  assert(it != st.rend());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
new file mode 100644
index 0000000000000..cbfac43b4beaf
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  size_type size() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+  assert(st.size() == 0);
+  st = test3();
+  assert(st.size() > 0);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
index 089ecccef1196..ac1e4282eeea5 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
@@ -8,12 +8,6 @@
 
 // REQUIRES: std-at-least-c++23
 
-#include <stacktrace>
-#include <iterator>
-
-#include <cassert>
-#include <type_traits>
-
 /*
   (19.6.4) Class template basic_stacktrace [stacktrace.basic]
   (19.6.4.1) Overview                      [stacktrace.basic.overview]
@@ -114,18 +108,12 @@ namespace std {
 
 */
 
-int main(int, char**) {
-  // using value_type = stacktrace_entry;
-  // using const_reference = const value_type&;
-  // using reference = value_type&;
-  // using const_iterator = implementation-defined;  // see [stacktrace.basic.obs]
-  // using iterator = const_iterator;
-  // using reverse_iterator = std::reverse_iterator<iterator>;
-  // using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-  // using difference_type = implementation-defined;
-  // using size_type = implementation-defined;
-  // using allocator_type = Allocator;
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+#include <type_traits>
 
+int main(int, char**) {
   // This test will only verify these member types exist and are
   // defined as expected; their actual behavior is tested in another .cpp.
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
new file mode 100644
index 0000000000000..cda5383e361ce
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.3.5) Comparison [stacktrace.entry.cmp]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr bool operator==(const stacktrace_entry& x,
+                                     const stacktrace_entry& y) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+namespace {
+int func1() { return 41; }
+int func2() { return 42; }
+} // namespace
+
+int main(int, char**) {
+  std::stacktrace_entry a;
+  std::stacktrace_entry b;
+  std::stacktrace_entry c;
+  *(uintptr_t*)(&a) = uintptr_t(&func1);
+  *(uintptr_t*)(&b) = uintptr_t(&func1);
+  *(uintptr_t*)(&c) = uintptr_t(&func2);
+  assert(a == b);
+  assert(a != c);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
similarity index 51%
rename from libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
rename to libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
index 963926b041f03..d7c33c64cb68f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
@@ -8,10 +8,6 @@
 
 // REQUIRES: std-at-least-c++23
 
-#include <stacktrace>
-
-#include <cassert>
-
 /*
   (19.6.3.5) Comparison [stacktrace.entry.cmp]
 
@@ -19,44 +15,33 @@ namespace std {
   class stacktrace_entry {
   public:
     // [stacktrace.entry.cmp], comparison
-    friend constexpr bool operator==(const stacktrace_entry& x,
-                                     const stacktrace_entry& y) noexcept;           // [T11]
     friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
-                                                 const stacktrace_entry& y) noexcept;  // [T12]
-
-    [. . .]
-  };
-}
+                                                 const stacktrace_entry& y) noexcept;
 */
 
+#include <cassert>
+#include <stacktrace>
+
+namespace {
+int func1() { return 41; }
+int func2() { return 42; }
+} // namespace
+
 int main(int, char**) {
-  // Two empty entries
+  auto addr1 = uintptr_t(&func1);
+  auto addr2 = uintptr_t(&func2);
+  assert(addr1 != addr2);
+  if (addr1 > addr2) {
+    std::swap(addr1, addr2);
+  }
 
   std::stacktrace_entry a;
-  assert(a.native_handle() == 0);
-  assert(!a);
-
   std::stacktrace_entry b;
-  assert(b.native_handle() == 0);
-  assert(!b);
-
-  // A non-empty entry.
   std::stacktrace_entry c;
-  ((std::__stacktrace::entry*)(&c))->__addr_ = (uintptr_t)&main;
-  assert(c);
-
-  // [T11]
-  // friend constexpr bool operator==(const stacktrace_entry& x,
-  //                                  const stacktrace_entry& y) noexcept;
-  // "Returns: true if and only if x and y represent the same
-  // stacktrace entry or both x and y are empty."
-  // (We represent "empty" with a native_handle of zero.)
-  assert(a == b);
-  assert(a != c);
+  *(uintptr_t*)(&a) = uintptr_t(addr1);
+  *(uintptr_t*)(&b) = uintptr_t(addr1);
+  *(uintptr_t*)(&c) = uintptr_t(addr2);
 
-  // [T12]
-  // friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
-  //                                              const stacktrace_entry& y) noexcept;
   assert(std::strong_ordering::equal == (a <=> b));
   assert(std::strong_ordering::equivalent == (a <=> b));
   assert(std::strong_ordering::greater == (c <=> a));
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
deleted file mode 100644
index 20f8a24d41b89..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-// ADDITIONAL_COMPILE_FLAGS: -g
-
-#include <cassert>
-#include <stacktrace>
-#include <type_traits>
-
-/*
-  (19.6.3.2) Constructors [stacktrace.entry.cons]
-
-namespace std {
-  class stacktrace_entry {
-  public:
-    // [stacktrace.entry.cons], constructors
-    constexpr stacktrace_entry() noexcept;                                          // [T2]
-    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;             // [T3]
-    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;  // [T4]
-    ~stacktrace_entry();                                                            // [T5]
-
-    [. . .]
-  };
-}
-*/
-
-_LIBCPP_NO_TAIL_CALLS
-int main(int, char**) {
-  // [T2]
-  // constexpr stacktrace_entry() noexcept;
-  // "Postconditions: *this is empty."
-  static_assert(std::is_default_constructible_v<std::stacktrace_entry>);
-  static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
-  std::stacktrace_entry entry_t2;
-  assert(!entry_t2);
-
-  // [T3]
-  // constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
-  static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
-  std::stacktrace_entry entry_t3(entry_t2);
-
-  // [T4]
-  // constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
-  static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
-  std::stacktrace_entry entry_t4;
-  entry_t4 = entry_t2;
-
-  // [T5]
-  // ~stacktrace_entry();
-  std::stacktrace_entry* entry_ptr{nullptr};
-  delete entry_ptr;
-  {
-    auto entry_t5(entry_t4); /* construct and immediately let it go out of scope */
-  }
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.cpp
new file mode 100644
index 0000000000000..c993a6df64508
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+  (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+}
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) noexcept {
+  static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
+  std::stacktrace_entry entry_t2;
+  std::stacktrace_entry entry_t4;
+  entry_t4 = entry_t2;
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.cpp
new file mode 100644
index 0000000000000..85dd6fb7853cc
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+  (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+}
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace_entry entry_t2;
+  static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
+  std::stacktrace_entry entry_t3(entry_t2);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.cpp
new file mode 100644
index 0000000000000..03bd82cfd9c73
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+  (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry() noexcept;
+}
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) noexcept {
+  // "Postconditions: *this is empty."
+  static_assert(std::is_default_constructible_v<std::stacktrace_entry>);
+  static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
+  std::stacktrace_entry entry_t2;
+  assert(!entry_t2);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.cpp
new file mode 100644
index 0000000000000..bf1a23080f307
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.3.3) Observers [stacktrace.entry.obs]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.obs], observers
+    constexpr native_handle_type native_handle() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) noexcept {
+  std::stacktrace_entry entry_t6;
+  assert(entry_t6.native_handle() == 0);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
similarity index 56%
rename from libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
rename to libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
index 229619f9c24a1..0cd3f11260ba7 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
@@ -8,43 +8,25 @@
 
 // REQUIRES: std-at-least-c++23
 
-#include <stacktrace>
-
-#include <cassert>
-
 /*
   (19.6.3.3) Observers [stacktrace.entry.obs]
 
 namespace std {
   class stacktrace_entry {
     // [stacktrace.entry.obs], observers
-    constexpr native_handle_type native_handle() const noexcept;                    // [T6]
-    constexpr explicit operator bool() const noexcept;                              // [T7]
-    
-    [. . .]
-  };
-}
+    constexpr explicit operator bool() const noexcept;
 */
 
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {}
-_LIBCPP_END_NAMESPACE_STD
+#include <cassert>
+#include <stacktrace>
 
 int main(int, char**) {
-  // [T6]
   std::stacktrace_entry entry_t6;
-
-  // constexpr native_handle_type native_handle() const noexcept;
-  assert(entry_t6.native_handle() == 0);
-
-  // [T7]
-  // constexpr explicit operator bool() const noexcept;
   // "Returns: false if and only if *this is empty."
   assert(!entry_t6);
-
   // Now set addr to something nonzero
-  ((std::__stacktrace::entry*)(&entry_t6))->__addr_ = (uintptr_t)&main;
-  assert(entry_t6.native_handle() == (uintptr_t)&main);
+  *(uintptr_t*)(&entry_t6) = uintptr_t(&main);
+  assert(entry_t6.native_handle() == uintptr_t(&main));
   assert(entry_t6);
 
   return 0;
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
index 359d4d5c2ea70..39bc5845e9718 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
@@ -21,28 +21,28 @@
 namespace std {
   class stacktrace_entry {
   public:
-    using native_handle_type = implementation-defined;                              // [T1] [entry.pass.cpp]
+    using native_handle_type = implementation-defined;
 
     // [stacktrace.entry.cons], constructors
-    constexpr stacktrace_entry() noexcept;                                          // [T2] [entry.cons.pass.cpp]
-    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;             // [T3]
-    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;  // [T4]
-    ~stacktrace_entry();                                                            // [T5]
-
+    constexpr stacktrace_entry() noexcept;                                          
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;             
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;  
+    ~stacktrace_entry();                                                            
+    
     // [stacktrace.entry.obs], observers
-    constexpr native_handle_type native_handle() const noexcept;                    // [T6] [entry.obs.pass.cpp]
-    constexpr explicit operator bool() const noexcept;                              // [T7]
-
+    constexpr native_handle_type native_handle() const noexcept;                    
+    constexpr explicit operator bool() const noexcept;                              
+    
     // [stacktrace.entry.query], query
-    string description() const;                                                     // [T8] [entry.query.pass.cpp]
-    string source_file() const;                                                     // [T9]
-    uint_least32_t source_line() const;                                             // [T10]
-
+    string description() const;                                                     
+    string source_file() const;                                                     
+    uint_least32_t source_line() const;                                             
+    
     // [stacktrace.entry.cmp], comparison
     friend constexpr bool operator==(const stacktrace_entry& x,
-                                     const stacktrace_entry& y) noexcept;           // [T11] [entry.cmp.pass.cpp]
+                                     const stacktrace_entry& y) noexcept;           
     friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
-                                                 const stacktrace_entry& y) noexcept;  // [T12]
+                                                 const stacktrace_entry& y) noexcept;
   };
 }
 */
@@ -54,12 +54,7 @@ int main(int, char**) {
   static_assert(std::regular<std::stacktrace_entry>);
   static_assert(std::three_way_comparable<std::stacktrace_entry, std::strong_ordering>);
 
-  // [T1]
   // using native_handle_type = implementation-defined;
-  //
-  // [Implementation note: For our purposes anything that unique identifies this stacktrace_entry
-  // would be good enough.  We'll use a pointer-sized numeric type (to represent location of the
-  // calling instruction).  Internally this is defined as `uintptr_t`.]
   static_assert(sizeof(std::stacktrace_entry::native_handle_type) >= sizeof(void*));
 
   return 0;
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
deleted file mode 100644
index b8fda6dab0fe3..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-// ADDITIONAL_COMPILE_FLAGS: -O0 -g
-
-#include <stacktrace>
-
-#include <cassert>
-#include <iostream>
-#include <string>
-
-/*
-    (19.6.3.4) Query [stacktrace.entry.query]
-
-namespace std {
-  class stacktrace_entry {
-  public:
-    // [stacktrace.entry.query], query
-    string description() const;                                                     // [T8]
-    string source_file() const;                                                     // [T9]
-    uint_least32_t source_line() const;                                             // [T10]
-
-    [. . .]
-  };
-}
-*/
-
-int main(int, char**) {
-  // empty trace entry
-  std::stacktrace_entry e;
-
-  // [T8]
-  // string description() const;
-  auto desc = e.description();
-  assert(desc.c_str()[0] == 0);
-  assert(desc.size() == 0);
-
-  // [T9]
-  // string source_file() const;
-  auto src = e.source_file();
-  assert(src.c_str()[0] == 0);
-  assert(src.size() == 0);
-
-  // [T10]
-  // uint_least32_t source_line() const;
-  assert(e.source_line() == 0);
-
-  // Get the current trace.
-  uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
-  auto trace           = std::stacktrace::current();
-
-  // First entry of this should be `main`.
-  e = trace.at(0);
-  assert(e);
-
-  std::cout << "main is at:      " << (void*)&main << std::endl;
-  std::cout << "e.native_handle: " << (void*)e.native_handle() << std::endl;
-  std::cout << "e.description:   " << e.description() << std::endl;
-  std::cout << "e.source_file:   " << e.source_file() << std::endl;
-  std::cout << "e.source_line:   " << e.source_line() << std::endl;
-
-  std::cout << trace << std::endl;
-
-  assert(e.native_handle());
-  assert(e.native_handle() >= (uintptr_t)&main);
-  assert(e.description() == "main" || e.description() == "_main");
-  // Even if we cannot get the debug info or call out to llvm-addr2line,
-  // we should at least get the executable filename, e.g. `entry.query.pass.cpp.dir/t.exe`
-  assert(!e.source_file().empty());
-  assert(e.source_file().contains("entry.query.pass.cpp"));
-
-  // These might not work at testing time, so disable.
-  // Complete tests checking the filename:line part are in `std/diagnostics/stacktrace`
-  //   assert(e.source_file().ends_with("entry.query.pass.cpp"));
-  //   assert(e.source_line() == line_number);
-  (void)line_number;
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query/description.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query/description.pass.cpp
new file mode 100644
index 0000000000000..2493e2bd8b490
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query/description.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
+/*
+    (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.query], query
+    string description() const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <string>
+
+int main(int, char**) {
+  std::stacktrace_entry e;
+  auto desc = e.description();
+  assert(desc.empty());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp
new file mode 100644
index 0000000000000..fc2d5efb6a31e
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
+/*
+    (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.query], query
+    string source_file() const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <string>
+
+int main(int, char**) {
+  std::stacktrace_entry e;
+  auto src = e.source_file();
+  assert(src.empty());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp
new file mode 100644
index 0000000000000..e601b74f032f0
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
+/*
+    (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.query], query
+    uint_least32_t source_line() const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) {
+  std::stacktrace_entry e;
+  assert(e.source_line() == 0);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp
new file mode 100644
index 0000000000000..97aa20d562cf6
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+  (19.6.5) Formatting support [stacktrace.format]
+
+  template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+*/
+
+#include <cassert>
+// #include <stacktrace>
+
+int main(int, char**) {
+  /*
+    For formatter<basic_stacktrace<Allocator>>, format-spec is empty.
+    
+    A basic_stacktrace<Allocator> object s is formatted as if by copying to_string(s) through the
+    output iterator of the context.
+  */
+
+  // TODO(stacktrace23): needs `formatter`
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.cpp
similarity index 65%
rename from libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
rename to libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.cpp
index 3db4920b3c388..73a9c43ec52ab 100644
--- a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.cpp
@@ -8,21 +8,17 @@
 
 // REQUIRES: std-at-least-c++23
 
-#include <cassert>
-#include <stacktrace>
-
 /*
   (19.6.5) Formatting support [stacktrace.format]
 
-  template<> struct formatter<stacktrace_entry>;                            [1]
-  template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;  [2]
+  template<> struct formatter<stacktrace_entry>;              
 */
 
+#include <cassert>
+// #include <stacktrace>
+
 int main(int, char**) {
   /*
-    [1]
-    template<> struct formatter<stacktrace_entry>;
-
     formatter<stacktrace_entry> interprets format-spec as a stacktrace-entry-format-spec.
     The syntax of format specifications is as follows:
 
@@ -37,17 +33,5 @@ int main(int, char**) {
 
   // TODO(stacktrace23): needs `formatter`
 
-  /*
-    [2]
-    template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
-    
-    For formatter<basic_stacktrace<Allocator>>, format-spec is empty.
-    
-    A basic_stacktrace<Allocator> object s is formatted as if by copying to_string(s) through the
-    output iterator of the context.
-  */
-
-  // TODO(stacktrace23): needs `formatter`
-
   return 0;
 }
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
deleted file mode 100644
index f6c44bf8fe44f..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-/*
-
-// (19.6.2) Header <stacktrace> synopsis [stacktrace.syn]
-
-namespace std {
-
-  // [stacktrace.entry], class stacktrace_entry
-  class stacktrace_entry;                                               [1]
-
-  // [stacktrace.basic], class template basic_stacktrace
-  template<class Allocator>
-    class basic_stacktrace;                                             [2]
-
-  // basic_stacktrace typedef-names
-  using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;     [3]
-
-  // [stacktrace.basic.nonmem], non-member functions
-  template<class Allocator>
-    void swap(basic_stacktrace<Allocator>& a,
-              basic_stacktrace<Allocator>& b)
-                noexcept(noexcept(a.swap(b)));                          [4]
-
-  string to_string(const stacktrace_entry& f);                          [5]
-
-  template<class Allocator>
-    string to_string(const basic_stacktrace<Allocator>& st);            [6]
-
-  ostream& operator<<(ostream& os, const stacktrace_entry& f);          [7]
-  template<class Allocator>
-    ostream& operator<<(ostream& os,
-                        const basic_stacktrace<Allocator>& st);         [8]
-
-  // [stacktrace.format], formatting support
-  template<> struct formatter<stacktrace_entry>;                        [9]
-  template<class Allocator>
-    struct formatter<basic_stacktrace<Allocator>>;                      [10]
-
-  namespace pmr {
-    using stacktrace =
-        basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;      [11]
-  }
-
-  // [stacktrace.basic.hash], hash support
-  template<> struct hash<stacktrace_entry>;                             [12]
-  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;   [13]
-}
-*/
-
-#include <__config>
-#include <cassert>
-#include <memory>
-#include <stacktrace>
-#include <type_traits>
-
-static_assert(__cpp_lib_stacktrace == 202011L);
-
-int main(int, char**) {
-  // Very basic tests to ensure the required things are declared.
-  // Only checking for types' and functions' existence, parameter and return types,
-  // and type-completeness.  Functional tests exist in the other .cpp's in this directory.
-
-  using Alloc = std::allocator<std::stacktrace_entry>;
-
-  // [1]
-  // [stacktrace.entry], class stacktrace_entry
-  // class stacktrace_entry;
-  using T1 = std::stacktrace_entry;
-  assert(std::is_constructible_v<T1>);
-
-  // [2]
-  // [stacktrace.basic], class template basic_stacktrace
-  // template<class Allocator>
-  //   class basic_stacktrace;
-  using T2 = std::basic_stacktrace<Alloc>;
-  assert(std::is_constructible_v<T2>);
-
-  // [3]
-  // basic_stacktrace typedef-names
-  // using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
-  using T3 = std::stacktrace;
-  static_assert(std::is_same_v<T3, std::basic_stacktrace<std::allocator<std::stacktrace_entry>>>);
-
-  // [4]
-  // [stacktrace.basic.nonmem], non-member functions
-  // template<class Allocator>
-  //   void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
-  //     noexcept(noexcept(a.swap(b)));
-  std::basic_stacktrace<Alloc> a;
-  std::basic_stacktrace<Alloc> b;
-  std::swap(a, b);
-
-  // [5]
-  // string to_string(const stacktrace_entry& f);
-  using T5 = decltype(std::to_string(std::stacktrace_entry()));
-  static_assert(std::is_same_v<std::string, T5>);
-
-  // [6]
-  // template<class Allocator>
-  // string to_string(const basic_stacktrace<Allocator>& st);
-  using T6 = decltype(std::to_string(std::basic_stacktrace<Alloc>()));
-  static_assert(std::is_same_v<std::string, T6>);
-
-  // [7]
-  // ostream& operator<<(ostream& os, const stacktrace_entry& f);
-  std::ostream* os;
-  using T7 = decltype(operator<<(*os, std::stacktrace_entry()));
-  static_assert(std::is_same_v<std::ostream&, T7>);
-
-  // [8]
-  // template<class Allocator>
-  //   ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
-  using T8 = decltype(operator<<(*os, std::basic_stacktrace<Alloc>()));
-  static_assert(std::is_same_v<std::ostream&, T8>);
-
-  // [9]
-  // template<> struct formatter<stacktrace_entry>;
-  //   using T9 = std::formatter<std::stacktrace_entry>;
-  //   static_assert(std::is_constructible_v<T9>);
-
-  // TODO(stacktrace23): needs `formatter`
-
-  // [10]
-  // template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
-  //   using T10 = std::formatter<std::basic_stacktrace<Alloc>>;
-  //   static_assert(std::is_constructible_v<T10>);
-
-  // TODO(stacktrace23): needs `formatter`
-
-  // [11]
-  // namespace pmr { using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>; }
-  using AllocPMR = std::pmr::polymorphic_allocator<std::stacktrace_entry>;
-  using BasicPMR = std::basic_stacktrace<AllocPMR>;
-  using T11      = std::pmr::stacktrace;
-  static_assert(std::is_same_v<T11, BasicPMR>);
-
-  return 0;
-}

>From 8fe6ee04861f112e5de599f5bd837f850df88261 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 24 Apr 2025 10:19:08 -0400
Subject: [PATCH 16/37] Minor: formatting

---
 .../support.limits.general/stacktrace.version.compile.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 461087467a270..8d8ce5665565b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,4 +105,3 @@
 #endif // TEST_STD_VER > 23
 
 // clang-format on
-

>From 09abca78071538c43791d9fbdacd04098ab30d53 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 24 Apr 2025 10:28:10 -0400
Subject: [PATCH 17/37] Remove changes not related to this (probably a previous
 bad merge)

---
 .../test/libcxx/transitive_includes/cxx03.csv   |  5 -----
 .../test/libcxx/transitive_includes/cxx11.csv   |  5 -----
 .../test/libcxx/transitive_includes/cxx14.csv   |  5 -----
 .../test/libcxx/transitive_includes/cxx17.csv   |  5 -----
 .../gn/build/sync_source_lists_from_cmake.py    | 12 +++++++++---
 llvm/utils/gn/build/toolchain/BUILD.gn          | 17 -----------------
 6 files changed, 9 insertions(+), 40 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index cfcf7bc7c4c19..c2eb5b44e8d7a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,19 +125,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2076,7 +2072,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 534776e6f0526..332cb62f35b5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2089,7 +2085,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/llvm/utils/gn/build/sync_source_lists_from_cmake.py b/llvm/utils/gn/build/sync_source_lists_from_cmake.py
index aada38bebd421..4cd30aa410864 100755
--- a/llvm/utils/gn/build/sync_source_lists_from_cmake.py
+++ b/llvm/utils/gn/build/sync_source_lists_from_cmake.py
@@ -61,13 +61,13 @@ def git_out(args):
     gn_files = git_out(["ls-files", "*BUILD.gn"]).splitlines()
 
     # Matches e.g. |   "foo.cpp",|, captures |foo| in group 1.
-    gn_cpp_re = re.compile(r'^\s*"([^$"]+\.(?:cpp|c|h|S))",$', re.MULTILINE)
+    gn_cpp_re = re.compile(r'^\s*"([^$"]+\.(?:cpp|c|h|mm|S))",$', re.MULTILINE)
     # Matches e.g. |   bar_sources = [ "foo.cpp" ]|, captures |foo| in group 1.
     gn_cpp_re2 = re.compile(
-        r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|S))" ]$', re.MULTILINE
+        r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|mm|S))" ]$', re.MULTILINE
     )
     # Matches e.g. |   foo.cpp|, captures |foo| in group 1.
-    cmake_cpp_re = re.compile(r"^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|S))$", re.MULTILINE)
+    cmake_cpp_re = re.compile(r"^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|mm|S))$", re.MULTILINE)
 
     changes_by_rev = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
 
@@ -108,6 +108,12 @@ def get_sources(source_re, text):
 
         gn_cpp = get_sources(gn_cpp_re, open(gn_file).read())
         gn_cpp |= get_sources(gn_cpp_re2, open(gn_file).read())
+
+        sources_file = os.path.join(os.path.dirname(gn_file), "sources.gni")
+        if os.path.exists(sources_file):
+            gn_cpp |= get_sources(gn_cpp_re, open(sources_file).read())
+            gn_cpp |= get_sources(gn_cpp_re2, open(sources_file).read())
+
         cmake_cpp = get_sources(cmake_cpp_re, open(cmake_file).read())
 
         if gn_cpp == cmake_cpp:
diff --git a/llvm/utils/gn/build/toolchain/BUILD.gn b/llvm/utils/gn/build/toolchain/BUILD.gn
index ce2e6df1b69f5..00d9df12f5a97 100644
--- a/llvm/utils/gn/build/toolchain/BUILD.gn
+++ b/llvm/utils/gn/build/toolchain/BUILD.gn
@@ -71,23 +71,6 @@ template("unix_toolchain") {
       default_output_dir = "{{root_out_dir}}/lib"
     }
 
-    if (current_os == "ios" || current_os == "mac") {
-      # gn < 1693 (e214b5d35898) doesn't support |frameworks|, requiring
-      # frameworks to be listed in |libs|, but gn >= 1808 (3028c6a426a4) forbids
-      # frameworks from appearing in |libs|. This assertion provides a helpful
-      # cue to upgrade, and is much more user-friendly than the failure that
-      # occurs when an older gn encounters |frameworks|.
-      #
-      # gn_version doesn’t actually exist in gn < 1709 (52cb644a3fb4), and
-      # defined(gn_version) doesn't actually work as expected
-      # (https://crbug.com/gn/183), so 1709 is the true minimum enforced by
-      # this construct, and if gn_version is not available, this line will still
-      # be blamed, making the resolution somewhat discoverable.
-      assert(gn_version >= 1693,
-             "Your GN is too old! " +
-                 "Update it, perhaps by running llvm/utils/gn/get.py")
-    }
-
     # Make these apply to all tools below.
     lib_switch = "-l"
     lib_dir_switch = "-L"

>From 4181d27531099d71e6ae6c8672ad5faafb252863 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 24 Apr 2025 10:29:57 -0400
Subject: [PATCH 18/37] Correcting doc for
 LIBCXX_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME

---
 libcxx/docs/VendorDocumentation.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/VendorDocumentation.rst b/libcxx/docs/VendorDocumentation.rst
index 11d67458b4df9..bde27aa8c6dab 100644
--- a/libcxx/docs/VendorDocumentation.rst
+++ b/libcxx/docs/VendorDocumentation.rst
@@ -187,7 +187,7 @@ General purpose options
 
 .. option:: LIBCXX_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME:BOOL
 
-   **Default**: ``OFF``
+   **Default**: ``ON``
 
    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

>From bef2021556d5eebf445df2031ebac23a23cb0a6f Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 16 Jan 2025 18:32:09 -0500
Subject: [PATCH 19/37] Add C++23 stacktrace

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

diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index dbacb44735d08..2a8c9600124c9 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/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 34895f334733c..efe74aab1a83d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -994,10 +994,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/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h
index 09ad401a48d60..0a6e4360201ba 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -571,7 +571,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 virtual void _anchor_vfunc();
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t __align) override {
+    // Avoiding "assert" in a system header, but we expect this to hold:
+    // assert(__align <= alignof(std::max_align_t));
+    (void)__align;
+    return __alloc_func_(__size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t __align) override {
+    (void)__align;
+    __dealloc_func_((std::byte*)__ptr, __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
+    auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
+    return __rhs_ba && (__rhs_ba->__alloc_opaque_ == __alloc_opaque_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
+    std::pmr::string __ret{this};
+    if (__size) {
+      __ret.reserve(__size);
+      __ret[0] = 0;
+    }
+    return __ret;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::string hex_string(uintptr_t __addr) {
+    char __ret[19]; // "0x" + 16 digits + NUL
+    auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
+    return {__ret, size_t(__size), this};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::string u64_string(uintptr_t __val) {
+    char __ret[21]; // 20 digits max + NUL
+    auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
+    return {__ret, size_t(__size), this};
+  }
+
+  template <typename _Tp>
+  _LIBCPP_HIDE_FROM_ABI std::pmr::list<_Tp> new_list() {
+    return std::pmr::list<_Tp>{this};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI std::pmr::list<std::pmr::string> new_string_list() { return new_list<std::pmr::string>(); }
+
+private:
+  std::function<std::byte*(size_t)> __alloc_func_;
+  std::function<void(std::byte*, size_t)> __dealloc_func_;
+  /** Only used for equality */
+  void const* __alloc_opaque_;
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
diff --git a/libcxx/include/experimental/__stacktrace/detail/context.h b/libcxx/include/experimental/__stacktrace/detail/context.h
new file mode 100644
index 0000000000000..df791b37b386d
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/context.h
@@ -0,0 +1,53 @@
+// -*- 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_CONTEXT
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+
+#include <__config>
+#include <__memory_resource/memory_resource.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__vector/pmr.h>
+#include <__vector/vector.h>
+#include <cstddef>
+#include <string>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct _LIBCPP_HIDE_FROM_ABI alloc;
+struct _LIBCPP_HIDE_FROM_ABI entry;
+
+namespace __stacktrace {
+
+/** Represents the state of the current in-progress stacktrace operation.  This includes
+the working list of `entry` objects, as well as the caller-provided allocator (wrapped
+in `polymorphic_allocator`) and a handful of utility functions involving that allocator. */
+struct _LIBCPP_HIDE_FROM_ABI context {
+  /** Encapsulates and type-removes the caller's allocator. */
+  alloc& __alloc_;
+
+  /** The working set of entry data; these will later be copied to their final `stacktrace_entry` objects. */
+  std::pmr::list<entry> __entries_;
+
+  /** Path to this process's main executable. */
+  std::pmr::string __main_prog_path_;
+
+  _LIBCPP_HIDE_FROM_ABI explicit context(alloc& __byte_alloc)
+      : __alloc_(__byte_alloc), __entries_(&__alloc_), __main_prog_path_(__alloc_.new_string()) {}
+
+  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void do_stacktrace(size_t __skip, size_t __max_depth);
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
diff --git a/libcxx/include/experimental/__stacktrace/detail/entry.h b/libcxx/include/experimental/__stacktrace/detail/entry.h
new file mode 100644
index 0000000000000..2bab14228b508
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/entry.h
@@ -0,0 +1,44 @@
+// -*- 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_DETAIL_ENTRY
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+
+#include <__config>
+#include <cstdint>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+/** Contains fields which will be used to generate the final `std::stacktrace_entry`.
+This is an intermediate object which owns strings allocated via the caller-provided allocator,
+which are later freed back to that allocator and converted to plain `std::string`s. */
+struct _LIBCPP_HIDE_FROM_ABI entry {
+  /** Caller's / faulting insn's address, including ASLR/slide */
+  uintptr_t __addr_{};
+
+  /** the address minus its image's slide offset */
+  uintptr_t __addr_unslid_{};
+
+  /** entry's description (symbol name) */
+  std::pmr::string __desc_{};
+
+  /** source file name */
+  std::pmr::string __file_{};
+
+  /** line number in source file */
+  uint32_t __line_{};
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
diff --git a/libcxx/include/experimental/__stacktrace/detail/to_string.h b/libcxx/include/experimental/__stacktrace/detail/to_string.h
new file mode 100644
index 0000000000000..47feddf07faf9
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/to_string.h
@@ -0,0 +1,46 @@
+// -*- 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_TO_STRING
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+
+#include <__config>
+#include <__fwd/sstream.h>
+#include <__ostream/basic_ostream.h>
+#include <cstddef>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Allocator>
+class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace;
+
+class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry;
+
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI __to_string {
+  _LIBCPP_HIDE_FROM_ABI string operator()(stacktrace_entry const& __entry);
+
+  _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
+
+  _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
+
+  string operator()(std::stacktrace_entry const* __entries, size_t __count);
+
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
+    return (*this)(__st.__entries_.data(), __st.__entries_.size());
+  }
+};
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
diff --git a/libcxx/include/experimental/__stacktrace/stacktrace_entry.h b/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
new file mode 100644
index 0000000000000..be8abb3ea91b2
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
@@ -0,0 +1,111 @@
+// -*- 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_ENTRY
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
+
+#include <__cstddef/byte.h>
+#include <__cstddef/ptrdiff_t.h>
+#include <__cstddef/size_t.h>
+#include <__format/formatter.h>
+#include <__functional/function.h>
+#include <__functional/hash.h>
+#include <__fwd/format.h>
+#include <__fwd/ostream.h>
+#include <__fwd/sstream.h>
+#include <__fwd/vector.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 <__utility/move.h>
+#include <__vector/pmr.h>
+#include <__vector/swap.h>
+#include <__vector/vector.h>
+#include <cstdint>
+#include <string>
+
+#include "experimental/__stacktrace/detail/entry.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
+public:
+  // (19.6.3.1) Overview [stacktrace.entry.overview]
+  using native_handle_type = uintptr_t;
+
+  // (19.6.3.2) [stacktrace.entry.cons], constructors
+  _LIBCPP_EXPORTED_FROM_ABI ~stacktrace_entry() noexcept                                            = default;
+  _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry() noexcept                                   = default;
+  _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry(const stacktrace_entry&) noexcept            = default;
+  _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry& operator=(const stacktrace_entry&) noexcept = default;
+
+  // (19.6.3.3) [stacktrace.entry.obs], observers
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr native_handle_type native_handle() const noexcept {
+    return __addr_;
+  }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr explicit operator bool() const noexcept {
+    return native_handle() != 0;
+  }
+
+  // (19.6.3.4) [stacktrace.entry.query], query
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string description() const { return __desc_; }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string source_file() const { return __file_; }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI uint_least32_t source_line() const { return __line_; }
+
+  // (19.6.3.5) [stacktrace.entry.cmp], comparison
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr bool
+  operator==(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+    return __x.native_handle() == __y.native_handle();
+  }
+
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr strong_ordering
+  operator<=>(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+    return __x.native_handle() <=> __y.native_handle();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI explicit stacktrace_entry(__stacktrace::entry&& __e)
+      : __addr_(__e.__addr_), __desc_(std::move(__e.__desc_)), __file_(std::move(__e.__file_)), __line_(__e.__line_) {}
+
+private:
+  uintptr_t __addr_{};
+  std::string __desc_{};
+  std::string __file_{};
+  uint_least32_t __line_{};
+};
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry);
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry);
+
+// (19.6.5)
+// Formatting support [stacktrace.format]
+
+// TODO(stacktrace23): needs `formatter`
+template <>
+struct _LIBCPP_EXPORTED_FROM_ABI formatter<stacktrace_entry>;
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <>
+struct _LIBCPP_EXPORTED_FROM_ABI hash<stacktrace_entry> {
+  [[nodiscard]] size_t operator()(stacktrace_entry const& __entry) const noexcept {
+    auto __addr = __entry.native_handle();
+    return hash<uintptr_t>()(__addr);
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
diff --git a/libcxx/include/experimental/stacktrace b/libcxx/include/experimental/stacktrace
new file mode 100644
index 0000000000000..9e1f7259ca3da
--- /dev/null
+++ b/libcxx/include/experimental/stacktrace
@@ -0,0 +1,191 @@
+// -*- 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
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE
+
+/*
+  Header <stacktrace> synopsis
+  (19.6.2)
+
+#include <compare>  // see [compare.syn]
+
+namespace std {
+  // [stacktrace.entry], class stacktrace_entry
+  class stacktrace_entry;
+
+  // [stacktrace.basic], class template basic_stacktrace
+  template<class Allocator>
+    class basic_stacktrace;
+
+  // basic_stacktrace typedef-names
+  using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+
+  // [stacktrace.basic.nonmem], non-member functions
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+      noexcept(noexcept(a.swap(b)));
+
+  string to_string(const stacktrace_entry& f);
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+
+  // [stacktrace.format], formatting support
+  template<> struct formatter<stacktrace_entry>;
+  template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+
+  namespace pmr {
+    using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
+  }
+
+  // [stacktrace.basic.hash], hash support
+  template<class T> struct hash;
+  template<> struct hash<stacktrace_entry>;
+  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;
+}
+
+// [stacktrace.entry]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    using native_handle_type = implementation-defined;
+
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry() noexcept;
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+
+    ~stacktrace_entry();
+
+    // [stacktrace.entry.obs], observers
+    constexpr native_handle_type native_handle() const noexcept;
+    constexpr explicit operator bool() const noexcept;
+
+    // [stacktrace.entry.query], query
+    string description() const;
+    string source_file() const;
+    uint_least32_t source_line() const;
+
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr bool operator==(const stacktrace_entry& x,
+                                     const stacktrace_entry& y) noexcept;
+    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+                                                 const stacktrace_entry& y) noexcept;
+  };
+}
+
+// [stacktrace.basic]
+
+namespace std {
+  template<class Allocator>
+  class basic_stacktrace {
+  public:
+    using value_type = stacktrace_entry;
+    using const_reference = const value_type&;
+    using reference = value_type&;
+    using const_iterator = implementation-defined;  // see [stacktrace.basic.obs]
+    using iterator = const_iterator;
+    using reverse_iterator = reverse_iterator<iterator>;
+    using const_reverse_iterator = reverse_iterator<const_iterator>;
+    using difference_type = implementation-defined;
+    using size_type = implementation-defined;
+    using allocator_type = Allocator;
+
+    // [stacktrace.basic.cons], creation and assignment
+    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+    static basic_stacktrace current(size_type skip, size_type max_depth,
+                                    const allocator_type& alloc = allocator_type()) noexcept;
+
+    basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+    explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+
+    basic_stacktrace(const basic_stacktrace& other);
+    basic_stacktrace(basic_stacktrace&& other) noexcept;
+    basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+    basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+    basic_stacktrace& operator=(const basic_stacktrace& other);
+    basic_stacktrace& operator=(basic_stacktrace&& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+    ~basic_stacktrace();
+
+    // [stacktrace.basic.obs], observers
+    allocator_type get_allocator() const noexcept;
+
+    const_iterator begin() const noexcept;
+    const_iterator end() const noexcept;
+    const_reverse_iterator rbegin() const noexcept;
+    const_reverse_iterator rend() const noexcept;
+
+    const_iterator cbegin() const noexcept;
+    const_iterator cend() const noexcept;
+    const_reverse_iterator crbegin() const noexcept;
+    const_reverse_iterator crend() const noexcept;
+
+    bool empty() const noexcept;
+    size_type size() const noexcept;
+    size_type max_size() const noexcept;
+
+    const_reference operator[](size_type) const;
+    const_reference at(size_type) const;
+
+    // [stacktrace.basic.cmp], comparisons
+    template<class Allocator2>
+    friend bool operator==(const basic_stacktrace& x,
+                           const basic_stacktrace<Allocator2>& y) noexcept;
+    template<class Allocator2>
+    friend strong_ordering operator<=>(const basic_stacktrace& x,
+                                       const basic_stacktrace<Allocator2>& y) noexcept;
+
+    // [stacktrace.basic.mod], modifiers
+    void swap(basic_stacktrace& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+        allocator_traits<Allocator>::is_always_equal::value);
+
+  private:
+    vector<value_type, allocator_type> frames_;         // exposition only
+  };
+}
+
+*/
+
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_STD_VER >= 23
+
+#    include <compare> // [stacktrace.syn]
+
+#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#      pragma GCC system_header
+#    endif
+
+_LIBCPP_PUSH_MACROS
+#    include <__undef_macros>
+
+#    include "experimental/__stacktrace/basic_stacktrace.h"
+#    include "experimental/__stacktrace/stacktrace_entry.h"
+
+_LIBCPP_POP_MACROS
+
+#  endif // _LIBCPP_STD_VER >= 23
+#endif   // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 9187e49031d70..d1a8ca74c09c2 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2238,6 +2238,16 @@ module std [system] {
       header "experimental/simd"
       export *
     }
+    module stacktrace {
+      private header "experimental/__stacktrace/basic_stacktrace.h"
+      private header "experimental/__stacktrace/detail/alloc.h"
+      private header "experimental/__stacktrace/detail/context.h"
+      private header "experimental/__stacktrace/detail/entry.h"
+      private header "experimental/__stacktrace/detail/to_string.h"
+      private header "experimental/__stacktrace/stacktrace_entry.h"
+      header "experimental/stacktrace"
+      export *
+    }
   }
 
   // Implementation detail headers that are private to libc++. These modules
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 45632d95daf45..00be89632213d 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -315,6 +315,24 @@ add_custom_target(cxx DEPENDS ${LIBCXX_BUILD_TARGETS})
 # Build the experimental static library
 set(LIBCXX_EXPERIMENTAL_SOURCES
   experimental/keep.cpp
+  experimental/stacktrace/alloc.cpp
+  experimental/stacktrace/common/debug.cpp
+  experimental/stacktrace/common/fd.cpp
+  experimental/stacktrace/context.cpp
+  experimental/stacktrace/linux/linux-dl.cpp
+  experimental/stacktrace/linux/linux-elf.cpp
+  experimental/stacktrace/linux/linux-sym.cpp
+  experimental/stacktrace/osx/osx.cpp
+  experimental/stacktrace/stacktrace.cpp
+  experimental/stacktrace/tools/addr2line.cpp
+  experimental/stacktrace/tools/atos.cpp
+  experimental/stacktrace/tools/llvm_symbolizer.cpp
+  experimental/stacktrace/tools/toolspawner.cpp
+  experimental/stacktrace/unwind/unwind.cpp
+  experimental/stacktrace/windows/dbghelp_dll.cpp
+  experimental/stacktrace/windows/dll.cpp
+  experimental/stacktrace/windows/psapi_dll.cpp
+  experimental/stacktrace/windows/win_impl.cpp
   )
 
 if (LIBCXX_PSTL_BACKEND STREQUAL "libdispatch")
diff --git a/libcxx/src/experimental/stacktrace/alloc.cpp b/libcxx/src/experimental/stacktrace/alloc.cpp
new file mode 100644
index 0000000000000..956e01b312d3b
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/alloc.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+void alloc::_anchor_vfunc() {}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/config.h b/libcxx/src/experimental/stacktrace/common/config.h
new file mode 100644
index 0000000000000..0915b57463deb
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/config.h
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_CONFIG_H
+#define _LIBCPP_STACKTRACE_CONFIG_H
+
+#include <__config>
+#include <__config_site>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+#if __has_include(<unwind.h>)
+#  define _LIBCPP_STACKTRACE_COLLECT_UNWIND
+#endif
+
+#if defined(__APPLE__)
+#  define _LIBCPP_STACKTRACE_APPLE
+#endif
+
+#if defined(__linux__)
+#  define _LIBCPP_STACKTRACE_LINUX
+#endif
+
+#if __has_include(<spawn.h>) && _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
+#  define _LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS
+#endif
+
+#if __has_include(<windows.h>) && __has_include(<dbghelp.h>) && __has_include(<psapi.h>)
+#  define _LIBCPP_STACKTRACE_WINDOWS
+#endif
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_CONFIG_H
diff --git a/libcxx/src/experimental/stacktrace/common/debug.cpp b/libcxx/src/experimental/stacktrace/common/debug.cpp
new file mode 100644
index 0000000000000..4f113e10c0c23
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/debug.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "debug.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+debug::~debug() = default;
+
+debug::dummy_ostream::~dummy_ostream() = default;
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/debug.h b/libcxx/src/experimental/stacktrace/common/debug.h
new file mode 100644
index 0000000000000..0cee301d42d8b
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/debug.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_DEBUG_H
+#define _LIBCPP_STACKTRACE_DEBUG_H
+
+#include <__config>
+#include <iostream>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/** Debug-message output stream.  If `LIBCXX_STACKTRACE_DEBUG` is defined in the environment
+or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
+otherwise its does nothing by returning a dummy stream. */
+struct debug : std::ostream {
+  virtual ~debug();
+
+  static bool enabled() {
+#if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
+    return true;
+#else
+    static bool ret = [] {
+      auto const* val = getenv("LIBCXX_STACKTRACE_DEBUG");
+      return val && !strncmp(val, "1", 1);
+    }();
+    return ret;
+#endif
+  }
+
+  /** No-op output stream. */
+  struct dummy_ostream final : std::ostream {
+    virtual ~dummy_ostream();
+    friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
+  };
+
+  friend std::ostream& operator<<(debug& dp, auto const& val) {
+    static dummy_ostream kdummy;
+    if (!enabled()) {
+      return kdummy;
+    }
+    std::cerr << val;
+    return std::cerr;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_DEBUG_H
diff --git a/libcxx/src/experimental/stacktrace/common/failed.h b/libcxx/src/experimental/stacktrace/common/failed.h
new file mode 100644
index 0000000000000..e53eb1123aabc
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/failed.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_FAILED_H
+#define _LIBCPP_STACKTRACE_FAILED_H
+
+#include <stdexcept>
+
+// TODO(stacktrace23) Use std::expected instead?
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct failed : std::runtime_error {
+  virtual ~failed() = default;
+  int errno_{0};
+  failed() : std::runtime_error({}) {}
+  failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_FAILED_H
diff --git a/libcxx/src/experimental/stacktrace/common/fd.cpp b/libcxx/src/experimental/stacktrace/common/fd.cpp
new file mode 100644
index 0000000000000..b7728ea7826dd
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/fd.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "fd.h"
+#include "failed.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+fd_streambuf::~fd_streambuf() = default;
+fd_istream::~fd_istream()     = default;
+
+int fd_streambuf::underflow() {
+  int bytesRead = ::read(fd_, buf_, size_);
+  if (bytesRead < 0) {
+    throw failed("I/O error reading from child process", errno);
+  }
+  if (bytesRead == 0) {
+    return traits_type::eof();
+  }
+  setg(buf_, buf_, buf_ + bytesRead);
+  return int(*buf_);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/fd.h b/libcxx/src/experimental/stacktrace/common/fd.h
new file mode 100644
index 0000000000000..ff003e16a6bd8
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/fd.h
@@ -0,0 +1,122 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_FD_H
+#define _LIBCPP_STACKTRACE_FD_H
+
+#include <__config>
+#include <cassert>
+#include <cerrno>
+#include <cstdio>
+#include <iostream>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <utility>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/** Encapsulates a plain old file descriptor `int`.  Avoids copies in order to
+force some component to "own" this, although it's freely convertible back to
+integer form.  Default-constructed, closed, and moved-out-of instances will have
+the invalid fd `-1`. */
+class fd {
+  int fd_{-1};
+
+public:
+  fd() : fd(-1) {}
+  fd(int fdint) : fd_(fdint) {}
+
+  fd(fd const&)            = delete;
+  fd& operator=(fd const&) = delete;
+
+  fd(fd&& rhs) {
+    if (&rhs != this) {
+      std::exchange(fd_, rhs.fd_);
+    }
+  }
+
+  fd& operator=(fd&& rhs) { return *new (this) fd(std::move(rhs)); }
+
+  ~fd() { close(); }
+
+  /** Returns true IFF fd is above zero */
+  bool valid() const { return fd_ > 0; }
+
+  /* implicit */ operator int() const {
+    auto ret = fd_;
+    assert(ret > 0);
+    return ret;
+  }
+
+  void close() {
+    int fd_old = -1;
+    std::exchange(fd_old, fd_);
+    if (fd_old != -1) {
+      ::close(fd_old);
+    }
+  }
+
+  static fd& null_fd() {
+    static fd ret = {::open("/dev/null", O_RDWR)};
+    return ret;
+  }
+
+  static fd open(std::string_view path) {
+    fd ret = {::open(path.data(), O_RDONLY)};
+    return ret;
+  }
+};
+
+/** Wraps a readable fd using the `streambuf` interface.  I/O errors arising
+from reading the provided fd will result in a `Failed` being thrown. */
+struct fd_streambuf final : std::streambuf {
+  fd& fd_;
+  char* buf_;
+  size_t size_;
+  fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
+  virtual ~fd_streambuf();
+  int underflow() override;
+};
+
+/** Wraps an `FDInStreamBuffer` in an `istream` */
+struct fd_istream final : std::istream {
+  fd_streambuf& buf_;
+  virtual ~fd_istream();
+  explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
+};
+
+struct fd_mmap final {
+  fd fd_{};
+  size_t size_{0};
+  std::byte const* addr_{nullptr};
+
+  explicit fd_mmap(std::string_view path) : fd_mmap(fd::open(path)) {}
+
+  explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
+    if (fd_) {
+      if ((size_ = ::lseek(fd, 0, SEEK_END))) {
+        addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
+      }
+    }
+  }
+
+  operator bool() const { return addr_; }
+
+  ~fd_mmap() {
+    if (addr_) {
+      ::munmap(const_cast<void*>((void const*)addr_), size_);
+    }
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_FD_H
diff --git a/libcxx/src/experimental/stacktrace/common/images.h b/libcxx/src/experimental/stacktrace/common/images.h
new file mode 100644
index 0000000000000..cb9eac47cf9dd
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/images.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_IMAGES_H
+#define _LIBCPP_STACKTRACE_IMAGES_H
+
+#include <cstdint>
+#include <string_view>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+constexpr unsigned k_max_images = 256;
+
+struct image {
+  uintptr_t loaded_at_{};
+  intptr_t slide_{};
+  std::string_view name_{};
+  bool is_main_prog_{};
+
+  bool operator<(image const& rhs) const { return loaded_at_ < rhs.loaded_at_; }
+  operator bool() const { return !name_.empty(); }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/experimental/stacktrace/context.cpp b/libcxx/src/experimental/stacktrace/context.cpp
new file mode 100644
index 0000000000000..3d311af4d7a89
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/context.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <list>
+
+#include <experimental/__stacktrace/basic_stacktrace.h>
+#include <experimental/__stacktrace/stacktrace_entry.h>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+#include <experimental/__stacktrace/detail/to_string.h>
+
+#include "common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+#  include "linux/linux.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+#  include "osx/osx.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+#  include "tools/pspawn.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+#  include "unwind/unwind.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+#  include "windows/win_impl.h"
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip, size_t max_depth) {
+  /*
+  Here we declare stacktrace components or "backends" which will handle the different tasks:
+
+  (1) get the addresses from the call stack
+  (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
+  (3) resolve using debug info, and possibly with an external tool on the $PATH
+  (4+) extra passes to get symbols, in case 3 couldn't
+
+  Based on the macros produced by `stacktrace.h`, throw all backends we have available at the task.  Ideally the #ifdef
+  gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
+  symbolizer.  If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
+
+  All these classes do their best to provide any of the requested fields they can: (symbol, filename, source line),
+  substituting if needed with something reasonable.  For example, if the source filename and line are not available
+  then we will at least report that the address and symbol are in the module `foo.exe`.
+
+  These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
+  already-populated data.  We take care not to crash / abort / throw in any of these, and we'll silently fail.  See
+  `common/debug.h` for a debugging logger you can enable at runtime.
+  */
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+  win_impl dbghelp{*this};
+  auto& collector  = dbghelp;
+  auto& mod_ident  = dbghelp;
+  auto& resolver   = dbghelp;
+  auto& symbolizer = dbghelp;
+#endif
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+  unwind unwind{*this};
+  auto& collector = unwind;
+#endif
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+  osx osx{*this};
+  auto& mod_ident  = osx;
+  auto& symbolizer = osx;
+#endif
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+  linux linux{*this};
+  auto& mod_ident  = linux;
+  auto& symbolizer = linux;
+#endif
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+  spawner pspawn{*this};
+  auto& resolver = pspawn;
+#endif
+
+  collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
+  if (__entries_.size()) {                // (Can't proceed if empty)
+    mod_ident.ident_modules();            // Associate addrs with binaries (ELF/MachO/etc.)
+    resolver.resolve_lines();             // Resolve addresses to symbols, filename, linenumber
+    symbolizer.symbolize();               // Populate missing symbols, if any.
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/linux/elf.h b/libcxx/src/experimental/stacktrace/linux/elf.h
new file mode 100644
index 0000000000000..214e8cc5a7819
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/elf.h
@@ -0,0 +1,315 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_ELF_H
+#define _LIBCPP_STACKTRACE_ELF_H
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <functional>
+#include <string_view>
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// Includes ELF constants and structs copied from <elf.h>, with a few changes.
+
+namespace elf {
+
+struct Header32 final {
+  uint8_t ident[16];  /* Magic number and other info */
+  uint16_t type;      /* Object file type */
+  uint16_t machine;   /* Architecture */
+  uint32_t version;   /* Object file version */
+  uint32_t entry;     /* Entry point virtual address */
+  uint32_t phoff;     /* Program header table file offset */
+  uint32_t shoff;     /* Section header table file offset */
+  uint32_t flags;     /* Processor-specific flags */
+  uint16_t ehsize;    /* ELF header size in bytes */
+  uint16_t phentsize; /* Program header table entry size */
+  uint16_t phnum;     /* Program header table entry count */
+  uint16_t shentsize; /* Section header table entry size */
+  uint16_t shnum;     /* Section header table entry count */
+  uint16_t shstrndx;  /* Section header string table index */
+};
+
+struct Section32 final {
+  uint32_t name;      /* Section name (string tbl index) */
+  uint32_t type;      /* Section type */
+  uint32_t flags;     /* Section flags */
+  uint32_t addr;      /* Section virtual addr at execution */
+  uint32_t offset;    /* Section file offset */
+  uint32_t size;      /* Section size in bytes */
+  uint32_t link;      /* Link to another section */
+  uint32_t info;      /* Additional section information */
+  uint32_t addralign; /* Section alignment */
+  uint32_t entsize;   /* Entry size if section holds table */
+};
+
+struct Symbol32 final {
+  uint32_t name;  /* Symbol name (string tbl index) */
+  uint32_t value; /* Symbol value */
+  uint32_t size;  /* Symbol size */
+  uint8_t info;   /* Symbol type and binding */
+  uint8_t other;  /* Symbol visibility */
+  uint16_t shndx; /* Section index */
+};
+
+struct Header64 final {
+  uint8_t ident[16];  /* Magic number and other info */
+  uint16_t type;      /* Object file type */
+  uint16_t machine;   /* Architecture */
+  uint32_t version;   /* Object file version */
+  uint64_t entry;     /* Entry point virtual address */
+  uint64_t phoff;     /* Program header table file offset */
+  uint64_t shoff;     /* Section header table file offset */
+  uint32_t flags;     /* Processor-specific flags */
+  uint16_t ehsize;    /* ELF header size in bytes */
+  uint16_t phentsize; /* Program header table entry size */
+  uint16_t phnum;     /* Program header table entry count */
+  uint16_t shentsize; /* Section header table entry size */
+  uint16_t shnum;     /* Section header table entry count */
+  uint16_t shstrndx;  /* Section header string table index */
+};
+
+struct Section64 final {
+  uint32_t name;      /* Section name (string tbl index) */
+  uint32_t type;      /* Section type */
+  uint64_t flags;     /* Section flags */
+  uint64_t addr;      /* Section virtual addr at execution */
+  uint64_t offset;    /* Section file offset */
+  uint64_t size;      /* Section size in bytes */
+  uint32_t link;      /* Link to another section */
+  uint32_t info;      /* Additional section information */
+  uint64_t addralign; /* Section alignment */
+  uint64_t entsize;   /* Entry size if section holds table */
+};
+
+struct Symbol64 final {
+  uint32_t name;  /* Symbol name (string tbl index) */
+  uint8_t info;   /* Symbol type and binding */
+  uint8_t other;  /* Symbol visibility */
+  uint16_t shndx; /* Section index */
+  uint64_t value; /* Symbol value */
+  uint64_t size;  /* Symbol size */
+};
+
+/** Represents an ELF header.  Supports the minimum needed to navigate an ELF file's sections and get at the symbol and
+ * string tables. */
+struct Header final {
+  std::byte const* ptr_{};
+  uintptr_t shoff_{};
+  size_t shnum_{};
+  size_t shstrndx_{};
+
+  Header()              = default;
+  Header(Header const&) = default;
+  Header& operator=(Header const& rhs) { return *new (this) Header(rhs); }
+
+  operator bool() { return ptr_; }
+
+  template <class H>
+  explicit Header(H* h)
+      : ptr_((std::byte const*)h),
+        shoff_(uintptr_t(h->shoff)),
+        shnum_(size_t(h->shnum)),
+        shstrndx_(size_t(h->shstrndx)) {}
+};
+
+struct ELF;
+struct StringTable;
+
+struct Section final {
+  constexpr static uint32_t kSymTab = 2; // symbol table
+  constexpr static uint32_t kStrTab = 3; // name table for symbols or sections
+
+  ELF* elf_{};
+  std::byte const* ptr_{};
+  uintptr_t nameIndex_{};
+  uint32_t type_{};
+  uintptr_t offset_{};
+  size_t size_{};
+
+  Section() = default;
+
+  template <class S>
+  Section(ELF* elf, S* sec)
+      : elf_(elf),
+        ptr_((std::byte const*)sec),
+        nameIndex_(sec->name),
+        type_(sec->type),
+        offset_(sec->offset),
+        size_(sec->size) {}
+
+  operator bool() const { return ptr_; }
+
+  template <class T = std::byte>
+  T const* data() const {
+    return (T const*)(elfBase() + offset_);
+  }
+
+  std::byte const* elfBase() const;
+  std::string_view name() const;
+};
+
+struct Symbol final {
+  constexpr static uint8_t kFunc = 0x02; // STT_FUNC (code object)
+
+  ELF* elf_{};
+  std::byte const* ptr_{};
+  uintptr_t nameIndex_{};
+  uint32_t type_{};
+  uintptr_t value_{};
+
+  Symbol()              = default;
+  Symbol(Symbol const&) = default;
+  Symbol& operator=(Symbol const& rhs) { return *new (this) Symbol(rhs); }
+
+  operator bool() { return ptr_; }
+
+  bool isCode() const { return type_ == kFunc; }
+
+  template <class S>
+  Symbol(ELF* elf, S* sym)
+      : elf_(elf), ptr_((std::byte const*)sym), nameIndex_(sym->name), type_(0x0f & sym->info), value_(sym->value) {}
+
+  std::byte const* elfBase() const;
+  std::string_view name() const;
+};
+
+/** Represents one of the ELF's `strtab`s.  This is a block of string data, with strings appended one after another, and
+ * NUL-terminated.  Strings are indexed according to their starting offset.  At offset 0 is typically an empty string.
+ */
+struct StringTable {
+  std::string_view data_{};
+
+  StringTable() = default;
+
+  /* implicit */ StringTable(Section const& sec) : data_(sec.data<char>(), sec.size_) {}
+
+  operator bool() { return data_.size(); }
+
+  std::string_view at(size_t index) {
+    auto* ret = data_.data() + index;
+    return {ret, strlen(ret)};
+  }
+};
+
+/** Encapsulates an ELF image specified by byte-address (e.g. from an mmapped file or a program image or shared object
+ * in memory).  If given a supported ELF image, this will test true with `operator bool` to indicate it is supported and
+ * was able to parse some basic information from the header. */
+struct ELF {
+  Header header_{};
+  Section (*makeSection_)(ELF*, std::byte const*){};
+  Symbol (*makeSymbol_)(ELF*, std::byte const*){};
+  size_t secSize_{};
+  size_t symSize_{};
+  StringTable nametab_{};
+  Section symtab_{};
+  StringTable strtab_{};
+  size_t symCount_{};
+
+  static Section makeSection32(ELF* elf, std::byte const* ptr) { return Section(elf, (Section32 const*)ptr); }
+  static Section makeSection64(ELF* elf, std::byte const* ptr) { return Section(elf, (Section64 const*)ptr); }
+  static Symbol makeSymbol32(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol32 const*)ptr); }
+  static Symbol makeSymbol64(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol64 const*)ptr); }
+
+  operator bool() { return header_; }
+
+  explicit ELF(std::byte const* image) {
+    auto* p = (uint8_t const*)image;
+    // Bytes 0..3: magic bytes: 0x7F, 'E', 'L', 'F'
+    if (*p++ == 0x7f && *p++ == 0x45 && *p++ == 0x4c && *p++ == 0x46) {
+      auto klass       = *p++; // Byte 4 (EI_CLASS): ELF class, 32- or 64-bit (0x01 or 0x02)
+      auto dataFormat  = *p++; // Byte 5 (EI_DATA): (0x01) little- or (0x02) big-endian
+      auto fileVersion = *p++; // Byte 6 (EI_VERSION): ELF version: expect 1 (latest ELF version)
+      constexpr static uint16_t kEndianTestWord{0x0201};
+      auto hostEndianness = *(uint8_t const*)&kEndianTestWord;
+      if (dataFormat == hostEndianness && fileVersion == 1) {
+        if (klass == 0x01) {
+          header_      = Header((Header32 const*)image);
+          makeSection_ = makeSection32;
+          makeSymbol_  = makeSymbol32;
+          secSize_     = sizeof(Section32);
+          symSize_     = sizeof(Symbol32);
+        } else if (klass == 0x02) {
+          header_      = Header((Header64 const*)image);
+          makeSection_ = makeSection64;
+          makeSymbol_  = makeSymbol64;
+          secSize_     = sizeof(Section64);
+          symSize_     = sizeof(Symbol64);
+        }
+      }
+    }
+    if (*this) {
+      nametab_ = section(header_.shstrndx_);
+      eachSection([&](auto& sec) mutable -> bool {
+        if (sec.type_ == Section::kSymTab && sec.name() == ".symtab") {
+          symtab_ = sec;
+        } else if (sec.type_ == Section::kStrTab && sec.name() == ".strtab") {
+          strtab_ = sec;
+        }
+        return !symtab_ || !strtab_;
+      });
+    }
+    if (symtab_) {
+      symCount_ = symtab_.size_ / symSize_;
+    }
+  }
+
+  Section section(size_t index) {
+    auto* addr = header_.ptr_ + header_.shoff_ + (index * secSize_);
+    return makeSection_(this, addr);
+  }
+
+  Symbol symbol(size_t index) {
+    auto* addr = symtab_.data() + (index * symSize_);
+    return makeSymbol_(this, addr);
+  }
+
+  template <class T>
+  using CB = std::function<bool(T const&)>;
+
+  void eachSection(CB<Section> cb) {
+    for (size_t i = 0; i < header_.shnum_ && cb(section(i)); i++)
+      ;
+  }
+
+  void eachSymbol(CB<Symbol> cb) {
+    for (size_t i = 0; i < symCount_ && cb(symbol(i)); i++)
+      ;
+  }
+
+  Symbol getSym(uintptr_t addr) {
+    Symbol ret{};
+    eachSymbol([&](auto& sym) -> bool {
+      if (sym.value_ <= addr && sym.value_ > ret.value_) {
+        ret = sym;
+      }
+      return true;
+    });
+    return ret;
+  }
+};
+
+inline std::byte const* Section::elfBase() const { return elf_->header_.ptr_; }
+inline std::byte const* Symbol::elfBase() const { return elf_->header_.ptr_; }
+
+inline std::string_view Section::name() const { return elf_->nametab_.at(nameIndex_); }
+inline std::string_view Symbol::name() const { return elf_->strtab_.at(nameIndex_); }
+
+} // namespace elf
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp b/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
new file mode 100644
index 0000000000000..62cad72b9f124
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+#  include "linux.h"
+
+#  include <cassert>
+#  include <dlfcn.h>
+#  include <link.h>
+#  include <unistd.h>
+
+#  include "../common/images.h"
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+void linux::ident_modules() {
+  auto& images = images::get();
+
+  // Aside from the left/right sentinels in the array (hence the 2),
+  // are there any other real images?
+  if (images.count_ <= 2) {
+    return;
+  }
+
+  auto mainProg = images.mainProg();
+  if (mainProg) {
+    cx_.__main_prog_path_ = mainProg->name_;
+  }
+
+  unsigned index = 1; // Starts at one, and is moved around in this loop
+  for (auto& entry : cx_.__entries_) {
+    while (images[index].loaded_at_ > entry.__addr_) {
+      --index;
+    }
+    while (images[index + 1].loaded_at_ <= entry.__addr_) {
+      ++index;
+    }
+    entry.__addr_unslid_ = entry.__addr_ - images[index].slide_;
+    entry.__file_        = images[index].name_;
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp b/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
new file mode 100644
index 0000000000000..7cf82789ea2ed
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+#  include "linux.h"
+
+#  include <cassert>
+#  include <unistd.h>
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/fd.h"
+#  include "elf.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/**
+When trying to collect a stacktrace under Linux, there are narrow (but still quite common) cases where we will fail
+to resolve symbols.  Linux's `dl` doesn't want to read symbols from the non-exported symbol table at runtime,
+and older versions of `addr2line` or `llvm-symbolizer` will also not resolve these.
+
+This implementation the minimum necessary to resolve symbols.  It can identify this as an ELF (32 or 64 bits), locate
+the symbol and symbol-string table, and fill in any remaining missing symbols.
+*/
+void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
+  // We can statically initialize these, because main_elf_name should be the same every time.
+  static fd_mmap _mm(main_elf_name);
+  if (_mm) {
+    static elf::ELF _this_elf(_mm.addr_);
+    if (_this_elf) {
+      for (auto& entry : cx_.__entries_) {
+        if (entry.__desc_.empty() && entry.__file_ == main_elf_name) {
+          entry.__desc_ = _this_elf.getSym(entry.__addr_unslid_).name();
+        }
+      }
+    }
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp b/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
new file mode 100644
index 0000000000000..fa7489c64c923
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+#  include "linux.h"
+
+#  include <cassert>
+#  include <dlfcn.h>
+#  include <unistd.h>
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+bool symbolize_entry(entry& entry) {
+  bool ret = false;
+  Dl_info info;
+  if (dladdr((void*)entry.__addr_, &info)) {
+    ret = true; // at least partially successful
+    if (info.dli_fname && entry.__file_.empty()) {
+      // provide at least the binary filename in case we cannot lookup source location
+      entry.__file_ = info.dli_fname;
+    }
+    if (info.dli_sname && entry.__desc_.empty()) {
+      // provide at least the mangled name; try to unmangle in a later step
+      entry.__desc_ = info.dli_sname;
+    }
+  }
+  return ret;
+}
+
+// NOTE:  We can use `dlfcn` to resolve addresses to symbols, which works great --
+// except for symbols in the main program.  If addr2line-style tools are enabled, that step
+// might also be able to get symbols directly from the binary's debug info.
+void linux::symbolize() {
+  for (auto& entry : cx_.__entries_) {
+    symbolize_entry(entry);
+  }
+  // Symbols might be missing, because both (1) Linux's `dladdr` won't try to resolve non-exported symbols,
+  // which can be the case for the main program executable; and (2) debug info was not preserved.
+  // As a last resort, this function (see `linux-elf.cpp`) can still access symbol table directly.
+  image* mainELF = images::get().mainProg();
+  if (mainELF && !mainELF->name_.empty()) {
+    resolve_main_elf_syms(mainELF->name_);
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/experimental/stacktrace/linux/linux.h b/libcxx/src/experimental/stacktrace/linux/linux.h
new file mode 100644
index 0000000000000..a8e8c2279f6fc
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux.h
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_LINUX_H
+#define _LIBCPP_STACKTRACE_LINUX_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+#  include <algorithm>
+#  include <array>
+#  include <cassert>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <link.h>
+#  include <string_view>
+#  include <unistd.h>
+
+#  include "../common/images.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct linux {
+  context& cx_;
+  void ident_modules();
+  void symbolize();
+
+private:
+  void resolve_main_elf_syms(std::string_view elf_name);
+};
+
+struct images {
+  // How many images this contains, including the left/right sentinels.
+  unsigned count_{0};
+  std::array<image, k_max_images + 2> images_{};
+
+  int add(dl_phdr_info& info) {
+    assert(count_ < k_max_images);
+    auto isFirst        = (count_ == 0);
+    auto& image         = images_.at(count_++);
+    image.loaded_at_    = info.dlpi_addr;
+    image.slide_        = info.dlpi_addr;
+    image.name_         = info.dlpi_name;
+    image.is_main_prog_ = isFirst; // first one is the main ELF
+    if (image.name_.empty() && isFirst) {
+      static char buffer[PATH_MAX + 1];
+      uint32_t length = sizeof(buffer);
+      if (readlink("/proc/self/exe", buffer, length) > 0) {
+        image.name_ = buffer;
+      }
+    }
+    return count_ == k_max_images; // return nonzero if we're at the limit
+  }
+
+  static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
+
+  images() {
+    dl_iterate_phdr(images::callback, this);
+    images_[count_++] = {0uz, 0};  // sentinel at low end
+    images_[count_++] = {~0uz, 0}; // sentinel at high end
+    std::sort(images_.begin(), images_.begin() + count_ - 1);
+  }
+
+  image& operator[](size_t index) {
+    assert(index < count_);
+    return images_.at(index);
+  }
+
+  image* mainProg() {
+    for (auto& image : images_) {
+      if (image.is_main_prog_) {
+        return ℑ
+      }
+    }
+    return nullptr;
+  }
+
+  static images& get() {
+    static images images;
+    return images;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
+
+#endif // _LIBCPP_STACKTRACE_LINUX_H
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.cpp b/libcxx/src/experimental/stacktrace/osx/osx.cpp
new file mode 100644
index 0000000000000..c00a7ea94e780
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/osx/osx.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+
+#  include <algorithm>
+#  include <array>
+#  include <dlfcn.h>
+#  include <mach-o/dyld.h>
+#  include <mach-o/loader.h>
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "osx.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+constexpr unsigned kMaxImages = 256;
+
+struct Image {
+  uintptr_t loadedAt{};
+  intptr_t slide{};
+  std::string_view name{};
+  bool operator<(Image const& rhs) const { return loadedAt < rhs.loadedAt; }
+  operator bool() const { return !name.empty(); }
+};
+
+void ident_module(entry& entry, unsigned& index, Image* images) {
+  if (entry.__addr_) {
+    while (images[index].loadedAt > entry.__addr_) {
+      --index;
+    }
+    while (images[index + 1].loadedAt <= entry.__addr_) {
+      ++index;
+    }
+    auto& image = images[index];
+    if (image) {
+      entry.__addr_unslid_ = entry.__addr_ - images[index].slide;
+      entry.__file_        = images[index].name;
+    }
+  }
+}
+
+bool enum_modules(unsigned& count, auto& images) {
+  count = std::min(kMaxImages, _dyld_image_count());
+  for (size_t i = 0; i < count; i++) {
+    auto& image    = images[i];
+    image.slide    = _dyld_get_image_vmaddr_slide(i);
+    image.loadedAt = uintptr_t(_dyld_get_image_header(i));
+    image.name     = _dyld_get_image_name(i);
+  }
+  images[count++] = {0uz, 0};  // sentinel at low end
+  images[count++] = {~0uz, 0}; // sentinel at high end
+  std::sort(images.begin(), images.begin() + count - 1);
+  return true;
+}
+
+void osx::ident_modules() {
+  static unsigned imageCount;
+  static std::array<Image, kMaxImages + 2> images;
+  static bool atomicInitialized = enum_modules(imageCount, images);
+  (void)atomicInitialized;
+
+  // Aside from the left/right sentinels in the array (hence the 2),
+  // are there any other real images?
+  if (imageCount <= 2) {
+    return;
+  }
+
+  // First image (the main program) is at index 1
+  cx_.__main_prog_path_ = images.at(1).name;
+
+  unsigned index = 1; // Starts at one, and is moved by 'ident_module'
+  for (auto& entry : cx_.__entries_) {
+    ident_module(entry, index, images.data());
+  }
+}
+
+void symbolize_entry(entry& entry) {
+  Dl_info info;
+  if (dladdr((void*)entry.__addr_, &info)) {
+    if (info.dli_fname && entry.__file_.empty()) {
+      // provide at least the binary filename in case we cannot lookup source location
+      entry.__file_ = info.dli_fname;
+    }
+    if (info.dli_sname && entry.__desc_.empty()) {
+      // provide at least the mangled name; try to unmangle in a later step
+      entry.__desc_ = info.dli_sname;
+    }
+  }
+}
+
+void osx::symbolize() {
+  for (auto& entry : cx_.__entries_) {
+    symbolize_entry(entry);
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_APPLE
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.h b/libcxx/src/experimental/stacktrace/osx/osx.h
new file mode 100644
index 0000000000000..ca0890929453e
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/osx/osx.h
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_OSX_H
+#define _LIBCPP_STACKTRACE_OSX_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct osx {
+  context& cx_;
+  void ident_modules();
+  void symbolize();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_OSX_H
diff --git a/libcxx/src/experimental/stacktrace/stacktrace.cpp b/libcxx/src/experimental/stacktrace/stacktrace.cpp
new file mode 100644
index 0000000000000..e33b94205a8ea
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/stacktrace.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <experimental/__stacktrace/basic_stacktrace.h>
+#include <experimental/__stacktrace/stacktrace_entry.h>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+#include <experimental/__stacktrace/detail/to_string.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/*
+ * `to_string` Helpers
+ */
+
+void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
+  // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
+  constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
+
+  __os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << entry.native_handle();
+  if (!entry.description().empty()) {
+    __os << ": " << entry.description();
+  }
+  if (!entry.source_file().empty()) {
+    __os << ": " << entry.source_file();
+  }
+  if (entry.source_line()) {
+    __os << ":" << std::dec << entry.source_line();
+  }
+}
+
+void __to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
+  /*
+   * Print each entry as a line, as per `operator()`, with additional whitespace
+   * at the start of the line, and only a newline added at the end:
+   *
+   *   frame   1: 0xbeefbeefbeef: _symbol_name: /path/to/file.cc:123
+   */
+  if (!__count) {
+    __os << "(empty stacktrace)";
+  } else {
+    for (size_t __i = 0; __i < __count; __i++) {
+      if (__i) {
+        // Insert newlines between entries (but not before the first or after the last)
+        __os << std::endl;
+      }
+      __os << "  frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": ";
+      (*this)(__os, __entries[__i]);
+    }
+  }
+}
+
+string __to_string::operator()(std::stacktrace_entry const& entry) {
+  stringstream __ss;
+  (*this)(__ss, entry);
+  return __ss.str();
+}
+
+string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
+  stringstream __ss;
+  (*this)(__ss, __entries, __count);
+  return __ss.str();
+}
+
+} // namespace __stacktrace
+
+// `to_string`-related non-member functions
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
+  return __stacktrace::__to_string()(__entry);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
+  __stacktrace::__to_string()(__os, __entry);
+  return __os;
+}
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/addr2line.cpp b/libcxx/src/experimental/stacktrace/tools/addr2line.cpp
new file mode 100644
index 0000000000000..310e99699569b
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/addr2line.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <iostream>
+
+#include "tools.h"
+
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+std::pmr::list<std::pmr::string> addr2line::buildArgs(context& cx) const {
+  auto& alloc = cx.__alloc_;
+  auto ret    = alloc.new_string_list();
+  if (cx.__main_prog_path_.empty()) {
+    // Should not have reached here but be graceful anyway
+    ret.push_back("/bin/false");
+    return ret;
+  }
+
+  ret.push_back(progName_);
+  ret.push_back("--functions");
+  ret.push_back("--demangle");
+  ret.push_back("--basenames");
+  ret.push_back("--pretty-print"); // This "human-readable form" is easier to parse
+  ret.push_back("-e");
+  ret.push_back(cx.__main_prog_path_);
+  for (auto& entry : cx.__entries_) {
+    ret.push_back(alloc.hex_string(entry.__addr_unslid_));
+  }
+  return ret;
+}
+
+void addr2line::parseOutput(context& trace, entry& entry, std::istream& output) const {
+  // clang-format off
+/*
+Example:
+--
+llvm-addr2line -e foo --functions --demangle --basenames --pretty-print --no-inlines 0x11a0 0x1120 0x3d58 0x1284
+
+Output: (1 line per input address)
+--
+main at foo.cc:15
+register_tm_clones at crtstuff.c:0
+GCC_except_table2 at foo.cc:0
+test::Foo::Foo(int) at foo.cc:11
+*/
+  // clang-format on
+
+  std::pmr::string line{&trace.__alloc_};
+  std::getline(output, line);
+  while (isspace(line.back())) {
+    line.pop_back();
+  }
+  if (line.empty()) {
+    return;
+  }
+  // Split at the sequence " at ".  Barring weird symbols
+  // having " at " in them, this should work.
+  auto sepIndex = line.find(" at ");
+  if (sepIndex == std::string::npos) {
+    return;
+  }
+  if (sepIndex > 0) {
+    entry.__desc_ = line.substr(0, sepIndex);
+  }
+  auto fileBegin = sepIndex + 4;
+  if (fileBegin >= line.size()) {
+    return;
+  }
+  auto fileline = line.substr(fileBegin);
+  auto colon    = fileline.find_last_of(":");
+  if (colon > 0 && !fileline.starts_with("?")) {
+    entry.__file_ = fileline.substr(0, colon);
+  }
+
+  if (colon == std::string::npos) {
+    return;
+  }
+  uint32_t lineno = 0;
+  auto pos        = colon;
+  while (isdigit(fileline[++pos])) {
+    lineno = lineno * 10 + (fileline[pos] - '0');
+  }
+  entry.__line_ = lineno;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/atos.cpp b/libcxx/src/experimental/stacktrace/tools/atos.cpp
new file mode 100644
index 0000000000000..569883a4b12bc
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/atos.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <istream>
+#include <string>
+#include <unistd.h>
+
+#include "tools.h"
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+std::pmr::list<std::pmr::string> atos::buildArgs(context& cx) const {
+  auto& alloc = cx.__alloc_;
+  auto ret    = alloc.new_string_list();
+  ret.push_back(progName_);
+  ret.push_back("-p");
+  ret.push_back(alloc.u64_string(getpid()));
+  // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
+  // ret.push_back("--fullPath");
+  for (auto& entry : cx.__entries_) {
+    ret.push_back(alloc.hex_string(entry.__addr_));
+  }
+  return ret;
+}
+
+void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
+  // Simple example:
+  //
+  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+  //
+  // Assuming this is always atos's format (except when it returns empty lines)
+  // we can split the string like so:
+  //
+  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+  //   ^^^^-----^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^-
+  //   sym      module     filename                            line
+  //
+  // Note that very strange filenames or module names can confuse this.
+  // We'll do the best we can for a decent result, while definitely ensuring safety
+  // (i.e. careful with our bound-checking).
+  //
+  // Another more interesting example (with an added newline for legibility):
+  //
+  //   std::__1::basic_ios<char, std::__1::char_traits<char>>::fill[abi:ne190107]() const (in testprog)
+  //   (/opt/homebrew/Cellar/llvm/19.1.7_1/bin/../include/c++/v1/ios:0
+  //
+  // If this more or less fits our expected format we'll take these data,
+  // even if the line number is 0.
+
+  auto line = cx.__alloc_.new_string(256);
+  std::getline(output, line);
+  while (isspace(line.back())) {
+    line.pop_back();
+  }
+  if (line.empty()) {
+    return;
+  }
+  auto buf  = line.data();
+  auto size = line.size();
+
+  auto* end    = buf + size;
+  auto* symEnd = strstr(buf, " (in ");
+  if (!symEnd) {
+    return;
+  }
+  auto* modBegin = symEnd + 5;
+  auto* modEnd   = strstr(modBegin, ") (");
+  if (!modEnd) {
+    return;
+  }
+  auto* fileBegin = modEnd + 3; // filename starts just after that
+  if (fileBegin >= end) {
+    return;
+  }
+  auto const* lastColon = fileBegin; // we'll search for last colon after filename
+  char const* nextColon;
+  while ((nextColon = strstr(lastColon + 1, ":"))) { // skip colons in filename (e.g. in "C:\foo.cpp")
+    lastColon = nextColon;
+  }
+
+  std::string_view sym{buf, size_t(symEnd - buf)};
+  // In case a previous step could not obtain the symbol name,
+  // we have the name provided by atos; only use that if we have no symbol
+  // (no need to copy more strings otherwise).
+  if (entry.__desc_.empty() && !sym.empty()) {
+    entry.__desc_ = sym;
+  }
+
+  std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
+  if (file != "?" && file != "??" && !file.empty()) {
+    entry.__file_ = file;
+  }
+
+  unsigned lineno = 0;
+  for (auto* digit = lastColon + 1; digit < end && isdigit(*digit); ++digit) {
+    lineno = (lineno * 10) + unsigned(*digit - '0');
+  }
+  entry.__line_ = lineno;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp b/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
new file mode 100644
index 0000000000000..0a9907978dcf2
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <iostream>
+#include <string_view>
+
+#include "../common/debug.h"
+#include "tools.h"
+
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// TODO(stacktrace23): possible to link against `libLLVMSymbolize.a`, or some shared obj at runtime (does that exist?)
+
+std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
+  auto& alloc = cx.__alloc_;
+  auto ret    = alloc.new_string_list();
+  ret.push_back(progName_);
+  ret.push_back("--demangle");
+  ret.push_back("--no-inlines");
+  ret.push_back("--verbose");
+  ret.push_back("--relativenames");
+  ret.push_back("--functions=short");
+  for (auto& entry : cx.__entries_) {
+    auto addr_string = alloc.hex_string(entry.__addr_unslid_);
+    debug() << "@@@ " << addr_string << " " << entry.__file_ << " " << entry.__file_.empty() << '\n';
+    if (!entry.__file_.empty()) {
+      auto arg = alloc.new_string(entry.__file_.size() + 40);
+      arg += "FILE:";
+      arg += entry.__file_;
+      arg += " ";
+      arg += addr_string;
+      ret.push_back(arg);
+    } else {
+      ret.push_back(addr_string);
+    }
+  }
+  return ret;
+}
+
+void llvm_symbolizer::parseOutput(context& cx, entry& entry, std::istream& output) const {
+  // clang-format off
+/*
+With "--verbose", parsing is a little easier, or at least, more reliable;
+probably the best solution (until we have a JSON parser).
+Example output, verbatim, between the '---' lines:
+---
+test1<test_alloc<std::__1::stacktrace_entry> >
+  Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+  Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+  Function start line: 114
+  Function start address: 0x8dd0
+  Line: 116
+  Column: 14
+
+---
+Note that this includes an extra empty line as a terminator.
+*/
+  // clang-format on
+
+  auto& alloc = cx.__alloc_;
+  auto line   = alloc.new_string(256);
+  std::string_view tmp;
+  while (true) {
+    std::getline(output, line);
+    while (isspace(line.back())) {
+      line.pop_back();
+    }
+    if (line.empty()) {
+      return;
+    }
+    if (!line.starts_with("  ")) {
+      // The symbol has no leading whitespace, while the other
+      // lines with "fields" like line, column, filename, etc.
+      // start with two spaces.
+      if (line != "??") {
+        entry.__desc_ = line;
+      }
+    } else if (line.starts_with("  Filename:")) {
+      tmp = line;
+      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+      if (tmp != "??") {
+        entry.__file_ = tmp;
+      }
+    } else if (line.starts_with("  Line:")) {
+      tmp = line;
+      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+      if (tmp != "??" && tmp != "0") {
+        uint32_t lineno = 0;
+        auto pos        = 0;
+        while (isdigit(tmp[pos])) {
+          lineno = lineno * 10 + (tmp[pos++] - '0');
+        }
+        entry.__line_ = lineno;
+      }
+    }
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/pspawn.h b/libcxx/src/experimental/stacktrace/tools/pspawn.h
new file mode 100644
index 0000000000000..906a504441368
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/pspawn.h
@@ -0,0 +1,163 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_PSPAWN_H
+#define _LIBCPP_STACKTRACE_PSPAWN_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+
+#  include <__config>
+#  include <__config_site>
+#  include <cassert>
+#  include <cerrno>
+#  include <csignal>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <list>
+#  include <spawn.h>
+#  include <string>
+#  include <sys/fcntl.h>
+#  include <sys/types.h>
+#  include <sys/wait.h>
+#  include <unistd.h>
+#  include <vector>
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/debug.h"
+#  include "../common/failed.h"
+#  include "../common/fd.h"
+#  include "tools.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct file_actions {
+  posix_spawn_file_actions_t fa_;
+
+  file_actions() {
+    if (posix_spawn_file_actions_init(&fa_)) {
+      throw failed("posix_spawn_file_actions_init", errno);
+    }
+  }
+
+  ~file_actions() { posix_spawn_file_actions_destroy(&fa_); }
+
+  void addClose(int fd) {
+    if (posix_spawn_file_actions_addclose(&fa_, fd)) {
+      throw failed("posix_spawn_file_actions_addclose", errno);
+    }
+  }
+  void addDup2(int fd, int stdfd) {
+    if (posix_spawn_file_actions_adddup2(&fa_, fd, stdfd)) {
+      throw failed("posix_spawn_file_actions_adddup2", errno);
+    }
+  }
+
+  fd redirectOutFD() {
+    int fds[2];
+    if (::pipe(fds)) {
+      throw failed("pipe", errno);
+    }
+    addClose(fds[0]);
+    addDup2(fds[1], 1);
+    return {fds[0]};
+  }
+
+  void redirectInNull() { addDup2(fd::null_fd(), 0); }
+  void redirectOutNull() { addDup2(fd::null_fd(), 1); }
+  void redirectErrNull() { addDup2(fd::null_fd(), 2); }
+};
+
+struct pspawn {
+  tool const& tool_;
+  pid_t pid_{0};
+  file_actions fa_{};
+
+  // TODO(stacktrace23): ignore SIGCHLD for spawned subprocess
+
+  ~pspawn() {
+    if (pid_) {
+      kill(pid_, SIGTERM);
+      wait();
+    }
+  }
+
+  void spawn(std::pmr::list<std::pmr::string> const& argStrings) {
+    std::pmr::vector<char const*> argv{argStrings.get_allocator()};
+    argv.reserve(argStrings.size() + 1);
+    for (auto const& str : argStrings) {
+      argv.push_back(str.data());
+    }
+    argv.push_back(nullptr);
+    int err;
+    if ((err = posix_spawnp(&pid_, argv[0], &fa_.fa_, nullptr, const_cast<char**>(argv.data()), nullptr))) {
+      throw failed("posix_spawnp", err);
+    }
+  }
+
+  int wait() {
+    int status;
+    waitpid(pid_, &status, 0);
+    return status;
+  }
+};
+
+struct pspawn_tool : pspawn {
+  context& cx_;
+  fd fd_;
+  fd_streambuf buf_;
+  fd_istream stream_;
+
+  pspawn_tool(tool const& a2l, context& cx, char* buf, size_t size)
+      : pspawn{a2l}, cx_(cx), fd_(fa_.redirectOutFD()), buf_(fd_, buf, size), stream_(buf_) {
+    if (!debug::enabled()) {
+      fa_.redirectErrNull();
+    }
+    fa_.redirectInNull();
+  }
+
+  void run() {
+    // Cannot run "addr2line" or similar without addresses, since we
+    // provide them in argv, and if there are none passed in argv, the
+    // tool will try to read from stdin and hang.
+    if (cx_.__entries_.empty()) {
+      return;
+    }
+
+    auto argStrings = tool_.buildArgs(cx_);
+    if (debug::enabled()) {
+      debug() << "Trying to get stacktrace using:";
+      for (auto& str : argStrings) {
+        debug() << " \"" << str << '"';
+      }
+      debug() << '\n';
+    }
+
+    spawn(argStrings);
+
+    for (auto& entry : cx_.__entries_) {
+      tool_.parseOutput(cx_, entry, stream_);
+    }
+  }
+};
+
+struct spawner {
+  context& cx_;
+  void resolve_lines();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif // _LIBCPP_STACKTRACE_PSPAWN_H
diff --git a/libcxx/src/experimental/stacktrace/tools/tools.h b/libcxx/src/experimental/stacktrace/tools/tools.h
new file mode 100644
index 0000000000000..d832974e69cbb
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/tools.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_TOOLS_H
+#define _LIBCPP_STACKTRACE_TOOLS_H
+
+#include <__config>
+#include <__config_site>
+#include <cassert>
+#include <cstddef>
+#include <list>
+#include <string>
+
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct tool {
+  char const* progName_;
+  explicit tool(char const* progName) : progName_(progName) {}
+  constexpr virtual ~tool() = default;
+
+  /** Construct complete `argv` for the spawned process.
+  Includes the program name at argv[0], followed by flags */
+  virtual std::pmr::list<std::pmr::string> buildArgs(context& trace) const = 0;
+
+  /** Parse line(s) output by the tool, and modify `entry`. */
+  virtual void parseOutput(context& trace, entry& e, std::istream& output) const = 0;
+};
+
+struct llvm_symbolizer : tool {
+  constexpr virtual ~llvm_symbolizer() = default;
+  llvm_symbolizer() : llvm_symbolizer("llvm-symbolizer") {}
+  explicit llvm_symbolizer(char const* progName) : tool{progName} {}
+  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
+struct addr2line : tool {
+  constexpr virtual ~addr2line() = default;
+  addr2line() : addr2line("addr2line") {}
+  explicit addr2line(char const* progName) : tool{progName} {}
+  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+  void parseOutput(context& trace, entry& e, std::istream& stream) const override;
+};
+
+struct atos : tool {
+  constexpr virtual ~atos() = default;
+  atos() : atos("atos") {}
+  explicit atos(char const* progName) : tool{progName} {}
+  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_TOOLS_H
diff --git a/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp b/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
new file mode 100644
index 0000000000000..05a2f8e6c2eb4
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
@@ -0,0 +1,185 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+
+#  include <__config>
+#  include <__config_site>
+#  include <cassert>
+#  include <cerrno>
+#  include <csignal>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <spawn.h>
+#  include <string>
+#  include <sys/fcntl.h>
+#  include <sys/types.h>
+#  include <sys/wait.h>
+#  include <unistd.h>
+
+#  include "../common/failed.h"
+#  include "pspawn.h"
+
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/**
+Returns a list of `tool` objects that can be tried during the first stacktrace operation.
+The result is an array of `tool*` pointers with a final `nullptr` terminator.
+
+The returned tool array pointer is saved during `resolve_lines`'s static init,
+with the first working tool being used from that point forward.
+
+This will first add any tools specified by these defines (in order):
+- LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH
+- LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH
+- LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH
+
+Then any tools specified by the environment variables of the same names (and added in the same order).
+
+Finally, this adds the tools `llvm-symbolizer`, `addr2line`, and `atos`, in that order.
+These tools won't have absolute paths, so $PATH will be searched for these.
+
+Called by `findWorkingTool` during its static initialization, therefore this is used in a threadsafe way.
+*/
+tool const** toolList() {
+  constexpr static size_t kMax = 20;
+  static tool const* array_[kMax];
+  size_t count = 0;
+
+  auto add = [&](tool* t) {
+    assert(count < kMax - 1);
+    array_[count++] = t;
+  };
+
+#  define STRINGIFY0(x) #x
+#  define STRINGIFY(x) STRINGIFY0(x)
+
+#  if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
+  {
+    static llvm_symbolizer t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)};
+    add(&t);
+  }
+#  endif
+#  if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
+  {
+    static addr2line t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)};
+    add(&t);
+  }
+#  endif
+#  if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
+  {
+    static atos t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)};
+    add(&t);
+  }
+#  endif
+
+  if (getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")) {
+    static llvm_symbolizer t{getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")};
+    add(&t);
+  }
+  if (getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")) {
+    static addr2line t{getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")};
+    add(&t);
+  }
+  if (getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")) {
+    static atos t{getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")};
+    add(&t);
+  }
+
+  {
+    static llvm_symbolizer t;
+    add(&t);
+  }
+  {
+    static addr2line t;
+    add(&t);
+  }
+  {
+    static atos t;
+    add(&t);
+  }
+
+  array_[count] = nullptr; // terminator
+  return array_;
+}
+
+// Try a handful of different addr2line-esque tools, returning the first discovered, or else nullptr
+tool const* findWorkingTool(auto& trace) {
+  // Try each of these programs and stop at the first one that works.
+  // There's no synchronization so this is subject to races on setting `prog`,
+  // but as long as one thread ends up setting it to something that works, we're good.
+  // Programs to try ($PATH is searched):
+  auto* it = toolList();
+  tool const* prog;
+  while ((prog = *it++)) {
+    pspawn test{*prog}; // Just try to run with "--help"
+    try {
+      std::pmr::list<std::pmr::string> testArgs{&trace.__alloc_};
+      testArgs.push_back(prog->progName_);
+      testArgs.push_back("--help");
+      test.fa_.redirectInNull();
+      test.fa_.redirectOutNull();
+      test.fa_.redirectErrNull();
+      test.spawn(testArgs);
+      if (test.wait() == 0) {
+        // Success
+        return prog;
+      }
+    } catch (failed const&) {
+      /* ignore during probe attempt */
+    }
+  }
+  return nullptr;
+}
+
+} // namespace
+
+void spawner::resolve_lines() {
+  // The address-to-line tool that worked (after lazy-setting, below)
+  static tool const* prog{nullptr};
+  // Whether we should not attempt because tool detection previously failed.
+  static bool fail{false};
+
+  // If this previously failed, don't try again.
+  if (fail) {
+    return;
+  }
+
+  if (!prog) {
+    prog = findWorkingTool(cx_);
+    if (!prog) {
+      fail = true;
+      return;
+    }
+  }
+  assert(prog);
+
+  char buf[256];
+  pspawn_tool proc(*prog, cx_, buf, sizeof(buf));
+  try {
+    proc.run();
+  } catch (failed const& failed) {
+    debug() << failed.what();
+    if (failed.errno_) {
+      debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
+    }
+    debug() << '\n';
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.cpp b/libcxx/src/experimental/stacktrace/unwind/unwind.cpp
new file mode 100644
index 0000000000000..a316306bc43eb
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/unwind/unwind.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+
+#  include "./unwind.h"
+
+#  include <experimental/__stacktrace/detail/context.h>
+#  include <experimental/__stacktrace/detail/entry.h>
+#  include <unwind.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct unwind_backtrace {
+  context& cx_;
+  size_t skip_;
+  size_t maxDepth_;
+
+  _Unwind_Reason_Code callback(_Unwind_Context* ucx) {
+    if (skip_) {
+      --skip_;
+      return _Unwind_Reason_Code::_URC_NO_REASON;
+    }
+    if (!maxDepth_) {
+      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+    }
+    --maxDepth_;
+    int ipBefore;
+    auto ip = _Unwind_GetIPInfo(ucx, &ipBefore);
+    if (!ip) {
+      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+    }
+    auto& entry          = cx_.__entries_.emplace_back();
+    entry.__addr_        = (ipBefore ? ip : ip - 1);
+    entry.__addr_unslid_ = entry.__addr_; // in case we can't un-slide
+    return _Unwind_Reason_Code::_URC_NO_REASON;
+  }
+
+  static _Unwind_Reason_Code callback(_Unwind_Context* cx, void* self) {
+    return ((unwind_backtrace*)self)->callback(cx);
+  }
+};
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void unwind::collect(size_t skip, size_t max_depth) {
+  unwind_backtrace bt{cx_, skip + 1, max_depth}; // skip this call as well
+  _Unwind_Backtrace(unwind_backtrace::callback, &bt);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.h b/libcxx/src/experimental/stacktrace/unwind/unwind.h
new file mode 100644
index 0000000000000..1b099e3a0b922
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/unwind/unwind.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_UNWIND_H
+#define _LIBCPP_STACKTRACE_UNWIND_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct unwind {
+  context& cx_;
+  void collect(size_t skip, size_t max_depth);
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_UNWIND_H
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
new file mode 100644
index 0000000000000..274c9ecb2b314
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include "dll.h"
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+dbghelp_dll::~dbghelp_dll() = default;
+
+dbghelp_dll& dbghelp_dll::get() {
+  dbghelp_dll ret;
+  return ret;
+}
+
+dbghelp_dll::dbghelp_dll() : dll("dbghelp.dll") {
+  // clang-format off
+if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+  if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+  if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+  if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+  if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+  if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+  if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+  if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+  if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+  if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+  if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+  valid_ = true;
+  // clang-format on
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
new file mode 100644
index 0000000000000..920391fb9ba52
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_WINDOWS_DBGHELP_DLL_H
+#define _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/debug.h"
+#  include "dll.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// clang-format off
+
+struct dbghelp_dll final : dll {
+  virtual ~dbghelp_dll() = default;
+  static dbghelp_dll& get() { static dbghelp_dll ret; return ret; }
+
+  IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
+  bool    (*StackWalk64)        (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
+  bool    (*SymCleanup)         (HANDLE);
+  void*   (*SymFunctionTableAccess64)(HANDLE, DWORD64);
+  bool    (*SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
+  DWORD64 (*SymGetModuleBase64) (HANDLE, DWORD64);
+  DWORD   (*SymGetOptions)      ();
+  bool    (*SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
+  bool    (*SymInitialize)      (HANDLE, char const*, bool);
+  DWORD64 (*SymLoadModule64)    (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
+  DWORD   (*SymSetOptions)      (DWORD);
+
+  dbghelp_dll() : dll("dbghelp.dll") {
+    if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+    if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+    if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+    if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+    if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+    if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+    if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+    if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+    if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+    if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+    if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+    valid_ = true;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WINDOWS
+#endif // _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.cpp b/libcxx/src/experimental/stacktrace/windows/dll.cpp
new file mode 100644
index 0000000000000..55fd729a9caf4
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dll.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include "dll.h"
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+dll::~dll() { FreeLibrary(module_); }
+
+dll::dll(char const* name) : name_(name), module_(LoadLibrary(name)) {
+  if (!module_) {
+    debug() << "LoadLibrary failed: " << name_ << ": " << GetLastError() << '\n';
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.h b/libcxx/src/experimental/stacktrace/windows/dll.h
new file mode 100644
index 0000000000000..ab363ce2c2561
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dll.h
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_WINDOWS_DLL_H
+#define _LIBCPP_STACKTRACE_WINDOWS_DLL_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/debug.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// clang-format off
+
+struct dll {
+  char const* name_;
+  HMODULE module_;
+  /** Set to true in subclass's ctor if initialized successfully. */
+  bool valid_{false};
+
+  operator bool() const { return valid_; }
+
+  explicit dll(char const* name)
+      : name_(name), module_(LoadLibrary(name)) {
+    if (!module_) {
+      debug() << "LoadLibrary failed: "
+              << name_ << ": " << GetLastError() << '\n';
+    }
+  }
+
+  virtual ~dll() { FreeLibrary(module_); }
+
+  template <typename F>
+  bool get_func(F* func, char const* name) {
+    if (!(*func = (F)GetProcAddress(module_, name))) {
+      debug() << "GetProcAddress failed: "
+              << name << "' (" << name_ << "): "
+              << GetLastError() << "\n";
+      return false;
+    }
+    return true;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WINDOWS
+#endif // _LIBCPP_STACKTRACE_WINDOWS_DLL_H
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp b/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
new file mode 100644
index 0000000000000..ab28df8d3fbd7
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include "dll.h"
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+psapi_dll::~psapi_dll() = default;
+
+psapi_dll& psapi_dll::get() {
+  psapi_dll ret;
+  return ret;
+}
+
+psapi_dll() : dll("psapi.dll") {
+  // clang-format off
+if (!getFunc(&EnumProcessModules, "EnumProcessModules")) { return; }
+  if (!getFunc(&GetModuleInformation, "GetModuleInformation")) { return; }
+  if (!getFunc(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+  valid_ = true;
+  // clang-format on
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.h b/libcxx/src/experimental/stacktrace/windows/psapi_dll.h
new file mode 100644
index 0000000000000..4e6a192f3d790
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/psapi_dll.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_WINDOWS_PSAPI_DLL_H
+#define _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include <experimental/__stacktrace/detail/entry.h>
+
+#  include "../common/debug.h"
+#  include "dll.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// clang-format off
+
+struct psapi_dll final : dll {
+  virtual ~psapi_dll() = default;
+  static psapi_dll& get() { static psapi_dll ret; return ret; }
+
+  bool  (*EnumProcessModules)   (HANDLE, HMODULE*, DWORD, DWORD*);
+  bool  (*GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
+  DWORD (*GetModuleBaseName)    (HANDLE, HMODULE, char**, DWORD);
+
+  psapi_dll() : dll("psapi.dll") {
+    if (!get_func(&EnumProcessModules, "EnumProcessModules")) { return; }
+    if (!get_func(&GetModuleInformation, "GetModuleInformation")) { return; }
+    if (!get_func(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+    valid_ = true;
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WINDOWS
+#endif // _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.cpp b/libcxx/src/experimental/stacktrace/windows/win_impl.cpp
new file mode 100644
index 0000000000000..7773d0d872816
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/win_impl.cpp
@@ -0,0 +1,204 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "win_impl.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+#  include <windows.h>
+// other windows-specific headers
+#  include <dbghelp.h>
+#  define PSAPI_VERSION 1
+#  include <psapi.h>
+// standard headers
+#  include <cstdlib>
+#  include <mutex>
+
+#  include "dll.h"
+#  include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/*
+Global objects, shared among all threads and among all stacktrace operations.
+The `dbghelp` APIs are not safe to call concurrently (according to their docs)
+so we claim a lock in the `WinDebugAPIs` constructor.
+*/
+
+// Statically-initialized
+std::mutex gWindowsAPILock;
+DbgHelpDLL dbg;
+PSAPIDLL ps;
+
+// Initialized once, in first WinDebugAPIs construction;
+// protected by the above mutex.
+HANDLE proc;
+HMODULE exe;
+IMAGE_NT_HEADERS* ntHeaders;
+bool globalInitialized{false};
+
+// Globals used across invocations of the functions below.
+// Also guarded by the above mutex.
+bool symsInitialized{false};
+HMODULE moduleHandles[1024];
+size_t moduleCount; // 0 IFF module enumeration failed
+
+} // namespace
+
+win_impl::WinDebugAPIs(context& trace) : trace_(trace), guard_(gWindowsAPILock) {
+  if (!globalInitialized) {
+    // Cannot proceed without these DLLs:
+    if (!dbg) {
+      return;
+    }
+    if (!ps) {
+      return;
+    }
+    proc = GetCurrentProcess();
+    if (!(exe = GetModuleHandle(nullptr))) {
+      return;
+    }
+    if (!(ntHeaders = (*dbg.ImageNtHeader)(exe))) {
+      return;
+    }
+
+    globalInitialized = true;
+  }
+
+  // Initialize symbol machinery.
+  // Presumably the symbols in this process's space can change between
+  // stacktraces, so we'll do this each time we take a trace.
+  // The final `true` means we want the runtime to enumerate all this
+  // process's modules' symbol tables.
+  symsInitialized  = (*dbg.SymInitialize)(proc, nullptr, true);
+  DWORD symOptions = (*dbg.SymGetOptions)();
+  symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
+  (*dbg.SymSetOptions)(symOptions);
+}
+
+win_impl::~WinDebugAPIs() {
+  if (symsInitialized) {
+    (*dbg.SymCleanup)(proc);
+    symsInitialized = false;
+  }
+}
+
+void win_impl::ident_modules() {
+  if (!globalInitialized) {
+    return;
+  }
+  DWORD needBytes;
+  auto enumMods = (*ps.EnumProcessModules)(proc, moduleHandles, sizeof(moduleHandles), LPDWORD(&needBytes));
+  if (enumMods) {
+    moduleCount = needBytes / sizeof(HMODULE);
+  } else {
+    moduleCount = 0;
+    Debug() << "EnumProcessModules failed: " << GetLastError() << '\n';
+  }
+}
+
+void win_impl::symbolize() {
+  // Very long symbols longer than this amount will be truncated.
+  static constexpr size_t kMaxSymName = 256;
+  if (!globalInitialized) {
+    return;
+  }
+
+  for (auto& entry : cx_.__entries_) {
+    char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName];
+    auto* sym          = (IMAGEHLP_SYMBOL64*)space;
+    sym->SizeOfStruct  = sizeof(IMAGEHLP_SYMBOL64);
+    sym->MaxNameLength = kMaxSymName;
+    uint64_t disp{0};
+    if ((*dbg.SymGetSymFromAddr64)(proc, entry.__addr_, &disp, sym)) {
+      // Copy chars into the destination string which uses the caller-provided allocator.
+      entry.__desc_ = {sym->Name};
+    } else {
+      Debug() << "SymGetSymFromAddr64 failed: " << GetLastError() << '\n';
+    }
+  }
+}
+
+void win_impl::resolve_lines() {
+  if (!globalInitialized) {
+    return;
+  }
+
+  for (auto& entry : cx_.__entries_) {
+    DWORD disp{0};
+    IMAGEHLP_LINE64 line;
+    line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+    if ((*dbg.SymGetLineFromAddr64)(proc, entry.__addr_, &disp, &line)) {
+      // Copy chars into the destination string which uses the caller-provided allocator.
+      entry.__file_ = line.FileName;
+      entry.__line_ = line.LineNumber;
+    } else {
+      Debug() << "SymGetLineFromAddr64 failed: " << GetLastError() << '\n';
+    }
+  }
+}
+
+/*
+Inlining is disabled from here on;
+this is to ensure `collect` below doesn't get merged into its caller
+and mess around with the top of the stack (making `skip` inaccurate).
+*/
+#  pragma auto_inline(off)
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_t max_depth) {
+  if (!globalInitialized) {
+    return;
+  }
+
+  auto thread  = GetCurrentThread();
+  auto machine = ntHeaders->FileHeader.Machine;
+
+  CONTEXT ccx;
+  RtlCaptureContext(&ccx);
+
+  STACKFRAME64 frame;
+  memset(&frame, 0, sizeof(frame));
+  frame.AddrPC.Mode      = AddrModeFlat;
+  frame.AddrStack.Mode   = AddrModeFlat;
+  frame.AddrFrame.Mode   = AddrModeFlat;
+  frame.AddrPC.Offset    = ccx.Rip;
+  frame.AddrStack.Offset = ccx.Rsp;
+  frame.AddrFrame.Offset = ccx.Rbp;
+
+  while (max_depth &&
+         (*dbg.StackWalk64)(
+             machine,
+             proc,
+             thread,
+             &frame,
+             &ccx,
+             nullptr,
+             dbg.SymFunctionTableAccess64,
+             dbg.SymGetModuleBase64,
+             nullptr)) {
+    if (skip) {
+      --skip;
+      continue;
+    }
+    --max_depth;
+    auto& entry = cx_.__entries_.emplace_back();
+    // We don't need to compute the un-slid addr; windbg only needs the actual addresses.
+    // Assume address is of the instruction after a call instruction, since we can't
+    // differentiate between a signal, SEH exception handler, or a normal function call.
+    entry.__addr_ = frame.AddrPC.Offset - 1; // Back up 1 byte to get into prev insn range
+  }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.h b/libcxx/src/experimental/stacktrace/windows/win_impl.h
new file mode 100644
index 0000000000000..459347ad9a00f
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/win_impl.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_STACKTRACE_WIN_IMPL_H
+#define _LIBCPP_STACKTRACE_WIN_IMPL_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+#include <mutex>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct win_impl {
+  context& cx_;
+  std::lock_guard<std::mutex> guard_;
+  win_impl(context& trace);
+  ~win_impl();
+
+  void collect(size_t skip, size_t max_depth);
+  void ident_modules();
+  void symbolize();
+  void resolve_lines();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WIN_IMPL_H
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2032,6 +2036,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2032,6 +2036,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..cfcf7bc7c4c19 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2072,6 +2076,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..534776e6f0526 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2085,6 +2089,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 29b49f2ae7e84..d064aa972dfe2 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -295,6 +295,35 @@ experimental/propagate_const version
 experimental/simd cstdint
 experimental/simd limits
 experimental/simd version
+experimental/stacktrace bitset
+experimental/stacktrace cctype
+experimental/stacktrace cerrno
+experimental/stacktrace climits
+experimental/stacktrace clocale
+experimental/stacktrace compare
+experimental/stacktrace cstddef
+experimental/stacktrace cstdint
+experimental/stacktrace cstdio
+experimental/stacktrace cstdlib
+experimental/stacktrace cstring
+experimental/stacktrace ctime
+experimental/stacktrace cwchar
+experimental/stacktrace cwctype
+experimental/stacktrace initializer_list
+experimental/stacktrace ios
+experimental/stacktrace iosfwd
+experimental/stacktrace limits
+experimental/stacktrace list
+experimental/stacktrace locale
+experimental/stacktrace memory
+experimental/stacktrace ratio
+experimental/stacktrace stdexcept
+experimental/stacktrace streambuf
+experimental/stacktrace string
+experimental/stacktrace string_view
+experimental/stacktrace tuple
+experimental/stacktrace typeinfo
+experimental/stacktrace version
 experimental/type_traits cstdint
 experimental/type_traits initializer_list
 experimental/type_traits type_traits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 5f7b5401f081a..d439dae8c8fa4 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -295,6 +295,35 @@ experimental/propagate_const version
 experimental/simd cstdint
 experimental/simd limits
 experimental/simd version
+experimental/stacktrace bitset
+experimental/stacktrace cctype
+experimental/stacktrace cerrno
+experimental/stacktrace climits
+experimental/stacktrace clocale
+experimental/stacktrace compare
+experimental/stacktrace cstddef
+experimental/stacktrace cstdint
+experimental/stacktrace cstdio
+experimental/stacktrace cstdlib
+experimental/stacktrace cstring
+experimental/stacktrace ctime
+experimental/stacktrace cwchar
+experimental/stacktrace cwctype
+experimental/stacktrace initializer_list
+experimental/stacktrace ios
+experimental/stacktrace iosfwd
+experimental/stacktrace limits
+experimental/stacktrace list
+experimental/stacktrace locale
+experimental/stacktrace memory
+experimental/stacktrace ratio
+experimental/stacktrace stdexcept
+experimental/stacktrace streambuf
+experimental/stacktrace string
+experimental/stacktrace string_view
+experimental/stacktrace tuple
+experimental/stacktrace typeinfo
+experimental/stacktrace version
 experimental/type_traits cstdint
 experimental/type_traits initializer_list
 experimental/type_traits type_traits
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
new file mode 100644
index 0000000000000..495abe79da20f
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+  (19.6.4.4) Comparisons [stacktrace.basic.cmp]
+
+  template<class Allocator2>
+  friend bool operator==(const basic_stacktrace& x,
+                          const basic_stacktrace<Allocator2>& y) noexcept;
+
+  template<class Allocator2>
+  friend strong_ordering operator<=>(const basic_stacktrace& x,
+                                      const basic_stacktrace<Allocator2>& y) noexcept;
+*/
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2a() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2b() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  /*    
+    template<class Allocator2>
+    friend bool operator==(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
+    Returns: equal(x.begin(), x.end(), y.begin(), y.end()).
+  */
+  auto st1a = test1(); // [test1, main, ...]
+  assert(st1a == st1a);
+
+  auto st1b = st1a;
+  assert(st1a == st1b);
+
+  auto st2a = test2a(); // [test1, test2a, main, ...]
+  assert(st1a != st2a);
+
+  std::stacktrace empty; // []
+  assert(st1a != empty);
+  assert(st2a != empty);
+
+  assert(st2a.size() > st1a.size());
+  assert(st1a.size() > empty.size());
+
+  auto st2b = test2b(); // [test1, test2b, main, ...]
+  assert(st2a.size() == st2b.size());
+  assert(st2a != st2b);
+
+  /*    
+    template<class Allocator2>
+    friend strong_ordering
+    operator<=>(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
+
+    Returns: x.size() <=> y.size() if x.size() != y.size();
+    lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end()) otherwise.
+  */
+
+  // empty:  []
+  // st1a:   [test1, main, ...]
+  // st1b:   [test1, main, ...] (copy of st1a)
+  // st2a:   [test1, test2a, main:X, ...]
+  // st2b:   [test1, test2b, main:Y, ...], Y > X
+
+  assert(std::strong_ordering::equal == empty <=> empty);
+  assert(std::strong_ordering::less == empty <=> st1a);
+  assert(std::strong_ordering::less == empty <=> st1b);
+  assert(std::strong_ordering::less == empty <=> st2a);
+  assert(std::strong_ordering::less == empty <=> st2b);
+
+  assert(std::strong_ordering::greater == st1a <=> empty);
+  assert(std::strong_ordering::equal == st1a <=> st1a);
+  assert(std::strong_ordering::equal == st1a <=> st1b);
+  assert(std::strong_ordering::less == st1a <=> st2a);
+  assert(std::strong_ordering::less == st1a <=> st2b);
+
+  assert(std::strong_ordering::greater == st1b <=> empty);
+  assert(std::strong_ordering::equal == st1b <=> st1a);
+  assert(std::strong_ordering::equal == st1b <=> st1b);
+  assert(std::strong_ordering::less == st1b <=> st2a);
+  assert(std::strong_ordering::less == st1b <=> st2b);
+
+  assert(std::strong_ordering::greater == st2a <=> empty);
+  assert(std::strong_ordering::greater == st2a <=> st1a);
+  assert(std::strong_ordering::greater == st2a <=> st1b);
+  assert(std::strong_ordering::equal == st2a <=> st2a);
+  assert(std::strong_ordering::less == st2a <=> st2b);
+
+  assert(std::strong_ordering::greater == st2b <=> empty);
+  assert(std::strong_ordering::greater == st2b <=> st1a);
+  assert(std::strong_ordering::greater == st2b <=> st1b);
+  assert(std::strong_ordering::greater == st2b <=> st2a);
+  assert(std::strong_ordering::equal == st2b <=> st2b);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
new file mode 100644
index 0000000000000..64e54fdf5793d
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
@@ -0,0 +1,308 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O1 -g
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <iostream>
+
+/**
+ * This file includes tests which ensure any allocations performed by `basic_stacktrace`
+ * are done via the user-provided allocator, and not via bare `malloc` / `operator new`s.
+ * Our overridden `malloc` below will fail if `malloc_disabled` is `true`.
+ * The program's startup code may need to malloc, so we'll allow malloc initially.
+ * This is only activated during the "test_no_malloc_or_new_ex_allocator" test,
+ * during which it should be using a `test_alloc` we'll provide (which knows how to
+ * unblock `malloc` temporarily).
+ */
+bool malloc_disabled{false};
+
+void* malloc(size_t size) {
+  // If flag has not been temporarily disabled by our allocator, abort
+  if (malloc_disabled) {
+    abort();
+  }
+  // Since we overrode malloc with this function, and there's no way to call up
+  // to the "real malloc", allocate a different way.  Assumes nothing actually uses `calloc`.
+  return calloc(1, size);
+}
+
+// All the various ways to monkey around with heap memory without an allocator:
+// we'll simply redirect these through our gate-keeping `malloc` above.
+void* operator new(size_t size) { return malloc(size); }
+void* operator new[](size_t size) { return malloc(size); }
+void operator delete(void* ptr) noexcept { free(ptr); }
+void operator delete(void* ptr, size_t) noexcept { free(ptr); }
+void operator delete[](void* ptr) noexcept { free(ptr); }
+void operator delete[](void* ptr, size_t) noexcept { free(ptr); }
+
+/** RAII-style scope object to temporarily permit heap allocations, used by `test_alloc`.*/
+struct scope_enable_malloc {
+  bool prev_malloc_disabled_;
+  scope_enable_malloc() : prev_malloc_disabled_(malloc_disabled) { malloc_disabled = false; }
+  ~scope_enable_malloc() { malloc_disabled = true; }
+};
+
+template <typename T>
+struct test_alloc {
+  using size_type     = size_t;
+  using value_type    = T;
+  using pointer       = T*;
+  using const_pointer = T const*;
+
+  template <typename U>
+  struct rebind {
+    using other = test_alloc<U>;
+  };
+
+  std::allocator<T> wrapped_{};
+
+  test_alloc() = default;
+
+  template <typename U>
+  test_alloc(test_alloc<U> const& rhs) : wrapped_(rhs.wrapped_) {}
+
+  bool operator==(auto const& rhs) const { return &rhs == this; }
+  bool operator==(test_alloc const&) const { return true; }
+
+  T* allocate(size_t n) { return wrapped_.allocate(n); }
+
+  auto allocate_at_least(size_t n) { return wrapped_.allocate_at_least(n); }
+
+  void deallocate(T* ptr, size_t n) { return wrapped_.deallocate(ptr, n); }
+};
+
+/*
+  (19.6.4.2)
+
+  // [stacktrace.basic.cons], creation and assignment
+  static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;   [1]
+  static basic_stacktrace current(size_type skip,
+                                  const allocator_type& alloc = allocator_type()) noexcept;   [2]
+  static basic_stacktrace current(size_type skip, size_type max_depth,
+                                  const allocator_type& alloc = allocator_type()) noexcept;   [3]
+
+  basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);            [4]
+  explicit basic_stacktrace(const allocator_type& alloc) noexcept;                            [5]
+
+  basic_stacktrace(const basic_stacktrace& other);                                            [6]
+  basic_stacktrace(basic_stacktrace&& other) noexcept;                                        [7]
+  basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);               [8]
+  basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);                    [9]
+  basic_stacktrace& operator=(const basic_stacktrace& other);                                 [10]
+  basic_stacktrace& operator=(basic_stacktrace&& other)
+    noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+      allocator_traits<Allocator>::is_always_equal::value);                                   [11]
+
+  ~basic_stacktrace();                                                                        [12]
+*/
+
+// clang-format off
+uint32_t test1_line;
+uint32_t test2_line;
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test1(A& alloc) {
+  test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret = std::basic_stacktrace<A>::current(alloc);
+  return ret;
+}
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test2(A& alloc) {
+  test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+  auto ret = test1(alloc);
+  return ret;
+}
+// clang-format on
+
+/*
+    [1]
+    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+
+    Returns: A basic_stacktrace object with frames_ storing the stacktrace of the current evaluation
+    in the current thread of execution, or an empty basic_stacktrace object if the initialization of
+    frames_ failed. alloc is passed to the constructor of the frames_ object.
+    [Note 1: If the stacktrace was successfully obtained, then frames_.front() is the stacktrace_entry
+    representing approximately the current evaluation, and frames_.back() is the stacktrace_entry
+    representing approximately the initial function of the current thread of execution. — end note]
+  */
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
+  test_alloc<std::stacktrace_entry> alloc;
+  uint32_t main_line = __LINE__ + 1;
+  auto st            = test2(alloc);
+
+  std::cerr << "*** Stacktrace obtained at line " << main_line << '\n' << st << '\n';
+
+  assert(st.size() >= 3);
+  assert(st[0]);
+  assert(st[0].native_handle());
+  assert(st[0].description().contains("test1"));
+  assert(st[0].source_file().contains("basic.cons.pass.cpp"));
+  assert(st[1]);
+  assert(st[1].native_handle());
+  assert(st[1].description().contains("test2"));
+  assert(st[1].source_file().contains("basic.cons.pass.cpp"));
+  assert(st[2]);
+  assert(st[2].native_handle());
+  assert(st[2].description().contains("test_current"));
+  assert(st[2].source_file().contains("basic.cons.pass.cpp"));
+
+  // We unfortunately cannot guarantee the following; in CI, and possibly on users' build machines,
+  // there may not be an up-to-date version of e.g. `addr2line`.
+  // assert(st[0].source_file().ends_with("basic.cons.pass.cpp"));
+  // assert(st[0].source_line() == test1_line);
+  // assert(st[1].source_file().ends_with("basic.cons.pass.cpp"));
+  // assert(st[1].source_line() == test2_line);
+  // assert(st[2].source_file().ends_with("basic.cons.pass.cpp"));
+  // assert(st[2].source_line() == main_line);
+}
+
+/*
+  [2]
+  static basic_stacktrace current(size_type skip,
+                              const allocator_type& alloc = allocator_type()) noexcept;
+  Let t be a stacktrace as-if obtained via basic_stacktrace​::​current(alloc). Let n be t.size().
+  Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
+  t.begin() + min(n, skip), t.end(), and alloc, or an empty basic_stacktrace object if the
+  initialization of frames_ failed.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
+  // Use default allocator for simplicity; alloc is covered above
+  auto st_skip0 = std::stacktrace::current();
+  std::cerr << "*** st_skip0:\n" << st_skip0 << '\n';
+  assert(st_skip0.size() >= 2);
+  auto st_skip1 = std::stacktrace::current(1);
+  std::cerr << "*** st_skip1:\n" << st_skip1 << '\n';
+  assert(st_skip1.size() >= 1);
+  assert(st_skip0.size() == st_skip1.size() + 1);
+  assert(st_skip0[1] == st_skip1[0]);
+  auto st_skip_many = std::stacktrace::current(1 << 20);
+  assert(st_skip_many.empty());
+}
+
+/*
+  [3]
+  static basic_stacktrace current(size_type skip, size_type max_depth,
+                              const allocator_type& alloc = allocator_type()) noexcept;
+  Let t be a stacktrace as-if obtained via basic_stacktrace​::​current(alloc). Let n be t.size().
+  Preconditions: skip <= skip + max_depth is true.
+  Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
+  t.begin() + min(n, skip), t.begin() + min(n, skip + max_depth), and alloc, or an empty
+  basic_stacktrace object if the initialization of frames_ failed.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
+  // current stack is: [this function, main, (possibly something else, e.g. `_start` from libc)]
+  // so it's probably 3 functions deep -- but certainly at least 2 deep.
+  auto st = std::stacktrace::current();
+  assert(st.size() >= 2);
+  auto it     = st.begin();
+  auto entry1 = *(it++); // represents this function
+  auto entry2 = *(it++); // represents our caller, `main`
+
+  // get current trace again, but skip the 1st
+  st = std::stacktrace::current(1, 1);
+  assert(st.size() >= 1);
+  assert(*st.begin() == entry2);
+}
+
+/*
+  [4]
+  basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+  Postconditions: empty() is true.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_default_construct() {
+  std::stacktrace st;
+  assert(st.empty());
+}
+
+/*
+  [5]
+  explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+  Effects: alloc is passed to the frames_ constructor.
+  Postconditions: empty() is true.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_construct_with_allocator() {
+  test_alloc<std::stacktrace_entry> alloc;
+  std::basic_stacktrace<decltype(alloc)> st(alloc);
+  assert(st.empty());
+
+  st = std::basic_stacktrace<decltype(alloc)>::current(alloc);
+  assert(!st.empty());
+}
+
+/*
+  [6] basic_stacktrace(const basic_stacktrace& other);
+  [7] basic_stacktrace(basic_stacktrace&& other) noexcept;
+  [8] basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+  [9] basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+  [10] basic_stacktrace& operator=(const basic_stacktrace& other);
+  [11] basic_stacktrace& operator=(basic_stacktrace&& other)
+    noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+    allocator_traits<Allocator>::is_always_equal::value);
+  
+  Remarks: Implementations may strengthen the exception specification for these functions
+  ([res.on.exception.handling]) by ensuring that empty() is true on failed allocation.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_copy_move_ctors() {
+  using A = std::allocator<std::stacktrace_entry>;
+  A alloc;
+  auto st = std::basic_stacktrace<A>::current(alloc);
+
+  auto copy_constr = std::basic_stacktrace<A>(st);
+  assert(st == copy_constr);
+
+  std::basic_stacktrace<A> copy_assign;
+  copy_assign = std::basic_stacktrace<A>(st);
+  assert(st == copy_assign);
+
+  auto st2 = test2(alloc);
+  assert(st2.size());
+  std::basic_stacktrace<A> move_constr(std::move(st2));
+  assert(move_constr.size());
+  assert(!st2.size());
+
+  auto st3 = test2(alloc);
+  assert(st3.size());
+  std::basic_stacktrace<A> move_assign;
+  move_assign = std::move(st3);
+  assert(move_assign.size());
+  assert(!st3.size());
+
+  // TODO(stacktrace23): should we add test cases with `select_on_container_copy_construction`?
+}
+
+/** Ensure all allocations take place through a given allocator, and that none
+ * are sneaking around it by accidentally using malloc or operator new. */
+void test_no_malloc_or_new_ex_allocator() {
+  // A generic allocator we'll use for everything
+  using A = test_alloc<std::stacktrace_entry>;
+  A alloc;
+  // After this point all stacktrace operations must properly use `alloc`
+  malloc_disabled = true;
+  // Try all the stack trace operations
+  auto st = std::basic_stacktrace<A>(alloc);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  test_current();
+  test_current_with_skip();
+  test_current_with_skip_depth();
+  test_default_construct();
+  test_construct_with_allocator();
+  test_copy_move_ctors();
+  test_no_malloc_or_new_ex_allocator();
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
new file mode 100644
index 0000000000000..5929cd8f8d0f9
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+  (19.6.4.5) Modifiers [stacktrace.basic.mod]
+
+  void swap(basic_stacktrace& other)
+      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+      allocator_traits<Allocator>::is_always_equal::value);
+
+  Effects: Exchanges the contents of *this and other.
+*/
+int main(int, char**) {
+  std::stacktrace empty;
+  auto current = std::stacktrace::current();
+
+  std::stacktrace a(empty);
+  std::stacktrace b(current);
+  assert(a == empty);
+  assert(b == current);
+
+  a.swap(b);
+  assert(a == current);
+  assert(b == empty);
+
+  // TODO(stacktrace23): should we also test swap w/ `select_on_container_swap` case
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
new file mode 100644
index 0000000000000..665fb475f56e9
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <sstream>
+
+/*
+  (19.6.4.6) Non-member functions
+
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+      noexcept(noexcept(a.swap(b)));
+
+  string to_string(const stacktrace_entry& f);
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+*/
+int main(int, char**) {
+  /*
+  template<class Allocator>
+  void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+    noexcept(noexcept(a.swap(b)));
+  Effects: Equivalent to a.swap(b).
+  */
+  std::stacktrace empty;
+  auto current = std::stacktrace::current();
+
+  std::stacktrace a(empty);
+  std::stacktrace b(current);
+  assert(a == empty);
+  assert(b == current);
+
+  std::swap(a, b);
+  assert(a == current);
+  assert(b == empty);
+
+  /*
+  string to_string(const stacktrace_entry& f);
+  Returns: A string with a description of f.
+  Recommended practice: The description should provide information about the contained evaluation,
+  including information from f.source_file() and f.source_line().
+  */
+
+  assert(std::to_string(a[0]).contains("main"));
+  assert(std::to_string(a[0]).contains("basic.nonmem.pass"));
+
+  /*
+  template<class Allocator>
+  string to_string(const basic_stacktrace<Allocator>& st);
+  Returns: A string with a description of st.
+  [Note 1: The number of lines is not guaranteed to be equal to st.size(). — end note]
+  */
+
+  assert(std::to_string(a).contains("main"));
+  assert(std::to_string(a).contains("basic.nonmem.pass"));
+
+  /*
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  Effects: Equivalent to: return os << to_string(f);
+  */
+
+  std::stringstream entry_os;
+  entry_os << a[0];
+  assert(entry_os.str() == std::to_string(a[0]));
+
+  /*
+  template<class Allocator>
+    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+  Effects: Equivalent to: return os << to_string(st);
+  */
+
+  std::stringstream trace_os;
+  trace_os << a;
+  assert(trace_os.str() == std::to_string(a));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
new file mode 100644
index 0000000000000..b5c6cf77dde71
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+#include <iostream>
+#include <iterator>
+#include <memory>
+
+#include <cassert>
+#include <stdexcept>
+
+/*
+  (19.6.4.3) Observers [stacktrace.basic.obs]
+
+  // [stacktrace.basic.obs], observers
+  allocator_type get_allocator() const noexcept;      [1]
+
+  const_iterator begin() const noexcept;              [2]
+  const_iterator end() const noexcept;                [3]
+  const_reverse_iterator rbegin() const noexcept;     [4]
+  const_reverse_iterator rend() const noexcept;       [5]
+
+  const_iterator cbegin() const noexcept;             [6]
+  const_iterator cend() const noexcept;               [7]
+  const_reverse_iterator crbegin() const noexcept;    [8]
+  const_reverse_iterator crend() const noexcept;      [9]
+
+  bool empty() const noexcept;                        [10]
+  size_type size() const noexcept;                    [11]
+  size_type max_size() const noexcept;                [12]
+
+  const_reference operator[](size_type) const;        [13]
+  const_reference at(size_type) const;                [14]
+*/
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  std::stacktrace st;
+
+  /*
+    using const_iterator = implementation-defined;
+    The type models random_access_iterator ([iterator.concept.random.access]) and meets the
+    Cpp17RandomAccessIterator requirements ([random.access.iterators]).
+  */
+  static_assert(std::random_access_iterator<decltype(st.begin())>);
+  static_assert(std::random_access_iterator<decltype(st.rbegin())>);
+  static_assert(std::random_access_iterator<decltype(st.cbegin())>);
+  static_assert(std::random_access_iterator<decltype(st.crbegin())>);
+
+  /*
+    allocator_type get_allocator() const noexcept;
+    Returns: frames_.get_allocator().
+  */
+  static_assert(std::same_as<decltype(st.get_allocator()), std::stacktrace::allocator_type>);
+
+  /*
+    const_iterator begin() const noexcept;
+    const_iterator cbegin() const noexcept;
+    Returns: An iterator referring to the first element in frames_. If empty() is true,
+    then it returns the same value as end().
+
+    const_iterator end() const noexcept;
+    const_iterator cend() const noexcept;
+    Returns: The end iterator.
+
+    const_reverse_iterator rbegin() const noexcept;
+    const_reverse_iterator crbegin() const noexcept;
+    Returns: reverse_iterator(cend()).
+
+    const_reverse_iterator rend() const noexcept;
+    const_reverse_iterator crend() const noexcept;
+    Returns: reverse_iterator(cbegin()).
+  */
+
+  // st is initially empty:
+  assert(st.begin() == st.end());
+  assert(st.cbegin() == st.cend());
+  assert(st.rbegin() == st.rend());
+  assert(st.crbegin() == st.crend());
+
+  /*
+    bool empty() const noexcept;
+    Returns: frames_.empty().
+  */
+  assert(st.empty());
+  assert(st.size() == 0);
+
+  // no longer empty:
+  st = test3();
+  assert(!st.empty());
+  assert(st.begin() != st.end());
+  assert(st.cbegin() != st.cend());
+
+  /*
+    size_type size() const noexcept;
+    Returns: frames_.size().
+
+    size_type max_size() const noexcept;
+    Returns: frames_.max_size().
+  */
+  std::cout << st << std::endl;
+  assert(st.size() >= 4);
+  assert(st.max_size() == (std::vector<std::stacktrace_entry, std::allocator<std::stacktrace_entry>>().max_size()));
+
+  /*
+    const_reference operator[](size_type frame_no) const;
+    Preconditions: frame_no < size() is true.
+    Returns: frames_[frame_no].
+    Throws: Nothing.
+  */
+  assert(st[0]);
+  assert(st[1]);
+  assert(st[2]);
+  assert(st[3]);
+
+  /*
+    const_reference at(size_type frame_no) const;
+    Returns: frames_[frame_no].
+    Throws: out_of_range if frame_no >= size().
+  */
+  assert(st.at(0));
+  assert(st.at(1));
+  assert(st.at(2));
+  assert(st.at(3));
+  try {
+    (void)st.at(42);
+    assert(false && "'at' should have thrown range_error");
+  } catch (std::out_of_range const&) {
+    // ok
+  }
+
+  auto f0 = st[0];
+  auto f1 = st[1];
+  auto f2 = st[2];
+  auto f3 = st[3];
+
+  auto fit = st.begin();
+  assert(*fit++ == f0);
+  assert(fit != st.end());
+
+  auto rit = st.rbegin();
+  assert(rit != st.rend());
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
new file mode 100644
index 0000000000000..4c6531319fc09
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+  (19.6.3.5) Comparison [stacktrace.entry.cmp]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.cmp], comparison
+    friend constexpr bool operator==(const stacktrace_entry& x,
+                                     const stacktrace_entry& y) noexcept;           // [T11]
+    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+                                                 const stacktrace_entry& y) noexcept;  // [T12]
+
+    [. . .]
+  };
+}
+*/
+
+int main(int, char**) {
+  // Two empty entries
+
+  std::stacktrace_entry a;
+  assert(a.native_handle() == 0);
+  assert(!a);
+
+  std::stacktrace_entry b;
+  assert(b.native_handle() == 0);
+  assert(!b);
+
+  // A non-empty entry.
+  std::stacktrace_entry c;
+  ((std::__stacktrace::entry*)(&c))->__addr_ = (uintptr_t)&main;
+  assert(c);
+
+  // [T11]
+  // friend constexpr bool operator==(const stacktrace_entry& x,
+  //                                  const stacktrace_entry& y) noexcept;
+  // "Returns: true if and only if x and y represent the same
+  // stacktrace entry or both x and y are empty."
+  // (We represent "empty" with a native_handle of zero.)
+  assert(a == b);
+  assert(a != c);
+
+  // [T12]
+  // friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+  //                                              const stacktrace_entry& y) noexcept;
+  assert(std::strong_ordering::equal == (a <=> b));
+  assert(std::strong_ordering::equivalent == (a <=> b));
+  assert(std::strong_ordering::greater == (c <=> a));
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
new file mode 100644
index 0000000000000..944b4c519f0fd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <type_traits>
+
+/*
+  (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.cons], constructors
+    constexpr stacktrace_entry() noexcept;                                          // [T2]
+    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;             // [T3]
+    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;  // [T4]
+    ~stacktrace_entry();                                                            // [T5]
+
+    [. . .]
+  };
+}
+*/
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+  // [T2]
+  // constexpr stacktrace_entry() noexcept;
+  // "Postconditions: *this is empty."
+  static_assert(std::is_default_constructible_v<std::stacktrace_entry>);
+  static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
+  std::stacktrace_entry entry_t2;
+  assert(!entry_t2);
+
+  // [T3]
+  // constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+  static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
+  std::stacktrace_entry entry_t3(entry_t2);
+
+  // [T4]
+  // constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+  static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
+  std::stacktrace_entry entry_t4;
+  entry_t4 = entry_t2;
+
+  // [T5]
+  // ~stacktrace_entry();
+  std::stacktrace_entry* entry_ptr{nullptr};
+  delete entry_ptr;
+  {
+    auto entry_t5(entry_t4); /* construct and immediately let it go out of scope */
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
new file mode 100644
index 0000000000000..da6e3ccec92b6
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+  (19.6.3.3) Observers [stacktrace.entry.obs]
+
+namespace std {
+  class stacktrace_entry {
+    // [stacktrace.entry.obs], observers
+    constexpr native_handle_type native_handle() const noexcept;                    // [T6]
+    constexpr explicit operator bool() const noexcept;                              // [T7]
+    
+    [. . .]
+  };
+}
+*/
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {}
+_LIBCPP_END_NAMESPACE_STD
+
+int main(int, char**) {
+  // [T6]
+  std::stacktrace_entry entry_t6;
+
+  // constexpr native_handle_type native_handle() const noexcept;
+  assert(entry_t6.native_handle() == 0);
+
+  // [T7]
+  // constexpr explicit operator bool() const noexcept;
+  // "Returns: false if and only if *this is empty."
+  assert(!entry_t6);
+
+  // Now set addr to something nonzero
+  ((std::__stacktrace::entry*)(&entry_t6))->__addr_ = (uintptr_t)&main;
+  assert(entry_t6.native_handle() == (uintptr_t)&main);
+  assert(entry_t6);
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
new file mode 100644
index 0000000000000..bfdb54e4df668
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <iostream>
+#include <string>
+
+/*
+    (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+  class stacktrace_entry {
+  public:
+    // [stacktrace.entry.query], query
+    string description() const;                                                     // [T8]
+    string source_file() const;                                                     // [T9]
+    uint_least32_t source_line() const;                                             // [T10]
+
+    [. . .]
+  };
+}
+*/
+
+int main(int, char**) {
+  // empty trace entry
+  std::stacktrace_entry e;
+
+  // [T8]
+  // string description() const;
+  auto desc = e.description();
+  assert(desc.c_str()[0] == 0);
+  assert(desc.size() == 0);
+
+  // [T9]
+  // string source_file() const;
+  auto src = e.source_file();
+  assert(src.c_str()[0] == 0);
+  assert(src.size() == 0);
+
+  // [T10]
+  // uint_least32_t source_line() const;
+  assert(e.source_line() == 0);
+
+  // Get the current trace.
+  uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+  auto trace           = std::stacktrace::current();
+
+  // First entry of this should be `main`.
+  e = trace.at(0);
+  assert(e);
+
+  std::cout << "main is at:      " << (void*)&main << std::endl;
+  std::cout << "e.native_handle: " << (void*)e.native_handle() << std::endl;
+  std::cout << "e.description:   " << e.description() << std::endl;
+  std::cout << "e.source_file:   " << e.source_file() << std::endl;
+  std::cout << "e.source_line:   " << e.source_line() << std::endl;
+
+  std::cout << trace << std::endl;
+
+  assert(e.native_handle());
+  assert(e.native_handle() >= (uintptr_t)&main);
+  assert(e.description() == "main" || e.description() == "_main");
+  // Even if we cannot get the debug info or call out to llvm-addr2line,
+  // we should at least get the executable filename, e.g. `entry.query.pass.cpp.dir/t.exe`
+  assert(!e.source_file().empty());
+  assert(e.source_file().contains("entry.query.pass.cpp"));
+
+  // These might not work at testing time, so disable.
+  // Complete tests checking the filename:line part are in `std/diagnostics/stacktrace`
+  //   assert(e.source_file().ends_with("entry.query.pass.cpp"));
+  //   assert(e.source_line() == line_number);
+  (void)line_number;
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
new file mode 100644
index 0000000000000..b37681659d43c
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <experimental/stacktrace>
+
+/*
+  (19.6.5) Formatting support [stacktrace.format]
+
+  template<> struct formatter<stacktrace_entry>;                            [1]
+  template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;  [2]
+*/
+
+int main(int, char**) {
+  /*
+    [1]
+    template<> struct formatter<stacktrace_entry>;
+
+    formatter<stacktrace_entry> interprets format-spec as a stacktrace-entry-format-spec.
+    The syntax of format specifications is as follows:
+
+      stacktrace-entry-format-spec :
+        fill-and-align_[opt] width_[opt]
+
+    [Note 1: The productions fill-and-align and width are described in [format.string.std]. — end note]
+
+    A stacktrace_entry object se is formatted as if by copying to_string(se) through the output iterator
+    of the context with additional padding and adjustments as specified by the format specifiers.
+  */
+
+  // TODO(stacktrace23): needs `formatter`
+
+  /*
+    [2]
+    template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+    
+    For formatter<basic_stacktrace<Allocator>>, format-spec is empty.
+    
+    A basic_stacktrace<Allocator> object s is formatted as if by copying to_string(s) through the
+    output iterator of the context.
+  */
+
+  // TODO(stacktrace23): needs `formatter`
+
+  return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
new file mode 100644
index 0000000000000..3c92016b2c900
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <experimental/stacktrace>
+
+#include <memory>
+#include <type_traits>
+
+/*
+
+// (19.6.2) Header <stacktrace> synopsis [stacktrace.syn]
+
+namespace std {
+
+  // [stacktrace.entry], class stacktrace_entry
+  class stacktrace_entry;                                               [1]
+
+  // [stacktrace.basic], class template basic_stacktrace
+  template<class Allocator>
+    class basic_stacktrace;                                             [2]
+
+  // basic_stacktrace typedef-names
+  using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;     [3]
+
+  // [stacktrace.basic.nonmem], non-member functions
+  template<class Allocator>
+    void swap(basic_stacktrace<Allocator>& a,
+              basic_stacktrace<Allocator>& b)
+                noexcept(noexcept(a.swap(b)));                          [4]
+
+  string to_string(const stacktrace_entry& f);                          [5]
+
+  template<class Allocator>
+    string to_string(const basic_stacktrace<Allocator>& st);            [6]
+
+  ostream& operator<<(ostream& os, const stacktrace_entry& f);          [7]
+  template<class Allocator>
+    ostream& operator<<(ostream& os,
+                        const basic_stacktrace<Allocator>& st);         [8]
+
+  // [stacktrace.format], formatting support
+  template<> struct formatter<stacktrace_entry>;                        [9]
+  template<class Allocator>
+    struct formatter<basic_stacktrace<Allocator>>;                      [10]
+
+  namespace pmr {
+    using stacktrace =
+        basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;      [11]
+  }
+
+  // [stacktrace.basic.hash], hash support
+  template<> struct hash<stacktrace_entry>;                             [12]
+  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;   [13]
+}
+*/
+
+// static_assert(__cpp_lib_stacktrace == 202011L);
+
+int main(int, char**) {
+  // Very basic tests to ensure the required things are declared.
+  // Only checking for types' and functions' existence, parameter and return types,
+  // and type-completeness.  Functional tests exist in the other .cpp's in this directory.
+
+  using Alloc = std::allocator<std::stacktrace_entry>;
+
+  // [1]
+  // [stacktrace.entry], class stacktrace_entry
+  // class stacktrace_entry;
+  using T1 = std::stacktrace_entry;
+  assert(std::is_constructible_v<T1>);
+
+  // [2]
+  // [stacktrace.basic], class template basic_stacktrace
+  // template<class Allocator>
+  //   class basic_stacktrace;
+  using T2 = std::basic_stacktrace<Alloc>;
+  assert(std::is_constructible_v<T2>);
+
+  // [3]
+  // basic_stacktrace typedef-names
+  // using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+  using T3 = std::stacktrace;
+  static_assert(std::is_same_v<T3, std::basic_stacktrace<std::allocator<std::stacktrace_entry>>>);
+
+  // [4]
+  // [stacktrace.basic.nonmem], non-member functions
+  // template<class Allocator>
+  //   void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+  //     noexcept(noexcept(a.swap(b)));
+  std::basic_stacktrace<Alloc> a;
+  std::basic_stacktrace<Alloc> b;
+  std::swap(a, b);
+
+  // [5]
+  // string to_string(const stacktrace_entry& f);
+  using T5 = decltype(std::to_string(std::stacktrace_entry()));
+  static_assert(std::is_same_v<std::string, T5>);
+
+  // [6]
+  // template<class Allocator>
+  // string to_string(const basic_stacktrace<Allocator>& st);
+  using T6 = decltype(std::to_string(std::basic_stacktrace<Alloc>()));
+  static_assert(std::is_same_v<std::string, T6>);
+
+  // [7]
+  // ostream& operator<<(ostream& os, const stacktrace_entry& f);
+  std::ostream* os;
+  using T7 = decltype(operator<<(*os, std::stacktrace_entry()));
+  static_assert(std::is_same_v<std::ostream&, T7>);
+
+  // [8]
+  // template<class Allocator>
+  //   ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+  using T8 = decltype(operator<<(*os, std::basic_stacktrace<Alloc>()));
+  static_assert(std::is_same_v<std::ostream&, T8>);
+
+  // [9]
+  // template<> struct formatter<stacktrace_entry>;
+  //   using T9 = std::formatter<std::stacktrace_entry>;
+  //   static_assert(std::is_constructible_v<T9>);
+
+  // TODO(stacktrace23): needs `formatter`
+
+  // [10]
+  // template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+  //   using T10 = std::formatter<std::basic_stacktrace<Alloc>>;
+  //   static_assert(std::is_constructible_v<T10>);
+
+  // TODO(stacktrace23): needs `formatter`
+
+  // [11]
+  // namespace pmr { using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>; }
+  using AllocPMR = std::pmr::polymorphic_allocator<std::stacktrace_entry>;
+  using BasicPMR = std::basic_stacktrace<AllocPMR>;
+  using T11      = std::pmr::stacktrace;
+  static_assert(std::is_same_v<T11, BasicPMR>);
+
+  return 0;
+}
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index 4f569ffba6325..df295a6454050 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -207,6 +207,7 @@ def __hash__(self) -> int:
     "experimental/iterator": "// UNSUPPORTED: c++03",
     "experimental/propagate_const": "// UNSUPPORTED: c++03",
     "experimental/simd": "// UNSUPPORTED: c++03",
+    "experimental/stacktrace": "// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20",
     "experimental/type_traits": "// UNSUPPORTED: c++03",
     "experimental/utility": "// UNSUPPORTED: c++03",
     "filesystem": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14",
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index b9e8d0717f7df..41755a276145b 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -318,6 +318,40 @@ if (libcxx_enable_experimental) {
     output_dir = runtimes_dir
     output_name = "c++experimental"
     sources = [ "experimental/keep.cpp" ]
+    sources += [
+      "experimental/stacktrace/alloc.cpp",
+      "experimental/stacktrace/common/config.h",
+      "experimental/stacktrace/common/debug.cpp",
+      "experimental/stacktrace/common/debug.h",
+      "experimental/stacktrace/common/failed.h",
+      "experimental/stacktrace/common/fd.cpp",
+      "experimental/stacktrace/common/fd.h",
+      "experimental/stacktrace/common/images.h",
+      "experimental/stacktrace/context.cpp",
+      "experimental/stacktrace/linux/linux-dl.cpp",
+      "experimental/stacktrace/linux/linux-elf.cpp",
+      "experimental/stacktrace/linux/linux-sym.cpp",
+      "experimental/stacktrace/linux/linux.h",
+      "experimental/stacktrace/osx/osx.cpp",
+      "experimental/stacktrace/osx/osx.h",
+      "experimental/stacktrace/stacktrace.cpp",
+      "experimental/stacktrace/tools/addr2line.cpp",
+      "experimental/stacktrace/tools/atos.cpp",
+      "experimental/stacktrace/tools/llvm_symbolizer.cpp",
+      "experimental/stacktrace/tools/pspawn.h",
+      "experimental/stacktrace/tools/tools.h",
+      "experimental/stacktrace/tools/toolspawner.cpp",
+      "experimental/stacktrace/unwind/unwind.cpp",
+      "experimental/stacktrace/unwind/unwind.h",
+      "experimental/stacktrace/windows/dbghelp_dll.cpp",
+      "experimental/stacktrace/windows/dbghelp_dll.h",
+      "experimental/stacktrace/windows/dll.cpp",
+      "experimental/stacktrace/windows/dll.h",
+      "experimental/stacktrace/windows/psapi_dll.cpp",
+      "experimental/stacktrace/windows/psapi_dll.h",
+      "experimental/stacktrace/windows/win_impl.cpp",
+      "experimental/stacktrace/windows/win_impl.h",
+    ]
     if (libcxx_enable_filesystem && libcxx_enable_time_zone_database) {
       sources += [
         # TODO TZDB The exception could be moved in chrono once the TZDB library

>From 134efda2ba278f90bc4bc9976d2fd2aea10e1421 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sun, 20 Apr 2025 23:48:58 -0400
Subject: [PATCH 20/37] Remove unrelated change

---
 libcxx/include/__ostream/basic_ostream.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h
index 0a6e4360201ba..09ad401a48d60 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -571,7 +571,7 @@ _LIBCPP_HIDE_FROM_ABI _Stream&& operator<<(_Stream&& __os, const _Tp& __x) {
 }
 
 template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
+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());
 }

>From 5c67fbfc61e64a01d764ac85b11ac3734055f03a Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 21 Apr 2025 19:26:15 -0400
Subject: [PATCH 21/37] Move stacktrace headers out of experimental

---
 libcxx/include/CMakeLists.txt                 |   7 -
 .../__stacktrace/detail/alloc.h               |   0
 .../__stacktrace/detail/context.h             |   0
 .../__stacktrace/detail/entry.h               |   0
 .../__stacktrace/detail/to_string.h           |   0
 .../__stacktrace/basic_stacktrace.h           | 306 ------------------
 .../__stacktrace/stacktrace_entry.h           | 111 -------
 libcxx/include/experimental/stacktrace        | 191 -----------
 libcxx/include/module.modulemap.in            |  10 -
 libcxx/modules/std.compat.cppm.in             |   2 +-
 llvm/utils/gn/secondary/libcxx/src/BUILD.gn   |  66 ++--
 11 files changed, 33 insertions(+), 660 deletions(-)
 rename libcxx/include/{experimental => }/__stacktrace/detail/alloc.h (100%)
 rename libcxx/include/{experimental => }/__stacktrace/detail/context.h (100%)
 rename libcxx/include/{experimental => }/__stacktrace/detail/entry.h (100%)
 rename libcxx/include/{experimental => }/__stacktrace/detail/to_string.h (100%)
 delete mode 100644 libcxx/include/experimental/__stacktrace/basic_stacktrace.h
 delete mode 100644 libcxx/include/experimental/__stacktrace/stacktrace_entry.h
 delete mode 100644 libcxx/include/experimental/stacktrace

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index efe74aab1a83d..34895f334733c 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -994,17 +994,10 @@ 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/experimental/__stacktrace/detail/alloc.h b/libcxx/include/__stacktrace/detail/alloc.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/alloc.h
rename to libcxx/include/__stacktrace/detail/alloc.h
diff --git a/libcxx/include/experimental/__stacktrace/detail/context.h b/libcxx/include/__stacktrace/detail/context.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/context.h
rename to libcxx/include/__stacktrace/detail/context.h
diff --git a/libcxx/include/experimental/__stacktrace/detail/entry.h b/libcxx/include/__stacktrace/detail/entry.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/entry.h
rename to libcxx/include/__stacktrace/detail/entry.h
diff --git a/libcxx/include/experimental/__stacktrace/detail/to_string.h b/libcxx/include/__stacktrace/detail/to_string.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/to_string.h
rename to libcxx/include/__stacktrace/detail/to_string.h
diff --git a/libcxx/include/experimental/__stacktrace/basic_stacktrace.h b/libcxx/include/experimental/__stacktrace/basic_stacktrace.h
deleted file mode 100644
index 8ac1de82b1c63..0000000000000
--- a/libcxx/include/experimental/__stacktrace/basic_stacktrace.h
+++ /dev/null
@@ -1,306 +0,0 @@
-// -*- 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/stacktrace_entry.h b/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
deleted file mode 100644
index be8abb3ea91b2..0000000000000
--- a/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// -*- 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_ENTRY
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
-
-#include <__cstddef/byte.h>
-#include <__cstddef/ptrdiff_t.h>
-#include <__cstddef/size_t.h>
-#include <__format/formatter.h>
-#include <__functional/function.h>
-#include <__functional/hash.h>
-#include <__fwd/format.h>
-#include <__fwd/ostream.h>
-#include <__fwd/sstream.h>
-#include <__fwd/vector.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 <__utility/move.h>
-#include <__vector/pmr.h>
-#include <__vector/swap.h>
-#include <__vector/vector.h>
-#include <cstdint>
-#include <string>
-
-#include "experimental/__stacktrace/detail/entry.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
-public:
-  // (19.6.3.1) Overview [stacktrace.entry.overview]
-  using native_handle_type = uintptr_t;
-
-  // (19.6.3.2) [stacktrace.entry.cons], constructors
-  _LIBCPP_EXPORTED_FROM_ABI ~stacktrace_entry() noexcept                                            = default;
-  _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry() noexcept                                   = default;
-  _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry(const stacktrace_entry&) noexcept            = default;
-  _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry& operator=(const stacktrace_entry&) noexcept = default;
-
-  // (19.6.3.3) [stacktrace.entry.obs], observers
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr native_handle_type native_handle() const noexcept {
-    return __addr_;
-  }
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr explicit operator bool() const noexcept {
-    return native_handle() != 0;
-  }
-
-  // (19.6.3.4) [stacktrace.entry.query], query
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string description() const { return __desc_; }
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string source_file() const { return __file_; }
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI uint_least32_t source_line() const { return __line_; }
-
-  // (19.6.3.5) [stacktrace.entry.cmp], comparison
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr bool
-  operator==(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
-    return __x.native_handle() == __y.native_handle();
-  }
-
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr strong_ordering
-  operator<=>(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
-    return __x.native_handle() <=> __y.native_handle();
-  }
-
-  _LIBCPP_HIDE_FROM_ABI explicit stacktrace_entry(__stacktrace::entry&& __e)
-      : __addr_(__e.__addr_), __desc_(std::move(__e.__desc_)), __file_(std::move(__e.__file_)), __line_(__e.__line_) {}
-
-private:
-  uintptr_t __addr_{};
-  std::string __desc_{};
-  std::string __file_{};
-  uint_least32_t __line_{};
-};
-
-// (19.6.4.6)
-// Non-member functions [stacktrace.basic.nonmem]
-
-_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry);
-_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry);
-
-// (19.6.5)
-// Formatting support [stacktrace.format]
-
-// TODO(stacktrace23): needs `formatter`
-template <>
-struct _LIBCPP_EXPORTED_FROM_ABI formatter<stacktrace_entry>;
-
-// (19.6.6)
-// Hash support [stacktrace.basic.hash]
-
-template <>
-struct _LIBCPP_EXPORTED_FROM_ABI hash<stacktrace_entry> {
-  [[nodiscard]] size_t operator()(stacktrace_entry const& __entry) const noexcept {
-    auto __addr = __entry.native_handle();
-    return hash<uintptr_t>()(__addr);
-  }
-};
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
diff --git a/libcxx/include/experimental/stacktrace b/libcxx/include/experimental/stacktrace
deleted file mode 100644
index 9e1f7259ca3da..0000000000000
--- a/libcxx/include/experimental/stacktrace
+++ /dev/null
@@ -1,191 +0,0 @@
-// -*- 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
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE
-
-/*
-  Header <stacktrace> synopsis
-  (19.6.2)
-
-#include <compare>  // see [compare.syn]
-
-namespace std {
-  // [stacktrace.entry], class stacktrace_entry
-  class stacktrace_entry;
-
-  // [stacktrace.basic], class template basic_stacktrace
-  template<class Allocator>
-    class basic_stacktrace;
-
-  // basic_stacktrace typedef-names
-  using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
-
-  // [stacktrace.basic.nonmem], non-member functions
-  template<class Allocator>
-    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
-      noexcept(noexcept(a.swap(b)));
-
-  string to_string(const stacktrace_entry& f);
-
-  template<class Allocator>
-    string to_string(const basic_stacktrace<Allocator>& st);
-
-  ostream& operator<<(ostream& os, const stacktrace_entry& f);
-  template<class Allocator>
-    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
-
-  // [stacktrace.format], formatting support
-  template<> struct formatter<stacktrace_entry>;
-  template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
-
-  namespace pmr {
-    using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
-  }
-
-  // [stacktrace.basic.hash], hash support
-  template<class T> struct hash;
-  template<> struct hash<stacktrace_entry>;
-  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;
-}
-
-// [stacktrace.entry]
-
-namespace std {
-  class stacktrace_entry {
-  public:
-    using native_handle_type = implementation-defined;
-
-    // [stacktrace.entry.cons], constructors
-    constexpr stacktrace_entry() noexcept;
-    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
-    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
-
-    ~stacktrace_entry();
-
-    // [stacktrace.entry.obs], observers
-    constexpr native_handle_type native_handle() const noexcept;
-    constexpr explicit operator bool() const noexcept;
-
-    // [stacktrace.entry.query], query
-    string description() const;
-    string source_file() const;
-    uint_least32_t source_line() const;
-
-    // [stacktrace.entry.cmp], comparison
-    friend constexpr bool operator==(const stacktrace_entry& x,
-                                     const stacktrace_entry& y) noexcept;
-    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
-                                                 const stacktrace_entry& y) noexcept;
-  };
-}
-
-// [stacktrace.basic]
-
-namespace std {
-  template<class Allocator>
-  class basic_stacktrace {
-  public:
-    using value_type = stacktrace_entry;
-    using const_reference = const value_type&;
-    using reference = value_type&;
-    using const_iterator = implementation-defined;  // see [stacktrace.basic.obs]
-    using iterator = const_iterator;
-    using reverse_iterator = reverse_iterator<iterator>;
-    using const_reverse_iterator = reverse_iterator<const_iterator>;
-    using difference_type = implementation-defined;
-    using size_type = implementation-defined;
-    using allocator_type = Allocator;
-
-    // [stacktrace.basic.cons], creation and assignment
-    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
-    static basic_stacktrace current(size_type skip,
-                                    const allocator_type& alloc = allocator_type()) noexcept;
-    static basic_stacktrace current(size_type skip, size_type max_depth,
-                                    const allocator_type& alloc = allocator_type()) noexcept;
-
-    basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
-    explicit basic_stacktrace(const allocator_type& alloc) noexcept;
-
-    basic_stacktrace(const basic_stacktrace& other);
-    basic_stacktrace(basic_stacktrace&& other) noexcept;
-    basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
-    basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
-    basic_stacktrace& operator=(const basic_stacktrace& other);
-    basic_stacktrace& operator=(basic_stacktrace&& other)
-      noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
-        allocator_traits<Allocator>::is_always_equal::value);
-
-    ~basic_stacktrace();
-
-    // [stacktrace.basic.obs], observers
-    allocator_type get_allocator() const noexcept;
-
-    const_iterator begin() const noexcept;
-    const_iterator end() const noexcept;
-    const_reverse_iterator rbegin() const noexcept;
-    const_reverse_iterator rend() const noexcept;
-
-    const_iterator cbegin() const noexcept;
-    const_iterator cend() const noexcept;
-    const_reverse_iterator crbegin() const noexcept;
-    const_reverse_iterator crend() const noexcept;
-
-    bool empty() const noexcept;
-    size_type size() const noexcept;
-    size_type max_size() const noexcept;
-
-    const_reference operator[](size_type) const;
-    const_reference at(size_type) const;
-
-    // [stacktrace.basic.cmp], comparisons
-    template<class Allocator2>
-    friend bool operator==(const basic_stacktrace& x,
-                           const basic_stacktrace<Allocator2>& y) noexcept;
-    template<class Allocator2>
-    friend strong_ordering operator<=>(const basic_stacktrace& x,
-                                       const basic_stacktrace<Allocator2>& y) noexcept;
-
-    // [stacktrace.basic.mod], modifiers
-    void swap(basic_stacktrace& other)
-      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
-        allocator_traits<Allocator>::is_always_equal::value);
-
-  private:
-    vector<value_type, allocator_type> frames_;         // exposition only
-  };
-}
-
-*/
-
-#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
-#  include <__cxx03/__config>
-#else
-#  include <__config>
-
-#  if _LIBCPP_STD_VER >= 23
-
-#    include <compare> // [stacktrace.syn]
-
-#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#      pragma GCC system_header
-#    endif
-
-_LIBCPP_PUSH_MACROS
-#    include <__undef_macros>
-
-#    include "experimental/__stacktrace/basic_stacktrace.h"
-#    include "experimental/__stacktrace/stacktrace_entry.h"
-
-_LIBCPP_POP_MACROS
-
-#  endif // _LIBCPP_STD_VER >= 23
-#endif   // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
-
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d1a8ca74c09c2..9187e49031d70 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2238,16 +2238,6 @@ module std [system] {
       header "experimental/simd"
       export *
     }
-    module stacktrace {
-      private header "experimental/__stacktrace/basic_stacktrace.h"
-      private header "experimental/__stacktrace/detail/alloc.h"
-      private header "experimental/__stacktrace/detail/context.h"
-      private header "experimental/__stacktrace/detail/entry.h"
-      private header "experimental/__stacktrace/detail/to_string.h"
-      private header "experimental/__stacktrace/stacktrace_entry.h"
-      header "experimental/stacktrace"
-      export *
-    }
   }
 
   // Implementation detail headers that are private to libc++. These modules
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index 55d21030c031b..2ee56c6b810c9 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -83,4 +83,4 @@ export module std.compat;
 export import std;
 
 
- at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
\ No newline at end of file
+ at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index 41755a276145b..3e9b6b678a847 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -168,6 +168,38 @@ cxx_sources = [
   "ryu/d2s.cpp",
   "ryu/f2s.cpp",
   "shared_mutex.cpp",
+  # "stacktrace/alloc.cpp",
+  # "stacktrace/common/config.h",
+  # "stacktrace/common/debug.cpp",
+  # "stacktrace/common/debug.h",
+  # "stacktrace/common/failed.h",
+  # "stacktrace/common/fd.cpp",
+  # "stacktrace/common/fd.h",
+  # "stacktrace/common/images.h",
+  # "stacktrace/context.cpp",
+  # "stacktrace/linux/linux-dl.cpp",
+  # "stacktrace/linux/linux-elf.cpp",
+  # "stacktrace/linux/linux-sym.cpp",
+  # "stacktrace/linux/linux.h",
+  # "stacktrace/osx/osx.cpp",
+  # "stacktrace/osx/osx.h",
+  # "stacktrace/stacktrace.cpp",
+  # "stacktrace/tools/addr2line.cpp",
+  # "stacktrace/tools/atos.cpp",
+  # "stacktrace/tools/llvm_symbolizer.cpp",
+  # "stacktrace/tools/pspawn.h",
+  # "stacktrace/tools/tools.h",
+  # "stacktrace/tools/toolspawner.cpp",
+  # "stacktrace/unwind/unwind.cpp",
+  # "stacktrace/unwind/unwind.h",
+  # "stacktrace/windows/dbghelp_dll.cpp",
+  # "stacktrace/windows/dbghelp_dll.h",
+  # "stacktrace/windows/dll.cpp",
+  # "stacktrace/windows/dll.h",
+  # "stacktrace/windows/psapi_dll.cpp",
+  # "stacktrace/windows/psapi_dll.h",
+  # "stacktrace/windows/win_impl.cpp",
+  # "stacktrace/windows/win_impl.h",
   "stdexcept.cpp",
   "string.cpp",
   "strstream.cpp",
@@ -318,40 +350,6 @@ if (libcxx_enable_experimental) {
     output_dir = runtimes_dir
     output_name = "c++experimental"
     sources = [ "experimental/keep.cpp" ]
-    sources += [
-      "experimental/stacktrace/alloc.cpp",
-      "experimental/stacktrace/common/config.h",
-      "experimental/stacktrace/common/debug.cpp",
-      "experimental/stacktrace/common/debug.h",
-      "experimental/stacktrace/common/failed.h",
-      "experimental/stacktrace/common/fd.cpp",
-      "experimental/stacktrace/common/fd.h",
-      "experimental/stacktrace/common/images.h",
-      "experimental/stacktrace/context.cpp",
-      "experimental/stacktrace/linux/linux-dl.cpp",
-      "experimental/stacktrace/linux/linux-elf.cpp",
-      "experimental/stacktrace/linux/linux-sym.cpp",
-      "experimental/stacktrace/linux/linux.h",
-      "experimental/stacktrace/osx/osx.cpp",
-      "experimental/stacktrace/osx/osx.h",
-      "experimental/stacktrace/stacktrace.cpp",
-      "experimental/stacktrace/tools/addr2line.cpp",
-      "experimental/stacktrace/tools/atos.cpp",
-      "experimental/stacktrace/tools/llvm_symbolizer.cpp",
-      "experimental/stacktrace/tools/pspawn.h",
-      "experimental/stacktrace/tools/tools.h",
-      "experimental/stacktrace/tools/toolspawner.cpp",
-      "experimental/stacktrace/unwind/unwind.cpp",
-      "experimental/stacktrace/unwind/unwind.h",
-      "experimental/stacktrace/windows/dbghelp_dll.cpp",
-      "experimental/stacktrace/windows/dbghelp_dll.h",
-      "experimental/stacktrace/windows/dll.cpp",
-      "experimental/stacktrace/windows/dll.h",
-      "experimental/stacktrace/windows/psapi_dll.cpp",
-      "experimental/stacktrace/windows/psapi_dll.h",
-      "experimental/stacktrace/windows/win_impl.cpp",
-      "experimental/stacktrace/windows/win_impl.h",
-    ]
     if (libcxx_enable_filesystem && libcxx_enable_time_zone_database) {
       sources += [
         # TODO TZDB The exception could be moved in chrono once the TZDB library

>From 17cb40750653cb7cee40ddc7831f7dbcf89e0087 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 21 Apr 2025 22:35:16 -0400
Subject: [PATCH 22/37] Move stacktrace srcs out of experimental

---
 libcxx/docs/UserDocumentation.rst             |  1 -
 .../include/__stacktrace/{detail => }/alloc.h | 28 +++---
 .../__stacktrace/{detail => }/context.h       | 20 ++--
 .../include/__stacktrace/{detail => }/entry.h |  6 +-
 .../__stacktrace/{detail => }/to_string.h     |  6 +-
 libcxx/src/CMakeLists.txt                     | 18 ----
 .../experimental/stacktrace/stacktrace.cpp    | 94 -------------------
 .../{experimental => }/stacktrace/alloc.cpp   |  4 +-
 .../stacktrace/common/config.h                |  0
 .../stacktrace/common/debug.cpp               |  0
 .../stacktrace/common/debug.h                 |  0
 .../stacktrace/common/failed.h                |  0
 .../stacktrace/common/fd.cpp                  |  0
 .../{experimental => }/stacktrace/common/fd.h |  0
 .../stacktrace/common/images.h                |  0
 .../{experimental => }/stacktrace/context.cpp | 14 +--
 .../{experimental => }/stacktrace/linux/elf.h |  0
 .../stacktrace/linux/linux-dl.cpp             |  4 +-
 .../stacktrace/linux/linux-elf.cpp            |  4 +-
 .../stacktrace/linux/linux-sym.cpp            |  4 +-
 .../stacktrace/linux/linux.h                  |  2 +-
 .../{experimental => }/stacktrace/osx/osx.cpp |  4 +-
 .../{experimental => }/stacktrace/osx/osx.h   |  2 +-
 .../stacktrace/tools/addr2line.cpp            |  4 +-
 .../stacktrace/tools/atos.cpp                 |  6 +-
 .../stacktrace/tools/llvm_symbolizer.cpp      |  4 +-
 .../stacktrace/tools/pspawn.h                 |  4 +-
 .../stacktrace/tools/tools.h                  |  4 +-
 .../stacktrace/tools/toolspawner.cpp          |  2 +-
 .../stacktrace/unwind/unwind.cpp              |  4 +-
 .../stacktrace/unwind/unwind.h                |  2 +-
 .../stacktrace/windows/dbghelp_dll.cpp        |  2 +-
 .../stacktrace/windows/dbghelp_dll.h          |  2 +-
 .../stacktrace/windows/dll.cpp                |  2 +-
 .../stacktrace/windows/dll.h                  |  2 +-
 .../stacktrace/windows/psapi_dll.cpp          |  2 +-
 .../stacktrace/windows/psapi_dll.h            |  2 +-
 .../stacktrace/windows/win_impl.cpp           |  2 +-
 .../stacktrace/windows/win_impl.h             |  2 +-
 .../test/libcxx/transitive_includes/cxx23.csv | 29 ------
 .../test/libcxx/transitive_includes/cxx26.csv | 29 ------
 .../diagnostics/stacktrace/basic.cmp.pass.cpp |  2 +-
 .../stacktrace/basic.cons.pass.cpp            |  2 +-
 .../diagnostics/stacktrace/basic.mod.pass.cpp |  2 +-
 .../stacktrace/basic.nonmem.pass.cpp          |  2 +-
 .../diagnostics/stacktrace/basic.obs.pass.cpp |  2 +-
 .../diagnostics/stacktrace/entry.cmp.pass.cpp |  2 +-
 .../stacktrace/entry.cons.pass.cpp            |  2 +-
 .../diagnostics/stacktrace/entry.obs.pass.cpp |  2 +-
 .../stacktrace/entry.query.pass.cpp           |  2 +-
 .../diagnostics/stacktrace/format.pass.cpp    |  2 +-
 .../std/diagnostics/stacktrace/syn.pass.cpp   |  2 +-
 libcxx/utils/libcxx/header_information.py     |  1 -
 llvm/utils/gn/secondary/libcxx/src/BUILD.gn   | 64 ++++++-------
 54 files changed, 112 insertions(+), 290 deletions(-)
 rename libcxx/include/__stacktrace/{detail => }/alloc.h (80%)
 rename libcxx/include/__stacktrace/{detail => }/context.h (72%)
 rename libcxx/include/__stacktrace/{detail => }/entry.h (88%)
 rename libcxx/include/__stacktrace/{detail => }/to_string.h (89%)
 delete mode 100644 libcxx/src/experimental/stacktrace/stacktrace.cpp
 rename libcxx/src/{experimental => }/stacktrace/alloc.cpp (86%)
 rename libcxx/src/{experimental => }/stacktrace/common/config.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/debug.cpp (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/debug.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/failed.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/fd.cpp (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/fd.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/common/images.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/context.cpp (88%)
 rename libcxx/src/{experimental => }/stacktrace/linux/elf.h (100%)
 rename libcxx/src/{experimental => }/stacktrace/linux/linux-dl.cpp (92%)
 rename libcxx/src/{experimental => }/stacktrace/linux/linux-elf.cpp (93%)
 rename libcxx/src/{experimental => }/stacktrace/linux/linux-sym.cpp (94%)
 rename libcxx/src/{experimental => }/stacktrace/linux/linux.h (98%)
 rename libcxx/src/{experimental => }/stacktrace/osx/osx.cpp (96%)
 rename libcxx/src/{experimental => }/stacktrace/osx/osx.h (95%)
 rename libcxx/src/{experimental => }/stacktrace/tools/addr2line.cpp (96%)
 rename libcxx/src/{experimental => }/stacktrace/tools/atos.cpp (95%)
 rename libcxx/src/{experimental => }/stacktrace/tools/llvm_symbolizer.cpp (96%)
 rename libcxx/src/{experimental => }/stacktrace/tools/pspawn.h (97%)
 rename libcxx/src/{experimental => }/stacktrace/tools/tools.h (95%)
 rename libcxx/src/{experimental => }/stacktrace/tools/toolspawner.cpp (98%)
 rename libcxx/src/{experimental => }/stacktrace/unwind/unwind.cpp (93%)
 rename libcxx/src/{experimental => }/stacktrace/unwind/unwind.h (95%)
 rename libcxx/src/{experimental => }/stacktrace/windows/dbghelp_dll.cpp (97%)
 rename libcxx/src/{experimental => }/stacktrace/windows/dbghelp_dll.h (98%)
 rename libcxx/src/{experimental => }/stacktrace/windows/dll.cpp (95%)
 rename libcxx/src/{experimental => }/stacktrace/windows/dll.h (96%)
 rename libcxx/src/{experimental => }/stacktrace/windows/psapi_dll.cpp (95%)
 rename libcxx/src/{experimental => }/stacktrace/windows/psapi_dll.h (96%)
 rename libcxx/src/{experimental => }/stacktrace/windows/win_impl.cpp (99%)
 rename libcxx/src/{experimental => }/stacktrace/windows/win_impl.h (96%)

diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 2a8c9600124c9..dbacb44735d08 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -70,7 +70,6 @@ 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/include/__stacktrace/detail/alloc.h b/libcxx/include/__stacktrace/alloc.h
similarity index 80%
rename from libcxx/include/__stacktrace/detail/alloc.h
rename to libcxx/include/__stacktrace/alloc.h
index a0949b5751899..402cf3c371161 100644
--- a/libcxx/include/__stacktrace/detail/alloc.h
+++ b/libcxx/include/__stacktrace/alloc.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+#ifndef _LIBCPP_STACKTRACE_ALLOC
+#define _LIBCPP_STACKTRACE_ALLOC
 
 #include <__config>
 #include <__functional/function.h>
@@ -29,7 +29,7 @@ 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 {
+struct _LIBCPP_HIDDEN 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
@@ -38,28 +38,22 @@ struct alloc final : std::pmr::memory_resource {
     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);
+    __alloc_ptr     = std::addressof(__a);
   }
 
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL ~alloc() override = default;
+  _LIBCPP_HIDE_FROM_ABI ~alloc() override;
 
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual void _anchor_vfunc();
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t __align) override {
-    // Avoiding "assert" in a system header, but we expect this to hold:
-    // assert(__align <= alignof(std::max_align_t));
-    (void)__align;
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t /*__align*/) override {
     return __alloc_func_(__size);
   }
 
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t __align) override {
-    (void)__align;
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t /*__align*/) override {
     __dealloc_func_((std::byte*)__ptr, __size);
   }
 
   _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
     auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
-    return __rhs_ba && (__rhs_ba->__alloc_opaque_ == __alloc_opaque_);
+    return __rhs_ba && (__rhs_ba->__alloc_ptr == __alloc_ptr);
   }
 
   _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
@@ -93,11 +87,11 @@ struct alloc final : std::pmr::memory_resource {
 private:
   std::function<std::byte*(size_t)> __alloc_func_;
   std::function<void(std::byte*, size_t)> __dealloc_func_;
-  /** Only used for equality */
-  void const* __alloc_opaque_;
+  /** Only used for checking equality */
+  void const* __alloc_ptr;
 };
 
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+#endif // _LIBCPP_STACKTRACE_ALLOC
diff --git a/libcxx/include/__stacktrace/detail/context.h b/libcxx/include/__stacktrace/context.h
similarity index 72%
rename from libcxx/include/__stacktrace/detail/context.h
rename to libcxx/include/__stacktrace/context.h
index df791b37b386d..2a23798751c60 100644
--- a/libcxx/include/__stacktrace/detail/context.h
+++ b/libcxx/include/__stacktrace/context.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+#ifndef _LIBCPP_STACKTRACE_CONTEXT
+#define _LIBCPP_STACKTRACE_CONTEXT
 
 #include <__config>
 #include <__memory_resource/memory_resource.h>
@@ -18,20 +18,20 @@
 #include <cstddef>
 #include <string>
 
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-struct _LIBCPP_HIDE_FROM_ABI alloc;
-struct _LIBCPP_HIDE_FROM_ABI entry;
+struct _LIBCPP_HIDDEN alloc;
+struct _LIBCPP_HIDDEN entry;
 
 namespace __stacktrace {
 
 /** Represents the state of the current in-progress stacktrace operation.  This includes
 the working list of `entry` objects, as well as the caller-provided allocator (wrapped
 in `polymorphic_allocator`) and a handful of utility functions involving that allocator. */
-struct _LIBCPP_HIDE_FROM_ABI context {
+struct _LIBCPP_HIDDEN context {
   /** Encapsulates and type-removes the caller's allocator. */
   alloc& __alloc_;
 
@@ -41,13 +41,13 @@ struct _LIBCPP_HIDE_FROM_ABI context {
   /** Path to this process's main executable. */
   std::pmr::string __main_prog_path_;
 
-  _LIBCPP_HIDE_FROM_ABI explicit context(alloc& __byte_alloc)
+  _LIBCPP_HIDDEN explicit context(alloc& __byte_alloc)
       : __alloc_(__byte_alloc), __entries_(&__alloc_), __main_prog_path_(__alloc_.new_string()) {}
 
-  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void do_stacktrace(size_t __skip, size_t __max_depth);
+  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void do_stacktrace(size_t __skip, size_t __max_depth);
 };
 
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+#endif // _LIBCPP_STACKTRACE_CONTEXT
diff --git a/libcxx/include/__stacktrace/detail/entry.h b/libcxx/include/__stacktrace/entry.h
similarity index 88%
rename from libcxx/include/__stacktrace/detail/entry.h
rename to libcxx/include/__stacktrace/entry.h
index 2bab14228b508..50853b87cc693 100644
--- a/libcxx/include/__stacktrace/detail/entry.h
+++ b/libcxx/include/__stacktrace/entry.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+#ifndef _LIBCPP_STACKTRACE_DETAIL_ENTRY
+#define _LIBCPP_STACKTRACE_DETAIL_ENTRY
 
 #include <__config>
 #include <cstdint>
@@ -41,4 +41,4 @@ struct _LIBCPP_HIDE_FROM_ABI entry {
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+#endif // _LIBCPP_STACKTRACE_DETAIL_ENTRY
diff --git a/libcxx/include/__stacktrace/detail/to_string.h b/libcxx/include/__stacktrace/to_string.h
similarity index 89%
rename from libcxx/include/__stacktrace/detail/to_string.h
rename to libcxx/include/__stacktrace/to_string.h
index 47feddf07faf9..1d2c51a7a5c10 100644
--- a/libcxx/include/__stacktrace/detail/to_string.h
+++ b/libcxx/include/__stacktrace/to_string.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+#ifndef _LIBCPP_STACKTRACE_TO_STRING
+#define _LIBCPP_STACKTRACE_TO_STRING
 
 #include <__config>
 #include <__fwd/sstream.h>
@@ -43,4 +43,4 @@ struct _LIBCPP_HIDE_FROM_ABI __to_string {
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 00be89632213d..45632d95daf45 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -315,24 +315,6 @@ add_custom_target(cxx DEPENDS ${LIBCXX_BUILD_TARGETS})
 # Build the experimental static library
 set(LIBCXX_EXPERIMENTAL_SOURCES
   experimental/keep.cpp
-  experimental/stacktrace/alloc.cpp
-  experimental/stacktrace/common/debug.cpp
-  experimental/stacktrace/common/fd.cpp
-  experimental/stacktrace/context.cpp
-  experimental/stacktrace/linux/linux-dl.cpp
-  experimental/stacktrace/linux/linux-elf.cpp
-  experimental/stacktrace/linux/linux-sym.cpp
-  experimental/stacktrace/osx/osx.cpp
-  experimental/stacktrace/stacktrace.cpp
-  experimental/stacktrace/tools/addr2line.cpp
-  experimental/stacktrace/tools/atos.cpp
-  experimental/stacktrace/tools/llvm_symbolizer.cpp
-  experimental/stacktrace/tools/toolspawner.cpp
-  experimental/stacktrace/unwind/unwind.cpp
-  experimental/stacktrace/windows/dbghelp_dll.cpp
-  experimental/stacktrace/windows/dll.cpp
-  experimental/stacktrace/windows/psapi_dll.cpp
-  experimental/stacktrace/windows/win_impl.cpp
   )
 
 if (LIBCXX_PSTL_BACKEND STREQUAL "libdispatch")
diff --git a/libcxx/src/experimental/stacktrace/stacktrace.cpp b/libcxx/src/experimental/stacktrace/stacktrace.cpp
deleted file mode 100644
index e33b94205a8ea..0000000000000
--- a/libcxx/src/experimental/stacktrace/stacktrace.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-
-#include <iomanip>
-#include <iostream>
-#include <sstream>
-#include <string>
-
-#include <experimental/__stacktrace/basic_stacktrace.h>
-#include <experimental/__stacktrace/stacktrace_entry.h>
-
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
-#include <experimental/__stacktrace/detail/to_string.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/*
- * `to_string` Helpers
- */
-
-void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
-  // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
-  constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
-
-  __os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << entry.native_handle();
-  if (!entry.description().empty()) {
-    __os << ": " << entry.description();
-  }
-  if (!entry.source_file().empty()) {
-    __os << ": " << entry.source_file();
-  }
-  if (entry.source_line()) {
-    __os << ":" << std::dec << entry.source_line();
-  }
-}
-
-void __to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
-  /*
-   * Print each entry as a line, as per `operator()`, with additional whitespace
-   * at the start of the line, and only a newline added at the end:
-   *
-   *   frame   1: 0xbeefbeefbeef: _symbol_name: /path/to/file.cc:123
-   */
-  if (!__count) {
-    __os << "(empty stacktrace)";
-  } else {
-    for (size_t __i = 0; __i < __count; __i++) {
-      if (__i) {
-        // Insert newlines between entries (but not before the first or after the last)
-        __os << std::endl;
-      }
-      __os << "  frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": ";
-      (*this)(__os, __entries[__i]);
-    }
-  }
-}
-
-string __to_string::operator()(std::stacktrace_entry const& entry) {
-  stringstream __ss;
-  (*this)(__ss, entry);
-  return __ss.str();
-}
-
-string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
-  stringstream __ss;
-  (*this)(__ss, __entries, __count);
-  return __ss.str();
-}
-
-} // namespace __stacktrace
-
-// `to_string`-related non-member functions
-
-_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
-  return __stacktrace::__to_string()(__entry);
-}
-
-_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
-  __stacktrace::__to_string()(__os, __entry);
-  return __os;
-}
-
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/alloc.cpp b/libcxx/src/stacktrace/alloc.cpp
similarity index 86%
rename from libcxx/src/experimental/stacktrace/alloc.cpp
rename to libcxx/src/stacktrace/alloc.cpp
index 956e01b312d3b..43842aa1a5b47 100644
--- a/libcxx/src/experimental/stacktrace/alloc.cpp
+++ b/libcxx/src/stacktrace/alloc.cpp
@@ -9,12 +9,12 @@
 #include <__config>
 #include <__config_site>
 
-#include <experimental/__stacktrace/detail/alloc.h>
+#include <__stacktrace/alloc.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-void alloc::_anchor_vfunc() {}
+alloc::~alloc() {}
 
 } // namespace __stacktrace
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/config.h b/libcxx/src/stacktrace/common/config.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/config.h
rename to libcxx/src/stacktrace/common/config.h
diff --git a/libcxx/src/experimental/stacktrace/common/debug.cpp b/libcxx/src/stacktrace/common/debug.cpp
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/debug.cpp
rename to libcxx/src/stacktrace/common/debug.cpp
diff --git a/libcxx/src/experimental/stacktrace/common/debug.h b/libcxx/src/stacktrace/common/debug.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/debug.h
rename to libcxx/src/stacktrace/common/debug.h
diff --git a/libcxx/src/experimental/stacktrace/common/failed.h b/libcxx/src/stacktrace/common/failed.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/failed.h
rename to libcxx/src/stacktrace/common/failed.h
diff --git a/libcxx/src/experimental/stacktrace/common/fd.cpp b/libcxx/src/stacktrace/common/fd.cpp
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/fd.cpp
rename to libcxx/src/stacktrace/common/fd.cpp
diff --git a/libcxx/src/experimental/stacktrace/common/fd.h b/libcxx/src/stacktrace/common/fd.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/fd.h
rename to libcxx/src/stacktrace/common/fd.h
diff --git a/libcxx/src/experimental/stacktrace/common/images.h b/libcxx/src/stacktrace/common/images.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/images.h
rename to libcxx/src/stacktrace/common/images.h
diff --git a/libcxx/src/experimental/stacktrace/context.cpp b/libcxx/src/stacktrace/context.cpp
similarity index 88%
rename from libcxx/src/experimental/stacktrace/context.cpp
rename to libcxx/src/stacktrace/context.cpp
index 3d311af4d7a89..e2d1445aa4995 100644
--- a/libcxx/src/experimental/stacktrace/context.cpp
+++ b/libcxx/src/stacktrace/context.cpp
@@ -11,13 +11,13 @@
 
 #include <list>
 
-#include <experimental/__stacktrace/basic_stacktrace.h>
-#include <experimental/__stacktrace/stacktrace_entry.h>
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
 
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
-#include <experimental/__stacktrace/detail/to_string.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/to_string.h>
 
 #include "common/config.h"
 
@@ -44,7 +44,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip, size_t max_depth) {
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void context::do_stacktrace(size_t skip, size_t max_depth) {
   /*
   Here we declare stacktrace components or "backends" which will handle the different tasks:
 
diff --git a/libcxx/src/experimental/stacktrace/linux/elf.h b/libcxx/src/stacktrace/linux/elf.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/linux/elf.h
rename to libcxx/src/stacktrace/linux/elf.h
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp b/libcxx/src/stacktrace/linux/linux-dl.cpp
similarity index 92%
rename from libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
rename to libcxx/src/stacktrace/linux/linux-dl.cpp
index 62cad72b9f124..e15403f7d3a50 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
+++ b/libcxx/src/stacktrace/linux/linux-dl.cpp
@@ -19,8 +19,8 @@
 
 #  include "../common/images.h"
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp b/libcxx/src/stacktrace/linux/linux-elf.cpp
similarity index 93%
rename from libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
rename to libcxx/src/stacktrace/linux/linux-elf.cpp
index 7cf82789ea2ed..2cbeade56534b 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
+++ b/libcxx/src/stacktrace/linux/linux-elf.cpp
@@ -15,8 +15,8 @@
 #  include <cassert>
 #  include <unistd.h>
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/fd.h"
 #  include "elf.h"
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp b/libcxx/src/stacktrace/linux/linux-sym.cpp
similarity index 94%
rename from libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
rename to libcxx/src/stacktrace/linux/linux-sym.cpp
index fa7489c64c923..b9a8c313e668b 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
+++ b/libcxx/src/stacktrace/linux/linux-sym.cpp
@@ -16,8 +16,8 @@
 #  include <dlfcn.h>
 #  include <unistd.h>
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/linux/linux.h b/libcxx/src/stacktrace/linux/linux.h
similarity index 98%
rename from libcxx/src/experimental/stacktrace/linux/linux.h
rename to libcxx/src/stacktrace/linux/linux.h
index a8e8c2279f6fc..52b0377bcb144 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux.h
+++ b/libcxx/src/stacktrace/linux/linux.h
@@ -27,7 +27,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
 
 struct linux {
   context& cx_;
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.cpp b/libcxx/src/stacktrace/osx/osx.cpp
similarity index 96%
rename from libcxx/src/experimental/stacktrace/osx/osx.cpp
rename to libcxx/src/stacktrace/osx/osx.cpp
index c00a7ea94e780..f377c0bded38a 100644
--- a/libcxx/src/experimental/stacktrace/osx/osx.cpp
+++ b/libcxx/src/stacktrace/osx/osx.cpp
@@ -16,8 +16,8 @@
 #  include <mach-o/dyld.h>
 #  include <mach-o/loader.h>
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 #  include "osx.h"
 
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.h b/libcxx/src/stacktrace/osx/osx.h
similarity index 95%
rename from libcxx/src/experimental/stacktrace/osx/osx.h
rename to libcxx/src/stacktrace/osx/osx.h
index ca0890929453e..ce38aa0a378c6 100644
--- a/libcxx/src/experimental/stacktrace/osx/osx.h
+++ b/libcxx/src/stacktrace/osx/osx.h
@@ -17,7 +17,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
 
 struct osx {
   context& cx_;
diff --git a/libcxx/src/experimental/stacktrace/tools/addr2line.cpp b/libcxx/src/stacktrace/tools/addr2line.cpp
similarity index 96%
rename from libcxx/src/experimental/stacktrace/tools/addr2line.cpp
rename to libcxx/src/stacktrace/tools/addr2line.cpp
index 310e99699569b..c1d36e83f13a6 100644
--- a/libcxx/src/experimental/stacktrace/tools/addr2line.cpp
+++ b/libcxx/src/stacktrace/tools/addr2line.cpp
@@ -13,8 +13,8 @@
 
 #include "tools.h"
 
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
 #include <string>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/atos.cpp b/libcxx/src/stacktrace/tools/atos.cpp
similarity index 95%
rename from libcxx/src/experimental/stacktrace/tools/atos.cpp
rename to libcxx/src/stacktrace/tools/atos.cpp
index 569883a4b12bc..f6e6458aaae0d 100644
--- a/libcxx/src/experimental/stacktrace/tools/atos.cpp
+++ b/libcxx/src/stacktrace/tools/atos.cpp
@@ -15,9 +15,9 @@
 
 #include "tools.h"
 
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp b/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
similarity index 96%
rename from libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
rename to libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
index 0a9907978dcf2..18b493c97a040 100644
--- a/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
+++ b/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
@@ -15,8 +15,8 @@
 #include "../common/debug.h"
 #include "tools.h"
 
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/pspawn.h b/libcxx/src/stacktrace/tools/pspawn.h
similarity index 97%
rename from libcxx/src/experimental/stacktrace/tools/pspawn.h
rename to libcxx/src/stacktrace/tools/pspawn.h
index 906a504441368..4e2fa381083ad 100644
--- a/libcxx/src/experimental/stacktrace/tools/pspawn.h
+++ b/libcxx/src/stacktrace/tools/pspawn.h
@@ -29,8 +29,8 @@
 #  include <unistd.h>
 #  include <vector>
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/debug.h"
 #  include "../common/failed.h"
diff --git a/libcxx/src/experimental/stacktrace/tools/tools.h b/libcxx/src/stacktrace/tools/tools.h
similarity index 95%
rename from libcxx/src/experimental/stacktrace/tools/tools.h
rename to libcxx/src/stacktrace/tools/tools.h
index d832974e69cbb..70374cf075ebf 100644
--- a/libcxx/src/experimental/stacktrace/tools/tools.h
+++ b/libcxx/src/stacktrace/tools/tools.h
@@ -16,8 +16,8 @@
 #include <list>
 #include <string>
 
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp b/libcxx/src/stacktrace/tools/toolspawner.cpp
similarity index 98%
rename from libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
rename to libcxx/src/stacktrace/tools/toolspawner.cpp
index 05a2f8e6c2eb4..54ddaf156fc64 100644
--- a/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
+++ b/libcxx/src/stacktrace/tools/toolspawner.cpp
@@ -27,7 +27,7 @@
 #  include "../common/failed.h"
 #  include "pspawn.h"
 
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.cpp b/libcxx/src/stacktrace/unwind/unwind.cpp
similarity index 93%
rename from libcxx/src/experimental/stacktrace/unwind/unwind.cpp
rename to libcxx/src/stacktrace/unwind/unwind.cpp
index a316306bc43eb..51689c5f1c15d 100644
--- a/libcxx/src/experimental/stacktrace/unwind/unwind.cpp
+++ b/libcxx/src/stacktrace/unwind/unwind.cpp
@@ -12,8 +12,8 @@
 
 #  include "./unwind.h"
 
-#  include <experimental/__stacktrace/detail/context.h>
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/context.h>
+#  include <__stacktrace/entry.h>
 #  include <unwind.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.h b/libcxx/src/stacktrace/unwind/unwind.h
similarity index 95%
rename from libcxx/src/experimental/stacktrace/unwind/unwind.h
rename to libcxx/src/stacktrace/unwind/unwind.h
index 1b099e3a0b922..a258a32f50c5d 100644
--- a/libcxx/src/experimental/stacktrace/unwind/unwind.h
+++ b/libcxx/src/stacktrace/unwind/unwind.h
@@ -17,7 +17,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
 
 struct unwind {
   context& cx_;
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp b/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
similarity index 97%
rename from libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
rename to libcxx/src/stacktrace/windows/dbghelp_dll.cpp
index 274c9ecb2b314..7753ad77d3d26 100644
--- a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
+++ b/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
@@ -21,7 +21,7 @@
 #  include <mutex>
 
 #  include "dll.h"
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h b/libcxx/src/stacktrace/windows/dbghelp_dll.h
similarity index 98%
rename from libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
rename to libcxx/src/stacktrace/windows/dbghelp_dll.h
index 920391fb9ba52..b1204a369a3f1 100644
--- a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
+++ b/libcxx/src/stacktrace/windows/dbghelp_dll.h
@@ -22,7 +22,7 @@
 #  include <cstdlib>
 #  include <mutex>
 
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/debug.h"
 #  include "dll.h"
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.cpp b/libcxx/src/stacktrace/windows/dll.cpp
similarity index 95%
rename from libcxx/src/experimental/stacktrace/windows/dll.cpp
rename to libcxx/src/stacktrace/windows/dll.cpp
index 55fd729a9caf4..b0ea2609b55cd 100644
--- a/libcxx/src/experimental/stacktrace/windows/dll.cpp
+++ b/libcxx/src/stacktrace/windows/dll.cpp
@@ -21,7 +21,7 @@
 #  include <mutex>
 
 #  include "dll.h"
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.h b/libcxx/src/stacktrace/windows/dll.h
similarity index 96%
rename from libcxx/src/experimental/stacktrace/windows/dll.h
rename to libcxx/src/stacktrace/windows/dll.h
index ab363ce2c2561..4ef8e7c5df190 100644
--- a/libcxx/src/experimental/stacktrace/windows/dll.h
+++ b/libcxx/src/stacktrace/windows/dll.h
@@ -22,7 +22,7 @@
 #  include <cstdlib>
 #  include <mutex>
 
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/debug.h"
 
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp b/libcxx/src/stacktrace/windows/psapi_dll.cpp
similarity index 95%
rename from libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
rename to libcxx/src/stacktrace/windows/psapi_dll.cpp
index ab28df8d3fbd7..d4f7fdc006341 100644
--- a/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
+++ b/libcxx/src/stacktrace/windows/psapi_dll.cpp
@@ -21,7 +21,7 @@
 #  include <mutex>
 
 #  include "dll.h"
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.h b/libcxx/src/stacktrace/windows/psapi_dll.h
similarity index 96%
rename from libcxx/src/experimental/stacktrace/windows/psapi_dll.h
rename to libcxx/src/stacktrace/windows/psapi_dll.h
index 4e6a192f3d790..bcad1bfb72d0a 100644
--- a/libcxx/src/experimental/stacktrace/windows/psapi_dll.h
+++ b/libcxx/src/stacktrace/windows/psapi_dll.h
@@ -22,7 +22,7 @@
 #  include <cstdlib>
 #  include <mutex>
 
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 #  include "../common/debug.h"
 #  include "dll.h"
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.cpp b/libcxx/src/stacktrace/windows/win_impl.cpp
similarity index 99%
rename from libcxx/src/experimental/stacktrace/windows/win_impl.cpp
rename to libcxx/src/stacktrace/windows/win_impl.cpp
index 7773d0d872816..44d6052706b85 100644
--- a/libcxx/src/experimental/stacktrace/windows/win_impl.cpp
+++ b/libcxx/src/stacktrace/windows/win_impl.cpp
@@ -21,7 +21,7 @@
 #  include <mutex>
 
 #  include "dll.h"
-#  include <experimental/__stacktrace/detail/entry.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.h b/libcxx/src/stacktrace/windows/win_impl.h
similarity index 96%
rename from libcxx/src/experimental/stacktrace/windows/win_impl.h
rename to libcxx/src/stacktrace/windows/win_impl.h
index 459347ad9a00f..c065987120425 100644
--- a/libcxx/src/experimental/stacktrace/windows/win_impl.h
+++ b/libcxx/src/stacktrace/windows/win_impl.h
@@ -18,7 +18,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
 
 struct win_impl {
   context& cx_;
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index d064aa972dfe2..29b49f2ae7e84 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -295,35 +295,6 @@ experimental/propagate_const version
 experimental/simd cstdint
 experimental/simd limits
 experimental/simd version
-experimental/stacktrace bitset
-experimental/stacktrace cctype
-experimental/stacktrace cerrno
-experimental/stacktrace climits
-experimental/stacktrace clocale
-experimental/stacktrace compare
-experimental/stacktrace cstddef
-experimental/stacktrace cstdint
-experimental/stacktrace cstdio
-experimental/stacktrace cstdlib
-experimental/stacktrace cstring
-experimental/stacktrace ctime
-experimental/stacktrace cwchar
-experimental/stacktrace cwctype
-experimental/stacktrace initializer_list
-experimental/stacktrace ios
-experimental/stacktrace iosfwd
-experimental/stacktrace limits
-experimental/stacktrace list
-experimental/stacktrace locale
-experimental/stacktrace memory
-experimental/stacktrace ratio
-experimental/stacktrace stdexcept
-experimental/stacktrace streambuf
-experimental/stacktrace string
-experimental/stacktrace string_view
-experimental/stacktrace tuple
-experimental/stacktrace typeinfo
-experimental/stacktrace version
 experimental/type_traits cstdint
 experimental/type_traits initializer_list
 experimental/type_traits type_traits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d439dae8c8fa4..5f7b5401f081a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -295,35 +295,6 @@ experimental/propagate_const version
 experimental/simd cstdint
 experimental/simd limits
 experimental/simd version
-experimental/stacktrace bitset
-experimental/stacktrace cctype
-experimental/stacktrace cerrno
-experimental/stacktrace climits
-experimental/stacktrace clocale
-experimental/stacktrace compare
-experimental/stacktrace cstddef
-experimental/stacktrace cstdint
-experimental/stacktrace cstdio
-experimental/stacktrace cstdlib
-experimental/stacktrace cstring
-experimental/stacktrace ctime
-experimental/stacktrace cwchar
-experimental/stacktrace cwctype
-experimental/stacktrace initializer_list
-experimental/stacktrace ios
-experimental/stacktrace iosfwd
-experimental/stacktrace limits
-experimental/stacktrace list
-experimental/stacktrace locale
-experimental/stacktrace memory
-experimental/stacktrace ratio
-experimental/stacktrace stdexcept
-experimental/stacktrace streambuf
-experimental/stacktrace string
-experimental/stacktrace string_view
-experimental/stacktrace tuple
-experimental/stacktrace typeinfo
-experimental/stacktrace version
 experimental/type_traits cstdint
 experimental/type_traits initializer_list
 experimental/type_traits type_traits
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
index 495abe79da20f..e8b5c36d05ba1 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
index 64e54fdf5793d..edf07510687dc 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // ADDITIONAL_COMPILE_FLAGS: -O1 -g
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 #include <iostream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
index 5929cd8f8d0f9..9d473202d492f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
index 665fb475f56e9..d0b327e738c6c 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 #include <sstream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
index b5c6cf77dde71..1f4102bd1ca61 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <iostream>
 #include <iterator>
 #include <memory>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
index 4c6531319fc09..6da4d40c5e08a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
index 944b4c519f0fd..84472ea91b2aa 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
@@ -10,7 +10,7 @@
 // ADDITIONAL_COMPILE_FLAGS: -g
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 #include <type_traits>
 
 /*
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
index da6e3ccec92b6..f31d7d3e9f8b7 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
@@ -8,7 +8,7 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
index bfdb54e4df668..0d8fac58f30ee 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
 
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <cassert>
 #include <iostream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
index b37681659d43c..42accf6ce1daa 100644
--- a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 /*
   (19.6.5) Formatting support [stacktrace.format]
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
index 3c92016b2c900..9682f6bfc146d 100644
--- a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 #include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
 
 #include <memory>
 #include <type_traits>
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index df295a6454050..4f569ffba6325 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -207,7 +207,6 @@ def __hash__(self) -> int:
     "experimental/iterator": "// UNSUPPORTED: c++03",
     "experimental/propagate_const": "// UNSUPPORTED: c++03",
     "experimental/simd": "// UNSUPPORTED: c++03",
-    "experimental/stacktrace": "// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20",
     "experimental/type_traits": "// UNSUPPORTED: c++03",
     "experimental/utility": "// UNSUPPORTED: c++03",
     "filesystem": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14",
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index 3e9b6b678a847..f824b25349068 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -168,38 +168,38 @@ cxx_sources = [
   "ryu/d2s.cpp",
   "ryu/f2s.cpp",
   "shared_mutex.cpp",
-  # "stacktrace/alloc.cpp",
-  # "stacktrace/common/config.h",
-  # "stacktrace/common/debug.cpp",
-  # "stacktrace/common/debug.h",
-  # "stacktrace/common/failed.h",
-  # "stacktrace/common/fd.cpp",
-  # "stacktrace/common/fd.h",
-  # "stacktrace/common/images.h",
-  # "stacktrace/context.cpp",
-  # "stacktrace/linux/linux-dl.cpp",
-  # "stacktrace/linux/linux-elf.cpp",
-  # "stacktrace/linux/linux-sym.cpp",
-  # "stacktrace/linux/linux.h",
-  # "stacktrace/osx/osx.cpp",
-  # "stacktrace/osx/osx.h",
-  # "stacktrace/stacktrace.cpp",
-  # "stacktrace/tools/addr2line.cpp",
-  # "stacktrace/tools/atos.cpp",
-  # "stacktrace/tools/llvm_symbolizer.cpp",
-  # "stacktrace/tools/pspawn.h",
-  # "stacktrace/tools/tools.h",
-  # "stacktrace/tools/toolspawner.cpp",
-  # "stacktrace/unwind/unwind.cpp",
-  # "stacktrace/unwind/unwind.h",
-  # "stacktrace/windows/dbghelp_dll.cpp",
-  # "stacktrace/windows/dbghelp_dll.h",
-  # "stacktrace/windows/dll.cpp",
-  # "stacktrace/windows/dll.h",
-  # "stacktrace/windows/psapi_dll.cpp",
-  # "stacktrace/windows/psapi_dll.h",
-  # "stacktrace/windows/win_impl.cpp",
-  # "stacktrace/windows/win_impl.h",
+  "stacktrace/alloc.cpp",
+  "stacktrace/common/config.h",
+  "stacktrace/common/debug.cpp",
+  "stacktrace/common/debug.h",
+  "stacktrace/common/failed.h",
+  "stacktrace/common/fd.cpp",
+  "stacktrace/common/fd.h",
+  "stacktrace/common/images.h",
+  "stacktrace/context.cpp",
+  "stacktrace/linux/linux-dl.cpp",
+  "stacktrace/linux/linux-elf.cpp",
+  "stacktrace/linux/linux-sym.cpp",
+  "stacktrace/linux/linux.h",
+  "stacktrace/osx/osx.cpp",
+  "stacktrace/osx/osx.h",
+  "stacktrace/stacktrace.cpp",
+  "stacktrace/tools/addr2line.cpp",
+  "stacktrace/tools/atos.cpp",
+  "stacktrace/tools/llvm_symbolizer.cpp",
+  "stacktrace/tools/pspawn.h",
+  "stacktrace/tools/tools.h",
+  "stacktrace/tools/toolspawner.cpp",
+  "stacktrace/unwind/unwind.cpp",
+  "stacktrace/unwind/unwind.h",
+  "stacktrace/windows/dbghelp_dll.cpp",
+  "stacktrace/windows/dbghelp_dll.h",
+  "stacktrace/windows/dll.cpp",
+  "stacktrace/windows/dll.h",
+  "stacktrace/windows/psapi_dll.cpp",
+  "stacktrace/windows/psapi_dll.h",
+  "stacktrace/windows/win_impl.cpp",
+  "stacktrace/windows/win_impl.h",
   "stdexcept.cpp",
   "string.cpp",
   "strstream.cpp",

>From 36364fa2f9eb7c041890291ecb28ed9e7478b06c Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 09:50:44 -0400
Subject: [PATCH 23/37] simplify layout of stacktrace files: condense, collapse

---
 libcxx/include/__stacktrace/alloc.h           |  97 ------
 libcxx/include/__stacktrace/context.h         |  53 ---
 libcxx/include/__stacktrace/entry.h           |  44 ---
 libcxx/include/__stacktrace/to_string.h       |  46 ---
 libcxx/src/stacktrace/alloc.cpp               |  20 --
 libcxx/src/stacktrace/common/config.h         |  41 ---
 libcxx/src/stacktrace/common/debug.cpp        |  19 --
 libcxx/src/stacktrace/common/debug.h          |  55 ---
 libcxx/src/stacktrace/common/failed.h         |  29 --
 libcxx/src/stacktrace/common/fd.cpp           |  31 --
 libcxx/src/stacktrace/common/fd.h             | 122 -------
 libcxx/src/stacktrace/common/images.h         |  33 --
 libcxx/src/stacktrace/context.cpp             | 104 ------
 libcxx/src/stacktrace/linux/elf.h             | 315 ------------------
 libcxx/src/stacktrace/linux/linux-dl.cpp      |  58 ----
 libcxx/src/stacktrace/linux/linux-elf.cpp     |  53 ---
 libcxx/src/stacktrace/linux/linux-sym.cpp     |  61 ----
 libcxx/src/stacktrace/linux/linux.h           |  98 ------
 libcxx/src/stacktrace/{osx => }/osx.cpp       |  13 +-
 libcxx/src/stacktrace/{osx => }/osx.h         |   0
 libcxx/src/stacktrace/stacktrace.cpp          |   7 +
 libcxx/src/stacktrace/tools/addr2line.cpp     | 100 ------
 libcxx/src/stacktrace/tools/atos.cpp          | 115 -------
 .../src/stacktrace/tools/llvm_symbolizer.cpp  | 112 -------
 libcxx/src/stacktrace/tools/pspawn.h          | 163 ---------
 libcxx/src/stacktrace/tools/tools.h           |  65 ----
 libcxx/src/stacktrace/tools/toolspawner.cpp   | 185 ----------
 libcxx/src/stacktrace/unwind/unwind.cpp       |  60 ----
 libcxx/src/stacktrace/unwind/unwind.h         |  30 --
 libcxx/src/stacktrace/windows/dbghelp_dll.cpp |  56 ----
 libcxx/src/stacktrace/windows/dbghelp_dll.h   |  71 ----
 libcxx/src/stacktrace/windows/dll.cpp         |  40 ---
 libcxx/src/stacktrace/windows/dll.h           |  68 ----
 libcxx/src/stacktrace/windows/psapi_dll.cpp   |  48 ---
 libcxx/src/stacktrace/windows/psapi_dll.h     |  55 ---
 libcxx/src/stacktrace/windows/win_impl.cpp    | 204 ------------
 libcxx/src/stacktrace/windows/win_impl.h      |  38 ---
 llvm/utils/gn/secondary/libcxx/src/BUILD.gn   |  42 +--
 38 files changed, 26 insertions(+), 2725 deletions(-)
 delete mode 100644 libcxx/include/__stacktrace/alloc.h
 delete mode 100644 libcxx/include/__stacktrace/context.h
 delete mode 100644 libcxx/include/__stacktrace/entry.h
 delete mode 100644 libcxx/include/__stacktrace/to_string.h
 delete mode 100644 libcxx/src/stacktrace/alloc.cpp
 delete mode 100644 libcxx/src/stacktrace/common/config.h
 delete mode 100644 libcxx/src/stacktrace/common/debug.cpp
 delete mode 100644 libcxx/src/stacktrace/common/debug.h
 delete mode 100644 libcxx/src/stacktrace/common/failed.h
 delete mode 100644 libcxx/src/stacktrace/common/fd.cpp
 delete mode 100644 libcxx/src/stacktrace/common/fd.h
 delete mode 100644 libcxx/src/stacktrace/common/images.h
 delete mode 100644 libcxx/src/stacktrace/context.cpp
 delete mode 100644 libcxx/src/stacktrace/linux/elf.h
 delete mode 100644 libcxx/src/stacktrace/linux/linux-dl.cpp
 delete mode 100644 libcxx/src/stacktrace/linux/linux-elf.cpp
 delete mode 100644 libcxx/src/stacktrace/linux/linux-sym.cpp
 delete mode 100644 libcxx/src/stacktrace/linux/linux.h
 rename libcxx/src/stacktrace/{osx => }/osx.cpp (92%)
 rename libcxx/src/stacktrace/{osx => }/osx.h (100%)
 delete mode 100644 libcxx/src/stacktrace/tools/addr2line.cpp
 delete mode 100644 libcxx/src/stacktrace/tools/atos.cpp
 delete mode 100644 libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
 delete mode 100644 libcxx/src/stacktrace/tools/pspawn.h
 delete mode 100644 libcxx/src/stacktrace/tools/tools.h
 delete mode 100644 libcxx/src/stacktrace/tools/toolspawner.cpp
 delete mode 100644 libcxx/src/stacktrace/unwind/unwind.cpp
 delete mode 100644 libcxx/src/stacktrace/unwind/unwind.h
 delete mode 100644 libcxx/src/stacktrace/windows/dbghelp_dll.cpp
 delete mode 100644 libcxx/src/stacktrace/windows/dbghelp_dll.h
 delete mode 100644 libcxx/src/stacktrace/windows/dll.cpp
 delete mode 100644 libcxx/src/stacktrace/windows/dll.h
 delete mode 100644 libcxx/src/stacktrace/windows/psapi_dll.cpp
 delete mode 100644 libcxx/src/stacktrace/windows/psapi_dll.h
 delete mode 100644 libcxx/src/stacktrace/windows/win_impl.cpp
 delete mode 100644 libcxx/src/stacktrace/windows/win_impl.h

diff --git a/libcxx/include/__stacktrace/alloc.h b/libcxx/include/__stacktrace/alloc.h
deleted file mode 100644
index 402cf3c371161..0000000000000
--- a/libcxx/include/__stacktrace/alloc.h
+++ /dev/null
@@ -1,97 +0,0 @@
-// -*- 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_STACKTRACE_ALLOC
-#define _LIBCPP_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 _LIBCPP_HIDDEN 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_ptr     = std::addressof(__a);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI ~alloc() override;
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t /*__align*/) override {
-    return __alloc_func_(__size);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t /*__align*/) override {
-    __dealloc_func_((std::byte*)__ptr, __size);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
-    auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
-    return __rhs_ba && (__rhs_ba->__alloc_ptr == __alloc_ptr);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
-    std::pmr::string __ret{this};
-    if (__size) {
-      __ret.reserve(__size);
-      __ret[0] = 0;
-    }
-    return __ret;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::string hex_string(uintptr_t __addr) {
-    char __ret[19]; // "0x" + 16 digits + NUL
-    auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
-    return {__ret, size_t(__size), this};
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::string u64_string(uintptr_t __val) {
-    char __ret[21]; // 20 digits max + NUL
-    auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
-    return {__ret, size_t(__size), this};
-  }
-
-  template <typename _Tp>
-  _LIBCPP_HIDE_FROM_ABI std::pmr::list<_Tp> new_list() {
-    return std::pmr::list<_Tp>{this};
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::list<std::pmr::string> new_string_list() { return new_list<std::pmr::string>(); }
-
-private:
-  std::function<std::byte*(size_t)> __alloc_func_;
-  std::function<void(std::byte*, size_t)> __dealloc_func_;
-  /** Only used for checking equality */
-  void const* __alloc_ptr;
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_ALLOC
diff --git a/libcxx/include/__stacktrace/context.h b/libcxx/include/__stacktrace/context.h
deleted file mode 100644
index 2a23798751c60..0000000000000
--- a/libcxx/include/__stacktrace/context.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// -*- 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_STACKTRACE_CONTEXT
-#define _LIBCPP_STACKTRACE_CONTEXT
-
-#include <__config>
-#include <__memory_resource/memory_resource.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <__vector/pmr.h>
-#include <__vector/vector.h>
-#include <cstddef>
-#include <string>
-
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-struct _LIBCPP_HIDDEN alloc;
-struct _LIBCPP_HIDDEN entry;
-
-namespace __stacktrace {
-
-/** Represents the state of the current in-progress stacktrace operation.  This includes
-the working list of `entry` objects, as well as the caller-provided allocator (wrapped
-in `polymorphic_allocator`) and a handful of utility functions involving that allocator. */
-struct _LIBCPP_HIDDEN context {
-  /** Encapsulates and type-removes the caller's allocator. */
-  alloc& __alloc_;
-
-  /** The working set of entry data; these will later be copied to their final `stacktrace_entry` objects. */
-  std::pmr::list<entry> __entries_;
-
-  /** Path to this process's main executable. */
-  std::pmr::string __main_prog_path_;
-
-  _LIBCPP_HIDDEN explicit context(alloc& __byte_alloc)
-      : __alloc_(__byte_alloc), __entries_(&__alloc_), __main_prog_path_(__alloc_.new_string()) {}
-
-  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void do_stacktrace(size_t __skip, size_t __max_depth);
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_CONTEXT
diff --git a/libcxx/include/__stacktrace/entry.h b/libcxx/include/__stacktrace/entry.h
deleted file mode 100644
index 50853b87cc693..0000000000000
--- a/libcxx/include/__stacktrace/entry.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// -*- 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_STACKTRACE_DETAIL_ENTRY
-#define _LIBCPP_STACKTRACE_DETAIL_ENTRY
-
-#include <__config>
-#include <cstdint>
-#include <string>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-namespace __stacktrace {
-
-/** Contains fields which will be used to generate the final `std::stacktrace_entry`.
-This is an intermediate object which owns strings allocated via the caller-provided allocator,
-which are later freed back to that allocator and converted to plain `std::string`s. */
-struct _LIBCPP_HIDE_FROM_ABI entry {
-  /** Caller's / faulting insn's address, including ASLR/slide */
-  uintptr_t __addr_{};
-
-  /** the address minus its image's slide offset */
-  uintptr_t __addr_unslid_{};
-
-  /** entry's description (symbol name) */
-  std::pmr::string __desc_{};
-
-  /** source file name */
-  std::pmr::string __file_{};
-
-  /** line number in source file */
-  uint32_t __line_{};
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_DETAIL_ENTRY
diff --git a/libcxx/include/__stacktrace/to_string.h b/libcxx/include/__stacktrace/to_string.h
deleted file mode 100644
index 1d2c51a7a5c10..0000000000000
--- a/libcxx/include/__stacktrace/to_string.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// -*- 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_STACKTRACE_TO_STRING
-#define _LIBCPP_STACKTRACE_TO_STRING
-
-#include <__config>
-#include <__fwd/sstream.h>
-#include <__ostream/basic_ostream.h>
-#include <cstddef>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <class _Allocator>
-class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace;
-
-class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry;
-
-namespace __stacktrace {
-
-struct _LIBCPP_HIDE_FROM_ABI __to_string {
-  _LIBCPP_HIDE_FROM_ABI string operator()(stacktrace_entry const& __entry);
-
-  _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
-
-  _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
-
-  string operator()(std::stacktrace_entry const* __entries, size_t __count);
-
-  template <class _Allocator>
-  _LIBCPP_HIDE_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
-    return (*this)(__st.__entries_.data(), __st.__entries_.size());
-  }
-};
-
-} // namespace __stacktrace
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/src/stacktrace/alloc.cpp b/libcxx/src/stacktrace/alloc.cpp
deleted file mode 100644
index 43842aa1a5b47..0000000000000
--- a/libcxx/src/stacktrace/alloc.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-
-#include <__stacktrace/alloc.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-alloc::~alloc() {}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/common/config.h b/libcxx/src/stacktrace/common/config.h
deleted file mode 100644
index 0915b57463deb..0000000000000
--- a/libcxx/src/stacktrace/common/config.h
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_CONFIG_H
-#define _LIBCPP_STACKTRACE_CONFIG_H
-
-#include <__config>
-#include <__config_site>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-#if __has_include(<unwind.h>)
-#  define _LIBCPP_STACKTRACE_COLLECT_UNWIND
-#endif
-
-#if defined(__APPLE__)
-#  define _LIBCPP_STACKTRACE_APPLE
-#endif
-
-#if defined(__linux__)
-#  define _LIBCPP_STACKTRACE_LINUX
-#endif
-
-#if __has_include(<spawn.h>) && _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
-#  define _LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS
-#endif
-
-#if __has_include(<windows.h>) && __has_include(<dbghelp.h>) && __has_include(<psapi.h>)
-#  define _LIBCPP_STACKTRACE_WINDOWS
-#endif
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_CONFIG_H
diff --git a/libcxx/src/stacktrace/common/debug.cpp b/libcxx/src/stacktrace/common/debug.cpp
deleted file mode 100644
index 4f113e10c0c23..0000000000000
--- a/libcxx/src/stacktrace/common/debug.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "debug.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-debug::~debug() = default;
-
-debug::dummy_ostream::~dummy_ostream() = default;
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/common/debug.h b/libcxx/src/stacktrace/common/debug.h
deleted file mode 100644
index 0cee301d42d8b..0000000000000
--- a/libcxx/src/stacktrace/common/debug.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_DEBUG_H
-#define _LIBCPP_STACKTRACE_DEBUG_H
-
-#include <__config>
-#include <iostream>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/** Debug-message output stream.  If `LIBCXX_STACKTRACE_DEBUG` is defined in the environment
-or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
-otherwise its does nothing by returning a dummy stream. */
-struct debug : std::ostream {
-  virtual ~debug();
-
-  static bool enabled() {
-#if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
-    return true;
-#else
-    static bool ret = [] {
-      auto const* val = getenv("LIBCXX_STACKTRACE_DEBUG");
-      return val && !strncmp(val, "1", 1);
-    }();
-    return ret;
-#endif
-  }
-
-  /** No-op output stream. */
-  struct dummy_ostream final : std::ostream {
-    virtual ~dummy_ostream();
-    friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
-  };
-
-  friend std::ostream& operator<<(debug& dp, auto const& val) {
-    static dummy_ostream kdummy;
-    if (!enabled()) {
-      return kdummy;
-    }
-    std::cerr << val;
-    return std::cerr;
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_DEBUG_H
diff --git a/libcxx/src/stacktrace/common/failed.h b/libcxx/src/stacktrace/common/failed.h
deleted file mode 100644
index e53eb1123aabc..0000000000000
--- a/libcxx/src/stacktrace/common/failed.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_FAILED_H
-#define _LIBCPP_STACKTRACE_FAILED_H
-
-#include <stdexcept>
-
-// TODO(stacktrace23) Use std::expected instead?
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct failed : std::runtime_error {
-  virtual ~failed() = default;
-  int errno_{0};
-  failed() : std::runtime_error({}) {}
-  failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_FAILED_H
diff --git a/libcxx/src/stacktrace/common/fd.cpp b/libcxx/src/stacktrace/common/fd.cpp
deleted file mode 100644
index b7728ea7826dd..0000000000000
--- a/libcxx/src/stacktrace/common/fd.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "fd.h"
-#include "failed.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-fd_streambuf::~fd_streambuf() = default;
-fd_istream::~fd_istream()     = default;
-
-int fd_streambuf::underflow() {
-  int bytesRead = ::read(fd_, buf_, size_);
-  if (bytesRead < 0) {
-    throw failed("I/O error reading from child process", errno);
-  }
-  if (bytesRead == 0) {
-    return traits_type::eof();
-  }
-  setg(buf_, buf_, buf_ + bytesRead);
-  return int(*buf_);
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/common/fd.h b/libcxx/src/stacktrace/common/fd.h
deleted file mode 100644
index ff003e16a6bd8..0000000000000
--- a/libcxx/src/stacktrace/common/fd.h
+++ /dev/null
@@ -1,122 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_FD_H
-#define _LIBCPP_STACKTRACE_FD_H
-
-#include <__config>
-#include <cassert>
-#include <cerrno>
-#include <cstdio>
-#include <iostream>
-#include <sys/fcntl.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include <utility>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/** Encapsulates a plain old file descriptor `int`.  Avoids copies in order to
-force some component to "own" this, although it's freely convertible back to
-integer form.  Default-constructed, closed, and moved-out-of instances will have
-the invalid fd `-1`. */
-class fd {
-  int fd_{-1};
-
-public:
-  fd() : fd(-1) {}
-  fd(int fdint) : fd_(fdint) {}
-
-  fd(fd const&)            = delete;
-  fd& operator=(fd const&) = delete;
-
-  fd(fd&& rhs) {
-    if (&rhs != this) {
-      std::exchange(fd_, rhs.fd_);
-    }
-  }
-
-  fd& operator=(fd&& rhs) { return *new (this) fd(std::move(rhs)); }
-
-  ~fd() { close(); }
-
-  /** Returns true IFF fd is above zero */
-  bool valid() const { return fd_ > 0; }
-
-  /* implicit */ operator int() const {
-    auto ret = fd_;
-    assert(ret > 0);
-    return ret;
-  }
-
-  void close() {
-    int fd_old = -1;
-    std::exchange(fd_old, fd_);
-    if (fd_old != -1) {
-      ::close(fd_old);
-    }
-  }
-
-  static fd& null_fd() {
-    static fd ret = {::open("/dev/null", O_RDWR)};
-    return ret;
-  }
-
-  static fd open(std::string_view path) {
-    fd ret = {::open(path.data(), O_RDONLY)};
-    return ret;
-  }
-};
-
-/** Wraps a readable fd using the `streambuf` interface.  I/O errors arising
-from reading the provided fd will result in a `Failed` being thrown. */
-struct fd_streambuf final : std::streambuf {
-  fd& fd_;
-  char* buf_;
-  size_t size_;
-  fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
-  virtual ~fd_streambuf();
-  int underflow() override;
-};
-
-/** Wraps an `FDInStreamBuffer` in an `istream` */
-struct fd_istream final : std::istream {
-  fd_streambuf& buf_;
-  virtual ~fd_istream();
-  explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
-};
-
-struct fd_mmap final {
-  fd fd_{};
-  size_t size_{0};
-  std::byte const* addr_{nullptr};
-
-  explicit fd_mmap(std::string_view path) : fd_mmap(fd::open(path)) {}
-
-  explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
-    if (fd_) {
-      if ((size_ = ::lseek(fd, 0, SEEK_END))) {
-        addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
-      }
-    }
-  }
-
-  operator bool() const { return addr_; }
-
-  ~fd_mmap() {
-    if (addr_) {
-      ::munmap(const_cast<void*>((void const*)addr_), size_);
-    }
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_FD_H
diff --git a/libcxx/src/stacktrace/common/images.h b/libcxx/src/stacktrace/common/images.h
deleted file mode 100644
index cb9eac47cf9dd..0000000000000
--- a/libcxx/src/stacktrace/common/images.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_IMAGES_H
-#define _LIBCPP_STACKTRACE_IMAGES_H
-
-#include <cstdint>
-#include <string_view>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-constexpr unsigned k_max_images = 256;
-
-struct image {
-  uintptr_t loaded_at_{};
-  intptr_t slide_{};
-  std::string_view name_{};
-  bool is_main_prog_{};
-
-  bool operator<(image const& rhs) const { return loaded_at_ < rhs.loaded_at_; }
-  operator bool() const { return !name_.empty(); }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/stacktrace/context.cpp b/libcxx/src/stacktrace/context.cpp
deleted file mode 100644
index e2d1445aa4995..0000000000000
--- a/libcxx/src/stacktrace/context.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-
-#include <list>
-
-#include <__stacktrace/basic_stacktrace.h>
-#include <__stacktrace/stacktrace_entry.h>
-
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-#include <__stacktrace/to_string.h>
-
-#include "common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-#  include "linux/linux.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_APPLE)
-#  include "osx/osx.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-#  include "tools/pspawn.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
-#  include "unwind/unwind.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
-#  include "windows/win_impl.h"
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void context::do_stacktrace(size_t skip, size_t max_depth) {
-  /*
-  Here we declare stacktrace components or "backends" which will handle the different tasks:
-
-  (1) get the addresses from the call stack
-  (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
-  (3) resolve using debug info, and possibly with an external tool on the $PATH
-  (4+) extra passes to get symbols, in case 3 couldn't
-
-  Based on the macros produced by `stacktrace.h`, throw all backends we have available at the task.  Ideally the #ifdef
-  gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
-  symbolizer.  If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
-
-  All these classes do their best to provide any of the requested fields they can: (symbol, filename, source line),
-  substituting if needed with something reasonable.  For example, if the source filename and line are not available
-  then we will at least report that the address and symbol are in the module `foo.exe`.
-
-  These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
-  already-populated data.  We take care not to crash / abort / throw in any of these, and we'll silently fail.  See
-  `common/debug.h` for a debugging logger you can enable at runtime.
-  */
-
-#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
-  win_impl dbghelp{*this};
-  auto& collector  = dbghelp;
-  auto& mod_ident  = dbghelp;
-  auto& resolver   = dbghelp;
-  auto& symbolizer = dbghelp;
-#endif
-#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
-  unwind unwind{*this};
-  auto& collector = unwind;
-#endif
-#if defined(_LIBCPP_STACKTRACE_APPLE)
-  osx osx{*this};
-  auto& mod_ident  = osx;
-  auto& symbolizer = osx;
-#endif
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-  linux linux{*this};
-  auto& mod_ident  = linux;
-  auto& symbolizer = linux;
-#endif
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-  spawner pspawn{*this};
-  auto& resolver = pspawn;
-#endif
-
-  collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
-  if (__entries_.size()) {                // (Can't proceed if empty)
-    mod_ident.ident_modules();            // Associate addrs with binaries (ELF/MachO/etc.)
-    resolver.resolve_lines();             // Resolve addresses to symbols, filename, linenumber
-    symbolizer.symbolize();               // Populate missing symbols, if any.
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/linux/elf.h b/libcxx/src/stacktrace/linux/elf.h
deleted file mode 100644
index 214e8cc5a7819..0000000000000
--- a/libcxx/src/stacktrace/linux/elf.h
+++ /dev/null
@@ -1,315 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_ELF_H
-#define _LIBCPP_STACKTRACE_ELF_H
-
-#include <cstddef>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <functional>
-#include <string_view>
-#include <sys/fcntl.h>
-#include <unistd.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// Includes ELF constants and structs copied from <elf.h>, with a few changes.
-
-namespace elf {
-
-struct Header32 final {
-  uint8_t ident[16];  /* Magic number and other info */
-  uint16_t type;      /* Object file type */
-  uint16_t machine;   /* Architecture */
-  uint32_t version;   /* Object file version */
-  uint32_t entry;     /* Entry point virtual address */
-  uint32_t phoff;     /* Program header table file offset */
-  uint32_t shoff;     /* Section header table file offset */
-  uint32_t flags;     /* Processor-specific flags */
-  uint16_t ehsize;    /* ELF header size in bytes */
-  uint16_t phentsize; /* Program header table entry size */
-  uint16_t phnum;     /* Program header table entry count */
-  uint16_t shentsize; /* Section header table entry size */
-  uint16_t shnum;     /* Section header table entry count */
-  uint16_t shstrndx;  /* Section header string table index */
-};
-
-struct Section32 final {
-  uint32_t name;      /* Section name (string tbl index) */
-  uint32_t type;      /* Section type */
-  uint32_t flags;     /* Section flags */
-  uint32_t addr;      /* Section virtual addr at execution */
-  uint32_t offset;    /* Section file offset */
-  uint32_t size;      /* Section size in bytes */
-  uint32_t link;      /* Link to another section */
-  uint32_t info;      /* Additional section information */
-  uint32_t addralign; /* Section alignment */
-  uint32_t entsize;   /* Entry size if section holds table */
-};
-
-struct Symbol32 final {
-  uint32_t name;  /* Symbol name (string tbl index) */
-  uint32_t value; /* Symbol value */
-  uint32_t size;  /* Symbol size */
-  uint8_t info;   /* Symbol type and binding */
-  uint8_t other;  /* Symbol visibility */
-  uint16_t shndx; /* Section index */
-};
-
-struct Header64 final {
-  uint8_t ident[16];  /* Magic number and other info */
-  uint16_t type;      /* Object file type */
-  uint16_t machine;   /* Architecture */
-  uint32_t version;   /* Object file version */
-  uint64_t entry;     /* Entry point virtual address */
-  uint64_t phoff;     /* Program header table file offset */
-  uint64_t shoff;     /* Section header table file offset */
-  uint32_t flags;     /* Processor-specific flags */
-  uint16_t ehsize;    /* ELF header size in bytes */
-  uint16_t phentsize; /* Program header table entry size */
-  uint16_t phnum;     /* Program header table entry count */
-  uint16_t shentsize; /* Section header table entry size */
-  uint16_t shnum;     /* Section header table entry count */
-  uint16_t shstrndx;  /* Section header string table index */
-};
-
-struct Section64 final {
-  uint32_t name;      /* Section name (string tbl index) */
-  uint32_t type;      /* Section type */
-  uint64_t flags;     /* Section flags */
-  uint64_t addr;      /* Section virtual addr at execution */
-  uint64_t offset;    /* Section file offset */
-  uint64_t size;      /* Section size in bytes */
-  uint32_t link;      /* Link to another section */
-  uint32_t info;      /* Additional section information */
-  uint64_t addralign; /* Section alignment */
-  uint64_t entsize;   /* Entry size if section holds table */
-};
-
-struct Symbol64 final {
-  uint32_t name;  /* Symbol name (string tbl index) */
-  uint8_t info;   /* Symbol type and binding */
-  uint8_t other;  /* Symbol visibility */
-  uint16_t shndx; /* Section index */
-  uint64_t value; /* Symbol value */
-  uint64_t size;  /* Symbol size */
-};
-
-/** Represents an ELF header.  Supports the minimum needed to navigate an ELF file's sections and get at the symbol and
- * string tables. */
-struct Header final {
-  std::byte const* ptr_{};
-  uintptr_t shoff_{};
-  size_t shnum_{};
-  size_t shstrndx_{};
-
-  Header()              = default;
-  Header(Header const&) = default;
-  Header& operator=(Header const& rhs) { return *new (this) Header(rhs); }
-
-  operator bool() { return ptr_; }
-
-  template <class H>
-  explicit Header(H* h)
-      : ptr_((std::byte const*)h),
-        shoff_(uintptr_t(h->shoff)),
-        shnum_(size_t(h->shnum)),
-        shstrndx_(size_t(h->shstrndx)) {}
-};
-
-struct ELF;
-struct StringTable;
-
-struct Section final {
-  constexpr static uint32_t kSymTab = 2; // symbol table
-  constexpr static uint32_t kStrTab = 3; // name table for symbols or sections
-
-  ELF* elf_{};
-  std::byte const* ptr_{};
-  uintptr_t nameIndex_{};
-  uint32_t type_{};
-  uintptr_t offset_{};
-  size_t size_{};
-
-  Section() = default;
-
-  template <class S>
-  Section(ELF* elf, S* sec)
-      : elf_(elf),
-        ptr_((std::byte const*)sec),
-        nameIndex_(sec->name),
-        type_(sec->type),
-        offset_(sec->offset),
-        size_(sec->size) {}
-
-  operator bool() const { return ptr_; }
-
-  template <class T = std::byte>
-  T const* data() const {
-    return (T const*)(elfBase() + offset_);
-  }
-
-  std::byte const* elfBase() const;
-  std::string_view name() const;
-};
-
-struct Symbol final {
-  constexpr static uint8_t kFunc = 0x02; // STT_FUNC (code object)
-
-  ELF* elf_{};
-  std::byte const* ptr_{};
-  uintptr_t nameIndex_{};
-  uint32_t type_{};
-  uintptr_t value_{};
-
-  Symbol()              = default;
-  Symbol(Symbol const&) = default;
-  Symbol& operator=(Symbol const& rhs) { return *new (this) Symbol(rhs); }
-
-  operator bool() { return ptr_; }
-
-  bool isCode() const { return type_ == kFunc; }
-
-  template <class S>
-  Symbol(ELF* elf, S* sym)
-      : elf_(elf), ptr_((std::byte const*)sym), nameIndex_(sym->name), type_(0x0f & sym->info), value_(sym->value) {}
-
-  std::byte const* elfBase() const;
-  std::string_view name() const;
-};
-
-/** Represents one of the ELF's `strtab`s.  This is a block of string data, with strings appended one after another, and
- * NUL-terminated.  Strings are indexed according to their starting offset.  At offset 0 is typically an empty string.
- */
-struct StringTable {
-  std::string_view data_{};
-
-  StringTable() = default;
-
-  /* implicit */ StringTable(Section const& sec) : data_(sec.data<char>(), sec.size_) {}
-
-  operator bool() { return data_.size(); }
-
-  std::string_view at(size_t index) {
-    auto* ret = data_.data() + index;
-    return {ret, strlen(ret)};
-  }
-};
-
-/** Encapsulates an ELF image specified by byte-address (e.g. from an mmapped file or a program image or shared object
- * in memory).  If given a supported ELF image, this will test true with `operator bool` to indicate it is supported and
- * was able to parse some basic information from the header. */
-struct ELF {
-  Header header_{};
-  Section (*makeSection_)(ELF*, std::byte const*){};
-  Symbol (*makeSymbol_)(ELF*, std::byte const*){};
-  size_t secSize_{};
-  size_t symSize_{};
-  StringTable nametab_{};
-  Section symtab_{};
-  StringTable strtab_{};
-  size_t symCount_{};
-
-  static Section makeSection32(ELF* elf, std::byte const* ptr) { return Section(elf, (Section32 const*)ptr); }
-  static Section makeSection64(ELF* elf, std::byte const* ptr) { return Section(elf, (Section64 const*)ptr); }
-  static Symbol makeSymbol32(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol32 const*)ptr); }
-  static Symbol makeSymbol64(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol64 const*)ptr); }
-
-  operator bool() { return header_; }
-
-  explicit ELF(std::byte const* image) {
-    auto* p = (uint8_t const*)image;
-    // Bytes 0..3: magic bytes: 0x7F, 'E', 'L', 'F'
-    if (*p++ == 0x7f && *p++ == 0x45 && *p++ == 0x4c && *p++ == 0x46) {
-      auto klass       = *p++; // Byte 4 (EI_CLASS): ELF class, 32- or 64-bit (0x01 or 0x02)
-      auto dataFormat  = *p++; // Byte 5 (EI_DATA): (0x01) little- or (0x02) big-endian
-      auto fileVersion = *p++; // Byte 6 (EI_VERSION): ELF version: expect 1 (latest ELF version)
-      constexpr static uint16_t kEndianTestWord{0x0201};
-      auto hostEndianness = *(uint8_t const*)&kEndianTestWord;
-      if (dataFormat == hostEndianness && fileVersion == 1) {
-        if (klass == 0x01) {
-          header_      = Header((Header32 const*)image);
-          makeSection_ = makeSection32;
-          makeSymbol_  = makeSymbol32;
-          secSize_     = sizeof(Section32);
-          symSize_     = sizeof(Symbol32);
-        } else if (klass == 0x02) {
-          header_      = Header((Header64 const*)image);
-          makeSection_ = makeSection64;
-          makeSymbol_  = makeSymbol64;
-          secSize_     = sizeof(Section64);
-          symSize_     = sizeof(Symbol64);
-        }
-      }
-    }
-    if (*this) {
-      nametab_ = section(header_.shstrndx_);
-      eachSection([&](auto& sec) mutable -> bool {
-        if (sec.type_ == Section::kSymTab && sec.name() == ".symtab") {
-          symtab_ = sec;
-        } else if (sec.type_ == Section::kStrTab && sec.name() == ".strtab") {
-          strtab_ = sec;
-        }
-        return !symtab_ || !strtab_;
-      });
-    }
-    if (symtab_) {
-      symCount_ = symtab_.size_ / symSize_;
-    }
-  }
-
-  Section section(size_t index) {
-    auto* addr = header_.ptr_ + header_.shoff_ + (index * secSize_);
-    return makeSection_(this, addr);
-  }
-
-  Symbol symbol(size_t index) {
-    auto* addr = symtab_.data() + (index * symSize_);
-    return makeSymbol_(this, addr);
-  }
-
-  template <class T>
-  using CB = std::function<bool(T const&)>;
-
-  void eachSection(CB<Section> cb) {
-    for (size_t i = 0; i < header_.shnum_ && cb(section(i)); i++)
-      ;
-  }
-
-  void eachSymbol(CB<Symbol> cb) {
-    for (size_t i = 0; i < symCount_ && cb(symbol(i)); i++)
-      ;
-  }
-
-  Symbol getSym(uintptr_t addr) {
-    Symbol ret{};
-    eachSymbol([&](auto& sym) -> bool {
-      if (sym.value_ <= addr && sym.value_ > ret.value_) {
-        ret = sym;
-      }
-      return true;
-    });
-    return ret;
-  }
-};
-
-inline std::byte const* Section::elfBase() const { return elf_->header_.ptr_; }
-inline std::byte const* Symbol::elfBase() const { return elf_->header_.ptr_; }
-
-inline std::string_view Section::name() const { return elf_->nametab_.at(nameIndex_); }
-inline std::string_view Symbol::name() const { return elf_->strtab_.at(nameIndex_); }
-
-} // namespace elf
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif
diff --git a/libcxx/src/stacktrace/linux/linux-dl.cpp b/libcxx/src/stacktrace/linux/linux-dl.cpp
deleted file mode 100644
index e15403f7d3a50..0000000000000
--- a/libcxx/src/stacktrace/linux/linux-dl.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-#  include "linux.h"
-
-#  include <cassert>
-#  include <dlfcn.h>
-#  include <link.h>
-#  include <unistd.h>
-
-#  include "../common/images.h"
-
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-void linux::ident_modules() {
-  auto& images = images::get();
-
-  // Aside from the left/right sentinels in the array (hence the 2),
-  // are there any other real images?
-  if (images.count_ <= 2) {
-    return;
-  }
-
-  auto mainProg = images.mainProg();
-  if (mainProg) {
-    cx_.__main_prog_path_ = mainProg->name_;
-  }
-
-  unsigned index = 1; // Starts at one, and is moved around in this loop
-  for (auto& entry : cx_.__entries_) {
-    while (images[index].loaded_at_ > entry.__addr_) {
-      --index;
-    }
-    while (images[index + 1].loaded_at_ <= entry.__addr_) {
-      ++index;
-    }
-    entry.__addr_unslid_ = entry.__addr_ - images[index].slide_;
-    entry.__file_        = images[index].name_;
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/stacktrace/linux/linux-elf.cpp b/libcxx/src/stacktrace/linux/linux-elf.cpp
deleted file mode 100644
index 2cbeade56534b..0000000000000
--- a/libcxx/src/stacktrace/linux/linux-elf.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-#  include "linux.h"
-
-#  include <cassert>
-#  include <unistd.h>
-
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-
-#  include "../common/fd.h"
-#  include "elf.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/**
-When trying to collect a stacktrace under Linux, there are narrow (but still quite common) cases where we will fail
-to resolve symbols.  Linux's `dl` doesn't want to read symbols from the non-exported symbol table at runtime,
-and older versions of `addr2line` or `llvm-symbolizer` will also not resolve these.
-
-This implementation the minimum necessary to resolve symbols.  It can identify this as an ELF (32 or 64 bits), locate
-the symbol and symbol-string table, and fill in any remaining missing symbols.
-*/
-void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
-  // We can statically initialize these, because main_elf_name should be the same every time.
-  static fd_mmap _mm(main_elf_name);
-  if (_mm) {
-    static elf::ELF _this_elf(_mm.addr_);
-    if (_this_elf) {
-      for (auto& entry : cx_.__entries_) {
-        if (entry.__desc_.empty() && entry.__file_ == main_elf_name) {
-          entry.__desc_ = _this_elf.getSym(entry.__addr_unslid_).name();
-        }
-      }
-    }
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/stacktrace/linux/linux-sym.cpp b/libcxx/src/stacktrace/linux/linux-sym.cpp
deleted file mode 100644
index b9a8c313e668b..0000000000000
--- a/libcxx/src/stacktrace/linux/linux-sym.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-#  include "linux.h"
-
-#  include <cassert>
-#  include <dlfcn.h>
-#  include <unistd.h>
-
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-bool symbolize_entry(entry& entry) {
-  bool ret = false;
-  Dl_info info;
-  if (dladdr((void*)entry.__addr_, &info)) {
-    ret = true; // at least partially successful
-    if (info.dli_fname && entry.__file_.empty()) {
-      // provide at least the binary filename in case we cannot lookup source location
-      entry.__file_ = info.dli_fname;
-    }
-    if (info.dli_sname && entry.__desc_.empty()) {
-      // provide at least the mangled name; try to unmangle in a later step
-      entry.__desc_ = info.dli_sname;
-    }
-  }
-  return ret;
-}
-
-// NOTE:  We can use `dlfcn` to resolve addresses to symbols, which works great --
-// except for symbols in the main program.  If addr2line-style tools are enabled, that step
-// might also be able to get symbols directly from the binary's debug info.
-void linux::symbolize() {
-  for (auto& entry : cx_.__entries_) {
-    symbolize_entry(entry);
-  }
-  // Symbols might be missing, because both (1) Linux's `dladdr` won't try to resolve non-exported symbols,
-  // which can be the case for the main program executable; and (2) debug info was not preserved.
-  // As a last resort, this function (see `linux-elf.cpp`) can still access symbol table directly.
-  image* mainELF = images::get().mainProg();
-  if (mainELF && !mainELF->name_.empty()) {
-    resolve_main_elf_syms(mainELF->name_);
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/stacktrace/linux/linux.h b/libcxx/src/stacktrace/linux/linux.h
deleted file mode 100644
index 52b0377bcb144..0000000000000
--- a/libcxx/src/stacktrace/linux/linux.h
+++ /dev/null
@@ -1,98 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_LINUX_H
-#define _LIBCPP_STACKTRACE_LINUX_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-#  include <algorithm>
-#  include <array>
-#  include <cassert>
-#  include <cstddef>
-#  include <cstdlib>
-#  include <link.h>
-#  include <string_view>
-#  include <unistd.h>
-
-#  include "../common/images.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct context;
-
-struct linux {
-  context& cx_;
-  void ident_modules();
-  void symbolize();
-
-private:
-  void resolve_main_elf_syms(std::string_view elf_name);
-};
-
-struct images {
-  // How many images this contains, including the left/right sentinels.
-  unsigned count_{0};
-  std::array<image, k_max_images + 2> images_{};
-
-  int add(dl_phdr_info& info) {
-    assert(count_ < k_max_images);
-    auto isFirst        = (count_ == 0);
-    auto& image         = images_.at(count_++);
-    image.loaded_at_    = info.dlpi_addr;
-    image.slide_        = info.dlpi_addr;
-    image.name_         = info.dlpi_name;
-    image.is_main_prog_ = isFirst; // first one is the main ELF
-    if (image.name_.empty() && isFirst) {
-      static char buffer[PATH_MAX + 1];
-      uint32_t length = sizeof(buffer);
-      if (readlink("/proc/self/exe", buffer, length) > 0) {
-        image.name_ = buffer;
-      }
-    }
-    return count_ == k_max_images; // return nonzero if we're at the limit
-  }
-
-  static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
-
-  images() {
-    dl_iterate_phdr(images::callback, this);
-    images_[count_++] = {0uz, 0};  // sentinel at low end
-    images_[count_++] = {~0uz, 0}; // sentinel at high end
-    std::sort(images_.begin(), images_.begin() + count_ - 1);
-  }
-
-  image& operator[](size_t index) {
-    assert(index < count_);
-    return images_.at(index);
-  }
-
-  image* mainProg() {
-    for (auto& image : images_) {
-      if (image.is_main_prog_) {
-        return ℑ
-      }
-    }
-    return nullptr;
-  }
-
-  static images& get() {
-    static images images;
-    return images;
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
-
-#endif // _LIBCPP_STACKTRACE_LINUX_H
diff --git a/libcxx/src/stacktrace/osx/osx.cpp b/libcxx/src/stacktrace/osx.cpp
similarity index 92%
rename from libcxx/src/stacktrace/osx/osx.cpp
rename to libcxx/src/stacktrace/osx.cpp
index f377c0bded38a..39e4d34b3e1c3 100644
--- a/libcxx/src/stacktrace/osx/osx.cpp
+++ b/libcxx/src/stacktrace/osx.cpp
@@ -6,20 +6,23 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "../common/config.h"
+#include "stacktrace/config.h"
 
 #if defined(_LIBCPP_STACKTRACE_APPLE)
 
+#  include "stacktrace/osx.h"
+
 #  include <algorithm>
 #  include <array>
 #  include <dlfcn.h>
 #  include <mach-o/dyld.h>
 #  include <mach-o/loader.h>
 
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-
-#  include "osx.h"
+#  include "stacktrace/config.h"
+#  include "stacktrace/context.h"
+#  include "stacktrace/utils.h"
+#  include <__stacktrace/basic_stacktrace.h>
+#  include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
diff --git a/libcxx/src/stacktrace/osx/osx.h b/libcxx/src/stacktrace/osx.h
similarity index 100%
rename from libcxx/src/stacktrace/osx/osx.h
rename to libcxx/src/stacktrace/osx.h
diff --git a/libcxx/src/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
index 98acec1e92d0b..aa0e63b9eadc5 100644
--- a/libcxx/src/stacktrace/stacktrace.cpp
+++ b/libcxx/src/stacktrace/stacktrace.cpp
@@ -55,6 +55,13 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_en
 
 namespace __stacktrace {
 
+/* Anchors for classes w/ vtables */
+// alloc::~alloc()                        = default;
+debug::~debug()                        = default;
+debug::dummy_ostream::~dummy_ostream() = default;
+fd_streambuf::~fd_streambuf()          = default;
+fd_istream::~fd_istream()              = default;
+
 /*
  * `to_string` Helpers
  */
diff --git a/libcxx/src/stacktrace/tools/addr2line.cpp b/libcxx/src/stacktrace/tools/addr2line.cpp
deleted file mode 100644
index c1d36e83f13a6..0000000000000
--- a/libcxx/src/stacktrace/tools/addr2line.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <iostream>
-
-#include "tools.h"
-
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-#include <string>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-std::pmr::list<std::pmr::string> addr2line::buildArgs(context& cx) const {
-  auto& alloc = cx.__alloc_;
-  auto ret    = alloc.new_string_list();
-  if (cx.__main_prog_path_.empty()) {
-    // Should not have reached here but be graceful anyway
-    ret.push_back("/bin/false");
-    return ret;
-  }
-
-  ret.push_back(progName_);
-  ret.push_back("--functions");
-  ret.push_back("--demangle");
-  ret.push_back("--basenames");
-  ret.push_back("--pretty-print"); // This "human-readable form" is easier to parse
-  ret.push_back("-e");
-  ret.push_back(cx.__main_prog_path_);
-  for (auto& entry : cx.__entries_) {
-    ret.push_back(alloc.hex_string(entry.__addr_unslid_));
-  }
-  return ret;
-}
-
-void addr2line::parseOutput(context& trace, entry& entry, std::istream& output) const {
-  // clang-format off
-/*
-Example:
---
-llvm-addr2line -e foo --functions --demangle --basenames --pretty-print --no-inlines 0x11a0 0x1120 0x3d58 0x1284
-
-Output: (1 line per input address)
---
-main at foo.cc:15
-register_tm_clones at crtstuff.c:0
-GCC_except_table2 at foo.cc:0
-test::Foo::Foo(int) at foo.cc:11
-*/
-  // clang-format on
-
-  std::pmr::string line{&trace.__alloc_};
-  std::getline(output, line);
-  while (isspace(line.back())) {
-    line.pop_back();
-  }
-  if (line.empty()) {
-    return;
-  }
-  // Split at the sequence " at ".  Barring weird symbols
-  // having " at " in them, this should work.
-  auto sepIndex = line.find(" at ");
-  if (sepIndex == std::string::npos) {
-    return;
-  }
-  if (sepIndex > 0) {
-    entry.__desc_ = line.substr(0, sepIndex);
-  }
-  auto fileBegin = sepIndex + 4;
-  if (fileBegin >= line.size()) {
-    return;
-  }
-  auto fileline = line.substr(fileBegin);
-  auto colon    = fileline.find_last_of(":");
-  if (colon > 0 && !fileline.starts_with("?")) {
-    entry.__file_ = fileline.substr(0, colon);
-  }
-
-  if (colon == std::string::npos) {
-    return;
-  }
-  uint32_t lineno = 0;
-  auto pos        = colon;
-  while (isdigit(fileline[++pos])) {
-    lineno = lineno * 10 + (fileline[pos] - '0');
-  }
-  entry.__line_ = lineno;
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/atos.cpp b/libcxx/src/stacktrace/tools/atos.cpp
deleted file mode 100644
index f6e6458aaae0d..0000000000000
--- a/libcxx/src/stacktrace/tools/atos.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <istream>
-#include <string>
-#include <unistd.h>
-
-#include "tools.h"
-
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-std::pmr::list<std::pmr::string> atos::buildArgs(context& cx) const {
-  auto& alloc = cx.__alloc_;
-  auto ret    = alloc.new_string_list();
-  ret.push_back(progName_);
-  ret.push_back("-p");
-  ret.push_back(alloc.u64_string(getpid()));
-  // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
-  // ret.push_back("--fullPath");
-  for (auto& entry : cx.__entries_) {
-    ret.push_back(alloc.hex_string(entry.__addr_));
-  }
-  return ret;
-}
-
-void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
-  // Simple example:
-  //
-  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
-  //
-  // Assuming this is always atos's format (except when it returns empty lines)
-  // we can split the string like so:
-  //
-  //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
-  //   ^^^^-----^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^-
-  //   sym      module     filename                            line
-  //
-  // Note that very strange filenames or module names can confuse this.
-  // We'll do the best we can for a decent result, while definitely ensuring safety
-  // (i.e. careful with our bound-checking).
-  //
-  // Another more interesting example (with an added newline for legibility):
-  //
-  //   std::__1::basic_ios<char, std::__1::char_traits<char>>::fill[abi:ne190107]() const (in testprog)
-  //   (/opt/homebrew/Cellar/llvm/19.1.7_1/bin/../include/c++/v1/ios:0
-  //
-  // If this more or less fits our expected format we'll take these data,
-  // even if the line number is 0.
-
-  auto line = cx.__alloc_.new_string(256);
-  std::getline(output, line);
-  while (isspace(line.back())) {
-    line.pop_back();
-  }
-  if (line.empty()) {
-    return;
-  }
-  auto buf  = line.data();
-  auto size = line.size();
-
-  auto* end    = buf + size;
-  auto* symEnd = strstr(buf, " (in ");
-  if (!symEnd) {
-    return;
-  }
-  auto* modBegin = symEnd + 5;
-  auto* modEnd   = strstr(modBegin, ") (");
-  if (!modEnd) {
-    return;
-  }
-  auto* fileBegin = modEnd + 3; // filename starts just after that
-  if (fileBegin >= end) {
-    return;
-  }
-  auto const* lastColon = fileBegin; // we'll search for last colon after filename
-  char const* nextColon;
-  while ((nextColon = strstr(lastColon + 1, ":"))) { // skip colons in filename (e.g. in "C:\foo.cpp")
-    lastColon = nextColon;
-  }
-
-  std::string_view sym{buf, size_t(symEnd - buf)};
-  // In case a previous step could not obtain the symbol name,
-  // we have the name provided by atos; only use that if we have no symbol
-  // (no need to copy more strings otherwise).
-  if (entry.__desc_.empty() && !sym.empty()) {
-    entry.__desc_ = sym;
-  }
-
-  std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
-  if (file != "?" && file != "??" && !file.empty()) {
-    entry.__file_ = file;
-  }
-
-  unsigned lineno = 0;
-  for (auto* digit = lastColon + 1; digit < end && isdigit(*digit); ++digit) {
-    lineno = (lineno * 10) + unsigned(*digit - '0');
-  }
-  entry.__line_ = lineno;
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp b/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
deleted file mode 100644
index 18b493c97a040..0000000000000
--- a/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <iostream>
-#include <string_view>
-
-#include "../common/debug.h"
-#include "tools.h"
-
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// TODO(stacktrace23): possible to link against `libLLVMSymbolize.a`, or some shared obj at runtime (does that exist?)
-
-std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
-  auto& alloc = cx.__alloc_;
-  auto ret    = alloc.new_string_list();
-  ret.push_back(progName_);
-  ret.push_back("--demangle");
-  ret.push_back("--no-inlines");
-  ret.push_back("--verbose");
-  ret.push_back("--relativenames");
-  ret.push_back("--functions=short");
-  for (auto& entry : cx.__entries_) {
-    auto addr_string = alloc.hex_string(entry.__addr_unslid_);
-    debug() << "@@@ " << addr_string << " " << entry.__file_ << " " << entry.__file_.empty() << '\n';
-    if (!entry.__file_.empty()) {
-      auto arg = alloc.new_string(entry.__file_.size() + 40);
-      arg += "FILE:";
-      arg += entry.__file_;
-      arg += " ";
-      arg += addr_string;
-      ret.push_back(arg);
-    } else {
-      ret.push_back(addr_string);
-    }
-  }
-  return ret;
-}
-
-void llvm_symbolizer::parseOutput(context& cx, entry& entry, std::istream& output) const {
-  // clang-format off
-/*
-With "--verbose", parsing is a little easier, or at least, more reliable;
-probably the best solution (until we have a JSON parser).
-Example output, verbatim, between the '---' lines:
----
-test1<test_alloc<std::__1::stacktrace_entry> >
-  Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
-  Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
-  Function start line: 114
-  Function start address: 0x8dd0
-  Line: 116
-  Column: 14
-
----
-Note that this includes an extra empty line as a terminator.
-*/
-  // clang-format on
-
-  auto& alloc = cx.__alloc_;
-  auto line   = alloc.new_string(256);
-  std::string_view tmp;
-  while (true) {
-    std::getline(output, line);
-    while (isspace(line.back())) {
-      line.pop_back();
-    }
-    if (line.empty()) {
-      return;
-    }
-    if (!line.starts_with("  ")) {
-      // The symbol has no leading whitespace, while the other
-      // lines with "fields" like line, column, filename, etc.
-      // start with two spaces.
-      if (line != "??") {
-        entry.__desc_ = line;
-      }
-    } else if (line.starts_with("  Filename:")) {
-      tmp = line;
-      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
-      if (tmp != "??") {
-        entry.__file_ = tmp;
-      }
-    } else if (line.starts_with("  Line:")) {
-      tmp = line;
-      tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
-      if (tmp != "??" && tmp != "0") {
-        uint32_t lineno = 0;
-        auto pos        = 0;
-        while (isdigit(tmp[pos])) {
-          lineno = lineno * 10 + (tmp[pos++] - '0');
-        }
-        entry.__line_ = lineno;
-      }
-    }
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/pspawn.h b/libcxx/src/stacktrace/tools/pspawn.h
deleted file mode 100644
index 4e2fa381083ad..0000000000000
--- a/libcxx/src/stacktrace/tools/pspawn.h
+++ /dev/null
@@ -1,163 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_PSPAWN_H
-#define _LIBCPP_STACKTRACE_PSPAWN_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-
-#  include <__config>
-#  include <__config_site>
-#  include <cassert>
-#  include <cerrno>
-#  include <csignal>
-#  include <cstddef>
-#  include <cstdlib>
-#  include <list>
-#  include <spawn.h>
-#  include <string>
-#  include <sys/fcntl.h>
-#  include <sys/types.h>
-#  include <sys/wait.h>
-#  include <unistd.h>
-#  include <vector>
-
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-
-#  include "../common/debug.h"
-#  include "../common/failed.h"
-#  include "../common/fd.h"
-#  include "tools.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct file_actions {
-  posix_spawn_file_actions_t fa_;
-
-  file_actions() {
-    if (posix_spawn_file_actions_init(&fa_)) {
-      throw failed("posix_spawn_file_actions_init", errno);
-    }
-  }
-
-  ~file_actions() { posix_spawn_file_actions_destroy(&fa_); }
-
-  void addClose(int fd) {
-    if (posix_spawn_file_actions_addclose(&fa_, fd)) {
-      throw failed("posix_spawn_file_actions_addclose", errno);
-    }
-  }
-  void addDup2(int fd, int stdfd) {
-    if (posix_spawn_file_actions_adddup2(&fa_, fd, stdfd)) {
-      throw failed("posix_spawn_file_actions_adddup2", errno);
-    }
-  }
-
-  fd redirectOutFD() {
-    int fds[2];
-    if (::pipe(fds)) {
-      throw failed("pipe", errno);
-    }
-    addClose(fds[0]);
-    addDup2(fds[1], 1);
-    return {fds[0]};
-  }
-
-  void redirectInNull() { addDup2(fd::null_fd(), 0); }
-  void redirectOutNull() { addDup2(fd::null_fd(), 1); }
-  void redirectErrNull() { addDup2(fd::null_fd(), 2); }
-};
-
-struct pspawn {
-  tool const& tool_;
-  pid_t pid_{0};
-  file_actions fa_{};
-
-  // TODO(stacktrace23): ignore SIGCHLD for spawned subprocess
-
-  ~pspawn() {
-    if (pid_) {
-      kill(pid_, SIGTERM);
-      wait();
-    }
-  }
-
-  void spawn(std::pmr::list<std::pmr::string> const& argStrings) {
-    std::pmr::vector<char const*> argv{argStrings.get_allocator()};
-    argv.reserve(argStrings.size() + 1);
-    for (auto const& str : argStrings) {
-      argv.push_back(str.data());
-    }
-    argv.push_back(nullptr);
-    int err;
-    if ((err = posix_spawnp(&pid_, argv[0], &fa_.fa_, nullptr, const_cast<char**>(argv.data()), nullptr))) {
-      throw failed("posix_spawnp", err);
-    }
-  }
-
-  int wait() {
-    int status;
-    waitpid(pid_, &status, 0);
-    return status;
-  }
-};
-
-struct pspawn_tool : pspawn {
-  context& cx_;
-  fd fd_;
-  fd_streambuf buf_;
-  fd_istream stream_;
-
-  pspawn_tool(tool const& a2l, context& cx, char* buf, size_t size)
-      : pspawn{a2l}, cx_(cx), fd_(fa_.redirectOutFD()), buf_(fd_, buf, size), stream_(buf_) {
-    if (!debug::enabled()) {
-      fa_.redirectErrNull();
-    }
-    fa_.redirectInNull();
-  }
-
-  void run() {
-    // Cannot run "addr2line" or similar without addresses, since we
-    // provide them in argv, and if there are none passed in argv, the
-    // tool will try to read from stdin and hang.
-    if (cx_.__entries_.empty()) {
-      return;
-    }
-
-    auto argStrings = tool_.buildArgs(cx_);
-    if (debug::enabled()) {
-      debug() << "Trying to get stacktrace using:";
-      for (auto& str : argStrings) {
-        debug() << " \"" << str << '"';
-      }
-      debug() << '\n';
-    }
-
-    spawn(argStrings);
-
-    for (auto& entry : cx_.__entries_) {
-      tool_.parseOutput(cx_, entry, stream_);
-    }
-  }
-};
-
-struct spawner {
-  context& cx_;
-  void resolve_lines();
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif
-
-#endif // _LIBCPP_STACKTRACE_PSPAWN_H
diff --git a/libcxx/src/stacktrace/tools/tools.h b/libcxx/src/stacktrace/tools/tools.h
deleted file mode 100644
index 70374cf075ebf..0000000000000
--- a/libcxx/src/stacktrace/tools/tools.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_TOOLS_H
-#define _LIBCPP_STACKTRACE_TOOLS_H
-
-#include <__config>
-#include <__config_site>
-#include <cassert>
-#include <cstddef>
-#include <list>
-#include <string>
-
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct tool {
-  char const* progName_;
-  explicit tool(char const* progName) : progName_(progName) {}
-  constexpr virtual ~tool() = default;
-
-  /** Construct complete `argv` for the spawned process.
-  Includes the program name at argv[0], followed by flags */
-  virtual std::pmr::list<std::pmr::string> buildArgs(context& trace) const = 0;
-
-  /** Parse line(s) output by the tool, and modify `entry`. */
-  virtual void parseOutput(context& trace, entry& e, std::istream& output) const = 0;
-};
-
-struct llvm_symbolizer : tool {
-  constexpr virtual ~llvm_symbolizer() = default;
-  llvm_symbolizer() : llvm_symbolizer("llvm-symbolizer") {}
-  explicit llvm_symbolizer(char const* progName) : tool{progName} {}
-  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
-  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
-};
-
-struct addr2line : tool {
-  constexpr virtual ~addr2line() = default;
-  addr2line() : addr2line("addr2line") {}
-  explicit addr2line(char const* progName) : tool{progName} {}
-  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
-  void parseOutput(context& trace, entry& e, std::istream& stream) const override;
-};
-
-struct atos : tool {
-  constexpr virtual ~atos() = default;
-  atos() : atos("atos") {}
-  explicit atos(char const* progName) : tool{progName} {}
-  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
-  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_TOOLS_H
diff --git a/libcxx/src/stacktrace/tools/toolspawner.cpp b/libcxx/src/stacktrace/tools/toolspawner.cpp
deleted file mode 100644
index 54ddaf156fc64..0000000000000
--- a/libcxx/src/stacktrace/tools/toolspawner.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-
-#  include <__config>
-#  include <__config_site>
-#  include <cassert>
-#  include <cerrno>
-#  include <csignal>
-#  include <cstddef>
-#  include <cstdlib>
-#  include <spawn.h>
-#  include <string>
-#  include <sys/fcntl.h>
-#  include <sys/types.h>
-#  include <sys/wait.h>
-#  include <unistd.h>
-
-#  include "../common/failed.h"
-#  include "pspawn.h"
-
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-namespace {
-
-/**
-Returns a list of `tool` objects that can be tried during the first stacktrace operation.
-The result is an array of `tool*` pointers with a final `nullptr` terminator.
-
-The returned tool array pointer is saved during `resolve_lines`'s static init,
-with the first working tool being used from that point forward.
-
-This will first add any tools specified by these defines (in order):
-- LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH
-- LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH
-- LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH
-
-Then any tools specified by the environment variables of the same names (and added in the same order).
-
-Finally, this adds the tools `llvm-symbolizer`, `addr2line`, and `atos`, in that order.
-These tools won't have absolute paths, so $PATH will be searched for these.
-
-Called by `findWorkingTool` during its static initialization, therefore this is used in a threadsafe way.
-*/
-tool const** toolList() {
-  constexpr static size_t kMax = 20;
-  static tool const* array_[kMax];
-  size_t count = 0;
-
-  auto add = [&](tool* t) {
-    assert(count < kMax - 1);
-    array_[count++] = t;
-  };
-
-#  define STRINGIFY0(x) #x
-#  define STRINGIFY(x) STRINGIFY0(x)
-
-#  if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
-  {
-    static llvm_symbolizer t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)};
-    add(&t);
-  }
-#  endif
-#  if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
-  {
-    static addr2line t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)};
-    add(&t);
-  }
-#  endif
-#  if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
-  {
-    static atos t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)};
-    add(&t);
-  }
-#  endif
-
-  if (getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")) {
-    static llvm_symbolizer t{getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")};
-    add(&t);
-  }
-  if (getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")) {
-    static addr2line t{getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")};
-    add(&t);
-  }
-  if (getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")) {
-    static atos t{getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")};
-    add(&t);
-  }
-
-  {
-    static llvm_symbolizer t;
-    add(&t);
-  }
-  {
-    static addr2line t;
-    add(&t);
-  }
-  {
-    static atos t;
-    add(&t);
-  }
-
-  array_[count] = nullptr; // terminator
-  return array_;
-}
-
-// Try a handful of different addr2line-esque tools, returning the first discovered, or else nullptr
-tool const* findWorkingTool(auto& trace) {
-  // Try each of these programs and stop at the first one that works.
-  // There's no synchronization so this is subject to races on setting `prog`,
-  // but as long as one thread ends up setting it to something that works, we're good.
-  // Programs to try ($PATH is searched):
-  auto* it = toolList();
-  tool const* prog;
-  while ((prog = *it++)) {
-    pspawn test{*prog}; // Just try to run with "--help"
-    try {
-      std::pmr::list<std::pmr::string> testArgs{&trace.__alloc_};
-      testArgs.push_back(prog->progName_);
-      testArgs.push_back("--help");
-      test.fa_.redirectInNull();
-      test.fa_.redirectOutNull();
-      test.fa_.redirectErrNull();
-      test.spawn(testArgs);
-      if (test.wait() == 0) {
-        // Success
-        return prog;
-      }
-    } catch (failed const&) {
-      /* ignore during probe attempt */
-    }
-  }
-  return nullptr;
-}
-
-} // namespace
-
-void spawner::resolve_lines() {
-  // The address-to-line tool that worked (after lazy-setting, below)
-  static tool const* prog{nullptr};
-  // Whether we should not attempt because tool detection previously failed.
-  static bool fail{false};
-
-  // If this previously failed, don't try again.
-  if (fail) {
-    return;
-  }
-
-  if (!prog) {
-    prog = findWorkingTool(cx_);
-    if (!prog) {
-      fail = true;
-      return;
-    }
-  }
-  assert(prog);
-
-  char buf[256];
-  pspawn_tool proc(*prog, cx_, buf, sizeof(buf));
-  try {
-    proc.run();
-  } catch (failed const& failed) {
-    debug() << failed.what();
-    if (failed.errno_) {
-      debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
-    }
-    debug() << '\n';
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif
diff --git a/libcxx/src/stacktrace/unwind/unwind.cpp b/libcxx/src/stacktrace/unwind/unwind.cpp
deleted file mode 100644
index 51689c5f1c15d..0000000000000
--- a/libcxx/src/stacktrace/unwind/unwind.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
-
-#  include "./unwind.h"
-
-#  include <__stacktrace/context.h>
-#  include <__stacktrace/entry.h>
-#  include <unwind.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct unwind_backtrace {
-  context& cx_;
-  size_t skip_;
-  size_t maxDepth_;
-
-  _Unwind_Reason_Code callback(_Unwind_Context* ucx) {
-    if (skip_) {
-      --skip_;
-      return _Unwind_Reason_Code::_URC_NO_REASON;
-    }
-    if (!maxDepth_) {
-      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
-    }
-    --maxDepth_;
-    int ipBefore;
-    auto ip = _Unwind_GetIPInfo(ucx, &ipBefore);
-    if (!ip) {
-      return _Unwind_Reason_Code::_URC_NORMAL_STOP;
-    }
-    auto& entry          = cx_.__entries_.emplace_back();
-    entry.__addr_        = (ipBefore ? ip : ip - 1);
-    entry.__addr_unslid_ = entry.__addr_; // in case we can't un-slide
-    return _Unwind_Reason_Code::_URC_NO_REASON;
-  }
-
-  static _Unwind_Reason_Code callback(_Unwind_Context* cx, void* self) {
-    return ((unwind_backtrace*)self)->callback(cx);
-  }
-};
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void unwind::collect(size_t skip, size_t max_depth) {
-  unwind_backtrace bt{cx_, skip + 1, max_depth}; // skip this call as well
-  _Unwind_Backtrace(unwind_backtrace::callback, &bt);
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif
diff --git a/libcxx/src/stacktrace/unwind/unwind.h b/libcxx/src/stacktrace/unwind/unwind.h
deleted file mode 100644
index a258a32f50c5d..0000000000000
--- a/libcxx/src/stacktrace/unwind/unwind.h
+++ /dev/null
@@ -1,30 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_UNWIND_H
-#define _LIBCPP_STACKTRACE_UNWIND_H
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <cstdlib>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct context;
-
-struct unwind {
-  context& cx_;
-  void collect(size_t skip, size_t max_depth);
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_UNWIND_H
diff --git a/libcxx/src/stacktrace/windows/dbghelp_dll.cpp b/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
deleted file mode 100644
index 7753ad77d3d26..0000000000000
--- a/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include "dll.h"
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-dbghelp_dll::~dbghelp_dll() = default;
-
-dbghelp_dll& dbghelp_dll::get() {
-  dbghelp_dll ret;
-  return ret;
-}
-
-dbghelp_dll::dbghelp_dll() : dll("dbghelp.dll") {
-  // clang-format off
-if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
-  if (!get_func(&StackWalk64, "StackWalk64")) { return; }
-  if (!get_func(&SymCleanup, "SymCleanup")) { return; }
-  if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
-  if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
-  if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
-  if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
-  if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
-  if (!get_func(&SymInitialize, "SymInitialize")) { return; }
-  if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
-  if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
-  valid_ = true;
-  // clang-format on
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/dbghelp_dll.h b/libcxx/src/stacktrace/windows/dbghelp_dll.h
deleted file mode 100644
index b1204a369a3f1..0000000000000
--- a/libcxx/src/stacktrace/windows/dbghelp_dll.h
+++ /dev/null
@@ -1,71 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_WINDOWS_DBGHELP_DLL_H
-#define _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include <__stacktrace/entry.h>
-
-#  include "../common/debug.h"
-#  include "dll.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// clang-format off
-
-struct dbghelp_dll final : dll {
-  virtual ~dbghelp_dll() = default;
-  static dbghelp_dll& get() { static dbghelp_dll ret; return ret; }
-
-  IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
-  bool    (*StackWalk64)        (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
-  bool    (*SymCleanup)         (HANDLE);
-  void*   (*SymFunctionTableAccess64)(HANDLE, DWORD64);
-  bool    (*SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
-  DWORD64 (*SymGetModuleBase64) (HANDLE, DWORD64);
-  DWORD   (*SymGetOptions)      ();
-  bool    (*SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
-  bool    (*SymInitialize)      (HANDLE, char const*, bool);
-  DWORD64 (*SymLoadModule64)    (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
-  DWORD   (*SymSetOptions)      (DWORD);
-
-  dbghelp_dll() : dll("dbghelp.dll") {
-    if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
-    if (!get_func(&StackWalk64, "StackWalk64")) { return; }
-    if (!get_func(&SymCleanup, "SymCleanup")) { return; }
-    if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
-    if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
-    if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
-    if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
-    if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
-    if (!get_func(&SymInitialize, "SymInitialize")) { return; }
-    if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
-    if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
-    valid_ = true;
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WINDOWS
-#endif // _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
diff --git a/libcxx/src/stacktrace/windows/dll.cpp b/libcxx/src/stacktrace/windows/dll.cpp
deleted file mode 100644
index b0ea2609b55cd..0000000000000
--- a/libcxx/src/stacktrace/windows/dll.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include "dll.h"
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-dll::~dll() { FreeLibrary(module_); }
-
-dll::dll(char const* name) : name_(name), module_(LoadLibrary(name)) {
-  if (!module_) {
-    debug() << "LoadLibrary failed: " << name_ << ": " << GetLastError() << '\n';
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/dll.h b/libcxx/src/stacktrace/windows/dll.h
deleted file mode 100644
index 4ef8e7c5df190..0000000000000
--- a/libcxx/src/stacktrace/windows/dll.h
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_WINDOWS_DLL_H
-#define _LIBCPP_STACKTRACE_WINDOWS_DLL_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include <__stacktrace/entry.h>
-
-#  include "../common/debug.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// clang-format off
-
-struct dll {
-  char const* name_;
-  HMODULE module_;
-  /** Set to true in subclass's ctor if initialized successfully. */
-  bool valid_{false};
-
-  operator bool() const { return valid_; }
-
-  explicit dll(char const* name)
-      : name_(name), module_(LoadLibrary(name)) {
-    if (!module_) {
-      debug() << "LoadLibrary failed: "
-              << name_ << ": " << GetLastError() << '\n';
-    }
-  }
-
-  virtual ~dll() { FreeLibrary(module_); }
-
-  template <typename F>
-  bool get_func(F* func, char const* name) {
-    if (!(*func = (F)GetProcAddress(module_, name))) {
-      debug() << "GetProcAddress failed: "
-              << name << "' (" << name_ << "): "
-              << GetLastError() << "\n";
-      return false;
-    }
-    return true;
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WINDOWS
-#endif // _LIBCPP_STACKTRACE_WINDOWS_DLL_H
diff --git a/libcxx/src/stacktrace/windows/psapi_dll.cpp b/libcxx/src/stacktrace/windows/psapi_dll.cpp
deleted file mode 100644
index d4f7fdc006341..0000000000000
--- a/libcxx/src/stacktrace/windows/psapi_dll.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include "dll.h"
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-psapi_dll::~psapi_dll() = default;
-
-psapi_dll& psapi_dll::get() {
-  psapi_dll ret;
-  return ret;
-}
-
-psapi_dll() : dll("psapi.dll") {
-  // clang-format off
-if (!getFunc(&EnumProcessModules, "EnumProcessModules")) { return; }
-  if (!getFunc(&GetModuleInformation, "GetModuleInformation")) { return; }
-  if (!getFunc(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
-  valid_ = true;
-  // clang-format on
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/psapi_dll.h b/libcxx/src/stacktrace/windows/psapi_dll.h
deleted file mode 100644
index bcad1bfb72d0a..0000000000000
--- a/libcxx/src/stacktrace/windows/psapi_dll.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_WINDOWS_PSAPI_DLL_H
-#define _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include <__stacktrace/entry.h>
-
-#  include "../common/debug.h"
-#  include "dll.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// clang-format off
-
-struct psapi_dll final : dll {
-  virtual ~psapi_dll() = default;
-  static psapi_dll& get() { static psapi_dll ret; return ret; }
-
-  bool  (*EnumProcessModules)   (HANDLE, HMODULE*, DWORD, DWORD*);
-  bool  (*GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
-  DWORD (*GetModuleBaseName)    (HANDLE, HMODULE, char**, DWORD);
-
-  psapi_dll() : dll("psapi.dll") {
-    if (!get_func(&EnumProcessModules, "EnumProcessModules")) { return; }
-    if (!get_func(&GetModuleInformation, "GetModuleInformation")) { return; }
-    if (!get_func(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
-    valid_ = true;
-  }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WINDOWS
-#endif // _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
diff --git a/libcxx/src/stacktrace/windows/win_impl.cpp b/libcxx/src/stacktrace/windows/win_impl.cpp
deleted file mode 100644
index 44d6052706b85..0000000000000
--- a/libcxx/src/stacktrace/windows/win_impl.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "win_impl.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-#  include <windows.h>
-// other windows-specific headers
-#  include <dbghelp.h>
-#  define PSAPI_VERSION 1
-#  include <psapi.h>
-// standard headers
-#  include <cstdlib>
-#  include <mutex>
-
-#  include "dll.h"
-#  include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-namespace {
-
-/*
-Global objects, shared among all threads and among all stacktrace operations.
-The `dbghelp` APIs are not safe to call concurrently (according to their docs)
-so we claim a lock in the `WinDebugAPIs` constructor.
-*/
-
-// Statically-initialized
-std::mutex gWindowsAPILock;
-DbgHelpDLL dbg;
-PSAPIDLL ps;
-
-// Initialized once, in first WinDebugAPIs construction;
-// protected by the above mutex.
-HANDLE proc;
-HMODULE exe;
-IMAGE_NT_HEADERS* ntHeaders;
-bool globalInitialized{false};
-
-// Globals used across invocations of the functions below.
-// Also guarded by the above mutex.
-bool symsInitialized{false};
-HMODULE moduleHandles[1024];
-size_t moduleCount; // 0 IFF module enumeration failed
-
-} // namespace
-
-win_impl::WinDebugAPIs(context& trace) : trace_(trace), guard_(gWindowsAPILock) {
-  if (!globalInitialized) {
-    // Cannot proceed without these DLLs:
-    if (!dbg) {
-      return;
-    }
-    if (!ps) {
-      return;
-    }
-    proc = GetCurrentProcess();
-    if (!(exe = GetModuleHandle(nullptr))) {
-      return;
-    }
-    if (!(ntHeaders = (*dbg.ImageNtHeader)(exe))) {
-      return;
-    }
-
-    globalInitialized = true;
-  }
-
-  // Initialize symbol machinery.
-  // Presumably the symbols in this process's space can change between
-  // stacktraces, so we'll do this each time we take a trace.
-  // The final `true` means we want the runtime to enumerate all this
-  // process's modules' symbol tables.
-  symsInitialized  = (*dbg.SymInitialize)(proc, nullptr, true);
-  DWORD symOptions = (*dbg.SymGetOptions)();
-  symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
-  (*dbg.SymSetOptions)(symOptions);
-}
-
-win_impl::~WinDebugAPIs() {
-  if (symsInitialized) {
-    (*dbg.SymCleanup)(proc);
-    symsInitialized = false;
-  }
-}
-
-void win_impl::ident_modules() {
-  if (!globalInitialized) {
-    return;
-  }
-  DWORD needBytes;
-  auto enumMods = (*ps.EnumProcessModules)(proc, moduleHandles, sizeof(moduleHandles), LPDWORD(&needBytes));
-  if (enumMods) {
-    moduleCount = needBytes / sizeof(HMODULE);
-  } else {
-    moduleCount = 0;
-    Debug() << "EnumProcessModules failed: " << GetLastError() << '\n';
-  }
-}
-
-void win_impl::symbolize() {
-  // Very long symbols longer than this amount will be truncated.
-  static constexpr size_t kMaxSymName = 256;
-  if (!globalInitialized) {
-    return;
-  }
-
-  for (auto& entry : cx_.__entries_) {
-    char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName];
-    auto* sym          = (IMAGEHLP_SYMBOL64*)space;
-    sym->SizeOfStruct  = sizeof(IMAGEHLP_SYMBOL64);
-    sym->MaxNameLength = kMaxSymName;
-    uint64_t disp{0};
-    if ((*dbg.SymGetSymFromAddr64)(proc, entry.__addr_, &disp, sym)) {
-      // Copy chars into the destination string which uses the caller-provided allocator.
-      entry.__desc_ = {sym->Name};
-    } else {
-      Debug() << "SymGetSymFromAddr64 failed: " << GetLastError() << '\n';
-    }
-  }
-}
-
-void win_impl::resolve_lines() {
-  if (!globalInitialized) {
-    return;
-  }
-
-  for (auto& entry : cx_.__entries_) {
-    DWORD disp{0};
-    IMAGEHLP_LINE64 line;
-    line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
-    if ((*dbg.SymGetLineFromAddr64)(proc, entry.__addr_, &disp, &line)) {
-      // Copy chars into the destination string which uses the caller-provided allocator.
-      entry.__file_ = line.FileName;
-      entry.__line_ = line.LineNumber;
-    } else {
-      Debug() << "SymGetLineFromAddr64 failed: " << GetLastError() << '\n';
-    }
-  }
-}
-
-/*
-Inlining is disabled from here on;
-this is to ensure `collect` below doesn't get merged into its caller
-and mess around with the top of the stack (making `skip` inaccurate).
-*/
-#  pragma auto_inline(off)
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_t max_depth) {
-  if (!globalInitialized) {
-    return;
-  }
-
-  auto thread  = GetCurrentThread();
-  auto machine = ntHeaders->FileHeader.Machine;
-
-  CONTEXT ccx;
-  RtlCaptureContext(&ccx);
-
-  STACKFRAME64 frame;
-  memset(&frame, 0, sizeof(frame));
-  frame.AddrPC.Mode      = AddrModeFlat;
-  frame.AddrStack.Mode   = AddrModeFlat;
-  frame.AddrFrame.Mode   = AddrModeFlat;
-  frame.AddrPC.Offset    = ccx.Rip;
-  frame.AddrStack.Offset = ccx.Rsp;
-  frame.AddrFrame.Offset = ccx.Rbp;
-
-  while (max_depth &&
-         (*dbg.StackWalk64)(
-             machine,
-             proc,
-             thread,
-             &frame,
-             &ccx,
-             nullptr,
-             dbg.SymFunctionTableAccess64,
-             dbg.SymGetModuleBase64,
-             nullptr)) {
-    if (skip) {
-      --skip;
-      continue;
-    }
-    --max_depth;
-    auto& entry = cx_.__entries_.emplace_back();
-    // We don't need to compute the un-slid addr; windbg only needs the actual addresses.
-    // Assume address is of the instruction after a call instruction, since we can't
-    // differentiate between a signal, SEH exception handler, or a normal function call.
-    entry.__addr_ = frame.AddrPC.Offset - 1; // Back up 1 byte to get into prev insn range
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/win_impl.h b/libcxx/src/stacktrace/windows/win_impl.h
deleted file mode 100644
index c065987120425..0000000000000
--- a/libcxx/src/stacktrace/windows/win_impl.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_WIN_IMPL_H
-#define _LIBCPP_STACKTRACE_WIN_IMPL_H
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <cstdlib>
-#include <mutex>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct context;
-
-struct win_impl {
-  context& cx_;
-  std::lock_guard<std::mutex> guard_;
-  win_impl(context& trace);
-  ~win_impl();
-
-  void collect(size_t skip, size_t max_depth);
-  void ident_modules();
-  void symbolize();
-  void resolve_lines();
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WIN_IMPL_H
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index f824b25349068..ce6b52fbb0462 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -168,38 +168,18 @@ cxx_sources = [
   "ryu/d2s.cpp",
   "ryu/f2s.cpp",
   "shared_mutex.cpp",
-  "stacktrace/alloc.cpp",
-  "stacktrace/common/config.h",
-  "stacktrace/common/debug.cpp",
-  "stacktrace/common/debug.h",
-  "stacktrace/common/failed.h",
-  "stacktrace/common/fd.cpp",
-  "stacktrace/common/fd.h",
-  "stacktrace/common/images.h",
-  "stacktrace/context.cpp",
-  "stacktrace/linux/linux-dl.cpp",
-  "stacktrace/linux/linux-elf.cpp",
-  "stacktrace/linux/linux-sym.cpp",
-  "stacktrace/linux/linux.h",
-  "stacktrace/osx/osx.cpp",
-  "stacktrace/osx/osx.h",
+  "stacktrace/config.h",
+  "stacktrace/linux.cpp",
+  "stacktrace/linux.h",
+  "stacktrace/osx.cpp",
+  "stacktrace/osx.h",
   "stacktrace/stacktrace.cpp",
-  "stacktrace/tools/addr2line.cpp",
-  "stacktrace/tools/atos.cpp",
-  "stacktrace/tools/llvm_symbolizer.cpp",
-  "stacktrace/tools/pspawn.h",
-  "stacktrace/tools/tools.h",
-  "stacktrace/tools/toolspawner.cpp",
-  "stacktrace/unwind/unwind.cpp",
-  "stacktrace/unwind/unwind.h",
-  "stacktrace/windows/dbghelp_dll.cpp",
-  "stacktrace/windows/dbghelp_dll.h",
-  "stacktrace/windows/dll.cpp",
-  "stacktrace/windows/dll.h",
-  "stacktrace/windows/psapi_dll.cpp",
-  "stacktrace/windows/psapi_dll.h",
-  "stacktrace/windows/win_impl.cpp",
-  "stacktrace/windows/win_impl.h",
+  "stacktrace/tools.cpp",
+  "stacktrace/tools.h",
+  "stacktrace/unwind.cpp",
+  "stacktrace/utils.h",
+  "stacktrace/windows.cpp",
+  "stacktrace/windows.h",
   "stdexcept.cpp",
   "string.cpp",
   "strstream.cpp",

>From a2231fda339068b25bb22ed7abc38872e7e5a611 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 15:51:02 -0400
Subject: [PATCH 24/37] Updates per PR feedback

---
 libcxx/src/stacktrace/osx.cpp                 | 114 ------------------
 libcxx/src/stacktrace/osx.h                   |  31 -----
 .../diagnostics/stacktrace/basic.cmp.pass.cpp |   2 +-
 .../stacktrace/basic.cons.pass.cpp            |   4 +-
 .../diagnostics/stacktrace/basic.mod.pass.cpp |   2 +-
 .../stacktrace/basic.nonmem.pass.cpp          |   2 +-
 .../diagnostics/stacktrace/basic.obs.pass.cpp |   2 +-
 .../diagnostics/stacktrace/entry.cmp.pass.cpp |   2 +-
 .../stacktrace/entry.cons.pass.cpp            |   2 +-
 .../diagnostics/stacktrace/entry.obs.pass.cpp |   2 +-
 .../stacktrace/entry.query.pass.cpp           |   4 +-
 .../diagnostics/stacktrace/format.pass.cpp    |   2 +-
 .../std/diagnostics/stacktrace/syn.pass.cpp   |   2 +-
 llvm/utils/gn/secondary/libcxx/src/BUILD.gn   |  12 --
 14 files changed, 13 insertions(+), 170 deletions(-)
 delete mode 100644 libcxx/src/stacktrace/osx.cpp
 delete mode 100644 libcxx/src/stacktrace/osx.h

diff --git a/libcxx/src/stacktrace/osx.cpp b/libcxx/src/stacktrace/osx.cpp
deleted file mode 100644
index 39e4d34b3e1c3..0000000000000
--- a/libcxx/src/stacktrace/osx.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "stacktrace/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_APPLE)
-
-#  include "stacktrace/osx.h"
-
-#  include <algorithm>
-#  include <array>
-#  include <dlfcn.h>
-#  include <mach-o/dyld.h>
-#  include <mach-o/loader.h>
-
-#  include "stacktrace/config.h"
-#  include "stacktrace/context.h"
-#  include "stacktrace/utils.h"
-#  include <__stacktrace/basic_stacktrace.h>
-#  include <__stacktrace/stacktrace_entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-constexpr unsigned kMaxImages = 256;
-
-struct Image {
-  uintptr_t loadedAt{};
-  intptr_t slide{};
-  std::string_view name{};
-  bool operator<(Image const& rhs) const { return loadedAt < rhs.loadedAt; }
-  operator bool() const { return !name.empty(); }
-};
-
-void ident_module(entry& entry, unsigned& index, Image* images) {
-  if (entry.__addr_) {
-    while (images[index].loadedAt > entry.__addr_) {
-      --index;
-    }
-    while (images[index + 1].loadedAt <= entry.__addr_) {
-      ++index;
-    }
-    auto& image = images[index];
-    if (image) {
-      entry.__addr_unslid_ = entry.__addr_ - images[index].slide;
-      entry.__file_        = images[index].name;
-    }
-  }
-}
-
-bool enum_modules(unsigned& count, auto& images) {
-  count = std::min(kMaxImages, _dyld_image_count());
-  for (size_t i = 0; i < count; i++) {
-    auto& image    = images[i];
-    image.slide    = _dyld_get_image_vmaddr_slide(i);
-    image.loadedAt = uintptr_t(_dyld_get_image_header(i));
-    image.name     = _dyld_get_image_name(i);
-  }
-  images[count++] = {0uz, 0};  // sentinel at low end
-  images[count++] = {~0uz, 0}; // sentinel at high end
-  std::sort(images.begin(), images.begin() + count - 1);
-  return true;
-}
-
-void osx::ident_modules() {
-  static unsigned imageCount;
-  static std::array<Image, kMaxImages + 2> images;
-  static bool atomicInitialized = enum_modules(imageCount, images);
-  (void)atomicInitialized;
-
-  // Aside from the left/right sentinels in the array (hence the 2),
-  // are there any other real images?
-  if (imageCount <= 2) {
-    return;
-  }
-
-  // First image (the main program) is at index 1
-  cx_.__main_prog_path_ = images.at(1).name;
-
-  unsigned index = 1; // Starts at one, and is moved by 'ident_module'
-  for (auto& entry : cx_.__entries_) {
-    ident_module(entry, index, images.data());
-  }
-}
-
-void symbolize_entry(entry& entry) {
-  Dl_info info;
-  if (dladdr((void*)entry.__addr_, &info)) {
-    if (info.dli_fname && entry.__file_.empty()) {
-      // provide at least the binary filename in case we cannot lookup source location
-      entry.__file_ = info.dli_fname;
-    }
-    if (info.dli_sname && entry.__desc_.empty()) {
-      // provide at least the mangled name; try to unmangle in a later step
-      entry.__desc_ = info.dli_sname;
-    }
-  }
-}
-
-void osx::symbolize() {
-  for (auto& entry : cx_.__entries_) {
-    symbolize_entry(entry);
-  }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_APPLE
diff --git a/libcxx/src/stacktrace/osx.h b/libcxx/src/stacktrace/osx.h
deleted file mode 100644
index ce38aa0a378c6..0000000000000
--- a/libcxx/src/stacktrace/osx.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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_STACKTRACE_OSX_H
-#define _LIBCPP_STACKTRACE_OSX_H
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <cstdlib>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct context;
-
-struct osx {
-  context& cx_;
-  void ident_modules();
-  void symbolize();
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_OSX_H
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
index e8b5c36d05ba1..38c0f1fbe3c7a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
index edf07510687dc..f4d0dfbe0f8ed 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// ADDITIONAL_COMPILE_FLAGS: -O1 -g
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
index 9d473202d492f..e53890a39c2c4 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
index d0b327e738c6c..e1ef4245b83e9 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
index 1f4102bd1ca61..e88480093536b 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 #include <iostream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
index 6da4d40c5e08a..963926b041f03 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
index 84472ea91b2aa..20f8a24d41b89 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 // ADDITIONAL_COMPILE_FLAGS: -g
 
 #include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
index f31d7d3e9f8b7..229619f9c24a1 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
index 0d8fac58f30ee..b8fda6dab0fe3 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
 
 #include <stacktrace>
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
index 42accf6ce1daa..3db4920b3c388 100644
--- a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <cassert>
 #include <stacktrace>
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
index 9682f6bfc146d..173e81286506e 100644
--- a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// REQUIRES: std-at-least-c++23
 
 #include <cassert>
 #include <stacktrace>
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index ce6b52fbb0462..b9e8d0717f7df 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -168,18 +168,6 @@ cxx_sources = [
   "ryu/d2s.cpp",
   "ryu/f2s.cpp",
   "shared_mutex.cpp",
-  "stacktrace/config.h",
-  "stacktrace/linux.cpp",
-  "stacktrace/linux.h",
-  "stacktrace/osx.cpp",
-  "stacktrace/osx.h",
-  "stacktrace/stacktrace.cpp",
-  "stacktrace/tools.cpp",
-  "stacktrace/tools.h",
-  "stacktrace/unwind.cpp",
-  "stacktrace/utils.h",
-  "stacktrace/windows.cpp",
-  "stacktrace/windows.h",
   "stdexcept.cpp",
   "string.cpp",
   "strstream.cpp",

>From 49b01101a31863924cd8654ed3f6e0ba8d909038 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 17:08:25 -0400
Subject: [PATCH 25/37] Remove newline at end of file: std.compat.cppm.in

---
 libcxx/modules/std.compat.cppm.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index 2ee56c6b810c9..55d21030c031b 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -83,4 +83,4 @@ export module std.compat;
 export import std;
 
 
- at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
+ at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
\ No newline at end of file

>From 12bacc82cdc6002d3c3c4348f64231deee90a487 Mon Sep 17 00:00:00 2001
From: "steve at obrien.cc" <Steve O'Brien>
Date: Wed, 23 Apr 2025 18:15:36 -0400
Subject: [PATCH 26/37] Committing regenerated files

---
 .../gn/build/sync_source_lists_from_cmake.py    | 12 +++---------
 llvm/utils/gn/build/toolchain/BUILD.gn          | 17 +++++++++++++++++
 2 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/llvm/utils/gn/build/sync_source_lists_from_cmake.py b/llvm/utils/gn/build/sync_source_lists_from_cmake.py
index 4cd30aa410864..aada38bebd421 100755
--- a/llvm/utils/gn/build/sync_source_lists_from_cmake.py
+++ b/llvm/utils/gn/build/sync_source_lists_from_cmake.py
@@ -61,13 +61,13 @@ def git_out(args):
     gn_files = git_out(["ls-files", "*BUILD.gn"]).splitlines()
 
     # Matches e.g. |   "foo.cpp",|, captures |foo| in group 1.
-    gn_cpp_re = re.compile(r'^\s*"([^$"]+\.(?:cpp|c|h|mm|S))",$', re.MULTILINE)
+    gn_cpp_re = re.compile(r'^\s*"([^$"]+\.(?:cpp|c|h|S))",$', re.MULTILINE)
     # Matches e.g. |   bar_sources = [ "foo.cpp" ]|, captures |foo| in group 1.
     gn_cpp_re2 = re.compile(
-        r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|mm|S))" ]$', re.MULTILINE
+        r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|S))" ]$', re.MULTILINE
     )
     # Matches e.g. |   foo.cpp|, captures |foo| in group 1.
-    cmake_cpp_re = re.compile(r"^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|mm|S))$", re.MULTILINE)
+    cmake_cpp_re = re.compile(r"^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|S))$", re.MULTILINE)
 
     changes_by_rev = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
 
@@ -108,12 +108,6 @@ def get_sources(source_re, text):
 
         gn_cpp = get_sources(gn_cpp_re, open(gn_file).read())
         gn_cpp |= get_sources(gn_cpp_re2, open(gn_file).read())
-
-        sources_file = os.path.join(os.path.dirname(gn_file), "sources.gni")
-        if os.path.exists(sources_file):
-            gn_cpp |= get_sources(gn_cpp_re, open(sources_file).read())
-            gn_cpp |= get_sources(gn_cpp_re2, open(sources_file).read())
-
         cmake_cpp = get_sources(cmake_cpp_re, open(cmake_file).read())
 
         if gn_cpp == cmake_cpp:
diff --git a/llvm/utils/gn/build/toolchain/BUILD.gn b/llvm/utils/gn/build/toolchain/BUILD.gn
index 00d9df12f5a97..ce2e6df1b69f5 100644
--- a/llvm/utils/gn/build/toolchain/BUILD.gn
+++ b/llvm/utils/gn/build/toolchain/BUILD.gn
@@ -71,6 +71,23 @@ template("unix_toolchain") {
       default_output_dir = "{{root_out_dir}}/lib"
     }
 
+    if (current_os == "ios" || current_os == "mac") {
+      # gn < 1693 (e214b5d35898) doesn't support |frameworks|, requiring
+      # frameworks to be listed in |libs|, but gn >= 1808 (3028c6a426a4) forbids
+      # frameworks from appearing in |libs|. This assertion provides a helpful
+      # cue to upgrade, and is much more user-friendly than the failure that
+      # occurs when an older gn encounters |frameworks|.
+      #
+      # gn_version doesn’t actually exist in gn < 1709 (52cb644a3fb4), and
+      # defined(gn_version) doesn't actually work as expected
+      # (https://crbug.com/gn/183), so 1709 is the true minimum enforced by
+      # this construct, and if gn_version is not available, this line will still
+      # be blamed, making the resolution somewhat discoverable.
+      assert(gn_version >= 1693,
+             "Your GN is too old! " +
+                 "Update it, perhaps by running llvm/utils/gn/get.py")
+    }
+
     # Make these apply to all tools below.
     lib_switch = "-l"
     lib_dir_switch = "-L"

>From c1474388fa283fab7f685d1c08f933990bee523d Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 18:33:15 -0400
Subject: [PATCH 27/37] Address some errors from gcc regarding inlining

---
 libcxx/src/stacktrace/stacktrace.cpp | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/libcxx/src/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
index aa0e63b9eadc5..98acec1e92d0b 100644
--- a/libcxx/src/stacktrace/stacktrace.cpp
+++ b/libcxx/src/stacktrace/stacktrace.cpp
@@ -55,13 +55,6 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_en
 
 namespace __stacktrace {
 
-/* Anchors for classes w/ vtables */
-// alloc::~alloc()                        = default;
-debug::~debug()                        = default;
-debug::dummy_ostream::~dummy_ostream() = default;
-fd_streambuf::~fd_streambuf()          = default;
-fd_istream::~fd_istream()              = default;
-
 /*
  * `to_string` Helpers
  */

>From 5e3d3cdfc2d868b40ca6a774d442656ac0eb064b Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 22:51:00 -0400
Subject: [PATCH 28/37] Feature macros + regenerate

---
 .../test/std/diagnostics/stacktrace/syn.pass.cpp   | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
index 173e81286506e..f6c44bf8fe44f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -8,12 +8,6 @@
 
 // REQUIRES: std-at-least-c++23
 
-#include <cassert>
-#include <stacktrace>
-
-#include <memory>
-#include <type_traits>
-
 /*
 
 // (19.6.2) Header <stacktrace> synopsis [stacktrace.syn]
@@ -62,7 +56,13 @@ namespace std {
 }
 */
 
-// static_assert(__cpp_lib_stacktrace == 202011L);
+#include <__config>
+#include <cassert>
+#include <memory>
+#include <stacktrace>
+#include <type_traits>
+
+static_assert(__cpp_lib_stacktrace == 202011L);
 
 int main(int, char**) {
   // Very basic tests to ensure the required things are declared.

>From cca8c83e48435dbff747c2c75725be7cadccfedf Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 24 Apr 2025 00:16:39 -0400
Subject: [PATCH 29/37] Break up tests

---
 .../diagnostics/stacktrace/basic.cmp.pass.cpp | 106 ------
 .../stacktrace/basic.cons.pass.cpp            | 308 ------------------
 .../diagnostics/stacktrace/basic.mod.pass.cpp |  40 ---
 .../stacktrace/basic.nonmem.pass.cpp          |  91 ------
 .../diagnostics/stacktrace/basic.obs.pass.cpp | 156 ---------
 .../diagnostics/stacktrace/entry.cmp.pass.cpp |  65 ----
 .../stacktrace/entry.cons.pass.cpp            |  63 ----
 .../diagnostics/stacktrace/entry.obs.pass.cpp |  51 ---
 .../stacktrace/entry.query.pass.cpp           |  85 -----
 .../diagnostics/stacktrace/format.pass.cpp    |  53 ---
 .../std/diagnostics/stacktrace/syn.pass.cpp   | 147 ---------
 11 files changed, 1165 deletions(-)
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
 delete mode 100644 libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp

diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
deleted file mode 100644
index 38c0f1fbe3c7a..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-#include <stacktrace>
-
-#include <cassert>
-
-/*
-  (19.6.4.4) Comparisons [stacktrace.basic.cmp]
-
-  template<class Allocator2>
-  friend bool operator==(const basic_stacktrace& x,
-                          const basic_stacktrace<Allocator2>& y) noexcept;
-
-  template<class Allocator2>
-  friend strong_ordering operator<=>(const basic_stacktrace& x,
-                                      const basic_stacktrace<Allocator2>& y) noexcept;
-*/
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(); }
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2a() { return test1(); }
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2b() { return test1(); }
-
-_LIBCPP_NO_TAIL_CALLS
-int main(int, char**) {
-  /*    
-    template<class Allocator2>
-    friend bool operator==(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
-    Returns: equal(x.begin(), x.end(), y.begin(), y.end()).
-  */
-  auto st1a = test1(); // [test1, main, ...]
-  assert(st1a == st1a);
-
-  auto st1b = st1a;
-  assert(st1a == st1b);
-
-  auto st2a = test2a(); // [test1, test2a, main, ...]
-  assert(st1a != st2a);
-
-  std::stacktrace empty; // []
-  assert(st1a != empty);
-  assert(st2a != empty);
-
-  assert(st2a.size() > st1a.size());
-  assert(st1a.size() > empty.size());
-
-  auto st2b = test2b(); // [test1, test2b, main, ...]
-  assert(st2a.size() == st2b.size());
-  assert(st2a != st2b);
-
-  /*    
-    template<class Allocator2>
-    friend strong_ordering
-    operator<=>(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
-
-    Returns: x.size() <=> y.size() if x.size() != y.size();
-    lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end()) otherwise.
-  */
-
-  // empty:  []
-  // st1a:   [test1, main, ...]
-  // st1b:   [test1, main, ...] (copy of st1a)
-  // st2a:   [test1, test2a, main:X, ...]
-  // st2b:   [test1, test2b, main:Y, ...], Y > X
-
-  assert(std::strong_ordering::equal == empty <=> empty);
-  assert(std::strong_ordering::less == empty <=> st1a);
-  assert(std::strong_ordering::less == empty <=> st1b);
-  assert(std::strong_ordering::less == empty <=> st2a);
-  assert(std::strong_ordering::less == empty <=> st2b);
-
-  assert(std::strong_ordering::greater == st1a <=> empty);
-  assert(std::strong_ordering::equal == st1a <=> st1a);
-  assert(std::strong_ordering::equal == st1a <=> st1b);
-  assert(std::strong_ordering::less == st1a <=> st2a);
-  assert(std::strong_ordering::less == st1a <=> st2b);
-
-  assert(std::strong_ordering::greater == st1b <=> empty);
-  assert(std::strong_ordering::equal == st1b <=> st1a);
-  assert(std::strong_ordering::equal == st1b <=> st1b);
-  assert(std::strong_ordering::less == st1b <=> st2a);
-  assert(std::strong_ordering::less == st1b <=> st2b);
-
-  assert(std::strong_ordering::greater == st2a <=> empty);
-  assert(std::strong_ordering::greater == st2a <=> st1a);
-  assert(std::strong_ordering::greater == st2a <=> st1b);
-  assert(std::strong_ordering::equal == st2a <=> st2a);
-  assert(std::strong_ordering::less == st2a <=> st2b);
-
-  assert(std::strong_ordering::greater == st2b <=> empty);
-  assert(std::strong_ordering::greater == st2b <=> st1a);
-  assert(std::strong_ordering::greater == st2b <=> st1b);
-  assert(std::strong_ordering::greater == st2b <=> st2a);
-  assert(std::strong_ordering::equal == st2b <=> st2b);
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
deleted file mode 100644
index f4d0dfbe0f8ed..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-// ADDITIONAL_COMPILE_FLAGS: -g
-
-#include <stacktrace>
-
-#include <cassert>
-#include <iostream>
-
-/**
- * This file includes tests which ensure any allocations performed by `basic_stacktrace`
- * are done via the user-provided allocator, and not via bare `malloc` / `operator new`s.
- * Our overridden `malloc` below will fail if `malloc_disabled` is `true`.
- * The program's startup code may need to malloc, so we'll allow malloc initially.
- * This is only activated during the "test_no_malloc_or_new_ex_allocator" test,
- * during which it should be using a `test_alloc` we'll provide (which knows how to
- * unblock `malloc` temporarily).
- */
-bool malloc_disabled{false};
-
-void* malloc(size_t size) {
-  // If flag has not been temporarily disabled by our allocator, abort
-  if (malloc_disabled) {
-    abort();
-  }
-  // Since we overrode malloc with this function, and there's no way to call up
-  // to the "real malloc", allocate a different way.  Assumes nothing actually uses `calloc`.
-  return calloc(1, size);
-}
-
-// All the various ways to monkey around with heap memory without an allocator:
-// we'll simply redirect these through our gate-keeping `malloc` above.
-void* operator new(size_t size) { return malloc(size); }
-void* operator new[](size_t size) { return malloc(size); }
-void operator delete(void* ptr) noexcept { free(ptr); }
-void operator delete(void* ptr, size_t) noexcept { free(ptr); }
-void operator delete[](void* ptr) noexcept { free(ptr); }
-void operator delete[](void* ptr, size_t) noexcept { free(ptr); }
-
-/** RAII-style scope object to temporarily permit heap allocations, used by `test_alloc`.*/
-struct scope_enable_malloc {
-  bool prev_malloc_disabled_;
-  scope_enable_malloc() : prev_malloc_disabled_(malloc_disabled) { malloc_disabled = false; }
-  ~scope_enable_malloc() { malloc_disabled = true; }
-};
-
-template <typename T>
-struct test_alloc {
-  using size_type     = size_t;
-  using value_type    = T;
-  using pointer       = T*;
-  using const_pointer = T const*;
-
-  template <typename U>
-  struct rebind {
-    using other = test_alloc<U>;
-  };
-
-  std::allocator<T> wrapped_{};
-
-  test_alloc() = default;
-
-  template <typename U>
-  test_alloc(test_alloc<U> const& rhs) : wrapped_(rhs.wrapped_) {}
-
-  bool operator==(auto const& rhs) const { return &rhs == this; }
-  bool operator==(test_alloc const&) const { return true; }
-
-  T* allocate(size_t n) { return wrapped_.allocate(n); }
-
-  auto allocate_at_least(size_t n) { return wrapped_.allocate_at_least(n); }
-
-  void deallocate(T* ptr, size_t n) { return wrapped_.deallocate(ptr, n); }
-};
-
-/*
-  (19.6.4.2)
-
-  // [stacktrace.basic.cons], creation and assignment
-  static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;   [1]
-  static basic_stacktrace current(size_type skip,
-                                  const allocator_type& alloc = allocator_type()) noexcept;   [2]
-  static basic_stacktrace current(size_type skip, size_type max_depth,
-                                  const allocator_type& alloc = allocator_type()) noexcept;   [3]
-
-  basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);            [4]
-  explicit basic_stacktrace(const allocator_type& alloc) noexcept;                            [5]
-
-  basic_stacktrace(const basic_stacktrace& other);                                            [6]
-  basic_stacktrace(basic_stacktrace&& other) noexcept;                                        [7]
-  basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);               [8]
-  basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);                    [9]
-  basic_stacktrace& operator=(const basic_stacktrace& other);                                 [10]
-  basic_stacktrace& operator=(basic_stacktrace&& other)
-    noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
-      allocator_traits<Allocator>::is_always_equal::value);                                   [11]
-
-  ~basic_stacktrace();                                                                        [12]
-*/
-
-// clang-format off
-uint32_t test1_line;
-uint32_t test2_line;
-
-template <class A>
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
-std::basic_stacktrace<A> test1(A& alloc) {
-  test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
-  auto ret = std::basic_stacktrace<A>::current(alloc);
-  return ret;
-}
-
-template <class A>
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
-std::basic_stacktrace<A> test2(A& alloc) {
-  test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
-  auto ret = test1(alloc);
-  return ret;
-}
-// clang-format on
-
-/*
-    [1]
-    static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
-
-    Returns: A basic_stacktrace object with frames_ storing the stacktrace of the current evaluation
-    in the current thread of execution, or an empty basic_stacktrace object if the initialization of
-    frames_ failed. alloc is passed to the constructor of the frames_ object.
-    [Note 1: If the stacktrace was successfully obtained, then frames_.front() is the stacktrace_entry
-    representing approximately the current evaluation, and frames_.back() is the stacktrace_entry
-    representing approximately the initial function of the current thread of execution. — end note]
-  */
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
-  test_alloc<std::stacktrace_entry> alloc;
-  uint32_t main_line = __LINE__ + 1;
-  auto st            = test2(alloc);
-
-  std::cerr << "*** Stacktrace obtained at line " << main_line << '\n' << st << '\n';
-
-  assert(st.size() >= 3);
-  assert(st[0]);
-  assert(st[0].native_handle());
-  assert(st[0].description().contains("test1"));
-  assert(st[0].source_file().contains("basic.cons.pass.cpp"));
-  assert(st[1]);
-  assert(st[1].native_handle());
-  assert(st[1].description().contains("test2"));
-  assert(st[1].source_file().contains("basic.cons.pass.cpp"));
-  assert(st[2]);
-  assert(st[2].native_handle());
-  assert(st[2].description().contains("test_current"));
-  assert(st[2].source_file().contains("basic.cons.pass.cpp"));
-
-  // We unfortunately cannot guarantee the following; in CI, and possibly on users' build machines,
-  // there may not be an up-to-date version of e.g. `addr2line`.
-  // assert(st[0].source_file().ends_with("basic.cons.pass.cpp"));
-  // assert(st[0].source_line() == test1_line);
-  // assert(st[1].source_file().ends_with("basic.cons.pass.cpp"));
-  // assert(st[1].source_line() == test2_line);
-  // assert(st[2].source_file().ends_with("basic.cons.pass.cpp"));
-  // assert(st[2].source_line() == main_line);
-}
-
-/*
-  [2]
-  static basic_stacktrace current(size_type skip,
-                              const allocator_type& alloc = allocator_type()) noexcept;
-  Let t be a stacktrace as-if obtained via basic_stacktrace​::​current(alloc). Let n be t.size().
-  Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
-  t.begin() + min(n, skip), t.end(), and alloc, or an empty basic_stacktrace object if the
-  initialization of frames_ failed.
-*/
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
-  // Use default allocator for simplicity; alloc is covered above
-  auto st_skip0 = std::stacktrace::current();
-  std::cerr << "*** st_skip0:\n" << st_skip0 << '\n';
-  assert(st_skip0.size() >= 2);
-  auto st_skip1 = std::stacktrace::current(1);
-  std::cerr << "*** st_skip1:\n" << st_skip1 << '\n';
-  assert(st_skip1.size() >= 1);
-  assert(st_skip0.size() == st_skip1.size() + 1);
-  assert(st_skip0[1] == st_skip1[0]);
-  auto st_skip_many = std::stacktrace::current(1 << 20);
-  assert(st_skip_many.empty());
-}
-
-/*
-  [3]
-  static basic_stacktrace current(size_type skip, size_type max_depth,
-                              const allocator_type& alloc = allocator_type()) noexcept;
-  Let t be a stacktrace as-if obtained via basic_stacktrace​::​current(alloc). Let n be t.size().
-  Preconditions: skip <= skip + max_depth is true.
-  Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
-  t.begin() + min(n, skip), t.begin() + min(n, skip + max_depth), and alloc, or an empty
-  basic_stacktrace object if the initialization of frames_ failed.
-*/
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
-  // current stack is: [this function, main, (possibly something else, e.g. `_start` from libc)]
-  // so it's probably 3 functions deep -- but certainly at least 2 deep.
-  auto st = std::stacktrace::current();
-  assert(st.size() >= 2);
-  auto it     = st.begin();
-  auto entry1 = *(it++); // represents this function
-  auto entry2 = *(it++); // represents our caller, `main`
-
-  // get current trace again, but skip the 1st
-  st = std::stacktrace::current(1, 1);
-  assert(st.size() >= 1);
-  assert(*st.begin() == entry2);
-}
-
-/*
-  [4]
-  basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
-  Postconditions: empty() is true.
-*/
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_default_construct() {
-  std::stacktrace st;
-  assert(st.empty());
-}
-
-/*
-  [5]
-  explicit basic_stacktrace(const allocator_type& alloc) noexcept;
-  Effects: alloc is passed to the frames_ constructor.
-  Postconditions: empty() is true.
-*/
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_construct_with_allocator() {
-  test_alloc<std::stacktrace_entry> alloc;
-  std::basic_stacktrace<decltype(alloc)> st(alloc);
-  assert(st.empty());
-
-  st = std::basic_stacktrace<decltype(alloc)>::current(alloc);
-  assert(!st.empty());
-}
-
-/*
-  [6] basic_stacktrace(const basic_stacktrace& other);
-  [7] basic_stacktrace(basic_stacktrace&& other) noexcept;
-  [8] basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
-  [9] basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
-  [10] basic_stacktrace& operator=(const basic_stacktrace& other);
-  [11] basic_stacktrace& operator=(basic_stacktrace&& other)
-    noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
-    allocator_traits<Allocator>::is_always_equal::value);
-  
-  Remarks: Implementations may strengthen the exception specification for these functions
-  ([res.on.exception.handling]) by ensuring that empty() is true on failed allocation.
-*/
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_copy_move_ctors() {
-  using A = std::allocator<std::stacktrace_entry>;
-  A alloc;
-  auto st = std::basic_stacktrace<A>::current(alloc);
-
-  auto copy_constr = std::basic_stacktrace<A>(st);
-  assert(st == copy_constr);
-
-  std::basic_stacktrace<A> copy_assign;
-  copy_assign = std::basic_stacktrace<A>(st);
-  assert(st == copy_assign);
-
-  auto st2 = test2(alloc);
-  assert(st2.size());
-  std::basic_stacktrace<A> move_constr(std::move(st2));
-  assert(move_constr.size());
-  assert(!st2.size());
-
-  auto st3 = test2(alloc);
-  assert(st3.size());
-  std::basic_stacktrace<A> move_assign;
-  move_assign = std::move(st3);
-  assert(move_assign.size());
-  assert(!st3.size());
-
-  // TODO(stacktrace23): should we add test cases with `select_on_container_copy_construction`?
-}
-
-/** Ensure all allocations take place through a given allocator, and that none
- * are sneaking around it by accidentally using malloc or operator new. */
-void test_no_malloc_or_new_ex_allocator() {
-  // A generic allocator we'll use for everything
-  using A = test_alloc<std::stacktrace_entry>;
-  A alloc;
-  // After this point all stacktrace operations must properly use `alloc`
-  malloc_disabled = true;
-  // Try all the stack trace operations
-  auto st = std::basic_stacktrace<A>(alloc);
-}
-
-_LIBCPP_NO_TAIL_CALLS
-int main(int, char**) {
-  test_current();
-  test_current_with_skip();
-  test_current_with_skip_depth();
-  test_default_construct();
-  test_construct_with_allocator();
-  test_copy_move_ctors();
-  test_no_malloc_or_new_ex_allocator();
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
deleted file mode 100644
index e53890a39c2c4..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-#include <stacktrace>
-
-#include <cassert>
-
-/*
-  (19.6.4.5) Modifiers [stacktrace.basic.mod]
-
-  void swap(basic_stacktrace& other)
-      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
-      allocator_traits<Allocator>::is_always_equal::value);
-
-  Effects: Exchanges the contents of *this and other.
-*/
-int main(int, char**) {
-  std::stacktrace empty;
-  auto current = std::stacktrace::current();
-
-  std::stacktrace a(empty);
-  std::stacktrace b(current);
-  assert(a == empty);
-  assert(b == current);
-
-  a.swap(b);
-  assert(a == current);
-  assert(b == empty);
-
-  // TODO(stacktrace23): should we also test swap w/ `select_on_container_swap` case
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
deleted file mode 100644
index e1ef4245b83e9..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-#include <stacktrace>
-
-#include <cassert>
-#include <sstream>
-
-/*
-  (19.6.4.6) Non-member functions
-
-  template<class Allocator>
-    void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
-      noexcept(noexcept(a.swap(b)));
-
-  string to_string(const stacktrace_entry& f);
-
-  template<class Allocator>
-    string to_string(const basic_stacktrace<Allocator>& st);
-
-  ostream& operator<<(ostream& os, const stacktrace_entry& f);
-  template<class Allocator>
-    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
-*/
-int main(int, char**) {
-  /*
-  template<class Allocator>
-  void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
-    noexcept(noexcept(a.swap(b)));
-  Effects: Equivalent to a.swap(b).
-  */
-  std::stacktrace empty;
-  auto current = std::stacktrace::current();
-
-  std::stacktrace a(empty);
-  std::stacktrace b(current);
-  assert(a == empty);
-  assert(b == current);
-
-  std::swap(a, b);
-  assert(a == current);
-  assert(b == empty);
-
-  /*
-  string to_string(const stacktrace_entry& f);
-  Returns: A string with a description of f.
-  Recommended practice: The description should provide information about the contained evaluation,
-  including information from f.source_file() and f.source_line().
-  */
-
-  assert(std::to_string(a[0]).contains("main"));
-  assert(std::to_string(a[0]).contains("basic.nonmem.pass"));
-
-  /*
-  template<class Allocator>
-  string to_string(const basic_stacktrace<Allocator>& st);
-  Returns: A string with a description of st.
-  [Note 1: The number of lines is not guaranteed to be equal to st.size(). — end note]
-  */
-
-  assert(std::to_string(a).contains("main"));
-  assert(std::to_string(a).contains("basic.nonmem.pass"));
-
-  /*
-  ostream& operator<<(ostream& os, const stacktrace_entry& f);
-  Effects: Equivalent to: return os << to_string(f);
-  */
-
-  std::stringstream entry_os;
-  entry_os << a[0];
-  assert(entry_os.str() == std::to_string(a[0]));
-
-  /*
-  template<class Allocator>
-    ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
-  Effects: Equivalent to: return os << to_string(st);
-  */
-
-  std::stringstream trace_os;
-  trace_os << a;
-  assert(trace_os.str() == std::to_string(a));
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
deleted file mode 100644
index e88480093536b..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-#include <stacktrace>
-#include <iostream>
-#include <iterator>
-#include <memory>
-
-#include <cassert>
-#include <stdexcept>
-
-/*
-  (19.6.4.3) Observers [stacktrace.basic.obs]
-
-  // [stacktrace.basic.obs], observers
-  allocator_type get_allocator() const noexcept;      [1]
-
-  const_iterator begin() const noexcept;              [2]
-  const_iterator end() const noexcept;                [3]
-  const_reverse_iterator rbegin() const noexcept;     [4]
-  const_reverse_iterator rend() const noexcept;       [5]
-
-  const_iterator cbegin() const noexcept;             [6]
-  const_iterator cend() const noexcept;               [7]
-  const_reverse_iterator crbegin() const noexcept;    [8]
-  const_reverse_iterator crend() const noexcept;      [9]
-
-  bool empty() const noexcept;                        [10]
-  size_type size() const noexcept;                    [11]
-  size_type max_size() const noexcept;                [12]
-
-  const_reference operator[](size_type) const;        [13]
-  const_reference at(size_type) const;                [14]
-*/
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
-
-_LIBCPP_NO_TAIL_CALLS
-int main(int, char**) {
-  std::stacktrace st;
-
-  /*
-    using const_iterator = implementation-defined;
-    The type models random_access_iterator ([iterator.concept.random.access]) and meets the
-    Cpp17RandomAccessIterator requirements ([random.access.iterators]).
-  */
-  static_assert(std::random_access_iterator<decltype(st.begin())>);
-  static_assert(std::random_access_iterator<decltype(st.rbegin())>);
-  static_assert(std::random_access_iterator<decltype(st.cbegin())>);
-  static_assert(std::random_access_iterator<decltype(st.crbegin())>);
-
-  /*
-    allocator_type get_allocator() const noexcept;
-    Returns: frames_.get_allocator().
-  */
-  static_assert(std::same_as<decltype(st.get_allocator()), std::stacktrace::allocator_type>);
-
-  /*
-    const_iterator begin() const noexcept;
-    const_iterator cbegin() const noexcept;
-    Returns: An iterator referring to the first element in frames_. If empty() is true,
-    then it returns the same value as end().
-
-    const_iterator end() const noexcept;
-    const_iterator cend() const noexcept;
-    Returns: The end iterator.
-
-    const_reverse_iterator rbegin() const noexcept;
-    const_reverse_iterator crbegin() const noexcept;
-    Returns: reverse_iterator(cend()).
-
-    const_reverse_iterator rend() const noexcept;
-    const_reverse_iterator crend() const noexcept;
-    Returns: reverse_iterator(cbegin()).
-  */
-
-  // st is initially empty:
-  assert(st.begin() == st.end());
-  assert(st.cbegin() == st.cend());
-  assert(st.rbegin() == st.rend());
-  assert(st.crbegin() == st.crend());
-
-  /*
-    bool empty() const noexcept;
-    Returns: frames_.empty().
-  */
-  assert(st.empty());
-  assert(st.size() == 0);
-
-  // no longer empty:
-  st = test3();
-  assert(!st.empty());
-  assert(st.begin() != st.end());
-  assert(st.cbegin() != st.cend());
-
-  /*
-    size_type size() const noexcept;
-    Returns: frames_.size().
-
-    size_type max_size() const noexcept;
-    Returns: frames_.max_size().
-  */
-  std::cout << st << std::endl;
-  assert(st.size() >= 4);
-  assert(st.max_size() == (std::vector<std::stacktrace_entry, std::allocator<std::stacktrace_entry>>().max_size()));
-
-  /*
-    const_reference operator[](size_type frame_no) const;
-    Preconditions: frame_no < size() is true.
-    Returns: frames_[frame_no].
-    Throws: Nothing.
-  */
-  assert(st[0]);
-  assert(st[1]);
-  assert(st[2]);
-  assert(st[3]);
-
-  /*
-    const_reference at(size_type frame_no) const;
-    Returns: frames_[frame_no].
-    Throws: out_of_range if frame_no >= size().
-  */
-  assert(st.at(0));
-  assert(st.at(1));
-  assert(st.at(2));
-  assert(st.at(3));
-  try {
-    (void)st.at(42);
-    assert(false && "'at' should have thrown range_error");
-  } catch (std::out_of_range const&) {
-    // ok
-  }
-
-  auto f0 = st[0];
-  auto f1 = st[1];
-  auto f2 = st[2];
-  auto f3 = st[3];
-
-  auto fit = st.begin();
-  assert(*fit++ == f0);
-  assert(fit != st.end());
-
-  auto rit = st.rbegin();
-  assert(rit != st.rend());
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
deleted file mode 100644
index 963926b041f03..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-#include <stacktrace>
-
-#include <cassert>
-
-/*
-  (19.6.3.5) Comparison [stacktrace.entry.cmp]
-
-namespace std {
-  class stacktrace_entry {
-  public:
-    // [stacktrace.entry.cmp], comparison
-    friend constexpr bool operator==(const stacktrace_entry& x,
-                                     const stacktrace_entry& y) noexcept;           // [T11]
-    friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
-                                                 const stacktrace_entry& y) noexcept;  // [T12]
-
-    [. . .]
-  };
-}
-*/
-
-int main(int, char**) {
-  // Two empty entries
-
-  std::stacktrace_entry a;
-  assert(a.native_handle() == 0);
-  assert(!a);
-
-  std::stacktrace_entry b;
-  assert(b.native_handle() == 0);
-  assert(!b);
-
-  // A non-empty entry.
-  std::stacktrace_entry c;
-  ((std::__stacktrace::entry*)(&c))->__addr_ = (uintptr_t)&main;
-  assert(c);
-
-  // [T11]
-  // friend constexpr bool operator==(const stacktrace_entry& x,
-  //                                  const stacktrace_entry& y) noexcept;
-  // "Returns: true if and only if x and y represent the same
-  // stacktrace entry or both x and y are empty."
-  // (We represent "empty" with a native_handle of zero.)
-  assert(a == b);
-  assert(a != c);
-
-  // [T12]
-  // friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
-  //                                              const stacktrace_entry& y) noexcept;
-  assert(std::strong_ordering::equal == (a <=> b));
-  assert(std::strong_ordering::equivalent == (a <=> b));
-  assert(std::strong_ordering::greater == (c <=> a));
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
deleted file mode 100644
index 20f8a24d41b89..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-// ADDITIONAL_COMPILE_FLAGS: -g
-
-#include <cassert>
-#include <stacktrace>
-#include <type_traits>
-
-/*
-  (19.6.3.2) Constructors [stacktrace.entry.cons]
-
-namespace std {
-  class stacktrace_entry {
-  public:
-    // [stacktrace.entry.cons], constructors
-    constexpr stacktrace_entry() noexcept;                                          // [T2]
-    constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;             // [T3]
-    constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;  // [T4]
-    ~stacktrace_entry();                                                            // [T5]
-
-    [. . .]
-  };
-}
-*/
-
-_LIBCPP_NO_TAIL_CALLS
-int main(int, char**) {
-  // [T2]
-  // constexpr stacktrace_entry() noexcept;
-  // "Postconditions: *this is empty."
-  static_assert(std::is_default_constructible_v<std::stacktrace_entry>);
-  static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
-  std::stacktrace_entry entry_t2;
-  assert(!entry_t2);
-
-  // [T3]
-  // constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
-  static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
-  std::stacktrace_entry entry_t3(entry_t2);
-
-  // [T4]
-  // constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
-  static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
-  std::stacktrace_entry entry_t4;
-  entry_t4 = entry_t2;
-
-  // [T5]
-  // ~stacktrace_entry();
-  std::stacktrace_entry* entry_ptr{nullptr};
-  delete entry_ptr;
-  {
-    auto entry_t5(entry_t4); /* construct and immediately let it go out of scope */
-  }
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
deleted file mode 100644
index 229619f9c24a1..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-#include <stacktrace>
-
-#include <cassert>
-
-/*
-  (19.6.3.3) Observers [stacktrace.entry.obs]
-
-namespace std {
-  class stacktrace_entry {
-    // [stacktrace.entry.obs], observers
-    constexpr native_handle_type native_handle() const noexcept;                    // [T6]
-    constexpr explicit operator bool() const noexcept;                              // [T7]
-    
-    [. . .]
-  };
-}
-*/
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {}
-_LIBCPP_END_NAMESPACE_STD
-
-int main(int, char**) {
-  // [T6]
-  std::stacktrace_entry entry_t6;
-
-  // constexpr native_handle_type native_handle() const noexcept;
-  assert(entry_t6.native_handle() == 0);
-
-  // [T7]
-  // constexpr explicit operator bool() const noexcept;
-  // "Returns: false if and only if *this is empty."
-  assert(!entry_t6);
-
-  // Now set addr to something nonzero
-  ((std::__stacktrace::entry*)(&entry_t6))->__addr_ = (uintptr_t)&main;
-  assert(entry_t6.native_handle() == (uintptr_t)&main);
-  assert(entry_t6);
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
deleted file mode 100644
index b8fda6dab0fe3..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-// ADDITIONAL_COMPILE_FLAGS: -O0 -g
-
-#include <stacktrace>
-
-#include <cassert>
-#include <iostream>
-#include <string>
-
-/*
-    (19.6.3.4) Query [stacktrace.entry.query]
-
-namespace std {
-  class stacktrace_entry {
-  public:
-    // [stacktrace.entry.query], query
-    string description() const;                                                     // [T8]
-    string source_file() const;                                                     // [T9]
-    uint_least32_t source_line() const;                                             // [T10]
-
-    [. . .]
-  };
-}
-*/
-
-int main(int, char**) {
-  // empty trace entry
-  std::stacktrace_entry e;
-
-  // [T8]
-  // string description() const;
-  auto desc = e.description();
-  assert(desc.c_str()[0] == 0);
-  assert(desc.size() == 0);
-
-  // [T9]
-  // string source_file() const;
-  auto src = e.source_file();
-  assert(src.c_str()[0] == 0);
-  assert(src.size() == 0);
-
-  // [T10]
-  // uint_least32_t source_line() const;
-  assert(e.source_line() == 0);
-
-  // Get the current trace.
-  uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
-  auto trace           = std::stacktrace::current();
-
-  // First entry of this should be `main`.
-  e = trace.at(0);
-  assert(e);
-
-  std::cout << "main is at:      " << (void*)&main << std::endl;
-  std::cout << "e.native_handle: " << (void*)e.native_handle() << std::endl;
-  std::cout << "e.description:   " << e.description() << std::endl;
-  std::cout << "e.source_file:   " << e.source_file() << std::endl;
-  std::cout << "e.source_line:   " << e.source_line() << std::endl;
-
-  std::cout << trace << std::endl;
-
-  assert(e.native_handle());
-  assert(e.native_handle() >= (uintptr_t)&main);
-  assert(e.description() == "main" || e.description() == "_main");
-  // Even if we cannot get the debug info or call out to llvm-addr2line,
-  // we should at least get the executable filename, e.g. `entry.query.pass.cpp.dir/t.exe`
-  assert(!e.source_file().empty());
-  assert(e.source_file().contains("entry.query.pass.cpp"));
-
-  // These might not work at testing time, so disable.
-  // Complete tests checking the filename:line part are in `std/diagnostics/stacktrace`
-  //   assert(e.source_file().ends_with("entry.query.pass.cpp"));
-  //   assert(e.source_line() == line_number);
-  (void)line_number;
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
deleted file mode 100644
index 3db4920b3c388..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-#include <cassert>
-#include <stacktrace>
-
-/*
-  (19.6.5) Formatting support [stacktrace.format]
-
-  template<> struct formatter<stacktrace_entry>;                            [1]
-  template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;  [2]
-*/
-
-int main(int, char**) {
-  /*
-    [1]
-    template<> struct formatter<stacktrace_entry>;
-
-    formatter<stacktrace_entry> interprets format-spec as a stacktrace-entry-format-spec.
-    The syntax of format specifications is as follows:
-
-      stacktrace-entry-format-spec :
-        fill-and-align_[opt] width_[opt]
-
-    [Note 1: The productions fill-and-align and width are described in [format.string.std]. — end note]
-
-    A stacktrace_entry object se is formatted as if by copying to_string(se) through the output iterator
-    of the context with additional padding and adjustments as specified by the format specifiers.
-  */
-
-  // TODO(stacktrace23): needs `formatter`
-
-  /*
-    [2]
-    template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
-    
-    For formatter<basic_stacktrace<Allocator>>, format-spec is empty.
-    
-    A basic_stacktrace<Allocator> object s is formatted as if by copying to_string(s) through the
-    output iterator of the context.
-  */
-
-  // TODO(stacktrace23): needs `formatter`
-
-  return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
deleted file mode 100644
index f6c44bf8fe44f..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-
-/*
-
-// (19.6.2) Header <stacktrace> synopsis [stacktrace.syn]
-
-namespace std {
-
-  // [stacktrace.entry], class stacktrace_entry
-  class stacktrace_entry;                                               [1]
-
-  // [stacktrace.basic], class template basic_stacktrace
-  template<class Allocator>
-    class basic_stacktrace;                                             [2]
-
-  // basic_stacktrace typedef-names
-  using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;     [3]
-
-  // [stacktrace.basic.nonmem], non-member functions
-  template<class Allocator>
-    void swap(basic_stacktrace<Allocator>& a,
-              basic_stacktrace<Allocator>& b)
-                noexcept(noexcept(a.swap(b)));                          [4]
-
-  string to_string(const stacktrace_entry& f);                          [5]
-
-  template<class Allocator>
-    string to_string(const basic_stacktrace<Allocator>& st);            [6]
-
-  ostream& operator<<(ostream& os, const stacktrace_entry& f);          [7]
-  template<class Allocator>
-    ostream& operator<<(ostream& os,
-                        const basic_stacktrace<Allocator>& st);         [8]
-
-  // [stacktrace.format], formatting support
-  template<> struct formatter<stacktrace_entry>;                        [9]
-  template<class Allocator>
-    struct formatter<basic_stacktrace<Allocator>>;                      [10]
-
-  namespace pmr {
-    using stacktrace =
-        basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;      [11]
-  }
-
-  // [stacktrace.basic.hash], hash support
-  template<> struct hash<stacktrace_entry>;                             [12]
-  template<class Allocator> struct hash<basic_stacktrace<Allocator>>;   [13]
-}
-*/
-
-#include <__config>
-#include <cassert>
-#include <memory>
-#include <stacktrace>
-#include <type_traits>
-
-static_assert(__cpp_lib_stacktrace == 202011L);
-
-int main(int, char**) {
-  // Very basic tests to ensure the required things are declared.
-  // Only checking for types' and functions' existence, parameter and return types,
-  // and type-completeness.  Functional tests exist in the other .cpp's in this directory.
-
-  using Alloc = std::allocator<std::stacktrace_entry>;
-
-  // [1]
-  // [stacktrace.entry], class stacktrace_entry
-  // class stacktrace_entry;
-  using T1 = std::stacktrace_entry;
-  assert(std::is_constructible_v<T1>);
-
-  // [2]
-  // [stacktrace.basic], class template basic_stacktrace
-  // template<class Allocator>
-  //   class basic_stacktrace;
-  using T2 = std::basic_stacktrace<Alloc>;
-  assert(std::is_constructible_v<T2>);
-
-  // [3]
-  // basic_stacktrace typedef-names
-  // using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
-  using T3 = std::stacktrace;
-  static_assert(std::is_same_v<T3, std::basic_stacktrace<std::allocator<std::stacktrace_entry>>>);
-
-  // [4]
-  // [stacktrace.basic.nonmem], non-member functions
-  // template<class Allocator>
-  //   void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
-  //     noexcept(noexcept(a.swap(b)));
-  std::basic_stacktrace<Alloc> a;
-  std::basic_stacktrace<Alloc> b;
-  std::swap(a, b);
-
-  // [5]
-  // string to_string(const stacktrace_entry& f);
-  using T5 = decltype(std::to_string(std::stacktrace_entry()));
-  static_assert(std::is_same_v<std::string, T5>);
-
-  // [6]
-  // template<class Allocator>
-  // string to_string(const basic_stacktrace<Allocator>& st);
-  using T6 = decltype(std::to_string(std::basic_stacktrace<Alloc>()));
-  static_assert(std::is_same_v<std::string, T6>);
-
-  // [7]
-  // ostream& operator<<(ostream& os, const stacktrace_entry& f);
-  std::ostream* os;
-  using T7 = decltype(operator<<(*os, std::stacktrace_entry()));
-  static_assert(std::is_same_v<std::ostream&, T7>);
-
-  // [8]
-  // template<class Allocator>
-  //   ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
-  using T8 = decltype(operator<<(*os, std::basic_stacktrace<Alloc>()));
-  static_assert(std::is_same_v<std::ostream&, T8>);
-
-  // [9]
-  // template<> struct formatter<stacktrace_entry>;
-  //   using T9 = std::formatter<std::stacktrace_entry>;
-  //   static_assert(std::is_constructible_v<T9>);
-
-  // TODO(stacktrace23): needs `formatter`
-
-  // [10]
-  // template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
-  //   using T10 = std::formatter<std::basic_stacktrace<Alloc>>;
-  //   static_assert(std::is_constructible_v<T10>);
-
-  // TODO(stacktrace23): needs `formatter`
-
-  // [11]
-  // namespace pmr { using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>; }
-  using AllocPMR = std::pmr::polymorphic_allocator<std::stacktrace_entry>;
-  using BasicPMR = std::basic_stacktrace<AllocPMR>;
-  using T11      = std::pmr::stacktrace;
-  static_assert(std::is_same_v<T11, BasicPMR>);
-
-  return 0;
-}

>From de5607fe8dc2198d52227900953bd0d34dd82e25 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 24 Apr 2025 10:28:10 -0400
Subject: [PATCH 30/37] Remove changes not related to this (probably a previous
 bad merge)

---
 .../test/libcxx/transitive_includes/cxx03.csv   |  5 -----
 .../test/libcxx/transitive_includes/cxx11.csv   |  5 -----
 .../test/libcxx/transitive_includes/cxx14.csv   |  5 -----
 .../test/libcxx/transitive_includes/cxx17.csv   |  5 -----
 .../gn/build/sync_source_lists_from_cmake.py    | 12 +++++++++---
 llvm/utils/gn/build/toolchain/BUILD.gn          | 17 -----------------
 6 files changed, 9 insertions(+), 40 deletions(-)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 920a6145a02b8..c0031543e47bc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2036,7 +2032,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index cfcf7bc7c4c19..c2eb5b44e8d7a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,19 +125,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2076,7 +2072,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 534776e6f0526..332cb62f35b5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,19 +122,15 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
-barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
-barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
-barrier cwchar
-barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -2089,7 +2085,6 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
-stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/llvm/utils/gn/build/sync_source_lists_from_cmake.py b/llvm/utils/gn/build/sync_source_lists_from_cmake.py
index aada38bebd421..4cd30aa410864 100755
--- a/llvm/utils/gn/build/sync_source_lists_from_cmake.py
+++ b/llvm/utils/gn/build/sync_source_lists_from_cmake.py
@@ -61,13 +61,13 @@ def git_out(args):
     gn_files = git_out(["ls-files", "*BUILD.gn"]).splitlines()
 
     # Matches e.g. |   "foo.cpp",|, captures |foo| in group 1.
-    gn_cpp_re = re.compile(r'^\s*"([^$"]+\.(?:cpp|c|h|S))",$', re.MULTILINE)
+    gn_cpp_re = re.compile(r'^\s*"([^$"]+\.(?:cpp|c|h|mm|S))",$', re.MULTILINE)
     # Matches e.g. |   bar_sources = [ "foo.cpp" ]|, captures |foo| in group 1.
     gn_cpp_re2 = re.compile(
-        r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|S))" ]$', re.MULTILINE
+        r'^\s*(?:.*_)?sources \+?= \[ "([^$"]+\.(?:cpp|c|h|mm|S))" ]$', re.MULTILINE
     )
     # Matches e.g. |   foo.cpp|, captures |foo| in group 1.
-    cmake_cpp_re = re.compile(r"^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|S))$", re.MULTILINE)
+    cmake_cpp_re = re.compile(r"^\s*([A-Za-z_0-9./-]+\.(?:cpp|c|h|mm|S))$", re.MULTILINE)
 
     changes_by_rev = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
 
@@ -108,6 +108,12 @@ def get_sources(source_re, text):
 
         gn_cpp = get_sources(gn_cpp_re, open(gn_file).read())
         gn_cpp |= get_sources(gn_cpp_re2, open(gn_file).read())
+
+        sources_file = os.path.join(os.path.dirname(gn_file), "sources.gni")
+        if os.path.exists(sources_file):
+            gn_cpp |= get_sources(gn_cpp_re, open(sources_file).read())
+            gn_cpp |= get_sources(gn_cpp_re2, open(sources_file).read())
+
         cmake_cpp = get_sources(cmake_cpp_re, open(cmake_file).read())
 
         if gn_cpp == cmake_cpp:
diff --git a/llvm/utils/gn/build/toolchain/BUILD.gn b/llvm/utils/gn/build/toolchain/BUILD.gn
index ce2e6df1b69f5..00d9df12f5a97 100644
--- a/llvm/utils/gn/build/toolchain/BUILD.gn
+++ b/llvm/utils/gn/build/toolchain/BUILD.gn
@@ -71,23 +71,6 @@ template("unix_toolchain") {
       default_output_dir = "{{root_out_dir}}/lib"
     }
 
-    if (current_os == "ios" || current_os == "mac") {
-      # gn < 1693 (e214b5d35898) doesn't support |frameworks|, requiring
-      # frameworks to be listed in |libs|, but gn >= 1808 (3028c6a426a4) forbids
-      # frameworks from appearing in |libs|. This assertion provides a helpful
-      # cue to upgrade, and is much more user-friendly than the failure that
-      # occurs when an older gn encounters |frameworks|.
-      #
-      # gn_version doesn’t actually exist in gn < 1709 (52cb644a3fb4), and
-      # defined(gn_version) doesn't actually work as expected
-      # (https://crbug.com/gn/183), so 1709 is the true minimum enforced by
-      # this construct, and if gn_version is not available, this line will still
-      # be blamed, making the resolution somewhat discoverable.
-      assert(gn_version >= 1693,
-             "Your GN is too old! " +
-                 "Update it, perhaps by running llvm/utils/gn/get.py")
-    }
-
     # Make these apply to all tools below.
     lib_switch = "-l"
     lib_dir_switch = "-L"

>From 8465ee61d3dfbf2b742919e909e9072876bf18ce Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 14 May 2025 10:05:23 -0400
Subject: [PATCH 31/37] Mention URL 'format' task (105257) for TODO's

---
 libcxx/include/__stacktrace/basic_stacktrace.h                  | 2 +-
 libcxx/include/__stacktrace/stacktrace_entry.h                  | 2 +-
 libcxx/src/stacktrace/stacktrace.cpp                            | 2 +-
 .../stacktrace/format/formatter_basic_stacktrace.pass.cpp       | 2 +-
 .../stacktrace/format/formatter_stacktrace_entry.cpp            | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index d90b53247ee27..d4753feeed1b1 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -275,7 +275,7 @@ _LIBCPP_EXPORTED_FROM_ABI inline ostream& operator<<(ostream& __os, const basic_
 // (19.6.5)
 // Formatting support [stacktrace.format]
 
-// TODO(stacktrace23): needs `formatter`
+// TODO: stacktrace formatter: https://github.com/llvm/llvm-project/issues/105257
 template <class _Allocator>
 struct _LIBCPP_EXPORTED_FROM_ABI formatter<basic_stacktrace<_Allocator>>;
 
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index 9099b1f849148..db85427a8bd0c 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -99,7 +99,7 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_en
 // (19.6.5)
 // Formatting support [stacktrace.format]
 
-// TODO(stacktrace23): needs `formatter`
+// TODO: stacktrace formatter: https://github.com/llvm/llvm-project/issues/105257
 template <>
 struct _LIBCPP_EXPORTED_FROM_ABI formatter<stacktrace_entry>;
 
diff --git a/libcxx/src/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
index 98acec1e92d0b..2760a14282ecb 100644
--- a/libcxx/src/stacktrace/stacktrace.cpp
+++ b/libcxx/src/stacktrace/stacktrace.cpp
@@ -146,7 +146,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip,
   gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
   symbolizer.  If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
 
-  All these classes do their best to provide any of the requested fields they can: (symbol, filename, source line),
+  All these classes do their best to provide any of the requested fields they can: symbol, filename, source line,
   substituting if needed with something reasonable.  For example, if the source filename and line are not available
   then we will at least report that the address and symbol are in the module `foo.exe`.
 
diff --git a/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp
index 97aa20d562cf6..bda374e0b2d90 100644
--- a/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp
@@ -25,7 +25,7 @@ int main(int, char**) {
     output iterator of the context.
   */
 
-  // TODO(stacktrace23): needs `formatter`
+  // TODO: stacktrace formatter: https://github.com/llvm/llvm-project/issues/105257
 
   return 0;
 }
diff --git a/libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.cpp b/libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.cpp
index 73a9c43ec52ab..bfa982b65658a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.cpp
@@ -31,7 +31,7 @@ int main(int, char**) {
     of the context with additional padding and adjustments as specified by the format specifiers.
   */
 
-  // TODO(stacktrace23): needs `formatter`
+  // TODO: stacktrace formatter: https://github.com/llvm/llvm-project/issues/105257
 
   return 0;
 }

>From 21a410f74274344446a6c033c5c34ebb1a335427 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 14 May 2025 12:34:25 -0400
Subject: [PATCH 32/37] commit new generated files

---
 .../support.limits.general/stacktrace.version.compile.pass.cpp   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 8d8ce5665565b..461087467a270 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,3 +105,4 @@
 #endif // TEST_STD_VER > 23
 
 // clang-format on
+

>From 442b2a2e3698d88f0866a0a69885c2f042f713e1 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 14 May 2025 12:40:24 -0400
Subject: [PATCH 33/37] formatting

---
 .../support.limits.general/stacktrace.version.compile.pass.cpp   | 1 -
 .../support.limits.general/version.version.compile.pass.cpp      | 1 -
 2 files changed, 2 deletions(-)

diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 461087467a270..8d8ce5665565b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,4 +105,3 @@
 #endif // TEST_STD_VER > 23
 
 // clang-format on
-
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 881764515b9cf..9350861528b4e 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -8007,4 +8007,3 @@
 #endif // TEST_STD_VER > 23
 
 // clang-format on
-

>From c3dc79ff88914a4b193e0956161ec91dbf07a5b3 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Fri, 16 May 2025 15:49:25 -0400
Subject: [PATCH 34/37] refactor; rework alloc/dealloc, strings; don't use pmr

---
 libcxx/include/CMakeLists.txt                 |  10 +-
 libcxx/include/__stacktrace/base.h            | 131 ++++++++
 .../{basic_stacktrace.h => basic.h}           |  83 +----
 .../{stacktrace_entry.h => entry.h}           |  60 ++--
 libcxx/include/__stacktrace/format.h          |  41 +++
 libcxx/include/__stacktrace/hash.h            |  50 +++
 libcxx/include/__stacktrace/impl.h            | 167 ----------
 libcxx/include/__stacktrace/nonmem.h          |  55 +++
 libcxx/include/__stacktrace/to_string.h       |  53 +++
 libcxx/include/module.modulemap.in            |  14 +-
 libcxx/include/stacktrace                     |  13 +-
 libcxx/src/CMakeLists.txt                     |   3 +-
 libcxx/src/stacktrace/README.md               |   1 +
 libcxx/src/stacktrace/base.cpp                |  96 ++++++
 libcxx/src/stacktrace/context.h               |  54 ---
 libcxx/src/stacktrace/linux.cpp               |  38 ++-
 libcxx/src/stacktrace/linux.h                 |   8 +-
 libcxx/src/stacktrace/macos.cpp               |  42 ++-
 libcxx/src/stacktrace/macos.h                 |   6 +-
 libcxx/src/stacktrace/stacktrace.cpp          | 206 ------------
 libcxx/src/stacktrace/to_string.cpp           |  93 ++++++
 libcxx/src/stacktrace/tools.cpp               | 314 ++++++++----------
 libcxx/src/stacktrace/tools.h                 |  72 ++--
 libcxx/src/stacktrace/unwind.cpp              |  15 +-
 libcxx/src/stacktrace/unwind.h                |  10 +-
 libcxx/src/stacktrace/utils.h                 |   1 +
 libcxx/src/stacktrace/windows.cpp             |  25 +-
 libcxx/src/stacktrace/windows.h               |   8 +-
 .../stacktrace.version.compile.pass.cpp       |   1 +
 .../version.version.compile.pass.cpp          |   1 +
 30 files changed, 830 insertions(+), 841 deletions(-)
 create mode 100644 libcxx/include/__stacktrace/base.h
 rename libcxx/include/__stacktrace/{basic_stacktrace.h => basic.h} (78%)
 rename libcxx/include/__stacktrace/{stacktrace_entry.h => entry.h} (77%)
 create mode 100644 libcxx/include/__stacktrace/format.h
 create mode 100644 libcxx/include/__stacktrace/hash.h
 delete mode 100644 libcxx/include/__stacktrace/impl.h
 create mode 100644 libcxx/include/__stacktrace/nonmem.h
 create mode 100644 libcxx/include/__stacktrace/to_string.h
 create mode 100644 libcxx/src/stacktrace/base.cpp
 delete mode 100644 libcxx/src/stacktrace/context.h
 delete mode 100644 libcxx/src/stacktrace/stacktrace.cpp
 create mode 100644 libcxx/src/stacktrace/to_string.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 34895f334733c..656cfc5518a13 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -727,9 +727,13 @@ set(files
   __ranges/views.h
   __ranges/zip_view.h
   __split_buffer
-  __stacktrace/basic_stacktrace.h
-  __stacktrace/impl.h
-  __stacktrace/stacktrace_entry.h
+  __stacktrace/base.h
+  __stacktrace/basic.h
+  __stacktrace/entry.h
+  __stacktrace/format.h
+  __stacktrace/hash.h
+  __stacktrace/nonmem.h
+  __stacktrace/to_string.h
   __std_mbstate_t.h
   __stop_token/atomic_unique_lock.h
   __stop_token/intrusive_list_view.h
diff --git a/libcxx/include/__stacktrace/base.h b/libcxx/include/__stacktrace/base.h
new file mode 100644
index 0000000000000..04839b68fd5e8
--- /dev/null
+++ b/libcxx/include/__stacktrace/base.h
@@ -0,0 +1,131 @@
+// -*- 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_STACKTRACE_BUILDER
+#define _LIBCPP_STACKTRACE_BUILDER
+
+#include <__config>
+#include <__cstddef/byte.h>
+#include <__cstddef/size_t.h>
+#include <__functional/function.h>
+#include <__fwd/format.h>
+#include <__fwd/ostream.h>
+#include <__memory/allocator_traits.h>
+#include <__vector/vector.h>
+#include <cstddef>
+#include <cstdint>
+#include <list>
+#include <optional>
+#include <string>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class stacktrace_entry;
+
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI alloc final {
+  function<byte*(size_t)> __alloc_bytes_;
+  function<void(byte*, size_t)> __dealloc_bytes_;
+
+  template <class _Allocator>
+  alloc(_Allocator __alloc) {
+    using _AT        = allocator_traits<_Allocator>;
+    using _BA        = typename _AT::template rebind_alloc<byte>;
+    auto __ba        = _BA(__alloc);
+    __alloc_bytes_   = [__ba](size_t __sz) mutable { return __ba.allocate(__sz); };
+    __dealloc_bytes_ = [__ba](void* __ptr, size_t __sz) mutable { __ba.deallocate((byte*)__ptr, __sz); };
+  }
+
+  template <typename _Tp>
+  struct Alloc {
+    function<byte*(size_t)> __alloc_bytes_;
+    function<void(byte*, size_t)> __dealloc_bytes_;
+
+    Alloc(function<byte*(size_t)> __alloc_bytes, function<void(byte*, size_t)> __dealloc_bytes)
+        : __alloc_bytes_(__alloc_bytes), __dealloc_bytes_(__dealloc_bytes) {}
+
+    template <typename _T2 = _Tp>
+    Alloc(Alloc<_T2> const& __rhs) : Alloc(__rhs.__alloc_bytes_, __rhs.__dealloc_bytes_) {}
+
+    using value_type = _Tp;
+    [[nodiscard]] _Tp* allocate(size_t __sz) { return (_Tp*)__alloc_bytes_(__sz * sizeof(_Tp)); }
+    void deallocate(_Tp* __ptr, size_t __sz) { __dealloc_bytes_((byte*)__ptr, __sz * sizeof(_Tp)); }
+
+    template <typename _A2>
+    bool operator==(_A2 const& __rhs) const {
+      return &__rhs == this;
+    }
+  };
+
+  template <typename _Tp>
+  Alloc<_Tp> make_alloc() {
+    return {__alloc_bytes_, __dealloc_bytes_};
+  }
+
+  using str = basic_string<char, char_traits<char>, Alloc<char>>;
+
+  template <typename... _Args>
+  str make_str(_Args... __args) {
+    return str(std::forward<_Args>(__args)..., make_alloc<char>());
+  }
+
+  template <typename _Tp>
+  using vec = vector<_Tp, Alloc<_Tp>>;
+
+  template <typename _Tp, typename... _Args>
+  vec<_Tp> make_vec(_Args... __args) {
+    return vec(std::forward<_Args>(__args)..., make_alloc<_Tp>());
+  }
+
+  template <typename _Tp>
+  using list = ::std::list<_Tp, Alloc<_Tp>>;
+
+  template <typename _Tp, typename... _Args>
+  list<_Tp> make_list(_Args... __args) {
+    return list(std::forward<_Args>(__args)..., make_alloc<_Tp>());
+  }
+};
+
+struct _LIBCPP_HIDE_FROM_ABI entry_base {
+  uintptr_t __addr_actual_{};                   // this address, as observed in this current process
+  uintptr_t __addr_unslid_{};                   // address adjusted for ASLR
+  optional<__stacktrace::alloc::str> __desc_{}; // uses wrapped _Allocator from caller
+  optional<__stacktrace::alloc::str> __file_{}; // uses wrapped _Allocator from caller
+  uint_least32_t __line_{};
+
+  stacktrace_entry to_stacktrace_entry() const;
+};
+
+struct _LIBCPP_HIDE_FROM_ABI builder final {
+  alloc __alloc_; // wraps the caller-provided allocator
+  alloc::vec<entry_base> __entries_;
+  alloc::str __main_prog_path_;
+
+  template <class _Allocator>
+  explicit builder(_Allocator __alloc)
+      : __alloc_(__alloc), __entries_(__alloc_.make_vec<entry_base>()), __main_prog_path_(__alloc_.make_str()) {}
+
+  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+  build_stacktrace(size_t __skip, size_t __max_depth);
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_BUILDER
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic.h
similarity index 78%
rename from libcxx/include/__stacktrace/basic_stacktrace.h
rename to libcxx/include/__stacktrace/basic.h
index d4753feeed1b1..6b4ac3e181f4f 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic.h
@@ -10,30 +10,19 @@
 #ifndef _LIBCPP_BASIC_STACKTRACE
 #define _LIBCPP_BASIC_STACKTRACE
 
-#include <__stacktrace/impl.h>
-#include <__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 <__stacktrace/base.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/to_string.h>
+#include <__type_traits/is_nothrow_constructible.h>
 #include <__vector/vector.h>
-#include <cstddef>
-#include <memory>
+#include <utility>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -63,12 +52,10 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
   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);
+  using __entry_vec = vector<stacktrace_entry, _Allocator>;
+  __entry_vec __entries_;
 
 public:
   // (19.6.4.1)
@@ -80,7 +67,7 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
   using difference_type = ptrdiff_t;
   using size_type       = size_t;
   using allocator_type  = _Allocator;
-  using const_iterator  = decltype(__entries_)::const_iterator;
+  using const_iterator  = __entry_vec::const_iterator;
   using iterator        = const_iterator;
 
   using reverse_iterator       = std::reverse_iterator<basic_stacktrace::iterator>;
@@ -103,13 +90,13 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
   current(size_type __skip,
           size_type __max_depth,
           const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+    __stacktrace::builder __builder(__caller_alloc);
+    __builder.build_stacktrace(__skip + 1, __max_depth);
     basic_stacktrace<_Allocator> __ret{__caller_alloc};
-    __stacktrace::alloc __alloc(__caller_alloc);
-    auto __resize = [&__ret](size_t __sz) { __ret.__entries_.resize(__sz); };
-    auto __assign = [&__ret](size_t __index, stacktrace_entry&& __entry) {
-      __ret.__entries_.at(__index) = std::move(__entry);
-    };
-    __stacktrace::__impl(__skip + 1, __max_depth, __alloc, __resize, __assign);
+    __ret.__entries_.reserve(__builder.__entries_.size());
+    for (auto& __base : __builder.__entries_) {
+      __ret.__entries_.emplace_back(__base.to_stacktrace_entry());
+    }
     return __ret;
   }
 
@@ -252,48 +239,6 @@ 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: stacktrace formatter: https://github.com/llvm/llvm-project/issues/105257
-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
 
 _LIBCPP_POP_MACROS
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/entry.h
similarity index 77%
rename from libcxx/include/__stacktrace/stacktrace_entry.h
rename to libcxx/include/__stacktrace/entry.h
index db85427a8bd0c..1e2d1d667130b 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/entry.h
@@ -10,29 +10,16 @@
 #ifndef _LIBCPP_STACKTRACE_ENTRY
 #define _LIBCPP_STACKTRACE_ENTRY
 
-#include <__cstddef/byte.h>
-#include <__cstddef/ptrdiff_t.h>
-#include <__cstddef/size_t.h>
-#include <__format/formatter.h>
-#include <__functional/function.h>
-#include <__functional/hash.h>
+#include <__config>
 #include <__fwd/format.h>
 #include <__fwd/ostream.h>
-#include <__fwd/sstream.h>
-#include <__fwd/vector.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 <__utility/move.h>
-#include <__vector/pmr.h>
-#include <__vector/swap.h>
-#include <__vector/vector.h>
+#include <cstddef>
 #include <cstdint>
+#include <optional>
 #include <string>
 
+#include <__stacktrace/base.h>
+
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -42,11 +29,10 @@ _LIBCPP_PUSH_MACROS
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-namespace __stacktrace {
-struct entry;
-} // namespace __stacktrace
+class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry : private __stacktrace::entry_base {
+  friend struct __stacktrace::entry_base;
+  stacktrace_entry(entry_base const& __base) : entry_base(__base) {}
 
-class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
 public:
   // (19.6.3.1) Overview [stacktrace.entry.overview]
   using native_handle_type = uintptr_t;
@@ -59,15 +45,25 @@ class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
 
   // (19.6.3.3) [stacktrace.entry.obs], observers
   [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr native_handle_type native_handle() const noexcept {
-    return __addr_;
+    return __addr_actual_;
   }
   [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr explicit operator bool() const noexcept {
     return native_handle() != 0;
   }
 
   // (19.6.3.4) [stacktrace.entry.query], query
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string description() const { return __desc_; }
-  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string source_file() const { return __file_; }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string description() const {
+    if (__desc_->empty()) {
+      return "";
+    }
+    return {__desc_->data(), __desc_->size()};
+  }
+  [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string source_file() const {
+    if (__desc_->empty()) {
+      return "";
+    }
+    return {__file_->data(), __file_->size()};
+  }
   [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI uint_least32_t source_line() const { return __line_; }
 
   // (19.6.3.5) [stacktrace.entry.cmp], comparison
@@ -80,20 +76,12 @@ class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
   operator<=>(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
     return __x.native_handle() <=> __y.native_handle();
   }
-
-private:
-  friend struct __stacktrace::entry;
-
-  uintptr_t __addr_{};
-  std::string __desc_{};
-  std::string __file_{};
-  uint_least32_t __line_{};
 };
 
 // (19.6.4.6)
 // Non-member functions [stacktrace.basic.nonmem]
 
-_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry);
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry);
 _LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry);
 
 // (19.6.5)
@@ -114,6 +102,10 @@ struct _LIBCPP_EXPORTED_FROM_ABI hash<stacktrace_entry> {
   }
 };
 
+namespace __stacktrace {
+inline stacktrace_entry entry_base::to_stacktrace_entry() const { return {*this}; }
+} // namespace __stacktrace
+
 _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
diff --git a/libcxx/include/__stacktrace/format.h b/libcxx/include/__stacktrace/format.h
new file mode 100644
index 0000000000000..135b86281088f
--- /dev/null
+++ b/libcxx/include/__stacktrace/format.h
@@ -0,0 +1,41 @@
+// -*- 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_BASIC_STACKTRACE_FORMAT
+#define _LIBCPP_BASIC_STACKTRACE_FORMAT
+
+#include <__config>
+#include <__fwd/format.h>
+#include <__memory/allocator_traits.h>
+#include <__vector/vector.h>
+
+#include <__stacktrace/base.h>
+#include <__stacktrace/to_string.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// (19.6.5)
+// Formatting support [stacktrace.format]
+
+// TODO: stacktrace formatter: https://github.com/llvm/llvm-project/issues/105257
+template <class _Allocator>
+struct _LIBCPP_EXPORTED_FROM_ABI formatter<basic_stacktrace<_Allocator>>;
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_BASIC_STACKTRACE_FORMAT
diff --git a/libcxx/include/__stacktrace/hash.h b/libcxx/include/__stacktrace/hash.h
new file mode 100644
index 0000000000000..817f4ff35d041
--- /dev/null
+++ b/libcxx/include/__stacktrace/hash.h
@@ -0,0 +1,50 @@
+// -*- 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_BASIC_STACKTRACE_HASH
+#define _LIBCPP_BASIC_STACKTRACE_HASH
+
+#include <__config>
+#include <__functional/hash.h>
+#include <__fwd/format.h>
+#include <__memory/allocator_traits.h>
+#include <__vector/vector.h>
+#include <cstdint>
+
+#include <__stacktrace/base.h>
+#include <__stacktrace/to_string.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// (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());
+    }
+    return __ret;
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_BASIC_STACKTRACE_HASH
diff --git a/libcxx/include/__stacktrace/impl.h b/libcxx/include/__stacktrace/impl.h
deleted file mode 100644
index a131d0940d2ef..0000000000000
--- a/libcxx/include/__stacktrace/impl.h
+++ /dev/null
@@ -1,167 +0,0 @@
-// -*- 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_STACKTRACE_IMPL
-#define _LIBCPP_STACKTRACE_IMPL
-
-#include <__cstddef/byte.h>
-#include <__cstddef/ptrdiff_t.h>
-#include <__cstddef/size_t.h>
-#include <__format/formatter.h>
-#include <__functional/function.h>
-#include <__functional/hash.h>
-#include <__fwd/format.h>
-#include <__fwd/ostream.h>
-#include <__fwd/sstream.h>
-#include <__fwd/vector.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 <__utility/move.h>
-#include <__vector/pmr.h>
-#include <__vector/swap.h>
-#include <__vector/vector.h>
-#include <cstdint>
-#include <list>
-#include <string>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
-
-_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <class _Allocator>
-class basic_stacktrace;
-
-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 _LIBCPP_HIDE_FROM_ABI alloc final : std::pmr::memory_resource {
-  template <class _Allocator>
-  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_ptr_    = std::addressof(__a);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI ~alloc() override = default;
-
-  _LIBCPP_HIDE_FROM_ABI void* do_allocate(size_t __size, size_t /*__align*/) override { return __alloc_func_(__size); }
-
-  _LIBCPP_HIDE_FROM_ABI void do_deallocate(void* __ptr, size_t __size, size_t /*__align*/) override {
-    __dealloc_func_((std::byte*)__ptr, __size);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
-    auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
-    return __rhs_ba && (__rhs_ba->__alloc_ptr_ == __alloc_ptr_);
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
-    std::pmr::string __ret{this};
-    if (__size) {
-      __ret.reserve(__size);
-      __ret[0] = 0;
-    }
-    return __ret;
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::string hex_string(uintptr_t __addr) {
-    char __ret[19]; // "0x" + 16 digits + NUL
-    auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
-    return {__ret, size_t(__size), this};
-  }
-
-  _LIBCPP_HIDE_FROM_ABI std::pmr::string u64_string(uintptr_t __val) {
-    char __ret[21]; // 20 digits max + NUL
-    auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
-    return {__ret, size_t(__size), this};
-  }
-
-  template <typename _Tp>
-  _LIBCPP_HIDE_FROM_ABI std::pmr::list<_Tp> new_list() {
-    return std::pmr::list<_Tp>{this};
-  }
-
-  std::pmr::list<std::pmr::string> new_string_list() { return new_list<std::pmr::string>(); }
-
-private:
-  std::function<std::byte*(size_t)> __alloc_func_;
-  std::function<void(std::byte*, size_t)> __dealloc_func_;
-  /** Only used for checking equality */
-  void const* __alloc_ptr_;
-};
-
-/** Contains fields which will be used to generate the final `std::stacktrace_entry`.
-This is an intermediate object which owns strings allocated via the caller-provided allocator,
-which are later freed back to that allocator and converted to plain `std::string`s. */
-struct entry {
-  /** Caller's / faulting insn's address, including ASLR/slide */
-  uintptr_t __addr_{};
-
-  /** the address minus its image's slide offset */
-  uintptr_t __addr_unslid_{};
-
-  /** entry's description (symbol name) */
-  std::pmr::string __desc_{};
-
-  /** source file name */
-  std::pmr::string __file_{};
-
-  /** line number in source file */
-  uint32_t __line_{};
-
-  /* implicit */ operator std::stacktrace_entry();
-};
-
-struct __to_string {
-  _LIBCPP_EXPORTED_FROM_ABI string operator()(stacktrace_entry const& __entry);
-
-  _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
-
-  _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
-
-  _LIBCPP_EXPORTED_FROM_ABI string operator()(std::stacktrace_entry const* __entries, size_t __count);
-
-  template <class _Allocator>
-  _LIBCPP_EXPORTED_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
-    return (*this)(__st.__entries_.data(), __st.__entries_.size());
-  }
-};
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
-__impl(size_t __skip,
-       size_t __max_depth,
-       alloc& __alloc,
-       std::function<void(size_t)> __resize_func,
-       std::function<void(size_t, std::stacktrace_entry&&)> __assign_func);
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-_LIBCPP_POP_MACROS
-
-#endif // _LIBCPP_STACKTRACE_IMPL
diff --git a/libcxx/include/__stacktrace/nonmem.h b/libcxx/include/__stacktrace/nonmem.h
new file mode 100644
index 0000000000000..7199e0cd69092
--- /dev/null
+++ b/libcxx/include/__stacktrace/nonmem.h
@@ -0,0 +1,55 @@
+// -*- 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_BASIC_STACKTRACE_NONMEM
+#define _LIBCPP_BASIC_STACKTRACE_NONMEM
+
+#include <__config>
+#include <__fwd/format.h>
+#include <__memory/allocator_traits.h>
+#include <__vector/vector.h>
+#include <string>
+
+#include <__stacktrace/base.h>
+#include <__stacktrace/to_string.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// (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;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_BASIC_STACKTRACE_NONMEM
diff --git a/libcxx/include/__stacktrace/to_string.h b/libcxx/include/__stacktrace/to_string.h
new file mode 100644
index 0000000000000..920c41133d5df
--- /dev/null
+++ b/libcxx/include/__stacktrace/to_string.h
@@ -0,0 +1,53 @@
+// -*- 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_STACKTRACE_TO_STRING
+#define _LIBCPP_STACKTRACE_TO_STRING
+
+#include <__config>
+#include <__fwd/ostream.h>
+#include <string>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Allocator>
+class basic_stacktrace;
+
+class stacktrace_entry;
+
+namespace __stacktrace {
+
+struct __to_string {
+  _LIBCPP_EXPORTED_FROM_ABI string operator()(stacktrace_entry const& __entry);
+
+  _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
+
+  _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
+
+  _LIBCPP_EXPORTED_FROM_ABI string operator()(std::stacktrace_entry const* __entries, size_t __count);
+
+  template <class _Allocator>
+  _LIBCPP_EXPORTED_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
+    return (*this)(__st.__entries_.data(), __st.__entries_.size());
+  }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 9187e49031d70..8c2a43b27d85f 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1986,13 +1986,13 @@ module std [system] {
   }
 
   module stacktrace {
-    module fwd {
-      header "__stacktrace/basic_stacktrace.h"
-      header "__stacktrace/stacktrace_entry.h"
-    }
-    module impl {
-      header "__stacktrace/impl.h"
-    }
+    module base         { header "__stacktrace/base.h" }
+    module basic        { header "__stacktrace/basic.h" }
+    module entry        { header "__stacktrace/entry.h" }
+    module format       { header "__stacktrace/format.h" }
+    module hash         { header "__stacktrace/hash.h" }
+    module nonmem       { header "__stacktrace/nonmem.h" }
+    module to_string    { header "__stacktrace/to_string.h" }
 
     header "stacktrace"
     export *
diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace
index 3a680b76b55db..558c13dd081ec 100644
--- a/libcxx/include/stacktrace
+++ b/libcxx/include/stacktrace
@@ -168,7 +168,7 @@ namespace std {
 
 #if _LIBCPP_STD_VER >= 23
 
-#  include <compare> // [stacktrace.syn]
+#  include <compare> // according to [stacktrace.syn]
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header
@@ -177,10 +177,13 @@ namespace std {
 _LIBCPP_PUSH_MACROS
 #  include <__undef_macros>
 
-#  include <__stacktrace/basic_stacktrace.h>
-#  include <__stacktrace/stacktrace_entry.h>
-
-#  include <__stacktrace/impl.h>
+#  include <__stacktrace/base.h>
+#  include <__stacktrace/basic.h>
+#  include <__stacktrace/entry.h>
+#  include <__stacktrace/format.h>
+#  include <__stacktrace/hash.h>
+#  include <__stacktrace/nonmem.h>
+#  include <__stacktrace/to_string.h>
 
 _LIBCPP_POP_MACROS
 
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 45632d95daf45..5ffbd83534ac9 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,9 +40,10 @@ set(LIBCXX_SOURCES
   ryu/d2fixed.cpp
   ryu/d2s.cpp
   ryu/f2s.cpp
+  stacktrace/base.cpp
   stacktrace/linux.cpp
   stacktrace/macos.cpp
-  stacktrace/stacktrace.cpp
+  stacktrace/to_string.cpp
   stacktrace/tools.cpp
   stacktrace/unwind.cpp
   stacktrace/windows.cpp
diff --git a/libcxx/src/stacktrace/README.md b/libcxx/src/stacktrace/README.md
index afdfb9bf2e0f9..9f25eb4ad6e22 100644
--- a/libcxx/src/stacktrace/README.md
+++ b/libcxx/src/stacktrace/README.md
@@ -1,5 +1,6 @@
 # C++23 `<stacktrace>`
 
+
 # References
 
 1. https://eel.is/c++draft/stacktrace
diff --git a/libcxx/src/stacktrace/base.cpp b/libcxx/src/stacktrace/base.cpp
new file mode 100644
index 0000000000000..1fbc0f08d6e0c
--- /dev/null
+++ b/libcxx/src/stacktrace/base.cpp
@@ -0,0 +1,96 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <__stacktrace/base.h>
+
+#include "stacktrace/config.h"
+#include "stacktrace/linux.h"
+#include "stacktrace/macos.h"
+#include "stacktrace/tools.h"
+#include "stacktrace/unwind.h"
+#include "stacktrace/utils.h"
+#include "stacktrace/windows.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void builder::build_stacktrace(size_t skip, size_t max_depth) {
+  /*
+  Here we declare stacktrace components or "backends" which will handle the different tasks:
+
+  (1) get the addresses from the call stack
+  (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
+  (3) resolve using debug info, and possibly with an external tool on the $PATH
+  (4+) extra passes to get symbols, in case 3 couldn't
+
+  Based on the macros defined in `config.h`, throw all backends we have available at the task.  Ideally the #ifdef
+  gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
+  symbolizer.  If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
+
+  All these classes do their best to provide any of the requested fields they can: symbol, filename, source line,
+  substituting if needed with something reasonable.  For example, if the source filename and line are not available
+  then we will at least report that the address and symbol are in the module `c:\path\foo.exe`.
+
+  These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
+  already-populated data.  We take care not to crash / abort / throw in any of these, and we'll silently fail.  See
+  `common/debug.h` for a debugging logger you can enable at runtime.
+  */
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+  win_impl dbghelp{*this};
+  auto& collector  = dbghelp;
+  auto& mod_ident  = dbghelp;
+  auto& resolver   = dbghelp;
+  auto& symbolizer = dbghelp;
+#endif
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+  unwind unwind{*this};
+  auto& collector = unwind;
+#endif
+#if defined(_LIBCPP_STACKTRACE_MACOS)
+  macos macos{*this};
+  auto& mod_ident  = macos;
+  auto& symbolizer = macos;
+#endif
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+  linux linux{*this};
+  auto& mod_ident  = linux;
+  auto& symbolizer = linux;
+#endif
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+  spawner pspawn{*this};
+  auto& resolver = pspawn;
+#endif
+
+  collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
+  if (__entries_.size()) {                // (Can't proceed if empty)
+    mod_ident.ident_modules();            // Associate addrs with binaries (ELF/MachO/etc.)
+    resolver.resolve_lines();             // Resolve addresses to symbols, filename, linenumber
+    symbolizer.symbolize();               // Populate missing symbols, if any.
+  }
+}
+
+int fd_streambuf::underflow() {
+  int bytesRead = ::read(fd_, buf_, size_);
+  if (bytesRead < 0) {
+    throw failed("I/O error reading from child process", errno);
+  }
+  if (bytesRead == 0) {
+    return traits_type::eof();
+  }
+  setg(buf_, buf_, buf_ + bytesRead);
+  return int(*buf_);
+}
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/context.h b/libcxx/src/stacktrace/context.h
deleted file mode 100644
index 585d5ff7ff4d1..0000000000000
--- a/libcxx/src/stacktrace/context.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// -*- 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_STACKTRACE_CONTEXT
-#define _LIBCPP_STACKTRACE_CONTEXT
-
-#include <__config>
-#include <__memory_resource/memory_resource.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <__vector/pmr.h>
-#include <__vector/vector.h>
-#include <cstddef>
-#include <list>
-#include <string>
-
-#include <__stacktrace/basic_stacktrace.h>
-#include <__stacktrace/stacktrace_entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-struct alloc;
-struct _LIBCPP_HIDDEN entry;
-
-namespace __stacktrace {
-
-/** Represents the state of the current in-progress stacktrace operation.  This includes
-the working list of `entry` objects, as well as the caller-provided allocator (wrapped
-in `polymorphic_allocator`) and a handful of utility functions involving that allocator. */
-struct _LIBCPP_HIDDEN context {
-  /** Encapsulates and type-removes the caller's allocator. */
-  alloc& __alloc_;
-
-  /** The working set of entry data; these will later be copied to their final `stacktrace_entry` objects. */
-  std::pmr::list<entry> __entries_;
-
-  /** Path to this process's main executable. */
-  std::pmr::string __main_prog_path_;
-
-  _LIBCPP_HIDDEN explicit context(alloc& __byte_alloc)
-      : __alloc_(__byte_alloc), __entries_(&__alloc_), __main_prog_path_(__alloc_.new_string()) {}
-
-  _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void do_stacktrace(size_t __skip, size_t __max_depth);
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_CONTEXT
diff --git a/libcxx/src/stacktrace/linux.cpp b/libcxx/src/stacktrace/linux.cpp
index 0ad3c276f5cb4..87f7bafbcccdb 100644
--- a/libcxx/src/stacktrace/linux.cpp
+++ b/libcxx/src/stacktrace/linux.cpp
@@ -16,8 +16,9 @@
 #  include <stacktrace>
 #  include <unistd.h>
 
+#  include <__stacktrace/base.h>
+
 #  include "stacktrace/config.h"
-#  include "stacktrace/context.h"
 #  include "stacktrace/linux.h"
 #  include "stacktrace/utils.h"
 
@@ -35,19 +36,19 @@ void linux::ident_modules() {
 
   auto mainProg = images.mainProg();
   if (mainProg) {
-    cx_.__main_prog_path_ = mainProg->name_;
+    builder_.__main_prog_path_ = mainProg->name_;
   }
 
   unsigned index = 1; // Starts at one, and is moved around in this loop
-  for (auto& entry : cx_.__entries_) {
-    while (images[index].loaded_at_ > entry.__addr_) {
+  for (auto& entry : builder_.__entries_) {
+    while (images[index].loaded_at_ > entry.__addr_actual_) {
       --index;
     }
-    while (images[index + 1].loaded_at_ <= entry.__addr_) {
+    while (images[index + 1].loaded_at_ <= entry.__addr_actual_) {
       ++index;
     }
-    entry.__addr_unslid_ = entry.__addr_ - images[index].slide_;
-    entry.__file_        = images[index].name_;
+    entry.__addr_unslid_ = entry.__addr_actual_ - images[index].slide_;
+    entry.__file_        = builder_.__alloc_.make_str(images[index].name_);
   }
 }
 
@@ -65,27 +66,28 @@ void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
   if (_mm) {
     static elf::ELF _this_elf(_mm.addr_);
     if (_this_elf) {
-      for (auto& entry : cx_.__entries_) {
-        if (entry.__desc_.empty() && entry.__file_ == main_elf_name) {
-          entry.__desc_ = _this_elf.getSym(entry.__addr_unslid_).name();
+      for (auto& entry : builder_.__entries_) {
+        if (entry.__desc_->empty() && entry.__file_ == main_elf_name) {
+          auto name     = _this_elf.getSym(entry.__addr_unslid_).name();
+          entry.__desc_ = builder_.__alloc_.make_str(name);
         }
       }
     }
   }
 }
 
-bool symbolize_entry(entry& entry) {
+bool symbolize_entry(alloc& alloc, entry_base& entry) {
   bool ret = false;
   Dl_info info;
-  if (dladdr((void*)entry.__addr_, &info)) {
+  if (dladdr((void*)entry.__addr_actual_, &info)) {
     ret = true; // at least partially successful
-    if (info.dli_fname && entry.__file_.empty()) {
+    if (info.dli_fname && entry.__file_->empty()) {
       // provide at least the binary filename in case we cannot lookup source location
-      entry.__file_ = info.dli_fname;
+      entry.__file_ = alloc.make_str(info.dli_fname);
     }
-    if (info.dli_sname && entry.__desc_.empty()) {
+    if (info.dli_sname && entry.__desc_->empty()) {
       // provide at least the mangled name; try to unmangle in a later step
-      entry.__desc_ = info.dli_sname;
+      entry.__desc_ = alloc.make_str(info.dli_sname);
     }
   }
   return ret;
@@ -95,8 +97,8 @@ bool symbolize_entry(entry& entry) {
 // except for symbols in the main program.  If addr2line-style tools are enabled, that step
 // might also be able to get symbols directly from the binary's debug info.
 void linux::symbolize() {
-  for (auto& entry : cx_.__entries_) {
-    symbolize_entry(entry);
+  for (auto& entry : builder_.__entries_) {
+    symbolize_entry(builder_.__alloc_, entry);
   }
   // Symbols might be missing, because both (1) Linux's `dladdr` won't try to resolve non-exported symbols,
   // which can be the case for the main program executable; and (2) debug info was not preserved.
diff --git a/libcxx/src/stacktrace/linux.h b/libcxx/src/stacktrace/linux.h
index fd337c0adc422..553998123d912 100644
--- a/libcxx/src/stacktrace/linux.h
+++ b/libcxx/src/stacktrace/linux.h
@@ -20,21 +20,19 @@
 #  include <cstdlib>
 #  include <functional>
 #  include <link.h>
-#  include <stacktrace>
 #  include <string_view>
 #  include <unistd.h>
 
+#  include <__stacktrace/base.h>
+
 #  include "stacktrace/config.h"
-#  include "stacktrace/context.h"
 #  include "stacktrace/utils.h"
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct context;
-
 struct linux {
-  context& cx_;
+  builder& builder_;
   void ident_modules();
   void symbolize();
 
diff --git a/libcxx/src/stacktrace/macos.cpp b/libcxx/src/stacktrace/macos.cpp
index f3b3194183752..1369dde94dec5 100644
--- a/libcxx/src/stacktrace/macos.cpp
+++ b/libcxx/src/stacktrace/macos.cpp
@@ -10,17 +10,15 @@
 
 #if defined(_LIBCPP_STACKTRACE_MACOS)
 
-#  include "stacktrace/context.h"
-#  include "stacktrace/macos.h"
-
 #  include <algorithm>
 #  include <array>
 #  include <dlfcn.h>
 #  include <mach-o/dyld.h>
 #  include <mach-o/loader.h>
 
-#  include <__stacktrace/basic_stacktrace.h>
-#  include <__stacktrace/stacktrace_entry.h>
+#  include <__stacktrace/base.h>
+
+#  include "stacktrace/macos.h"
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
@@ -35,18 +33,18 @@ struct Image {
   operator bool() const { return !name.empty(); }
 };
 
-void ident_module(entry& entry, unsigned& index, Image* images) {
-  if (entry.__addr_) {
-    while (images[index].loadedAt > entry.__addr_) {
+void ident_module(alloc& alloc, entry_base& entry, unsigned& index, Image* images) {
+  if (entry.__addr_actual_) {
+    while (images[index].loadedAt > entry.__addr_actual_) {
       --index;
     }
-    while (images[index + 1].loadedAt <= entry.__addr_) {
+    while (images[index + 1].loadedAt <= entry.__addr_actual_) {
       ++index;
     }
     auto& image = images[index];
     if (image) {
-      entry.__addr_unslid_ = entry.__addr_ - images[index].slide;
-      entry.__file_        = images[index].name;
+      entry.__addr_unslid_ = entry.__addr_actual_ - images[index].slide;
+      entry.__file_        = alloc.make_str(images[index].name);
     }
   }
 }
@@ -78,31 +76,31 @@ void macos::ident_modules() {
   }
 
   // First image (the main program) is at index 1
-  cx_.__main_prog_path_ = images.at(1).name;
+  builder_.__main_prog_path_ = builder_.__alloc_.make_str(images.at(1).name);
 
   unsigned index = 1; // Starts at one, and is moved by 'ident_module'
-  for (auto& entry : cx_.__entries_) {
-    ident_module(entry, index, images.data());
+  for (auto& entry : builder_.__entries_) {
+    ident_module(builder_.__alloc_, (entry_base&)entry, index, images.data());
   }
 }
 
-void symbolize_entry(entry& entry) {
+void symbolize_entry(alloc& alloc, entry_base& entry) {
   Dl_info info;
-  if (dladdr((void*)entry.__addr_, &info)) {
-    if (info.dli_fname && entry.__file_.empty()) {
+  if (dladdr((void*)entry.__addr_actual_, &info)) {
+    if (info.dli_fname && entry.__file_->empty()) {
       // provide at least the binary filename in case we cannot lookup source location
-      entry.__file_ = info.dli_fname;
+      entry.__file_ = alloc.make_str(info.dli_fname);
     }
-    if (info.dli_sname && entry.__desc_.empty()) {
+    if (info.dli_sname && entry.__desc_->empty()) {
       // provide at least the mangled name; try to unmangle in a later step
-      entry.__desc_ = info.dli_sname;
+      entry.__desc_ = alloc.make_str(info.dli_sname);
     }
   }
 }
 
 void macos::symbolize() {
-  for (auto& entry : cx_.__entries_) {
-    symbolize_entry(entry);
+  for (auto& entry : builder_.__entries_) {
+    symbolize_entry(builder_.__alloc_, (entry_base&)entry);
   }
 }
 
diff --git a/libcxx/src/stacktrace/macos.h b/libcxx/src/stacktrace/macos.h
index 98589a8f34494..ca9aa16b654e8 100644
--- a/libcxx/src/stacktrace/macos.h
+++ b/libcxx/src/stacktrace/macos.h
@@ -14,13 +14,13 @@
 #include <cstddef>
 #include <cstdlib>
 
+#include <__stacktrace/base.h>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct context;
-
 struct macos {
-  context& cx_;
+  builder& builder_;
   void ident_modules();
   void symbolize();
 };
diff --git a/libcxx/src/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
deleted file mode 100644
index 2760a14282ecb..0000000000000
--- a/libcxx/src/stacktrace/stacktrace.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-
-#include <iomanip>
-#include <ios>
-#include <iostream>
-#include <sstream>
-#include <stacktrace>
-#include <string>
-
-#include "stacktrace/config.h"
-#include "stacktrace/context.h"
-#include "stacktrace/utils.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-#  include "stacktrace/linux.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_MACOS)
-#  include "stacktrace/macos.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-#  include "stacktrace/tools.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
-#  include "stacktrace/unwind.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
-#  include "stacktrace/windows.h"
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-// `to_string`-related non-member functions
-
-_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
-  return __stacktrace::__to_string()(__entry);
-}
-
-_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
-  __stacktrace::__to_string()(__os, __entry);
-  return __os;
-}
-
-namespace __stacktrace {
-
-/*
- * `to_string` Helpers
- */
-
-_LIBCPP_EXPORTED_FROM_ABI void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
-  // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
-  constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
-
-  __os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << entry.native_handle();
-  if (!entry.description().empty()) {
-    __os << ": " << entry.description();
-  }
-  if (!entry.source_file().empty()) {
-    __os << ": " << entry.source_file();
-  }
-  if (entry.source_line()) {
-    __os << ":" << std::dec << entry.source_line();
-  }
-}
-
-_LIBCPP_EXPORTED_FROM_ABI void
-__to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
-  /*
-   * Print each entry as a line, as per `operator()`, with additional whitespace
-   * at the start of the line, and only a newline added at the end:
-   *
-   *   frame   1: 0xbeefbeefbeef: _symbol_name: /path/to/file.cc:123
-   */
-  if (!__count) {
-    __os << "(empty stacktrace)";
-  } else {
-    for (size_t __i = 0; __i < __count; __i++) {
-      if (__i) {
-        // Insert newlines between entries (but not before the first or after the last)
-        __os << std::endl;
-      }
-      __os << "  frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": ";
-      (*this)(__os, __entries[__i]);
-    }
-  }
-}
-
-_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const& entry) {
-  stringstream __ss;
-  (*this)(__ss, entry);
-  return __ss.str();
-}
-
-_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
-  stringstream __ss;
-  (*this)(__ss, __entries, __count);
-  return __ss.str();
-}
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void
-__impl(size_t skip,
-       size_t max_depth,
-       alloc& alloc,
-       std::function<void(size_t)> resize_func,
-       std::function<void(size_t, std::stacktrace_entry&&)> assign_func) {
-  context cx{alloc};
-  cx.do_stacktrace(1 + skip, max_depth);
-  resize_func(cx.__entries_.size());
-  size_t i = 0;
-  for (auto& entry : cx.__entries_) {
-    assign_func(i++, entry);
-  }
-}
-
-entry::operator std::stacktrace_entry() {
-  std::stacktrace_entry __ret;
-  __ret.__addr_ = __addr_;
-  __ret.__desc_ = __desc_;
-  __ret.__file_ = __file_;
-  __ret.__line_ = __line_;
-  return __ret;
-}
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip, size_t max_depth) {
-  /*
-  Here we declare stacktrace components or "backends" which will handle the different tasks:
-
-  (1) get the addresses from the call stack
-  (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
-  (3) resolve using debug info, and possibly with an external tool on the $PATH
-  (4+) extra passes to get symbols, in case 3 couldn't
-
-  Based on the macros produced by `stacktrace.h`, throw all backends we have available at the task.  Ideally the #ifdef
-  gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
-  symbolizer.  If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
-
-  All these classes do their best to provide any of the requested fields they can: symbol, filename, source line,
-  substituting if needed with something reasonable.  For example, if the source filename and line are not available
-  then we will at least report that the address and symbol are in the module `foo.exe`.
-
-  These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
-  already-populated data.  We take care not to crash / abort / throw in any of these, and we'll silently fail.  See
-  `common/debug.h` for a debugging logger you can enable at runtime.
-  */
-
-#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
-  win_impl dbghelp{*this};
-  auto& collector  = dbghelp;
-  auto& mod_ident  = dbghelp;
-  auto& resolver   = dbghelp;
-  auto& symbolizer = dbghelp;
-#endif
-#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
-  unwind unwind{*this};
-  auto& collector = unwind;
-#endif
-#if defined(_LIBCPP_STACKTRACE_MACOS)
-  macos macos{*this};
-  auto& mod_ident  = macos;
-  auto& symbolizer = macos;
-#endif
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-  linux linux{*this};
-  auto& mod_ident  = linux;
-  auto& symbolizer = linux;
-#endif
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-  spawner pspawn{*this};
-  auto& resolver = pspawn;
-#endif
-
-  collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
-  if (__entries_.size()) {                // (Can't proceed if empty)
-    mod_ident.ident_modules();            // Associate addrs with binaries (ELF/MachO/etc.)
-    resolver.resolve_lines();             // Resolve addresses to symbols, filename, linenumber
-    symbolizer.symbolize();               // Populate missing symbols, if any.
-  }
-}
-
-int fd_streambuf::underflow() {
-  int bytesRead = ::read(fd_, buf_, size_);
-  if (bytesRead < 0) {
-    throw failed("I/O error reading from child process", errno);
-  }
-  if (bytesRead == 0) {
-    return traits_type::eof();
-  }
-  setg(buf_, buf_, buf_ + bytesRead);
-  return int(*buf_);
-}
-
-} // namespace __stacktrace
-
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/to_string.cpp b/libcxx/src/stacktrace/to_string.cpp
new file mode 100644
index 0000000000000..6d02b1c973344
--- /dev/null
+++ b/libcxx/src/stacktrace/to_string.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <__stacktrace/basic.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/to_string.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// `to_string`-related non-member functions
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
+  return __stacktrace::__to_string()(__entry);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
+  __stacktrace::__to_string()(__os, __entry);
+  return __os;
+}
+
+namespace __stacktrace {
+
+/*
+ * `to_string` Helpers
+ */
+
+_LIBCPP_EXPORTED_FROM_ABI void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
+  // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
+  constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
+
+  __os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << entry.native_handle();
+  if (!entry.description().empty()) {
+    __os << ": " << entry.description();
+  }
+  if (!entry.source_file().empty()) {
+    __os << ": " << entry.source_file();
+  }
+  if (entry.source_line()) {
+    __os << ":" << std::dec << entry.source_line();
+  }
+}
+
+_LIBCPP_EXPORTED_FROM_ABI void
+__to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
+  /*
+   * Print each entry as a line, as per `operator()`, with additional whitespace
+   * at the start of the line, and only a newline added at the end:
+   *
+   *   frame   1: 0xbeefbeefbeef: _symbol_name: /path/to/file.cc:123
+   */
+  if (!__count) {
+    __os << "(empty stacktrace)";
+  } else {
+    for (size_t __i = 0; __i < __count; __i++) {
+      if (__i) {
+        // Insert newlines between entries (but not before the first or after the last)
+        __os << std::endl;
+      }
+      __os << "  frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": ";
+      (*this)(__os, __entries[__i]);
+    }
+  }
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const& entry) {
+  stringstream __ss;
+  (*this)(__ss, entry);
+  return __ss.str();
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
+  stringstream __ss;
+  (*this)(__ss, __entries, __count);
+  return __ss.str();
+}
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools.cpp b/libcxx/src/stacktrace/tools.cpp
index 05f14d2f08575..f1e865ee0ebf6 100644
--- a/libcxx/src/stacktrace/tools.cpp
+++ b/libcxx/src/stacktrace/tools.cpp
@@ -23,180 +23,121 @@
 #  include <sys/wait.h>
 #  include <unistd.h>
 
-#  include "stacktrace/context.h"
+#  include <__stacktrace/base.h>
+#  include <__stacktrace/basic.h>
+#  include <__stacktrace/entry.h>
+
 #  include "stacktrace/tools.h"
 #  include "stacktrace/utils.h"
-#  include <__stacktrace/basic_stacktrace.h>
-#  include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
 namespace {
 
-/**
-Returns a list of `tool` objects that can be tried during the first stacktrace operation.
-The result is an array of `tool*` pointers with a final `nullptr` terminator.
-
-The returned tool array pointer is saved during `resolve_lines`'s static init,
-with the first working tool being used from that point forward.
-
-This will first add any tools specified by these defines (in order):
-- LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH
-- LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH
-- LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH
-
-Then any tools specified by the environment variables of the same names (and added in the same order).
-
-Finally, this adds the tools `llvm-symbolizer`, `addr2line`, and `atos`, in that order.
-These tools won't have absolute paths, so $PATH will be searched for these.
-
-Called by `findWorkingTool` during its static initialization, therefore this is used in a threadsafe way.
-*/
-tool const** toolList() {
-  constexpr static size_t kMax = 20;
-  static tool const* array_[kMax];
-  size_t count = 0;
+_LIBCPP_HIDE_FROM_ABI alloc::str hex_string(alloc& alloc, uintptr_t __addr) {
+  char __ret[19]; // "0x" + 16 digits + NUL
+  auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
+  return alloc.make_str(__ret, size_t(__size));
+}
 
-  auto add = [&](tool* t) {
-    assert(count < kMax - 1);
-    array_[count++] = t;
-  };
+_LIBCPP_HIDE_FROM_ABI alloc::str u64_string(alloc& alloc, uintptr_t __val) {
+  char __ret[21]; // 20 digits max + NUL
+  auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
+  return alloc.make_str(__ret, size_t(__size));
+}
 
 #  define STRINGIFY0(x) #x
 #  define STRINGIFY(x) STRINGIFY0(x)
 
+void try_tools(alloc& alloc, function<bool(tool const&)> cb) {
+  char const* prog_name;
+
+  if ((prog_name = getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH"))) {
+    if (cb(llvm_symbolizer{alloc, prog_name})) {
+      return;
+    }
+  } else {
 #  if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
-  {
-    static llvm_symbolizer t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)};
-    add(&t);
-  }
-#  endif
-#  if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
-  {
-    static addr2line t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)};
-    add(&t);
-  }
-#  endif
-#  if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
-  {
-    static atos t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)};
-    add(&t);
-  }
+    if (cb(llvm_symbolizer{alloc, STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)})) {
+      return;
+    }
+#  else
+    if (cb(llvm_symbolizer{alloc})) {
+      return;
+    }
 #  endif
-
-  if (getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")) {
-    static llvm_symbolizer t{getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")};
-    add(&t);
-  }
-  if (getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")) {
-    static addr2line t{getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")};
-    add(&t);
-  }
-  if (getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")) {
-    static atos t{getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")};
-    add(&t);
   }
 
-  {
-    static llvm_symbolizer t;
-    add(&t);
-  }
-  {
-    static addr2line t;
-    add(&t);
-  }
-  {
-    static atos t;
-    add(&t);
+  if ((prog_name = getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH"))) {
+    if (cb(addr2line{alloc, prog_name})) {
+      return;
+    }
+  } else {
+#  if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
+    if (cb(addr2line{alloc, STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)})) {
+      return;
+    }
+#  else
+    if (cb(addr2line{alloc})) {
+      return;
+    }
+#  endif
   }
 
-  array_[count] = nullptr; // terminator
-  return array_;
-}
-
-// Try a handful of different addr2line-esque tools, returning the first discovered, or else nullptr
-tool const* findWorkingTool(auto& trace) {
-  // Try each of these programs and stop at the first one that works.
-  // There's no synchronization so this is subject to races on setting `prog`,
-  // but as long as one thread ends up setting it to something that works, we're good.
-  // Programs to try ($PATH is searched):
-  auto* it = toolList();
-  tool const* prog;
-  while ((prog = *it++)) {
-    pspawn test{*prog}; // Just try to run with "--help"
-    try {
-      std::pmr::list<std::pmr::string> testArgs{&trace.__alloc_};
-      testArgs.push_back(prog->progName_);
-      testArgs.push_back("--help");
-      test.fa_.redirectInNull();
-      test.fa_.redirectOutNull();
-      test.fa_.redirectErrNull();
-      test.spawn(testArgs);
-      if (test.wait() == 0) {
-        // Success
-        return prog;
-      }
-    } catch (failed const&) {
-      /* ignore during probe attempt */
+  if ((prog_name = getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH"))) {
+    if (cb(atos{alloc, prog_name})) {
+      return;
+    }
+  } else {
+#  if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
+    if (cb(atos{alloc, STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)})) {
+      return;
+    }
+#  else
+    if (cb(atos{alloc})) {
+      return;
     }
+#  endif
   }
-  return nullptr;
 }
 
 } // namespace
 
 void spawner::resolve_lines() {
-  // The address-to-line tool that worked (after lazy-setting, below)
-  static tool const* prog{nullptr};
-  // Whether we should not attempt because tool detection previously failed.
-  static bool fail{false};
-
-  // If this previously failed, don't try again.
-  if (fail) {
-    return;
-  }
-
-  if (!prog) {
-    prog = findWorkingTool(cx_);
-    if (!prog) {
-      fail = true;
-      return;
-    }
-  }
-  assert(prog);
-
-  char buf[256];
-  pspawn_tool proc(*prog, cx_, buf, sizeof(buf));
-  try {
-    proc.run();
-  } catch (failed const& failed) {
-    debug() << failed.what();
-    if (failed.errno_) {
-      debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
+  try_tools(builder_.__alloc_, [&](tool const& prog) {
+    char buf[512];
+    pspawn_tool proc(prog, builder_, buf, sizeof(buf));
+    try {
+      proc.run();
+      return true;
+    } catch (failed const& failed) {
+      debug() << failed.what();
+      if (failed.errno_) {
+        debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
+      }
+      debug() << '\n';
     }
-    debug() << '\n';
-  }
+    return false;
+  });
 }
 
-// TODO(stacktrace23): possible to link against `libLLVMSymbolize.a`, or some shared obj at runtime (does that exist?)
-
-std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
-  auto& alloc = cx.__alloc_;
-  auto ret    = alloc.new_string_list();
-  ret.push_back(progName_);
-  ret.push_back("--demangle");
-  ret.push_back("--no-inlines");
-  ret.push_back("--verbose");
-  ret.push_back("--relativenames");
-  ret.push_back("--functions=short");
-  for (auto& entry : cx.__entries_) {
-    auto addr_string = alloc.hex_string(entry.__addr_unslid_);
-    debug() << "@@@ " << addr_string << " " << entry.__file_ << " " << entry.__file_.empty() << '\n';
-    if (!entry.__file_.empty()) {
-      auto arg = alloc.new_string(entry.__file_.size() + 40);
+alloc::list<alloc::str> llvm_symbolizer::buildArgs(builder& builder) const {
+  auto ret = alloc_.make_list<alloc::str>();
+  ret.push_back(alloc_.make_str(progName_));
+  ret.push_back(alloc_.make_str("--demangle"));
+  ret.push_back(alloc_.make_str("--no-inlines"));
+  ret.push_back(alloc_.make_str("--verbose"));
+  ret.push_back(alloc_.make_str("--relativenames"));
+  ret.push_back(alloc_.make_str("--functions=short"));
+  for (auto& st_entry : builder.__entries_) {
+    auto& entry      = (entry_base&)st_entry;
+    auto addr_string = hex_string(alloc_, entry.__addr_unslid_);
+    if (entry.__file_) {
+      auto arg = alloc_.make_str();
+      arg.reserve(entry.__file_->size() + 40);
       arg += "FILE:";
-      arg += entry.__file_;
+      arg += *entry.__file_;
       arg += " ";
       arg += addr_string;
       ret.push_back(arg);
@@ -207,14 +148,14 @@ std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
   return ret;
 }
 
-void llvm_symbolizer::parseOutput(context& cx, entry& entry, std::istream& output) const {
+void llvm_symbolizer::parseOutput(builder& builder, __stacktrace::entry_base& entry, std::istream& output) const {
   // clang-format off
 /*
 With "--verbose", parsing is a little easier, or at least, more reliable;
 probably the best solution (until we have a JSON parser).
 Example output, verbatim, between the '---' lines:
 ---
-test1<test_alloc<std::__1::stacktrace_entry> >
+test1<test_alloc<std::__1::stackbuilder_entry> >
   Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
   Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
   Function start line: 114
@@ -227,12 +168,13 @@ Note that this includes an extra empty line as a terminator.
 */
   // clang-format on
 
-  auto& alloc = cx.__alloc_;
-  auto line   = alloc.new_string(256);
+  auto& alloc = builder.__alloc_;
+  auto line   = alloc.make_str();
+  line.reserve(512);
   std::string_view tmp;
   while (true) {
     std::getline(output, line);
-    while (isspace(line.back())) {
+    while (!line.empty() && isspace(line.back())) {
       line.pop_back();
     }
     if (line.empty()) {
@@ -249,7 +191,7 @@ Note that this includes an extra empty line as a terminator.
       tmp = line;
       tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
       if (tmp != "??") {
-        entry.__file_ = tmp;
+        entry.__file_ = alloc.make_str(tmp);
       }
     } else if (line.starts_with("  Line:")) {
       tmp = line;
@@ -266,29 +208,29 @@ Note that this includes an extra empty line as a terminator.
   }
 }
 
-std::pmr::list<std::pmr::string> addr2line::buildArgs(context& cx) const {
-  auto& alloc = cx.__alloc_;
-  auto ret    = alloc.new_string_list();
-  if (cx.__main_prog_path_.empty()) {
+alloc::list<alloc::str> addr2line::buildArgs(builder& builder) const {
+  auto& alloc = builder.__alloc_;
+  auto ret    = alloc.make_list<alloc::str>();
+  if (builder.__main_prog_path_.empty()) {
     // Should not have reached here but be graceful anyway
-    ret.push_back("/bin/false");
+    ret.push_back(alloc.make_str("/bin/false"));
     return ret;
   }
 
-  ret.push_back(progName_);
-  ret.push_back("--functions");
-  ret.push_back("--demangle");
-  ret.push_back("--basenames");
-  ret.push_back("--pretty-print"); // This "human-readable form" is easier to parse
-  ret.push_back("-e");
-  ret.push_back(cx.__main_prog_path_);
-  for (auto& entry : cx.__entries_) {
-    ret.push_back(alloc.hex_string(entry.__addr_unslid_));
+  ret.push_back(alloc.make_str(progName_));
+  ret.push_back(alloc.make_str("--functions"));
+  ret.push_back(alloc.make_str("--demangle"));
+  ret.push_back(alloc.make_str("--basenames"));
+  ret.push_back(alloc.make_str("--pretty-print")); // This "human-readable form" is easier to parse
+  ret.push_back(alloc.make_str("-e"));
+  ret.push_back(builder.__main_prog_path_);
+  for (auto& entry : builder.__entries_) {
+    ret.push_back(hex_string(alloc, ((entry_base&)entry).__addr_unslid_));
   }
   return ret;
 }
 
-void addr2line::parseOutput(context& trace, entry& entry, std::istream& output) const {
+void addr2line::parseOutput(builder& builder, entry_base& entry, std::istream& output) const {
   // clang-format off
 /*
 Example:
@@ -304,9 +246,11 @@ test::Foo::Foo(int) at foo.cc:11
 */
   // clang-format on
 
-  std::pmr::string line{&trace.__alloc_};
+  auto& alloc = builder.__alloc_;
+  auto line   = alloc.make_str();
+  line.reserve(512);
   std::getline(output, line);
-  while (isspace(line.back())) {
+  while (!line.empty() && isspace(line.back())) {
     line.pop_back();
   }
   if (line.empty()) {
@@ -319,16 +263,16 @@ test::Foo::Foo(int) at foo.cc:11
     return;
   }
   if (sepIndex > 0) {
-    entry.__desc_ = line.substr(0, sepIndex);
+    entry.__desc_ = alloc.make_str(string_view(line).substr(0, sepIndex));
   }
   auto fileBegin = sepIndex + 4;
   if (fileBegin >= line.size()) {
     return;
   }
-  auto fileline = line.substr(fileBegin);
+  auto fileline = alloc.make_str(string_view(line).substr(fileBegin));
   auto colon    = fileline.find_last_of(":");
   if (colon > 0 && !fileline.starts_with("?")) {
-    entry.__file_ = fileline.substr(0, colon);
+    entry.__file_ = alloc.make_str(string_view(fileline).substr(0, colon));
   }
 
   if (colon == std::string::npos) {
@@ -342,21 +286,21 @@ test::Foo::Foo(int) at foo.cc:11
   entry.__line_ = lineno;
 }
 
-std::pmr::list<std::pmr::string> atos::buildArgs(context& cx) const {
-  auto& alloc = cx.__alloc_;
-  auto ret    = alloc.new_string_list();
-  ret.push_back(progName_);
-  ret.push_back("-p");
-  ret.push_back(alloc.u64_string(getpid()));
+alloc::list<alloc::str> atos::buildArgs(builder& builder) const {
+  auto& alloc = builder.__alloc_;
+  auto ret    = alloc.make_list<alloc::str>();
+  ret.push_back(alloc.make_str(progName_));
+  ret.push_back(alloc.make_str("-p"));
+  ret.push_back(u64_string(alloc, getpid()));
   // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
   // ret.push_back("--fullPath");
-  for (auto& entry : cx.__entries_) {
-    ret.push_back(alloc.hex_string(entry.__addr_));
+  for (auto& entry : builder.__entries_) {
+    ret.push_back(hex_string(alloc, ((entry_base&)entry).__addr_actual_));
   }
   return ret;
 }
 
-void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
+void atos::parseOutput(builder& builder, entry_base& entry, std::istream& output) const {
   // Simple example:
   //
   //   main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
@@ -380,9 +324,11 @@ void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
   // If this more or less fits our expected format we'll take these data,
   // even if the line number is 0.
 
-  auto line = cx.__alloc_.new_string(256);
+  auto& alloc = builder.__alloc_;
+  auto line   = alloc.make_str();
+  line.reserve(512);
   std::getline(output, line);
-  while (isspace(line.back())) {
+  while (!line.empty() && isspace(line.back())) {
     line.pop_back();
   }
   if (line.empty()) {
@@ -415,13 +361,13 @@ void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
   // In case a previous step could not obtain the symbol name,
   // we have the name provided by atos; only use that if we have no symbol
   // (no need to copy more strings otherwise).
-  if (entry.__desc_.empty() && !sym.empty()) {
-    entry.__desc_ = sym;
+  if (entry.__desc_->empty() && !sym.empty()) {
+    entry.__desc_ = alloc.make_str(sym);
   }
 
   std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
   if (file != "?" && file != "??" && !file.empty()) {
-    entry.__file_ = file;
+    entry.__file_ = alloc.make_str(file);
   }
 
   unsigned lineno = 0;
diff --git a/libcxx/src/stacktrace/tools.h b/libcxx/src/stacktrace/tools.h
index b3e6e7ec22c30..c79c560745af4 100644
--- a/libcxx/src/stacktrace/tools.h
+++ b/libcxx/src/stacktrace/tools.h
@@ -16,58 +16,59 @@
 #include <csignal>
 #include <cstddef>
 #include <cstdlib>
-#include <list>
 #include <spawn.h>
 #include <string>
 #include <sys/fcntl.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <vector>
 
-#include "stacktrace/context.h"
+#include <__stacktrace/base.h>
+#include <__stacktrace/basic.h>
+#include <__stacktrace/entry.h>
+
 #include "stacktrace/utils.h"
-#include <__stacktrace/basic_stacktrace.h>
-#include <__stacktrace/stacktrace_entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
 struct tool {
+  alloc& alloc_;
   char const* progName_;
-  explicit tool(char const* progName) : progName_(progName) {}
-  constexpr virtual ~tool() = default;
+
+  tool(alloc& alloc, char const* progName) : alloc_(alloc), progName_(progName) {}
+  virtual ~tool() = default;
 
   /** Construct complete `argv` for the spawned process.
   Includes the program name at argv[0], followed by flags */
-  virtual std::pmr::list<std::pmr::string> buildArgs(context& trace) const = 0;
+  virtual alloc::list<alloc::str> buildArgs(builder& trace) const = 0;
 
   /** Parse line(s) output by the tool, and modify `entry`. */
-  virtual void parseOutput(context& trace, entry& e, std::istream& output) const = 0;
+  virtual void parseOutput(builder& trace, entry_base& entry, std::istream& output) const = 0;
 };
 
 struct llvm_symbolizer : tool {
-  constexpr virtual ~llvm_symbolizer() = default;
-  llvm_symbolizer() : llvm_symbolizer("llvm-symbolizer") {}
-  explicit llvm_symbolizer(char const* progName) : tool{progName} {}
-  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
-  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+  virtual ~llvm_symbolizer() = default;
+  explicit llvm_symbolizer(alloc& alloc) : llvm_symbolizer(alloc, "llvm_symbolizer") {}
+  llvm_symbolizer(alloc& alloc, char const* progName) : tool{alloc, progName} {}
+  alloc::list<alloc::str> buildArgs(builder& trace) const override;
+  void parseOutput(builder& trace, entry_base& entry, std::istream& output) const override;
 };
 
 struct addr2line : tool {
-  constexpr virtual ~addr2line() = default;
-  addr2line() : addr2line("addr2line") {}
-  explicit addr2line(char const* progName) : tool{progName} {}
-  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
-  void parseOutput(context& trace, entry& e, std::istream& stream) const override;
+  virtual ~addr2line() = default;
+  explicit addr2line(alloc& alloc) : addr2line(alloc, "addr2line") {}
+  addr2line(alloc& alloc, char const* progName) : tool{alloc, progName} {}
+  alloc::list<alloc::str> buildArgs(builder& trace) const override;
+  void parseOutput(builder& trace, entry_base& entry, std::istream& stream) const override;
 };
 
 struct atos : tool {
-  constexpr virtual ~atos() = default;
-  atos() : atos("atos") {}
-  explicit atos(char const* progName) : tool{progName} {}
-  std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
-  void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+  virtual ~atos() = default;
+  explicit atos(alloc& alloc) : atos(alloc, "atos") {}
+  atos(alloc& alloc, char const* progName) : tool{alloc, progName} {}
+  alloc::list<alloc::str> buildArgs(builder& trace) const override;
+  void parseOutput(builder& trace, entry_base& entry, std::istream& output) const override;
 };
 
 struct file_actions {
@@ -121,8 +122,8 @@ struct pspawn {
     }
   }
 
-  void spawn(std::pmr::list<std::pmr::string> const& argStrings) {
-    std::pmr::vector<char const*> argv{argStrings.get_allocator()};
+  void spawn(alloc::list<alloc::str> const& argStrings) {
+    alloc::vec<char const*> argv = tool_.alloc_.make_vec<char const*>();
     argv.reserve(argStrings.size() + 1);
     for (auto const& str : argStrings) {
       argv.push_back(str.data());
@@ -142,13 +143,13 @@ struct pspawn {
 };
 
 struct pspawn_tool : pspawn {
-  context& cx_;
+  builder& builder_;
   fd fd_;
   fd_streambuf buf_;
   fd_istream stream_;
 
-  pspawn_tool(tool const& a2l, context& cx, char* buf, size_t size)
-      : pspawn{a2l}, cx_(cx), fd_(fa_.redirectOutFD()), buf_(fd_, buf, size), stream_(buf_) {
+  pspawn_tool(tool const& a2l, builder& trace, char* buf, size_t size)
+      : pspawn{a2l}, builder_(trace), fd_(fa_.redirectOutFD()), buf_(fd_, buf, size), stream_(buf_) {
     if (!debug::enabled()) {
       fa_.redirectErrNull();
     }
@@ -159,11 +160,11 @@ struct pspawn_tool : pspawn {
     // Cannot run "addr2line" or similar without addresses, since we
     // provide them in argv, and if there are none passed in argv, the
     // tool will try to read from stdin and hang.
-    if (cx_.__entries_.empty()) {
+    if (builder_.__entries_.empty()) {
       return;
     }
 
-    auto argStrings = tool_.buildArgs(cx_);
+    auto argStrings = tool_.buildArgs(builder_);
     if (debug::enabled()) {
       debug() << "Trying to get stacktrace using:";
       for (auto& str : argStrings) {
@@ -174,14 +175,17 @@ struct pspawn_tool : pspawn {
 
     spawn(argStrings);
 
-    for (auto& entry : cx_.__entries_) {
-      tool_.parseOutput(cx_, entry, stream_);
+    auto end = builder_.__entries_.end();
+    auto it  = builder_.__entries_.begin();
+    while (it != end) {
+      auto& entry = (entry_base&)(*it++);
+      tool_.parseOutput(builder_, entry, stream_);
     }
   }
 };
 
 struct spawner {
-  context& cx_;
+  builder& builder_;
   void resolve_lines();
 };
 
diff --git a/libcxx/src/stacktrace/unwind.cpp b/libcxx/src/stacktrace/unwind.cpp
index ad5c1f7a443a9..71c09fbdfd8d9 100644
--- a/libcxx/src/stacktrace/unwind.cpp
+++ b/libcxx/src/stacktrace/unwind.cpp
@@ -13,14 +13,14 @@
 #  include <unwind.h>
 
 #  include "stacktrace/unwind.h"
-#  include <__stacktrace/basic_stacktrace.h>
-#  include <__stacktrace/stacktrace_entry.h>
+#  include <__stacktrace/basic.h>
+#  include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
 struct unwind_backtrace {
-  context& cx_;
+  builder& builder_;
   size_t skip_;
   size_t maxDepth_;
 
@@ -38,9 +38,10 @@ struct unwind_backtrace {
     if (!ip) {
       return _Unwind_Reason_Code::_URC_NORMAL_STOP;
     }
-    auto& entry          = cx_.__entries_.emplace_back();
-    entry.__addr_        = (ipBefore ? ip : ip - 1);
-    entry.__addr_unslid_ = entry.__addr_; // in case we can't un-slide
+    auto& entry       = builder_.__entries_.emplace_back();
+    auto& eb          = (entry_base&)entry;
+    eb.__addr_actual_ = (ipBefore ? ip : ip - 1);
+    eb.__addr_unslid_ = eb.__addr_actual_; // in case we can't un-slide
     return _Unwind_Reason_Code::_URC_NO_REASON;
   }
 
@@ -50,7 +51,7 @@ struct unwind_backtrace {
 };
 
 _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void unwind::collect(size_t skip, size_t max_depth) {
-  unwind_backtrace bt{cx_, skip + 1, max_depth}; // skip this call as well
+  unwind_backtrace bt{builder_, skip + 1, max_depth}; // skip this call as well
   _Unwind_Backtrace(unwind_backtrace::callback, &bt);
 }
 
diff --git a/libcxx/src/stacktrace/unwind.h b/libcxx/src/stacktrace/unwind.h
index e8efd96e03b56..4b552349d75bc 100644
--- a/libcxx/src/stacktrace/unwind.h
+++ b/libcxx/src/stacktrace/unwind.h
@@ -14,17 +14,15 @@
 #include <cstddef>
 #include <cstdlib>
 
-#include "stacktrace/context.h"
-#include <__stacktrace/basic_stacktrace.h>
-#include <__stacktrace/stacktrace_entry.h>
+#include <__stacktrace/base.h>
+#include <__stacktrace/basic.h>
+#include <__stacktrace/entry.h>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct context;
-
 struct unwind {
-  context& cx_;
+  builder& builder_;
   void collect(size_t skip, size_t max_depth);
 };
 
diff --git a/libcxx/src/stacktrace/utils.h b/libcxx/src/stacktrace/utils.h
index 9c39e0d184e9f..ff6a910762e9c 100644
--- a/libcxx/src/stacktrace/utils.h
+++ b/libcxx/src/stacktrace/utils.h
@@ -15,6 +15,7 @@
 #include <cstdio>
 #include <iostream>
 #include <stdexcept>
+#include <string_view>
 #include <sys/fcntl.h>
 #include <sys/mman.h>
 #include <unistd.h>
diff --git a/libcxx/src/stacktrace/windows.cpp b/libcxx/src/stacktrace/windows.cpp
index 2de064020db0e..f89793e4cb57e 100644
--- a/libcxx/src/stacktrace/windows.cpp
+++ b/libcxx/src/stacktrace/windows.cpp
@@ -10,9 +10,10 @@
 
 #if defined(_LIBCPP_STACKTRACE_WINDOWS)
 
+#  include <__stacktrace/base.h>
+
 #  include "stacktrace/alloc.h"
 #  include "stacktrace/config.h"
-#  include "stacktrace/context.h"
 #  include "stacktrace/utils.h"
 #  include "stacktrace/windows.h"
 
@@ -47,7 +48,7 @@ size_t moduleCount; // 0 IFF module enumeration failed
 
 } // namespace
 
-win_impl::WinDebugAPIs(context& trace) : trace_(trace), guard_(gWindowsAPILock) {
+win_impl::WinDebugAPIs(builder& trace) : builder_(trace), guard_(gWindowsAPILock) {
   if (!globalInitialized) {
     // Cannot proceed without these DLLs:
     if (!dbg) {
@@ -106,15 +107,15 @@ void win_impl::symbolize() {
     return;
   }
 
-  for (auto& entry : cx_.__entries_) {
+  for (auto& entry : builder_.__entries_) {
     char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName];
     auto* sym          = (IMAGEHLP_SYMBOL64*)space;
     sym->SizeOfStruct  = sizeof(IMAGEHLP_SYMBOL64);
     sym->MaxNameLength = kMaxSymName;
     uint64_t disp{0};
-    if ((*dbg.SymGetSymFromAddr64)(proc, entry.__addr_, &disp, sym)) {
+    if ((*dbg.SymGetSymFromAddr64)(proc, entry.__addr_actual_, &disp, sym)) {
       // Copy chars into the destination string which uses the caller-provided allocator.
-      entry.__desc_ = {sym->Name};
+      ((entry_base&)entry).__desc_ = {sym->Name};
     } else {
       Debug() << "SymGetSymFromAddr64 failed: " << GetLastError() << '\n';
     }
@@ -126,11 +127,11 @@ void win_impl::resolve_lines() {
     return;
   }
 
-  for (auto& entry : cx_.__entries_) {
+  for (auto& entry : builder_.__entries_) {
     DWORD disp{0};
     IMAGEHLP_LINE64 line;
     line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
-    if ((*dbg.SymGetLineFromAddr64)(proc, entry.__addr_, &disp, &line)) {
+    if ((*dbg.SymGetLineFromAddr64)(proc, entry.__addr_actual_, &disp, &line)) {
       // Copy chars into the destination string which uses the caller-provided allocator.
       entry.__file_ = line.FileName;
       entry.__line_ = line.LineNumber;
@@ -163,9 +164,9 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_
   frame.AddrPC.Mode      = AddrModeFlat;
   frame.AddrStack.Mode   = AddrModeFlat;
   frame.AddrFrame.Mode   = AddrModeFlat;
-  frame.AddrPC.Offset    = ccx.Rip;
-  frame.AddrStack.Offset = ccx.Rsp;
-  frame.AddrFrame.Offset = ccx.Rbp;
+  frame.AddrPC.Offset    = ctrace.Rip;
+  frame.AddrStack.Offset = ctrace.Rsp;
+  frame.AddrFrame.Offset = ctrace.Rbp;
 
   while (max_depth &&
          (*dbg.StackWalk64)(
@@ -183,11 +184,11 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_
       continue;
     }
     --max_depth;
-    auto& entry = cx_.__entries_.emplace_back();
+    auto& entry = builder_.__entries_.emplace_back();
     // We don't need to compute the un-slid addr; windbg only needs the actual addresses.
     // Assume address is of the instruction after a call instruction, since we can't
     // differentiate between a signal, SEH exception handler, or a normal function call.
-    entry.__addr_ = frame.AddrPC.Offset - 1; // Back up 1 byte to get into prev insn range
+    entry.__addr_actual_ = frame.AddrPC.Offset - 1; // Back up 1 byte to get into prev insn range
   }
 }
 
diff --git a/libcxx/src/stacktrace/windows.h b/libcxx/src/stacktrace/windows.h
index 91b3739f427c1..d6d9f4b52f722 100644
--- a/libcxx/src/stacktrace/windows.h
+++ b/libcxx/src/stacktrace/windows.h
@@ -26,15 +26,15 @@
 #include <cstdlib>
 #include <mutex>
 
+#include <__stacktrace/base.h>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 namespace __stacktrace {
 
-struct context;
-
 struct win_impl {
-  context& cx_;
+  builder& builder_;
   std::lock_guard<std::mutex> guard_;
-  win_impl(context& trace);
+  win_impl(builder& trace);
   ~win_impl();
 
   void collect(size_t skip, size_t max_depth);
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 8d8ce5665565b..461087467a270 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,3 +105,4 @@
 #endif // TEST_STD_VER > 23
 
 // clang-format on
+
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 9350861528b4e..881764515b9cf 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -8007,3 +8007,4 @@
 #endif // TEST_STD_VER > 23
 
 // clang-format on
+

>From 9cdf6d1ed8c52924a16795b55461d5aeb53cdc2a Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 19 May 2025 16:25:34 -0400
Subject: [PATCH 35/37] regenerated files

---
 ...bcxxabi.v1.stable.exceptions.nonew.abilist | 20 +++++++++++++++++--
 .../test/libcxx/transitive_includes/cxx23.csv | 12 ++---------
 .../test/libcxx/transitive_includes/cxx26.csv | 12 ++---------
 3 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 162757c7e37ec..7dc67154b26a3 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -534,6 +534,7 @@
 {'is_defined': True, 'name': '__ZNKSt3__115basic_streambufIwNS_11char_traitsIwEEE6getlocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEE3strEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__115error_condition7messageEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__117bad_function_call4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__117moneypunct_bynameIcLb0EE11do_groupingEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__117moneypunct_bynameIcLb0EE13do_neg_formatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNKSt3__117moneypunct_bynameIcLb0EE13do_pos_formatEv', 'type': 'FUNC'}
@@ -974,6 +975,11 @@
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclERKNS_16stacktrace_entryE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7builderB8ne21000016build_stacktraceEmm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD2Ev', 'type': 'FUNC'}
@@ -1125,6 +1131,7 @@
 {'is_defined': True, 'name': '__ZNSt3__112system_errorD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112system_errorD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__112system_errorD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE11__read_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE12__write_modeEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4openEPKcj', 'type': 'FUNC'}
@@ -1305,7 +1312,6 @@
 {'is_defined': True, 'name': '__ZNSt3__113shared_futureIvED1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113shared_futureIvED2Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__113shared_futureIvEaSERKS1_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__114__num_get_base10__get_baseERNS_8ios_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__114__num_get_base5__srcE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__114__num_put_base12__format_intEPcPKcbj', 'type': 'FUNC'}
@@ -1508,7 +1514,6 @@
 {'is_defined': True, 'name': '__ZNSt3__117bad_function_callD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117bad_function_callD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117bad_function_callD2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__117bad_function_call4whatEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117iostream_categoryEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
@@ -1935,6 +1940,7 @@
 {'is_defined': True, 'name': '__ZNSt3__19strstreamD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__19strstreamD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__19strstreamD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__19to_stringERKNS_16stacktrace_entryE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__19to_stringEd', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__19to_stringEe', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__19to_stringEf', 'type': 'FUNC'}
@@ -1944,6 +1950,7 @@
 {'is_defined': True, 'name': '__ZNSt3__19to_stringEm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__19to_stringEx', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__19to_stringEy', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__1lsERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt8bad_castC1Ev', 'type': 'I'}
 {'is_defined': True, 'name': '__ZNSt8bad_castC2Ev', 'type': 'I'}
@@ -2011,6 +2018,9 @@
 {'is_defined': True, 'name': '__ZTINSt3__110moneypunctIwLb1EEE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTINSt3__110ostrstreamE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTINSt3__111regex_errorE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTINSt3__112__stacktrace15llvm_symbolizerE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTINSt3__112__stacktrace4atosE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTINSt3__112__stacktrace9addr2lineE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTINSt3__112bad_weak_ptrE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTINSt3__112ctype_bynameIcEE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTINSt3__112ctype_bynameIwEE', 'size': 0, 'type': 'OBJECT'}
@@ -2225,6 +2235,9 @@
 {'is_defined': True, 'name': '__ZTSNSt3__110moneypunctIwLb1EEE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTSNSt3__110ostrstreamE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTSNSt3__111regex_errorE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTSNSt3__112__stacktrace15llvm_symbolizerE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTSNSt3__112__stacktrace4atosE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTSNSt3__112__stacktrace9addr2lineE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTSNSt3__112bad_weak_ptrE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTSNSt3__112ctype_bynameIcEE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTSNSt3__112ctype_bynameIwEE', 'size': 0, 'type': 'OBJECT'}
@@ -2447,6 +2460,9 @@
 {'is_defined': True, 'name': '__ZTVNSt3__110moneypunctIwLb1EEE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTVNSt3__110ostrstreamE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTVNSt3__111regex_errorE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTVNSt3__112__stacktrace15llvm_symbolizerE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTVNSt3__112__stacktrace4atosE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTVNSt3__112__stacktrace9addr2lineE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTVNSt3__112bad_weak_ptrE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTVNSt3__112ctype_bynameIcEE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZTVNSt3__112ctype_bynameIwEE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 29b49f2ae7e84..e67db3ac47cf9 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -926,34 +926,26 @@ stack limits
 stack stdexcept
 stack tuple
 stack version
-stacktrace bitset
 stacktrace cctype
-stacktrace cerrno
 stacktrace climits
-stacktrace clocale
 stacktrace compare
 stacktrace cstddef
 stacktrace cstdint
 stacktrace cstdio
-stacktrace cstdlib
 stacktrace cstring
-stacktrace ctime
 stacktrace cwchar
 stacktrace cwctype
 stacktrace initializer_list
-stacktrace ios
 stacktrace iosfwd
 stacktrace limits
 stacktrace list
-stacktrace locale
-stacktrace memory
-stacktrace ratio
+stacktrace optional
 stacktrace stdexcept
-stacktrace streambuf
 stacktrace string
 stacktrace string_view
 stacktrace tuple
 stacktrace typeinfo
+stacktrace utility
 stacktrace version
 stop_token atomic
 stop_token climits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 5f7b5401f081a..5a411936672af 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -926,34 +926,26 @@ stack limits
 stack stdexcept
 stack tuple
 stack version
-stacktrace bitset
 stacktrace cctype
-stacktrace cerrno
 stacktrace climits
-stacktrace clocale
 stacktrace compare
 stacktrace cstddef
 stacktrace cstdint
 stacktrace cstdio
-stacktrace cstdlib
 stacktrace cstring
-stacktrace ctime
 stacktrace cwchar
 stacktrace cwctype
 stacktrace initializer_list
-stacktrace ios
 stacktrace iosfwd
 stacktrace limits
 stacktrace list
-stacktrace locale
-stacktrace memory
-stacktrace ratio
+stacktrace optional
 stacktrace stdexcept
-stacktrace streambuf
 stacktrace string
 stacktrace string_view
 stacktrace tuple
 stacktrace typeinfo
+stacktrace utility
 stacktrace version
 stop_token atomic
 stop_token climits

>From 31a209da5cedf1390afb893537aa7d6fe21e95b2 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 19 May 2025 17:37:27 -0400
Subject: [PATCH 36/37] formatting

---
 .../support.limits.general/stacktrace.version.compile.pass.cpp   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 461087467a270..8d8ce5665565b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,4 +105,3 @@
 #endif // TEST_STD_VER > 23
 
 // clang-format on
-

>From ee82541b6d8a0db67a2d415a86b6b16b1decb867 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 19 May 2025 17:42:21 -0400
Subject: [PATCH 37/37] formatting

---
 .../support.limits.general/stacktrace.version.compile.pass.cpp   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 8d8ce5665565b..461087467a270 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,3 +105,4 @@
 #endif // TEST_STD_VER > 23
 
 // clang-format on
+



More information about the libcxx-commits mailing list