[libcxx-commits] [libcxx] [libc++] Add std::stacktrace (P0881R7) (PR #136528)
Steve O'Brien via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Oct 28 10:00:27 PDT 2025
https://github.com/elsteveogrande updated https://github.com/llvm/llvm-project/pull/136528
>From 1059993ee30a63fe868f9abe11affe42dfa54316 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/11] Add C++23 stacktrace
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/docs/ReleaseNotes/22.rst | 2 +
libcxx/docs/Status/Cxx23Issues.csv | 2 +-
libcxx/docs/Status/Cxx23Papers.csv | 6 +-
libcxx/include/CMakeLists.txt | 3 +
libcxx/include/__config | 27 ++
libcxx/include/__configuration/availability.h | 4 +
.../include/__stacktrace/basic_stacktrace.h | 342 ++++++++++++++++++
.../include/__stacktrace/stacktrace_entry.h | 211 +++++++++++
libcxx/include/module.modulemap.in | 25 ++
libcxx/include/stacktrace | 187 ++++++++++
libcxx/include/version | 4 +-
...bcxxabi.v1.stable.exceptions.nonew.abilist | 11 +-
...bcxxabi.v1.stable.exceptions.nonew.abilist | 11 +-
...xxabi.v1.stable.noexceptions.nonew.abilist | 11 +-
libcxx/modules/std.compat.cppm.in | 3 -
libcxx/modules/std.cppm.in | 4 +-
libcxx/modules/std/stacktrace.inc | 14 +-
libcxx/src/CMakeLists.txt | 5 +
libcxx/src/stacktrace/images.cpp | 92 +++++
libcxx/src/stacktrace/images.h | 119 ++++++
libcxx/src/stacktrace/impl.cpp | 84 +++++
libcxx/src/stacktrace/impl_generic.cpp | 91 +++++
libcxx/src/stacktrace/impl_windows.cpp | 248 +++++++++++++
libcxx/src/stacktrace/unwinding.h | 108 ++++++
.../test/libcxx/transitive_includes/cxx23.csv | 21 ++
.../test/libcxx/transitive_includes/cxx26.csv | 21 ++
.../stacktrace/basic.cmp/equality.pass.cpp | 59 +++
.../basic.cmp/strong_ordering.pass.cpp | 89 +++++
.../assert.current.no_overflow.pass.cpp | 30 ++
.../stacktrace/basic.cons/copy.pass.cpp | 81 +++++
.../basic.cons/ctor_no_args.pass.cpp | 51 +++
.../basic.cons/ctor_with_alloc.pass.cpp | 45 +++
.../basic.cons/current_no_args.pass.cpp | 67 ++++
.../basic.cons/current_skip.pass.cpp | 46 +++
.../basic.cons/current_skip_depth.pass.cpp | 58 +++
.../stacktrace/basic.cons/move.pass.cpp | 89 +++++
.../stacktrace/basic.hash.pass.cpp | 38 ++
.../stacktrace/basic.mod/swap.pass.cpp | 65 ++++
.../basic.nonmem/operator_left_shift.pass.cpp | 44 +++
.../stacktrace/basic.nonmem/swap.pass.cpp | 58 +++
.../basic.nonmem/to_string.pass.cpp | 45 +++
.../stacktrace/basic.obs/at.pass.cpp | 49 +++
.../stacktrace/basic.obs/begin_end.pass.cpp | 44 +++
.../stacktrace/basic.obs/cbegin_cend.pass.cpp | 39 ++
.../basic.obs/crbegin_crend.pass.cpp | 39 ++
.../stacktrace/basic.obs/empty.pass.cpp | 34 ++
.../basic.obs/get_allocator.pass.cpp | 27 ++
.../stacktrace/basic.obs/max_size.pass.cpp | 33 ++
.../basic.obs/operator_index.pass.cpp | 48 +++
.../stacktrace/basic.obs/rbegin_rend.pass.cpp | 40 ++
.../stacktrace/basic.obs/size.pass.cpp | 34 ++
.../std/diagnostics/stacktrace/basic.pass.cpp | 143 ++++++++
.../stacktrace/entry.cmp/equality.pass.cpp | 47 +++
.../entry.cmp/strong_ordering.pass.cpp | 56 +++
.../entry.cons/copy_assign.pass.cpp | 37 ++
.../entry.cons/copy_construct.pass.cpp | 36 ++
.../stacktrace/entry.cons/default.pass.cpp | 34 ++
.../entry.obs/native_handle.pass.cpp | 29 ++
.../entry.obs/operator_bool.pass.cpp | 37 ++
.../std/diagnostics/stacktrace/entry.pass.cpp | 62 ++++
.../entry.query/description.pass.cpp | 32 ++
.../entry.query/source_file.pass.cpp | 32 ++
.../entry.query/source_line.pass.cpp | 30 ++
.../stacktrace/only_uses_allocator.pass.cpp | 115 ++++++
.../diagnostics/stacktrace/simple.pass.cpp | 33 ++
.../std/diagnostics/stacktrace/test_allocs.h | 106 ++++++
.../stacktrace.version.compile.pass.cpp | 119 ++++++
.../version.version.compile.pass.cpp | 8 +-
libcxx/test/support/test_macros.h | 18 +
.../generate_feature_test_macro_components.py | 3 +-
libcxx/utils/libcxx/header_information.py | 3 +-
libcxx/utils/libcxx/test/features.py | 8 +
73 files changed, 3871 insertions(+), 27 deletions(-)
create mode 100644 libcxx/include/__stacktrace/basic_stacktrace.h
create mode 100644 libcxx/include/__stacktrace/stacktrace_entry.h
create mode 100644 libcxx/include/stacktrace
create mode 100644 libcxx/src/stacktrace/images.cpp
create mode 100644 libcxx/src/stacktrace/images.h
create mode 100644 libcxx/src/stacktrace/impl.cpp
create mode 100644 libcxx/src/stacktrace/impl_generic.cpp
create mode 100644 libcxx/src/stacktrace/impl_windows.cpp
create mode 100644 libcxx/src/stacktrace/unwinding.h
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.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.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/basic.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.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/only_uses_allocator.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/simple.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/test_allocs.h
create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8fba6db871f08..d0fbcd389760c 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -396,7 +396,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_spanstream`` *unimplemented*
---------------------------------------------------------- -----------------
- ``__cpp_lib_stacktrace`` *unimplemented*
+ ``__cpp_lib_stacktrace`` ``202011L``
---------------------------------------------------------- -----------------
``__cpp_lib_stdatomic_h`` ``202011L``
---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 25d33a9c2eb50..e1b252b696fef 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -46,6 +46,8 @@ Implemented Papers
- P2835R7: Expose ``std::atomic_ref``'s object address (`Github <https://llvm.org/PR118377>`__)
- P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://llvm.org/PR105424>`__)
- P3168R2: Give ``std::optional`` Range Support (`Github <https://llvm.org/PR105430>`__)
+- 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 5a68b51ec85fb..42419a5345eb1 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)","","","`#104995 <https://github.com/llvm/llvm-project/issues/104995>`__",""
"`LWG3118 <https://wg21.link/LWG3118>`__","``fpos`` equality comparison unspecified","2022-11 (Kona)","","","`#104996 <https://github.com/llvm/llvm-project/issues/104996>`__",""
"`LWG3177 <https://wg21.link/LWG3177>`__","Limit permission to specialize variable templates to program-defined types","2022-11 (Kona)","|Nothing To Do|","","`#104997 <https://github.com/llvm/llvm-project/issues/104997>`__",""
-"`LWG3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized","2022-11 (Kona)","","","`#104998 <https://github.com/llvm/llvm-project/issues/104998>`__",""
+"`LWG3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized","2022-11 (Kona)","|Complete|","","`#104998 <https://github.com/llvm/llvm-project/issues/104998>`__",""
"`LWG3545 <https://wg21.link/LWG3545>`__","``std::pointer_traits`` should be SFINAE-friendly","2022-11 (Kona)","|Complete|","18","`#104999 <https://github.com/llvm/llvm-project/issues/104999>`__",""
"`LWG3569 <https://wg21.link/LWG3569>`__","``join_view`` fails to support ranges of ranges with non-default_initializable iterators","2022-11 (Kona)","","","`#105000 <https://github.com/llvm/llvm-project/issues/105000>`__",""
"`LWG3594 <https://wg21.link/LWG3594>`__","``inout_ptr`` — inconsistent ``release()`` in destructor","2022-11 (Kona)","|Complete|","19","`#105001 <https://github.com/llvm/llvm-project/issues/105001>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index b655384bad7f2..f1f5e1ebc2ee2 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","GitHub issue","Notes"
-"`P0881R7 <https://wg21.link/P0881R7>`__","A Proposal to add stacktrace library","2020-11 (Virtual)","","","`#105131 <https://github.com/llvm/llvm-project/issues/105131>`__",""
+"`P0881R7 <https://wg21.link/P0881R7>`__","A Proposal to add stacktrace library","2020-11 (Virtual)","|Complete|","22","`#105131 <https://github.com/llvm/llvm-project/issues/105131>`__",""
"`P0943R6 <https://wg21.link/P0943R6>`__","Support C atomics in C++","2020-11 (Virtual)","|Complete|","15","`#105132 <https://github.com/llvm/llvm-project/issues/105132>`__",""
"`P1048R1 <https://wg21.link/P1048R1>`__","A proposal for a type trait to detect scoped enumerations","2020-11 (Virtual)","|Complete|","12","`#105134 <https://github.com/llvm/llvm-project/issues/105134>`__",""
"`P1679R3 <https://wg21.link/P1679R3>`__","string contains function","2020-11 (Virtual)","|Complete|","12","`#105135 <https://github.com/llvm/llvm-project/issues/105135>`__",""
@@ -32,7 +32,7 @@
"`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","","","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
"`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
-"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
+"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","|Complete|","22","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
"`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|In Progress|","","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
"`P2340R1 <https://wg21.link/P2340R1>`__","Clarifying the status of the 'C headers'","2021-10 (Virtual)","|Nothing To Do|","","`#105170 <https://github.com/llvm/llvm-project/issues/105170>`__",""
"`P2393R1 <https://wg21.link/P2393R1>`__","Cleaning up ``integer``-class types","2021-10 (Virtual)","","","`#105171 <https://github.com/llvm/llvm-project/issues/105171>`__",""
@@ -110,7 +110,7 @@
"`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","2023-02 (Issaquah)","|Complete|","19","`#105254 <https://github.com/llvm/llvm-project/issues/105254>`__",""
"`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","2023-02 (Issaquah)","|Complete|","17","`#105255 <https://github.com/llvm/llvm-project/issues/105255>`__",""
"`P2572R1 <https://wg21.link/P2572R1>`__","``std::format`` fill character allowances","2023-02 (Issaquah)","|Complete|","17","`#105256 <https://github.com/llvm/llvm-project/issues/105256>`__",""
-"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","2023-02 (Issaquah)","|Partial|","","`#105257 <https://github.com/llvm/llvm-project/issues/105257>`__","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|","22","`#105257 <https://github.com/llvm/llvm-project/issues/105257>`__",""
"`P2679R2 <https://wg21.link/P2679R2>`__","Fixing ``std::start_lifetime_as`` for arrays","2023-02 (Issaquah)","","","`#105258 <https://github.com/llvm/llvm-project/issues/105258>`__",""
"`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
"`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 37259a7e6e7dd..327fb28541b24 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -741,6 +741,8 @@ set(files
__ranges/zip_transform_view.h
__ranges/zip_view.h
__split_buffer
+ __stacktrace/basic_stacktrace.h
+ __stacktrace/stacktrace_entry.h
__std_mbstate_t.h
__stop_token/atomic_unique_lock.h
__stop_token/intrusive_list_view.h
@@ -1062,6 +1064,7 @@ set(files
span
sstream
stack
+ stacktrace
stdatomic.h
stdbool.h
stddef.h
diff --git a/libcxx/include/__config b/libcxx/include/__config
index b4c081dcdff1b..4d32f1fba03c7 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -965,6 +965,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/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index d0414ecfac2bb..d022922b78b36 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -291,6 +291,10 @@
#define _LIBCPP_AVAILABILITY_HAS_BAD_FUNCTION_CALL_GOOD_WHAT_MESSAGE _LIBCPP_INTRODUCED_IN_LLVM_21
// No attribute, since we've had bad_function_call::what() in the headers before
+// This controls the availability of C++23 <stacktrace>.
+#define _LIBCPP_AVAILABILITY_HAS_STACKTRACE _LIBCPP_INTRODUCED_IN_LLVM_21
+#define _LIBCPP_AVAILABILITY_STACKTRACE _LIBCPP_INTRODUCED_IN_LLVM_21_ATTRIBUTE
+
// Define availability attributes that depend on both
// _LIBCPP_HAS_EXCEPTIONS and _LIBCPP_HAS_RTTI.
#if !_LIBCPP_HAS_EXCEPTIONS || !_LIBCPP_HAS_RTTI
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
new file mode 100644
index 0000000000000..291ca672631b1
--- /dev/null
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -0,0 +1,342 @@
+// -*- 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_BASIC
+#define _LIBCPP_STACKTRACE_BASIC
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#include <__assert>
+#include <__cstddef/size_t.h>
+#include <__functional/function.h>
+#include <__functional/hash.h>
+#include <__iterator/iterator.h>
+#include <__iterator/reverse_iterator.h>
+#include <__memory/allocator_traits.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__new/allocate.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__vector/vector.h>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#if _LIBCPP_HAS_LOCALIZATION
+# include <__fwd/format.h>
+# include <__fwd/ostream.h>
+#endif // _LIBCPP_HAS_LOCALIZATION
+
+#if _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+
+# include <__stacktrace/stacktrace_entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+template <typename _Tp, typename _Bp = _Tp>
+struct iters {
+ _Tp* __data_{};
+ size_t __size_{};
+
+ _Bp* data() { return (_Bp*)__data_; }
+ size_t size() const { return __size_; }
+ _Bp* begin() { return data(); }
+ _Bp* end() { return data() + size(); }
+};
+
+struct base {
+ constexpr static size_t __default_max_depth = 64;
+ constexpr static size_t __absolute_max_depth = 256;
+
+ str_alloc<char> __string_alloc_;
+
+ using _EntryIters _LIBCPP_NODEBUG = iters<stacktrace_entry, entry_base>;
+ function<_EntryIters()> __entry_iters_;
+ function<entry_base&()> __entry_append_;
+
+ template <class _Allocator>
+ _LIBCPP_HIDE_FROM_ABI
+ base(_Allocator const& __alloc, function<_EntryIters()> __entry_iters, function<entry_base&()> __entry_append)
+ : __string_alloc_(std::move(str_alloc<char>::make(__alloc))),
+ __entry_iters_(__entry_iters),
+ __entry_append_(__entry_append) {}
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void current_impl(size_t __skip, size_t __max_depth);
+
+ _LIBCPP_HIDE_FROM_ABI void find_images();
+ _LIBCPP_HIDE_FROM_ABI void find_symbols();
+ _LIBCPP_HIDE_FROM_ABI void find_source_locs();
+
+ _LIBCPP_EXPORTED_FROM_ABI ostream& write_to(ostream& __os) const;
+ _LIBCPP_EXPORTED_FROM_ABI string to_string() const;
+
+ _LIBCPP_HIDE_FROM_ABI str __create_str() { return str(__string_alloc_); }
+};
+
+} // namespace __stacktrace
+
+// (19.6.4)
+// Class template basic_stacktrace [stacktrace.basic]
+
+class stacktrace_entry;
+
+template <class _Allocator>
+class basic_stacktrace : private __stacktrace::base {
+ friend struct hash<basic_stacktrace<_Allocator>>;
+ friend struct __stacktrace::base;
+
+ using _ATraits _LIBCPP_NODEBUG = allocator_traits<_Allocator>;
+ constexpr static bool __kPropOnCopyAssign = _ATraits::propagate_on_container_copy_assignment::value;
+ constexpr static bool __kPropOnMoveAssign = _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 __kNoThrowAlloc = noexcept(noexcept(_Allocator().allocate(1)));
+
+ _LIBCPP_NO_UNIQUE_ADDRESS _Allocator __alloc_;
+
+ vector<stacktrace_entry, _Allocator> __entries_;
+ _LIBCPP_HIDE_FROM_ABI _EntryIters entry_iters() { return {__entries_.data(), __entries_.size()}; }
+ _LIBCPP_HIDE_FROM_ABI __stacktrace::entry_base& entry_append() {
+ return (__stacktrace::entry_base&)__entries_.emplace_back();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI auto entry_iters_fn() {
+ return [this] -> _EntryIters { return entry_iters(); };
+ }
+ _LIBCPP_HIDE_FROM_ABI auto entry_append_fn() {
+ return [this] -> __stacktrace::entry_base& { return entry_append(); };
+ }
+
+public:
+ // (19.6.4.1)
+ // Overview [stacktrace.basic.overview]
+
+ using value_type = stacktrace_entry;
+ using const_reference = value_type const&;
+ using reference = value_type&;
+ using difference_type = ptrdiff_t;
+ using size_type = size_t;
+ using allocator_type = _Allocator;
+ using iterator = decltype(__entries_.begin());
+ using const_iterator = decltype(__entries_.cbegin());
+ using reverse_iterator = decltype(__entries_.rbegin());
+ using const_reverse_iterator = decltype(__entries_.crbegin());
+
+ // (19.6.4.2)
+ // Creation and assignment [stacktrace.basic.cons]
+
+ _LIBCPP_ALWAYS_INLINE static basic_stacktrace
+ current(const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ size_type __skip = 0;
+ size_type __max_depth = __default_max_depth;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
+ basic_stacktrace __ret{__caller_alloc};
+ __ret.current_impl(__skip, __max_depth);
+ return __ret;
+ }
+
+ _LIBCPP_ALWAYS_INLINE static basic_stacktrace
+ current(size_type __skip, const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ size_type __max_depth = __default_max_depth;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
+ basic_stacktrace __ret{__caller_alloc};
+ __ret.current_impl(__skip, __max_depth);
+ return __ret;
+ }
+
+ _LIBCPP_ALWAYS_INLINE static basic_stacktrace
+ current(size_type __skip,
+ size_type __max_depth,
+ const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
+ basic_stacktrace __ret{__caller_alloc};
+ if (__max_depth) [[likely]] {
+ __ret.current_impl(__skip, __max_depth);
+ }
+ return __ret;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~basic_stacktrace() = default;
+
+ static_assert(sizeof(__stacktrace::entry_base) == sizeof(stacktrace_entry));
+
+ _LIBCPP_HIDE_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc)
+ : base(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__alloc_) {}
+
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
+ : base(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__other.__entries_) {}
+
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
+ : base(__alloc, entry_iters_fn(), entry_append_fn()),
+ __alloc_(__alloc),
+ __entries_(std::move(__other.__entries_)) {}
+
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>)
+ : basic_stacktrace(allocator_type()) {}
+
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) noexcept
+ : basic_stacktrace(__other, _ATraits::select_on_container_copy_construction(__other.__alloc_)) {}
+
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept
+ : basic_stacktrace(std::move(__other), __other.__alloc_) {}
+
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other) {
+ if (std::addressof(__other) != this) {
+ if (__kPropOnCopyAssign) {
+ new (this) basic_stacktrace(__other, __other.__alloc_);
+ } else {
+ new (this) basic_stacktrace(__other);
+ }
+ }
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace&
+ operator=(basic_stacktrace&& __other) noexcept(__kPropOnMoveAssign || __kAlwaysEqual) {
+ if (std::addressof(__other) != this) {
+ if (__kPropOnMoveAssign) {
+ auto __alloc = __other.__alloc_;
+ new (this) basic_stacktrace(std::move(__other), __alloc);
+ } else {
+ new (this) basic_stacktrace(std::move(__other));
+ }
+ }
+ return *this;
+ }
+
+ // (19.6.4.3)
+ // [stacktrace.basic.obs], observers
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __alloc_; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const noexcept { return __entries_.begin(); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const noexcept { return __entries_.end(); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const noexcept { return __entries_.rbegin(); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const noexcept { return __entries_.rend(); }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const noexcept { return __entries_.cbegin(); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator cend() const noexcept { return __entries_.cend(); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const noexcept { return __entries_.crbegin(); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const noexcept { return __entries_.crend(); }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool empty() const noexcept { return __entries_.empty(); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_type size() const noexcept { return __entries_.size(); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_type max_size() const noexcept { return __entries_.max_size(); }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_reference operator[](size_type __i) const { return __entries_[__i]; }
+ [[nodiscard]] _LIBCPP_HIDE_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_HIDE_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_HIDE_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_HIDE_FROM_ABI void swap(basic_stacktrace& __other) noexcept(
+ allocator_traits<_Allocator>::propagate_on_container_swap::value ||
+ allocator_traits<_Allocator>::is_always_equal::value) {
+ 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_HIDE_FROM_ABI inline void
+swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) noexcept(noexcept(__a.swap(__b))) {
+ __a.swap(__b);
+}
+
+# if _LIBCPP_HAS_LOCALIZATION
+template <class _Allocator>
+_LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __stacktrace) {
+ return ((__stacktrace::base const&)__stacktrace).write_to(__os);
+}
+template <class _Allocator>
+_LIBCPP_HIDE_FROM_ABI inline string to_string(const basic_stacktrace<_Allocator>& __stacktrace) {
+ return ((__stacktrace::base const&)__stacktrace).to_string();
+}
+# endif // _LIBCPP_HAS_LOCALIZATION
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <class _Allocator>
+struct hash<basic_stacktrace<_Allocator>> {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI 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
+
+#endif // _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_BASIC
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
new file mode 100644
index 0000000000000..48daf14ccccc6
--- /dev/null
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -0,0 +1,211 @@
+// -*- 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_ENTRY
+#define _LIBCPP_STACKTRACE_ENTRY
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#include <__assert>
+#include <__functional/function.h>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+
+#if _LIBCPP_HAS_LOCALIZATION
+# include <__fwd/format.h>
+# include <__fwd/ostream.h>
+#endif // _LIBCPP_HAS_LOCALIZATION
+
+#if _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class stacktrace_entry;
+
+namespace __stacktrace {
+
+struct str;
+
+template <class _Tp>
+struct str_alloc {
+ using result_t _LIBCPP_NODEBUG = allocation_result<_Tp*, size_t>;
+
+ // Lambdas wrap the caller's allocator, re-bound so we can deal with `chars`.
+ function<char*(size_t)> __alloc_;
+ function<void(char*, size_t)> __dealloc_;
+
+ // This only works with chars or other 1-byte things.
+ static_assert(sizeof(_Tp) == 1);
+
+ using value_type = _Tp;
+ using pointer = _Tp*;
+
+ template <class _Tp2>
+ struct rebind {
+ using other = str_alloc<_Tp2>;
+ };
+
+ str_alloc(const str_alloc&) = default;
+ str_alloc(str_alloc&&) = default;
+ str_alloc& operator=(const str_alloc&) = default;
+ str_alloc& operator=(str_alloc&&) = default;
+
+ str_alloc(function<char*(size_t)> __alloc, function<void(char*, size_t)> __dealloc)
+ : __alloc_(std::move(__alloc)), __dealloc_(std::move(__dealloc)) {}
+
+ template <class _A0, // some allocator; can be of any type
+ class _AT = allocator_traits<_A0>,
+ class _CA = typename _AT::template rebind_alloc<char>>
+ requires __is_allocator_v<_A0>
+ static str_alloc make(_A0 __a) {
+ auto __ca = _CA(__a);
+ return {[__ca](size_t __n) mutable -> char* { return __ca.allocate(__n); },
+ [__ca](char* __p, size_t __n) mutable { __ca.deallocate(__p, __n); }};
+ }
+
+ _Tp* allocate(size_t __n) { return __alloc_(__n); }
+ void deallocate(_Tp* __p, size_t __n) { __dealloc_(__p, __n); }
+ bool operator==(str_alloc<_Tp> const& __rhs) const { return std::addressof(__rhs) == this; }
+};
+
+struct str : basic_string<char, char_traits<char>, str_alloc<char>> {
+ using base = basic_string<char, char_traits<char>, str_alloc<char>>;
+ _LIBCPP_HIDE_FROM_ABI str(str_alloc<char> const& __alloc) : base(__alloc) {}
+ _LIBCPP_HIDE_FROM_ABI string_view view() const { return {this->data(), this->size()}; }
+};
+
+struct image;
+
+struct entry_base {
+ constexpr static size_t __max_sym_len = 512;
+# if defined(PATH_MAX)
+ constexpr static size_t __max_file_len = PATH_MAX;
+# elif defined(MAX_PATH)
+ constexpr static size_t __max_file_len = MAX_PATH;
+# else
+ constexpr static size_t __max_file_len = (1 << 10);
+# endif
+
+ uintptr_t __addr_{};
+ optional<str> __desc_{};
+ optional<str> __file_{};
+ uint_least32_t __line_{};
+ image const* __image_{};
+
+ _LIBCPP_HIDE_FROM_ABI str& assign_desc(str&& __s) { return *(__desc_ = std::move(__s)); }
+ _LIBCPP_HIDE_FROM_ABI str& assign_file(str&& __s) { return *(__file_ = std::move(__s)); }
+
+# if _LIBCPP_HAS_LOCALIZATION
+ _LIBCPP_EXPORTED_FROM_ABI std::ostream& write_to(std::ostream& __os) const;
+ _LIBCPP_EXPORTED_FROM_ABI string to_string() const;
+# endif // _LIBCPP_HAS_LOCALIZATION
+
+ _LIBCPP_HIDE_FROM_ABI uintptr_t adjusted_addr() const;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr static entry_base* of(auto& __s) { return static_cast<entry_base*>(__s); }
+
+ _LIBCPP_HIDE_FROM_ABI ~entry_base() = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr entry_base() = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr entry_base(const entry_base&) = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr entry_base& operator=(const entry_base&) = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr entry_base(entry_base&&) = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr entry_base& operator=(entry_base&&) = default;
+};
+
+} // namespace __stacktrace
+
+class stacktrace_entry {
+ friend _LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, std::stacktrace_entry const& __entry);
+ friend _LIBCPP_HIDE_FROM_ABI inline string to_string(std::stacktrace_entry const& __entry);
+
+ __stacktrace::entry_base __base_{};
+
+public:
+ // (19.6.3.1) Overview [stacktrace.entry.overview]
+ using native_handle_type = uintptr_t;
+
+ // (19.6.3.2) [stacktrace.entry.cons], constructors
+ _LIBCPP_HIDE_FROM_ABI ~stacktrace_entry() noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr stacktrace_entry() noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr stacktrace_entry(const stacktrace_entry&) noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr stacktrace_entry& operator=(const stacktrace_entry&) noexcept = default;
+
+ // (19.6.3.3) [stacktrace.entry.obs], observers
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr native_handle_type native_handle() const noexcept {
+ return __base_.__addr_;
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return native_handle() != 0; }
+
+ // (19.6.3.4) [stacktrace.entry.query], query
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string description() const {
+ return __base_.__desc_ ? string(*__base_.__desc_) : string();
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string source_file() const {
+ return __base_.__file_ ? string(*__base_.__file_) : string();
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI uint_least32_t source_line() const { return __base_.__line_; }
+
+ // (19.6.3.5) [stacktrace.entry.cmp], comparison
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+ return __x.native_handle() == __y.native_handle();
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering
+ operator<=>(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+ return __x.native_handle() <=> __y.native_handle();
+ }
+};
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI string to_string(const stacktrace_entry& __entry);
+
+# if _LIBCPP_HAS_LOCALIZATION
+_LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, std::stacktrace_entry const& __entry) {
+ return __entry.__base_.write_to(__os);
+}
+_LIBCPP_HIDE_FROM_ABI inline string to_string(std::stacktrace_entry const& __entry) {
+ return __entry.__base_.to_string();
+}
+# endif // _LIBCPP_HAS_LOCALIZATION
+
+// (19.6.5)
+// Formatting support [stacktrace.format]:
+// https://github.com/llvm/llvm-project/issues/105257
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <>
+struct _LIBCPP_HIDE_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_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_ENTRY
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index a86d6c6a43d0e..034dd93f1c27c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2014,6 +2014,31 @@ module std [system] {
export *
}
+ module stacktrace {
+ module basic_stacktrace { header "__stacktrace/basic_stacktrace.h" }
+ module stacktrace_entry { header "__stacktrace/stacktrace_entry.h" }
+
+ header "stacktrace"
+ export *
+ // TODO: Workaround for https://github.com/llvm/llvm-project/issues/120108
+ export std.cstddef.byte
+ export std.cstddef.size_t
+ export std.format.escaped_output_table
+ export std.format.formatter
+ export std.format.width_estimation_table
+ export std.functional.function
+ export std.functional.hash
+ export std.iterator.iterator
+ export std.iterator.reverse_iterator
+ export std.memory.allocator
+ export std.memory.allocator_traits
+ export std.memory_resource.polymorphic_allocator
+ export std.new.allocate
+ export std.ostream.basic_ostream
+ export std.type_traits.is_nothrow_constructible
+ export std.vector.vector
+ }
+
module stdexcept {
header "stdexcept"
export *
diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace
new file mode 100644
index 0000000000000..106731d9be2d1
--- /dev/null
+++ b/libcxx/include/stacktrace
@@ -0,0 +1,187 @@
+// -*- 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
+#define _LIBCPP_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
+ // Not yet implemented; see https://github.com/llvm/llvm-project/issues/105257
+ 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 <__stacktrace/basic_stacktrace.h>
+# include <__stacktrace/stacktrace_entry.h>
+# include <compare> // per [stacktrace.syn]
+# endif
+
+# include <version>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
+
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
+#endif // _LIBCPP_STACKTRACE
diff --git a/libcxx/include/version b/libcxx/include/version
index 0fef1bb87cf60..c4fb60e7fba54 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -532,7 +532,9 @@ __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
+# if _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+# define __cpp_lib_stacktrace 202011L
+# endif
# define __cpp_lib_stdatomic_h 202011L
# define __cpp_lib_string_contains 202011L
# define __cpp_lib_string_resize_and_overwrite 202110L
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 3a1d8950c2db0..0998827ebf058 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
@@ -413,6 +413,10 @@
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -534,6 +538,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 +979,9 @@
{'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__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC2Ev', '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 +1133,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 +1314,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 +1516,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'}
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 90166073b135f..f6d454ccb2f23 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
@@ -104,6 +104,10 @@
{'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -225,6 +229,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'}
@@ -622,6 +627,9 @@
{'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__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', '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 +781,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 +962,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'}
@@ -1156,7 +1164,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'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 5855c17cf11ed..15be31d56ab1e 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -75,6 +75,10 @@
{'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -196,6 +200,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'}
@@ -593,6 +598,9 @@
{'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__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', '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'}
@@ -744,6 +752,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'}
@@ -923,7 +932,6 @@
{'is_defined': True, 'name': '_ZNSt3__113random_deviceclEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__113shared_futureIvED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__113shared_futureIvED2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__113__hash_memoryEPKvm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__113shared_futureIvEaSERKS1_', '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'}
@@ -1127,7 +1135,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'}
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index dd7385bf33a42..91d4b051adac4 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -69,9 +69,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/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 984b18321923c..a70199d6f3b17 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -102,6 +102,7 @@ module;
#include <span>
#include <sstream>
#include <stack>
+#include <stacktrace>
#include <stdexcept>
#include <stop_token>
#include <streambuf>
@@ -153,9 +154,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/libcxx/modules/std/stacktrace.inc b/libcxx/modules/std/stacktrace.inc
index c1184087c0df4..19040b729d9f8 100644
--- a/libcxx/modules/std/stacktrace.inc
+++ b/libcxx/modules/std/stacktrace.inc
@@ -8,7 +8,8 @@
//===----------------------------------------------------------------------===//
export namespace std {
-#if 0
+#if _LIBCPP_STD_VER >= 23
+
// [stacktrace.entry], class stacktrace_entry
using std::stacktrace_entry;
@@ -21,9 +22,10 @@ export namespace std {
// [stacktrace.basic.nonmem], non-member functions
using std::swap;
+# if _LIBCPP_HAS_LOCALIZATION
using std::to_string;
-
using std::operator<<;
+# endif // _LIBCPP_HAS_LOCALIZATION
namespace pmr {
using std::pmr::stacktrace;
@@ -31,5 +33,11 @@ export namespace std {
// [stacktrace.basic.hash], hash support
using std::hash;
-#endif
+
+ // [stacktrace.format], formatting support
+ // (Excluded for now; issue #105257 will implement
+ // P2693R1, "Formatting thread::id and stacktrace")
+ // using std::formatter;
+
+#endif // _LIBCPP_STD_VER >= 23
} // namespace std
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index f59fe0e08fccb..aca66727d6442 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,6 +40,11 @@ set(LIBCXX_SOURCES
ryu/d2fixed.cpp
ryu/d2s.cpp
ryu/f2s.cpp
+ stacktrace/images.cpp
+ stacktrace/impl_generic.cpp
+ stacktrace/impl_windows.cpp
+ stacktrace/impl.cpp
+ stacktrace/unwinding.h
stdexcept.cpp
string.cpp
support/runtime/exception_fallback.ipp
diff --git a/libcxx/src/stacktrace/images.cpp b/libcxx/src/stacktrace/images.cpp
new file mode 100644
index 0000000000000..4120ff409bb3c
--- /dev/null
+++ b/libcxx/src/stacktrace/images.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+//
+// OS-specific construction
+//
+
+#include "__config"
+
+#if defined(__APPLE__)
+// MacOS-specific: use the `dyld` loader to access info about loaded Mach-O images.
+# include "stacktrace/images.h"
+# include <algorithm>
+# include <cstdlib>
+# include <dlfcn.h>
+# include <mach-o/dyld.h>
+# include <mach-o/loader.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+images::images() {
+ images_[count_++] = {0uz, 0}; // sentinel at low end
+ images_[count_++] = {~0uz, 0}; // sentinel at high end
+ auto dyld_count = _dyld_image_count();
+ for (unsigned i = 0; i < dyld_count && count_ < k_max_images; i++) {
+ auto& image = images_[count_++];
+ image.slide_ = uintptr_t(_dyld_get_image_vmaddr_slide(i));
+ image.loaded_at_ = uintptr_t(_dyld_get_image_header(i));
+ image.is_main_prog_ = (i == 0);
+ strncpy(image.name_, _dyld_get_image_name(i), sizeof(image.name_));
+ }
+ std::sort(images_.begin(), images_.begin() + count_);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#elif !defined(_WIN32)
+// Non-MacOS and non-Windows, including Linux: assume environment has these headers.
+# include "stacktrace/images.h"
+# include <algorithm>
+# include <cstdlib>
+# include <dlfcn.h>
+# include <link.h>
+# include <unistd.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+int add_image(dl_phdr_info* info, size_t, void* images_v) {
+ auto& imgs = *(images*)images_v;
+ if (imgs.count_ == images::k_max_images) {
+ return 0;
+ }
+ auto is_first = (imgs.count_ == 0);
+ auto& image = imgs.images_.at(imgs.count_++);
+ // Absolute address at which this ELF is loaded
+ image.loaded_at_ = info->dlpi_addr;
+ // This also happens to be the "slide" amount since ELF has zero-relative offsets
+ image.slide_ = info->dlpi_addr;
+ strncpy(image.name_, info->dlpi_name, sizeof(image.name_));
+ // `dl_iterate_phdr` gives us the main program image first
+ image.is_main_prog_ = is_first;
+ if (!image.name_[0] && is_first) {
+ char buf[entry_base::__max_file_len]{0};
+ if (readlink("/proc/self/exe", buf, sizeof(buf)) != -1) { // Ignores errno if error
+ strncpy(image.name_, buf, sizeof(image.name_));
+ }
+ }
+ // If we're at the limit, return nonzero to stop iterating
+ return imgs.count_ == images::k_max_images;
+}
+} // namespace
+
+images::images() {
+ dl_iterate_phdr(add_image, this);
+ images_[count_++] = {0uz, 0}; // sentinel at low end
+ images_[count_++] = {~0uz, 0}; // sentinel at high end
+ std::sort(images_.begin(), images_.begin() + count_);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/stacktrace/images.h b/libcxx/src/stacktrace/images.h
new file mode 100644
index 0000000000000..5c7af94de31d6
--- /dev/null
+++ b/libcxx/src/stacktrace/images.h
@@ -0,0 +1,119 @@
+// -*- 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_IMAGES_H
+#define _LIBCPP_STACKTRACE_IMAGES_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 23
+
+# include <__stacktrace/stacktrace_entry.h>
+# include <array>
+# include <cstdint>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct image;
+struct images;
+
+struct image {
+ uintptr_t loaded_at_{};
+ uintptr_t slide_{};
+ char name_[__stacktrace::entry_base::__max_file_len]{0};
+ bool is_main_prog_{};
+
+ _LIBCPP_HIDE_FROM_ABI bool operator<(image const& __rhs) const {
+ if (loaded_at_ < __rhs.loaded_at_) {
+ return true;
+ }
+ if (loaded_at_ > __rhs.loaded_at_) {
+ return false;
+ }
+ return strcmp(name_, __rhs.name_) < 0;
+ }
+ _LIBCPP_HIDE_FROM_ABI operator bool() const { return name_[0]; }
+};
+
+/**
+ * Contains an array `images_`, which will include `prog_image` objects (see above)
+ * collected in an OS-dependent way. After construction these images will be sorted
+ * according to their load address; there will also be two sentinels with dummy
+ * addresses (0x0000... and 0xFFFF...) to simplify search functions.
+ *
+ * After construction, images_ and count_ look like:
+ * [0] [1] [2] [3] ... [count_ - 1]
+ * (sentinel) foo.exe libc++so.1 libc.so.6 (sentinel)
+ * 0x000000000000 0x000100000000 0x633b00000000 0x7c5500000000 0xffffffffffff
+ */
+struct images {
+ constexpr static size_t k_max_images = 256;
+ std::array<image, k_max_images + 2> images_{}; // space for the L/R sentinels
+ unsigned count_{0}; // image count, including sentinels
+
+ /** An OS-specific constructor is defined. */
+ _LIBCPP_EXPORTED_FROM_ABI images();
+
+ /** Get prog_image by index (0 <= index < count_) */
+ _LIBCPP_HIDE_FROM_ABI image& operator[](size_t __index) {
+ _LIBCPP_ASSERT(__index < count_, "index out of range");
+ return images_.at(__index);
+ }
+
+ /** Image representing the main program, or nullptr if we couldn't find it */
+ _LIBCPP_HIDE_FROM_ABI image* main_prog_image() {
+ for (size_t __i = 1; __i < count_ - 1; __i++) {
+ auto& __image = images_[__i];
+ if (__image.is_main_prog_) {
+ return &__image;
+ }
+ }
+ return nullptr;
+ }
+
+ /** Search the sorted images array for one containing this address. */
+ _LIBCPP_HIDE_FROM_ABI void find(size_t* __index, uintptr_t __addr) {
+ // `index` slides left/right as we search through images.
+ // It's (probably) likely several consecutive entries are from the same image, so
+ // each iteration's `find` uses the same starting point, making it (probably) constant-time.
+ // XXX Is this more efficient in practice than e.g. `std::set` and `upper_bound`?
+ if (*__index < 1) {
+ *__index = 1;
+ }
+ if (*__index > count_ - 1) {
+ *__index = count_ - 1;
+ }
+ while (images_[*__index]) {
+ if (__addr < images_[*__index].loaded_at_) {
+ --*__index;
+ } else if (__addr >= images_[*__index + 1].loaded_at_) {
+ ++*__index;
+ } else {
+ break;
+ }
+ }
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/stacktrace/impl.cpp b/libcxx/src/stacktrace/impl.cpp
new file mode 100644
index 0000000000000..c3b9f906a4cf3
--- /dev/null
+++ b/libcxx/src/stacktrace/impl.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
+#include <string>
+
+#if _LIBCPP_HAS_LOCALIZATION
+# include <iomanip>
+# include <iostream>
+# include <sstream>
+#endif //_LIBCPP_HAS_LOCALIZATION
+
+#include "stacktrace/images.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+#if _LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& entry_base::write_to(ostream& __os) const {
+ // 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) << __addr_;
+ if (__desc_) {
+ __os << ": " << __desc_->view();
+ }
+ if (__file_) {
+ __os << ": " << __file_->view();
+ }
+ if (__line_) {
+ __os << ":" << std::dec << __line_;
+ }
+ return __os;
+}
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& base::write_to(std::ostream& __os) const {
+ auto iters = __entry_iters_();
+ auto count = iters.size();
+ if (!count) {
+ __os << "(empty stacktrace)";
+ } else {
+ for (size_t __i = 0; __i < count; __i++) {
+ // Insert newlines between entries (but not before the first or after the last)
+ if (__i) {
+ __os << '\n';
+ }
+ __os << " frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": "
+ << *(stacktrace_entry const*)(iters.data() + __i);
+ }
+ }
+ return __os;
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string entry_base::to_string() const {
+ stringstream __ss;
+ write_to(__ss);
+ return __ss.str();
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string base::to_string() const {
+ stringstream __ss;
+ write_to(__ss);
+ return __ss.str();
+}
+
+#endif // _LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_HIDE_FROM_ABI uintptr_t entry_base::adjusted_addr() const {
+ auto sub = __image_ ? __image_->slide_ : 0;
+ return __addr_ - sub;
+}
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/impl_generic.cpp b/libcxx/src/stacktrace/impl_generic.cpp
new file mode 100644
index 0000000000000..315e0f21c30aa
--- /dev/null
+++ b/libcxx/src/stacktrace/impl_generic.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
+//
+//===----------------------------------------------------------------------===//
+
+/*
+"Generic" implementation for any platform that doesn't have its own special implementation.
+(Currently this means any platform other than Windows)
+*/
+
+#if !defined(_WIN32)
+
+# include <__config>
+# include <__stacktrace/stacktrace_entry.h>
+
+# include "stacktrace/images.h"
+# include "stacktrace/unwinding.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+base::current_impl(size_t skip, size_t max_depth) {
+ if (!max_depth) [[unlikely]] {
+ return;
+ }
+
+ /*
+ Build up the stacktrace entries and fill out their fields the best we can.
+ An `entry_base` structure looks more or less like:
+
+ __addr_ uintptr_t Instruction address (of a call instruction, faulting insn, ...)
+ __desc_ string "Description" which we'll say is synonymous with "symbol"
+ __file_ string Source filename, or if that's not available, program/library path
+ __line_ int Line number within the source file, or failing that, zero
+ __image_ image* The image, loaded in by the OS, containing that address (program, library)
+
+ On Windows, there are DLLs which take care of all this (dbghelp, psapi), with essentially
+ zero overlap with any other OS, so it's in its own file (impl_windows). Otherwise, i.e.
+ on non-Windows platforms, taking a stacktrace looks like:
+
+ 0. Create the basic_stacktrace, its internal vector, using provided allocator.
+ (This was handled by the `current` member functions inside `basic_stacktrace`.)
+ 1. Collect instruction addresses to build up the entries, observing skip and max_depth.
+ The unwinding library we have available to us should take care of walking the call stack
+ and finding addresses.
+ 2. Resolve addresses into the program images (executable, libraries); normalize the addresses
+ from step 1, as they were seen in the wild, into program-image-relative addresses
+ (i.e. deal with ASLR)
+ 3. Resolve adjusted addresses into their symbols; some environments provide this out of the box
+ (MacOS) and others make this less easy (Linux)
+ 4. To get the source file and line number, we have to dig through debug information (DWARF format);
+ we might need the help of a library.
+ */
+
+ unwind_addrs(*this, skip + 1, max_depth);
+ if (__entry_iters_().size()) {
+ find_images();
+ find_symbols();
+ find_source_locs();
+ }
+}
+
+void base::find_images() {
+ images images;
+ size_t i = 0;
+ for (auto& entry : __entry_iters_()) {
+ images.find(&i, entry.__addr_);
+ if (auto& image = images[i]) {
+ entry.__image_ = ℑ
+ // While we're in this loop, get the executable's path, and tentatively use this for source file.
+ entry.assign_file(__create_str()).assign(image.name_);
+ }
+ }
+}
+
+void base::find_symbols() {
+ // TODO
+}
+
+void base::find_source_locs() {
+ // TODO
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/impl_windows.cpp
new file mode 100644
index 0000000000000..c739f3731c175
--- /dev/null
+++ b/libcxx/src/stacktrace/impl_windows.cpp
@@ -0,0 +1,248 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_WIN32)
+
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+//
+# include <dbghelp.h>
+# include <psapi.h>
+//
+# include <cstring>
+# include <iostream>
+# include <mutex>
+# include <stacktrace>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+struct dll {
+ HMODULE module_{};
+ bool loaded_{};
+
+ explicit dll(char const* name) : module_(LoadLibraryA(name)) {}
+
+ ~dll() {
+ if (module_) {
+ FreeLibrary(module_);
+ }
+ }
+
+ template <typename F>
+ bool get_func(F** func, char const* name) {
+ return ((*func = (F*)(void*)GetProcAddress(module_, name)) != nullptr);
+ }
+};
+
+// clang-format off
+
+struct dbghelp_dll final : dll {
+ IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
+ bool (WINAPI *SymCleanup) (HANDLE);
+ DWORD (WINAPI *SymGetOptions) ();
+ bool (WINAPI *SymGetSearchPath) (HANDLE, char const*, DWORD);
+ bool (WINAPI *SymInitialize) (HANDLE, char const*, bool);
+ DWORD (WINAPI *SymSetOptions) (DWORD);
+ bool (WINAPI *SymSetSearchPath) (HANDLE, char const*);
+ bool (WINAPI *StackWalk64) (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
+ void* (WINAPI *SymFunctionTableAccess64)(HANDLE, DWORD64);
+ bool (WINAPI *SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
+ DWORD64 (WINAPI *SymGetModuleBase64) (HANDLE, DWORD64);
+ bool (WINAPI *SymGetModuleInfo64) (HANDLE, DWORD64, IMAGEHLP_MODULE64*);
+ bool (WINAPI *SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
+ DWORD64 (WINAPI *SymLoadModule64) (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
+
+ dbghelp_dll() : dll("dbghelp.dll") {
+ loaded_ = true
+ && get_func(&ImageNtHeader, "ImageNtHeader")
+ && get_func(&SymCleanup, "SymCleanup")
+ && get_func(&SymGetOptions, "SymGetOptions")
+ && get_func(&SymGetSearchPath, "SymGetSearchPath")
+ && get_func(&SymInitialize, "SymInitialize")
+ && get_func(&SymSetOptions, "SymSetOptions")
+ && get_func(&SymSetSearchPath, "SymSetSearchPath")
+ && get_func(&StackWalk64, "StackWalk64")
+ && get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")
+ && get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")
+ && get_func(&SymGetModuleBase64, "SymGetModuleBase64")
+ && get_func(&SymGetModuleInfo64, "SymGetModuleInfo64")
+ && get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")
+ && get_func(&SymLoadModule64, "SymLoadModule64")
+ ;
+ }
+};
+
+struct psapi_dll final : dll {
+ bool (WINAPI *EnumProcessModules) (HANDLE, HMODULE*, DWORD, DWORD*);
+ bool (WINAPI *GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
+ DWORD (WINAPI *GetModuleBaseName) (HANDLE, HMODULE, char**, DWORD);
+
+ psapi_dll() : dll("psapi.dll") {
+ loaded_ = true
+ && get_func(&EnumProcessModules, "EnumProcessModules")
+ && get_func(&GetModuleInformation, "GetModuleInformation")
+ && get_func(&GetModuleBaseName, "GetModuleBaseNameA")
+ ;
+ }
+};
+
+struct sym_init_scope {
+ dbghelp_dll& dbghelp_;
+ HANDLE proc_;
+
+ sym_init_scope(dbghelp_dll& dbghelp, HANDLE proc)
+ : dbghelp_(dbghelp), proc_(proc) {
+ (*dbghelp_.SymInitialize)(proc_, nullptr, true);
+ }
+ ~sym_init_scope() {
+ (*dbghelp_.SymCleanup)(proc_);
+ }
+};
+
+} // namespace
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+base::current_impl(size_t skip, size_t max_depth) {
+ if (!max_depth) [[unlikely]] {
+ return;
+ }
+
+ static psapi_dll psapi;
+ static dbghelp_dll dbghelp;
+ if (!psapi.loaded_ || !dbghelp.loaded_) { return; }
+
+ // Not thread-safe according to docs
+ static std::mutex api_mutex;
+ std::lock_guard<std::mutex> api_guard(api_mutex);
+
+ HANDLE proc = GetCurrentProcess();
+ HMODULE exe = GetModuleHandle(nullptr);
+ if (!exe) { return; }
+
+ sym_init_scope symscope(dbghelp, proc);
+
+ char sym_path[MAX_PATH * 4]; // arbitrary
+ if (!(*dbghelp.SymGetSearchPath)(proc, sym_path, sizeof(sym_path))) { return; }
+
+ char exe_dir[MAX_PATH];
+ if (!GetModuleFileNameA(nullptr, exe_dir, sizeof(exe_dir))) { return; }
+ size_t exe_dir_len = strlen(exe_dir);
+ while (exe_dir_len > 0 && exe_dir[exe_dir_len - 1] != '\\') { exe_dir[--exe_dir_len] = 0; }
+ if (exe_dir_len > 0) { exe_dir[--exe_dir_len] = 0; } // strip last backslash
+
+ if (!strstr(sym_path, exe_dir)) {
+ (void) strncat(sym_path, ";", sizeof(sym_path) - 1);
+ (void) strncat(sym_path, exe_dir, sizeof(sym_path) - 1);
+ if (!(*dbghelp.SymSetSearchPath)(proc, sym_path)) { return; }
+ }
+
+ IMAGE_NT_HEADERS* nt_headers;
+ if (!(nt_headers = (*dbghelp.ImageNtHeader)(exe))) { return; }
+
+ (*dbghelp.SymSetOptions)(
+ (*dbghelp.SymGetOptions)()
+ | SYMOPT_LOAD_LINES
+ | SYMOPT_UNDNAME);
+
+ auto thread = GetCurrentThread();
+ auto machine = nt_headers->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;
+#if defined(_M_AMD64)
+ frame.AddrPC.Offset = ccx.Rip;
+ frame.AddrStack.Offset = ccx.Rsp;
+ frame.AddrFrame.Offset = ccx.Rbp;
+#elif defined(_M_ARM64)
+ frame.AddrPC.Offset = ccx.Pc;
+ frame.AddrStack.Offset = ccx.Sp;
+ frame.AddrFrame.Offset = ccx.Fp;
+#else
+# warning stacktrace requires x86-64 or ARM64; returned stacktraces will be empty
+ return;
+#endif
+
+ // Skip call to this `current_impl` func
+ ++skip;
+
+ while (max_depth) {
+
+ if (!(*dbghelp.StackWalk64)(
+ machine, proc, thread, &frame, &ccx, nullptr,
+ (void*) dbghelp.SymFunctionTableAccess64,
+ (void*) dbghelp.SymGetModuleBase64,
+ nullptr)) {
+ break;
+ }
+
+ if (skip && skip--) { continue; }
+ if (!frame.AddrPC.Offset) { break; }
+
+ auto& entry = this->__entry_append_();
+
+ // Note: can't differentiate between a signal / exception, or a normal function call.
+ // This assumes the more common (presumably) case of normal function calls, so we'll
+ // always back up 1 byte to get into the previous (calling) instruction.
+ entry.__addr_ = frame.AddrPC.Offset - 1;
+
+ // Get the filename of the module containing this calling instruction, i.e. the program
+ // itself or a DLL. This is used in place of the source filename, if the source filename
+ // cannot be found (missing PDB, etc.). If the source file can be determined this will
+ // be overwritten.
+ IMAGEHLP_MODULE64 mod_info;
+ memset(&mod_info, 0, sizeof(mod_info));
+ mod_info.SizeOfStruct = sizeof(mod_info);
+ if ((*dbghelp.SymGetModuleInfo64)(proc, frame.AddrPC.Offset, &mod_info)) {
+ entry.assign_file(__create_str()).assign(mod_info.LoadedImageName);
+ }
+
+ --max_depth;
+ }
+
+ DWORD need_bytes = 0;
+ HMODULE module_handles[1024] {0};
+ if (!(*psapi.EnumProcessModules)(
+ proc, module_handles, sizeof(module_handles), LPDWORD(&need_bytes))) {
+ return;
+ }
+
+ // Symbols longer than this will be truncated.
+ static constexpr size_t kMaxSymName = 256;
+
+ for (auto& entry : __entry_iters_()) {
+ char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName + 1];
+ auto* sym = (IMAGEHLP_SYMBOL64*)space;
+ sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ sym->MaxNameLength = kMaxSymName;
+ uint64_t symdisp{0};
+ DWORD linedisp{0};
+ IMAGEHLP_LINE64 line;
+ if ((*dbghelp.SymGetSymFromAddr64)(proc, entry.__addr_, &symdisp, sym)) {
+ entry.assign_desc(__create_str()).assign(sym->Name);
+ }
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ if ((*dbghelp.SymGetLineFromAddr64)(proc, entry.__addr_, &linedisp, &line)) {
+ entry.assign_file(__create_str()).assign(line.FileName);
+ entry.__line_ = line.LineNumber;
+ }
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _WIN32
diff --git a/libcxx/src/stacktrace/unwinding.h b/libcxx/src/stacktrace/unwinding.h
new file mode 100644
index 0000000000000..d363ea1bc5a15
--- /dev/null
+++ b/libcxx/src/stacktrace/unwinding.h
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_ADDRS_H
+#define __LIBCPP_STACKTRACE_UNWIND_ADDRS_H
+
+#include <__stacktrace/basic_stacktrace.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+void unwind_addrs(std::__stacktrace::base& base, size_t skip, size_t depth);
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#ifndef _WIN32
+
+# if __has_include(<libunwind.h>)
+# include <libunwind.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE inline void unwind_addrs(base& base, size_t skip, size_t depth) {
+ if (!depth) {
+ return;
+ }
+ unw_context_t cx;
+ unw_getcontext(&cx);
+ unw_cursor_t cur;
+ unw_init_local(&cur, &cx);
+ while (unw_step(&cur) > 0) {
+ if (skip && skip--) {
+ continue;
+ }
+ if (!depth--) {
+ break;
+ }
+ auto& entry = base.__entry_append_();
+ auto& eb = (__stacktrace::entry_base&)entry;
+ unw_get_reg(&cur, UNW_REG_IP, &eb.__addr_);
+ if (!unw_is_signal_frame(&cur)) {
+ --eb.__addr_;
+ }
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+# elif __has_include(<unwind.h>)
+# include <unwind.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct unwind_backtrace {
+ base& base_;
+ 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{0};
+ auto ip = _Unwind_GetIPInfo(ucx, &ipBefore);
+ if (!ip) {
+ return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+ }
+ auto& entry = base_.__entry_append_();
+ auto& eb = (entry_base&)entry;
+ eb.__addr_ = (ipBefore ? ip : ip - 1);
+ 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 inline void unwind_addrs(base& base, size_t skip, size_t depth) {
+ if (!depth) {
+ return;
+ }
+ unwind_backtrace bt{base, skip + 1, depth}; // skip this call as well
+ _Unwind_Backtrace(unwind_backtrace::callback, &bt);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+# else
+# error need <libunwind.h> or <unwind.h>
+# endif
+
+#endif // _WIN32
+
+#endif // __LIBCPP_STACKTRACE_UNWIND_ADDRS_H
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index cb23c7a98de1b..e33d1f07e3fc3 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -926,6 +926,27 @@ stack limits
stack stdexcept
stack tuple
stack version
+stacktrace cctype
+stacktrace climits
+stacktrace compare
+stacktrace cstddef
+stacktrace cstdint
+stacktrace cstdio
+stacktrace cstring
+stacktrace cwchar
+stacktrace cwctype
+stacktrace initializer_list
+stacktrace iosfwd
+stacktrace limits
+stacktrace memory
+stacktrace optional
+stacktrace stdexcept
+stacktrace string
+stacktrace string_view
+stacktrace tuple
+stacktrace typeinfo
+stacktrace utility
+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 d047b29b63cc6..c89c6d50b3a4f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -908,6 +908,27 @@ stack limits
stack stdexcept
stack tuple
stack version
+stacktrace cctype
+stacktrace climits
+stacktrace compare
+stacktrace cstddef
+stacktrace cstdint
+stacktrace cstdio
+stacktrace cstring
+stacktrace cwchar
+stacktrace cwctype
+stacktrace initializer_list
+stacktrace iosfwd
+stacktrace limits
+stacktrace memory
+stacktrace optional
+stacktrace stdexcept
+stacktrace string
+stacktrace string_view
+stacktrace tuple
+stacktrace typeinfo
+stacktrace utility
+stacktrace version
stop_token atomic
stop_token climits
stop_token cstdint
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
new file mode 100644
index 0000000000000..f388f4fe62425
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+// (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>
+
+#include "test_macros.h"
+
+// Disable TCO for calls into, and out from, the annotated function.
+#define STACKTRACE_AVOID_OPT TEST_NO_TAIL_CALLS_IN TEST_NO_TAIL_CALLS_OUT TEST_NOINLINE
+
+// Some non-inlinable functions to help contrive different stacktraces:
+// main calls the "middle" funcs, and those both call "top".
+// We'll consider main the "bottom" func, even though there are other functions
+// like `_start` which call main; those are trimmed via `max_depth` argument.
+
+STACKTRACE_AVOID_OPT std::stacktrace top(size_t skip, size_t depth) { return std::stacktrace::current(skip, depth); }
+STACKTRACE_AVOID_OPT std::stacktrace middle1(size_t skip, size_t depth) { return top(skip, depth); }
+STACKTRACE_AVOID_OPT std::stacktrace middle2(size_t skip, size_t depth) { return top(skip, depth); }
+
+STACKTRACE_AVOID_OPT int main(int, char**) {
+ // Collect a few different stacktraces and test `operator==` and `operator!=`.
+
+ std::stacktrace st0; // default-initializable empty stacktrace
+ static_assert(noexcept(st0 == st0)); // verify noexcept-ness
+ static_assert(noexcept(st0 != st0)); // verify noexcept-ness
+ assert(st0 == st0); // trivial: self-equality
+
+ std::stacktrace st1a = top(0, 2); // st1a = [top, main]
+ assert(st1a == st1a); // trivial: self-equality
+ assert(st1a != st0); //
+ std::stacktrace st2a = middle1(0, 3); // st2a = [top, middle1, main]
+ assert(st2a == st2a); //
+ assert(st1a != st2a); //
+ std::stacktrace st2b = middle2(0, 3); // st2b = [top, middle2, main]
+ assert(st2b == st2b); //
+ assert(st2a != st2b); //
+
+ // Verify two equivalent stacktrace instances are equal, even if not "same".
+ // For both, we'll take only two entries, which should be equivalent.
+ std::stacktrace st3a = middle1(0, 2); // st3a = [top, middle1]
+ std::stacktrace st3b = middle1(0, 2); // st3b = [top, middle1]
+ assert(st3a == st3b);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp
new file mode 100644
index 0000000000000..6c03bf804b4bf
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+// (19.6.4.4) Comparisons [stacktrace.basic.cmp]
+// 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.
+
+#include <cassert>
+#include <stacktrace>
+
+#include "test_macros.h"
+
+// Disable TCO for calls into, and out from, the annotated function.
+#define STACKTRACE_AVOID_OPT TEST_NO_TAIL_CALLS_IN TEST_NO_TAIL_CALLS_OUT TEST_NOINLINE
+
+// Some non-inlinable functions to help contrive different stacktraces:
+// main calls the "middle" funcs, and those both call "top".
+// We'll consider main the "bottom" func, even though there are other functions
+// like `_start` which call main; those are trimmed via `max_depth` argument.
+
+STACKTRACE_AVOID_OPT std::stacktrace top() { return std::stacktrace::current(); }
+STACKTRACE_AVOID_OPT std::stacktrace middle1() { return top(); }
+STACKTRACE_AVOID_OPT std::stacktrace middle2() { return top(); }
+
+STACKTRACE_AVOID_OPT int main(int, char**) {
+ // Collect a few different stacktraces and test `operator<=>`.
+
+ auto st1a = top(); // [top, main, ...]
+ auto st1b = st1a;
+
+ static_assert(noexcept(st1a <=> st1b));
+
+ assert(st1a == st1b);
+ auto st2a = middle1(); // [top, middle1, main, ...]
+ assert(st1a != st2a);
+ std::stacktrace empty; // []
+ auto st2b = middle2(); // [top, middle2, main, ...]
+ assert(st2a != st2b);
+
+ // empty: []
+ // st1a: [top, main, ...]
+ // st1b: [top, main, ...] (copy of st1a)
+ // st2a: [top, middle1, main:X, ...]
+ // st2b: [top, middle2, 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/assert.current.no_overflow.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
new file mode 100644
index 0000000000000..50866ff5902e3
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.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, has-unix-headers, libcpp-hardening-mode={{extensive|debug}}
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+// XFAIL: availability-stacktrace-missing
+
+// Hardened requirements for the `current` call with given `skip` and `max_depth` amounts:
+// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3697r0.html#basic_stacktrace
+// Specifically: "Hardened preconditions: skip <= skip + max_depth is true."
+//
+// (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 <stacktrace>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ TEST_LIBCPP_ASSERT_FAILURE(
+ std::stacktrace::current(1, 0xffffffffffffffff), "sum of skip and max_depth overflows size_type");
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
new file mode 100644
index 0000000000000..2b3baffefeb50
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.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
+// XFAIL: availability-stacktrace-missing
+
+// (19.6.4.2): [stacktrace.basic.cons], creation and assignment
+// basic_stacktrace(const basic_stacktrace& other);
+// basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+// basic_stacktrace& operator=(const basic_stacktrace& other);
+
+#include <cassert>
+#include <stacktrace>
+
+#include "../test_allocs.h"
+
+void test_copy_construct() {
+ std::stacktrace a = std::stacktrace::current();
+ std::stacktrace b{a};
+ assert(a == b);
+}
+
+void test_copy_assign() {
+ {
+ using A =
+ TestAlloc<std::stacktrace_entry,
+ /*_KNoExCtors=*/true,
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/false,
+ /*_KAlwaysEqual=*/false>;
+ std::basic_stacktrace<A> s1 = std::basic_stacktrace<A>::current();
+ std::basic_stacktrace<A> s2{s1};
+ assert(s1 == s2);
+ A a1 = s1.get_allocator();
+ A a2 = s2.get_allocator();
+ // Allocator should not propagate
+ assert(a1 != a2);
+ }
+ {
+ using A =
+ TestAlloc<std::stacktrace_entry,
+ /*_KNoExCtors=*/true,
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/true,
+ /*_KAlwaysEqual=*/false>;
+ std::basic_stacktrace<A> s1 = std::basic_stacktrace<A>::current();
+ std::basic_stacktrace<A> s2{s1};
+ assert(s1 == s2);
+ A a1 = s1.get_allocator();
+ A a2 = s2.get_allocator();
+ // Allocator should propagate
+ assert(a1 == a2);
+ }
+ {
+ using A =
+ TestAlloc<std::stacktrace_entry,
+ /*_KNoExCtors=*/true,
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/false,
+ /*_KAlwaysEqual=*/true>;
+ std::basic_stacktrace<A> s1 = std::basic_stacktrace<A>::current();
+ std::basic_stacktrace<A> s2{s1};
+ assert(s1 == s2);
+ A a1 = s1.get_allocator();
+ A a2 = s2.get_allocator();
+ // Allocator should propagate
+ assert(a1 == a2);
+ }
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ test_copy_construct();
+ test_copy_assign();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
new file mode 100644
index 0000000000000..131e6ca2ad29b
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+// (19.6.4.2): [stacktrace.basic.cons], creation and assignment
+// basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+#include "../test_allocs.h"
+
+void test_default_construct() {
+ std::stacktrace st;
+ assert(st.empty()); // Postconditions: empty() is true.
+
+ using A1 = std::allocator<std::stacktrace_entry>;
+ assert(std::basic_stacktrace<A1>().empty());
+}
+
+void test_default_construct_noexcept() {
+ static_assert(noexcept(std::stacktrace()));
+
+ using A1 = std::allocator<std::stacktrace_entry>;
+ static_assert(std::is_nothrow_default_constructible_v<A1>); // std::allocator is noexcept constructible
+ static_assert(noexcept(std::basic_stacktrace<A1>())); // therefore this ctor should also be noexcept
+
+ using A2 =
+ TestAlloc<std::stacktrace_entry,
+ /*_KNoExCtors=*/false,
+ // Don't care about the following:
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/true,
+ /*_KAlwaysEqual=*/true>;
+ static_assert(!std::is_nothrow_default_constructible_v<A2>); // A2 is not noexcept constructible
+ static_assert(!noexcept(std::basic_stacktrace<A2>())); // this ctor should not be noexcept
+}
+
+int main(int, char**) {
+ test_default_construct();
+ test_default_construct_noexcept();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
new file mode 100644
index 0000000000000..f28def7e70b6e
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+#include "../test_allocs.h"
+
+void test_construct_with_alloc() {
+ std::stacktrace st;
+ assert(st.empty());
+}
+
+void test_construct_with_alloc_noexcept() {
+ static_assert(noexcept(std::stacktrace()));
+
+ using A1 = std::allocator<std::stacktrace_entry>;
+ static_assert(std::is_nothrow_constructible_v<A1>);
+ using A2 = TestAlloc<std::stacktrace_entry, false, true, true, true>;
+ static_assert(!std::is_nothrow_constructible_v<A2>);
+
+ static_assert(!noexcept(std::basic_stacktrace<A2>(A2())));
+}
+
+int main(int, char**) {
+ test_construct_with_alloc();
+ test_construct_with_alloc_noexcept();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
new file mode 100644
index 0000000000000..bee38f57ffad0
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.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: -O0 -g
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+*/
+
+#include <cassert>
+#include <cstdint>
+#include <stacktrace>
+
+uint32_t test1_line;
+uint32_t test2_line;
+uint32_t main_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() {
+ main_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ 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().ends_with("current_no_args.pass.cpp"));
+ // assert(st[0].source_line() == test1_line);
+ assert(st[1]);
+ assert(st[1].native_handle());
+ // assert(st[1].description().contains("test2"));
+ // assert(st[1].source_file().ends_with("current_no_args.pass.cpp"));
+ // assert(st[1].source_line() == test2_line);
+ assert(st[2]);
+ assert(st[2].native_handle());
+ // assert(st[2].description().contains("test_current"));
+ // assert(st[2].source_file().ends_with("current_no_args.pass.cpp"));
+ // assert(st[2].source_line() == main_line);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ static_assert(noexcept(std::stacktrace::current()));
+ test_current();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
new file mode 100644
index 0000000000000..d0bf282d5fccd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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**) {
+ static_assert(noexcept(std::stacktrace::current(0)));
+ test_current_with_skip();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
new file mode 100644
index 0000000000000..300f95e8e97b3
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: no-localization
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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 <__config_site>
+#if _LIBCPP_HAS_LOCALIZATION
+
+# include <cassert>
+# include <iostream>
+# include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
+ // current stack is: [this function, main, (possibly something else, such as libc _start)]
+ // so it's probably 3 functions deep -- but certainly at least 2 deep.
+ std::stacktrace_entry entry;
+ {
+ auto st1 = std::stacktrace::current(0);
+ std::cout << st1 << '\n';
+ assert(st1.size() >= 2);
+ auto it1 = st1.begin();
+ ++it1;
+ entry = *it1; // represents our caller, `main`
+ }
+
+ // get current trace again, but skip the 1st
+ auto st2 = std::stacktrace::current(1);
+ std::cout << st2 << '\n';
+ assert(st2.size() >= 1);
+ auto it2 = st2.begin();
+ assert(*it2 == entry);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ static_assert(noexcept(std::stacktrace::current(0, 0)));
+ test_current_with_skip_depth();
+ return 0;
+}
+
+#else
+int main() { return 0; }
+#endif // _LIBCPP_HAS_LOCALIZATION
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
new file mode 100644
index 0000000000000..a659a515231f4
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ basic_stacktrace(basic_stacktrace&& other) noexcept;
+ basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+ 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>
+#include <utility>
+
+#include "../test_allocs.h"
+
+void test_move_construct() {
+ auto a = std::stacktrace::current();
+ std::stacktrace b{a};
+ assert(a == b);
+}
+
+void test_move_assign() {
+ {
+ using A =
+ TestAlloc<std::stacktrace_entry,
+ /*_KNoExCtors=*/true,
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/false,
+ /*_KAlwaysEqual=*/false>;
+ auto s0 = std::basic_stacktrace<A>::current();
+ std::basic_stacktrace<A> s1{s0};
+ std::basic_stacktrace<A> s2(std::move(s0));
+ assert(s1 == s2);
+ auto a1 = s1.get_allocator();
+ auto a2 = s2.get_allocator();
+ // Allocator should not propagate
+ assert(a1 != a2);
+ }
+ {
+ using A =
+ TestAlloc<std::stacktrace_entry,
+ /*_KNoExCtors=*/true,
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/true,
+ /*_KAlwaysEqual=*/false>;
+ auto s0 = std::basic_stacktrace<A>::current();
+ std::basic_stacktrace<A> s1{s0};
+ std::basic_stacktrace<A> s2(std::move(s0));
+ auto a1 = s1.get_allocator();
+ auto a2 = s2.get_allocator();
+ // Allocator should propagate
+ assert(a1 == a2);
+ }
+ {
+ using A =
+ TestAlloc<std::stacktrace_entry,
+ /*_KNoExCtors=*/true,
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/false,
+ /*_KAlwaysEqual=*/true>;
+ auto s0 = std::basic_stacktrace<A>::current();
+ std::basic_stacktrace<A> s1{s0};
+ std::basic_stacktrace<A> s2(std::move(s0));
+ auto a1 = s1.get_allocator();
+ auto a2 = s2.get_allocator();
+ // Allocator should propagate
+ assert(a1 == a2);
+ }
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ test_move_construct();
+ test_move_assign();
+ 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..2b5cdc2256d9c
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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]).
+*/
+
+#include <cassert>
+#include <functional>
+#include <stacktrace>
+
+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/swap.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
new file mode 100644
index 0000000000000..6307ced797302
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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>
+
+#include "../test_allocs.h"
+
+int main(int, char**) {
+ std::stacktrace empty;
+ auto a = std::stacktrace::current();
+ std::stacktrace b(empty);
+ assert(!a.empty());
+ assert(b.empty());
+
+ a.swap(b);
+ assert(a.empty());
+ assert(!b.empty());
+
+ // Check `noexcept`: `swap` is noexcept if either:
+ // (1) the allocator propagates on swap
+ // (2) if instances of that allocator type are always equal.
+
+ // `AllocPropagate` satisfies the first (but not the second); stacktrace swap should be noexcept
+ AllocPropagate<std::stacktrace_entry> prop1;
+ AllocPropagate<std::stacktrace_entry> prop2;
+ auto prop_st1 = std::basic_stacktrace<decltype(prop1)>(prop1);
+ auto prop_st2 = std::basic_stacktrace<decltype(prop2)>(prop2);
+ static_assert(noexcept(prop_st1.swap(prop_st2)));
+
+ // `AllocNoPropagate` satisfies neither; stacktrace swap should not be noexcept
+ AllocNoPropagate<std::stacktrace_entry> no_prop1;
+ AllocNoPropagate<std::stacktrace_entry> no_prop2;
+ auto no_prop_st1 = std::basic_stacktrace<decltype(no_prop1)>(no_prop1);
+ auto no_prop_st2 = std::basic_stacktrace<decltype(no_prop2)>(no_prop2);
+ static_assert(!noexcept(no_prop_st1.swap(no_prop_st2)));
+
+ // `AllocAlwaysEqual` satisfies second; stacktrace swap should be noexcept
+ AllocAlwaysEqual<std::stacktrace_entry> always_eq1;
+ AllocAlwaysEqual<std::stacktrace_entry> always_eq2;
+ auto always_eq_st1 = std::basic_stacktrace<decltype(always_eq1)>(always_eq1);
+ auto always_eq_st2 = std::basic_stacktrace<decltype(always_eq2)>(always_eq2);
+ static_assert(noexcept(always_eq_st1.swap(always_eq_st2)));
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.cpp
new file mode 100644
index 0000000000000..790d408407336
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// UNSUPPORTED: no-localization
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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 <__config_site>
+#if _LIBCPP_HAS_LOCALIZATION
+
+# 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;
+}
+
+#else
+int main() { return 0; }
+#endif // _LIBCPP_HAS_LOCALIZATION
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..792441921adbd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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>
+
+#include "../test_allocs.h"
+
+int main(int, char**) {
+ std::stacktrace empty;
+ auto a = std::stacktrace::current();
+ std::stacktrace b(empty);
+ assert(!a.empty());
+ assert(b.empty());
+
+ std::swap(a, b);
+ assert(a.empty());
+ assert(!b.empty());
+
+ // `AllocPropagate` satisfies the first (but not the second); stacktrace swap should be noexcept
+ AllocPropagate<std::stacktrace_entry> prop1;
+ AllocPropagate<std::stacktrace_entry> prop2;
+ auto prop_st1 = std::basic_stacktrace<decltype(prop1)>(prop1);
+ auto prop_st2 = std::basic_stacktrace<decltype(prop2)>(prop2);
+ static_assert(noexcept(std::swap(prop_st1, prop_st2)));
+
+ // `AllocNoPropagate` satisfies neither; stacktrace swap should not be noexcept
+ AllocNoPropagate<std::stacktrace_entry> no_prop1;
+ AllocNoPropagate<std::stacktrace_entry> no_prop2;
+ auto no_prop_st1 = std::basic_stacktrace<decltype(no_prop1)>(no_prop1);
+ auto no_prop_st2 = std::basic_stacktrace<decltype(no_prop2)>(no_prop2);
+ static_assert(!noexcept(std::swap(no_prop_st1, no_prop_st2)));
+
+ // `AllocAlwaysEqual` satisfies second; stacktrace swap should be noexcept
+ AllocAlwaysEqual<std::stacktrace_entry> always_eq1;
+ AllocAlwaysEqual<std::stacktrace_entry> always_eq2;
+ auto always_eq_st1 = std::basic_stacktrace<decltype(always_eq1)>(always_eq1);
+ auto always_eq_st2 = std::basic_stacktrace<decltype(always_eq2)>(always_eq2);
+ static_assert(noexcept(std::swap(always_eq_st1, always_eq_st2)));
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.pass.cpp
new file mode 100644
index 0000000000000..4630622b7bd8e
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.pass.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
+// UNSUPPORTED: no-localization
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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 <__config_site>
+#if _LIBCPP_HAS_LOCALIZATION
+
+# include <cassert>
+# include <iostream>
+# include <stacktrace>
+
+int main(int, char**) {
+ auto a = std::stacktrace::current();
+ auto astr = std::to_string(a);
+ std::cerr << astr << '\n';
+
+ // assert(std::to_string(a[0]).contains("main"));
+ assert(std::to_string(a[0]).contains("to_string.pass"));
+
+ // assert(std::to_string(a).contains("main"));
+ assert(std::to_string(a).contains("to_string.pass"));
+
+ return 0;
+}
+
+#else
+int main() { return 0; }
+#endif // _LIBCPP_HAS_LOCALIZATION
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..6084f3995b2c0
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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..3206879964134
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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(noexcept(st.begin()));
+ static_assert(noexcept(st.end()));
+ 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..a3628e454b9bc
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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(noexcept(st.cbegin()));
+ static_assert(noexcept(st.cend()));
+ 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..b211fb753a688
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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(noexcept(st.crbegin()));
+ static_assert(noexcept(st.crend()));
+ 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..837e0bbf80acf
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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;
+ static_assert(noexcept(st.empty()));
+ 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..243073efae0bd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ allocator_type get_allocator() const noexcept;
+*/
+
+#include <cassert>
+#include <concepts>
+#include <stacktrace>
+
+int main(int, char**) {
+ std::stacktrace st;
+ static_assert(noexcept(st.get_allocator()));
+ 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..3ba11885eff0b
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ size_type max_size() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <vector>
+
+_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(noexcept(st.max_size()));
+ assert(st.max_size() == (std::vector<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..bce77a7047f40
--- /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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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..471367e19436f
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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(noexcept(st.rbegin()));
+ static_assert(noexcept(st.rend()));
+ 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..03e003c6d2c0c
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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;
+ static_assert(noexcept(st.size()));
+ 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
new file mode 100644
index 0000000000000..270ea3494e0bb
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
@@ -0,0 +1,143 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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);
+}
+
+*/
+
+#include <cassert>
+#include <iterator>
+#include <memory>
+#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.
+
+ 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()).rbegin())>);
+ static_assert(std::is_same_v<CRI, decltype(S(A()).crbegin())>);
+
+ 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/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
new file mode 100644
index 0000000000000..0b05c3b09fcfd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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 <cstdint>
+#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);
+
+ static_assert(noexcept(a == b));
+
+ assert(a == b);
+ assert(a != c);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
new file mode 100644
index 0000000000000..7f0340f62687e
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (19.6.3.5) Comparison [stacktrace.entry.cmp]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ // [stacktrace.entry.cmp], comparison
+ friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+ const stacktrace_entry& y) noexcept;
+*/
+
+#include <cassert>
+#include <cstdint>
+#include <stacktrace>
+#include <utility>
+
+namespace {
+int func1() { return 41; }
+int func2() { return 42; }
+} // namespace
+
+int main(int, char**) {
+ auto addr1 = uintptr_t(&func1);
+ auto addr2 = uintptr_t(&func2);
+ assert(addr1 != addr2);
+ if (addr1 > addr2) {
+ std::swap(addr1, addr2);
+ }
+
+ std::stacktrace_entry a;
+ std::stacktrace_entry b;
+ std::stacktrace_entry c;
+
+ *(uintptr_t*)(&a) = uintptr_t(addr1);
+ *(uintptr_t*)(&b) = uintptr_t(addr1);
+ *(uintptr_t*)(&c) = uintptr_t(addr2);
+
+ static_assert(noexcept(a <=> b));
+
+ 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/copy_assign.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..93198a4ac64a4
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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**) {
+ static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
+
+ auto e1 = std::stacktrace::current()[0];
+ std::stacktrace_entry e2;
+ static_assert(noexcept(e2 = e1));
+ e2 = e1;
+ assert(e2 == e1);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
new file mode 100644
index 0000000000000..822047c65d44b
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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**) {
+ static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
+
+ auto e1 = std::stacktrace::current()[0];
+ static_assert(noexcept(std::stacktrace_entry(e1)));
+ std::stacktrace_entry e2(e1);
+ assert(e2 == e1);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp
new file mode 100644
index 0000000000000..6b6f0db515971
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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>
+
+int main(int, char**) {
+ static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
+
+ std::stacktrace_entry entry;
+ // "Postconditions: *this is empty."
+ assert(!entry);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
new file mode 100644
index 0000000000000..1975e4afbae15
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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 e;
+ static_assert(noexcept(e.native_handle()));
+ assert(e.native_handle() == 0);
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
new file mode 100644
index 0000000000000..5d58fe1e80923
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (19.6.3.3) Observers [stacktrace.entry.obs]
+
+namespace std {
+ class stacktrace_entry {
+ // [stacktrace.entry.obs], observers
+ constexpr explicit operator bool() const noexcept;
+*/
+
+#include <cassert>
+#include <cstdint>
+#include <stacktrace>
+
+int main(int, char**) {
+ std::stacktrace_entry e;
+ // "Returns: false if and only if *this is empty."
+ assert(!e);
+ // Now set addr to something nonzero
+ *(uintptr_t*)(&e) = uintptr_t(&main);
+ assert(e.native_handle() == uintptr_t(&main));
+ assert(e);
+
+ static_assert(noexcept(bool(e)));
+
+ 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..19783b031dbf0
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-stacktrace-missing
+
+#include <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;
+
+ // [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;
+ };
+}
+*/
+
+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>);
+
+ // using native_handle_type = implementation-defined;
+ static_assert(sizeof(std::stacktrace_entry::native_handle_type) >= sizeof(void*));
+
+ 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..a6f9be7ab92c4
--- /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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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..83b7784038a73
--- /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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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..70b04ada147af
--- /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
+// XFAIL: availability-stacktrace-missing
+
+/*
+ (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/only_uses_allocator.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
new file mode 100644
index 0000000000000..f733ae5786881
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+// UNSUPPORTED: asan, msan, tsan, hwasan, sanitizer-new-delete
+// XFAIL: availability-stacktrace-missing
+
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <memory>
+#include <stacktrace>
+
+#include "test_allocs.h"
+
+/*
+ * This file includes tests which ensure any allocations performed by `basic_stacktrace`
+ * are done via the user-provided allocator. We intercept the usual ways to allocate,
+ * counting the number of calls, through and not through the allocator.
+ * (This won't work properly with sanitizers, hence the `UNSUPPORTED` above.)
+ */
+
+unsigned new_count = 0;
+unsigned del_count = 0;
+unsigned custom_alloc = 0;
+unsigned custom_dealloc = 0;
+
+void* operator new(size_t sz) {
+ ++new_count;
+ auto* ret = malloc(sz);
+ return ret;
+}
+
+void* operator new[](size_t sz) {
+ ++new_count;
+ auto* ret = malloc(sz);
+ return ret;
+}
+
+void operator delete(void* ptr) noexcept {
+ ++del_count;
+ free(ptr);
+}
+
+void operator delete(void* ptr, size_t) noexcept {
+ ++del_count;
+ free(ptr);
+}
+
+void operator delete[](void* ptr) noexcept {
+ ++del_count;
+ free(ptr);
+}
+
+void operator delete[](void* ptr, size_t) noexcept {
+ ++del_count;
+ free(ptr);
+}
+
+template <typename T>
+struct test_alloc : std::allocator<T> {
+ using base = std::allocator<T>;
+
+ template <typename U>
+ struct rebind {
+ using other = test_alloc<U>;
+ };
+
+ T* allocate(size_t n) {
+ ++custom_alloc;
+ auto* ret = base::allocate(n);
+ return ret;
+ }
+
+ std::allocation_result<T*, size_t> allocate_at_least(size_t n) {
+ ++custom_alloc;
+ auto ret = base::allocate_at_least(n);
+ return ret;
+ }
+
+ void deallocate(T* p, size_t n) {
+ ++custom_dealloc;
+ base::deallocate(p, n);
+ }
+};
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ (void)std::stacktrace::current();
+
+ // Clear these counters in case anything was created/deleted prior to `main`,
+ // and in case taking a stacktrace involved initialization of objects in bss
+ // or some other space.
+ new_count = del_count = 0;
+
+ {
+ using A = test_alloc<std::stacktrace_entry>;
+ A alloc;
+ auto st = std::basic_stacktrace<A>::current(alloc);
+ // Ensure allocator was called at some point
+ assert(custom_alloc > 0);
+ // Exit this scope to destroy stacktrace and allocator
+ }
+
+ assert(custom_alloc == new_count); // All objects should have come from allocator,
+ assert(custom_alloc == custom_dealloc); // and all allocations should be deallocated
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/simple.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/simple.pass.cpp
new file mode 100644
index 0000000000000..69f67e244742a
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/simple.pass.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: -O0 -g
+// UNSUPPORTED: no-localization
+// XFAIL: availability-stacktrace-missing
+
+/*
+This isn't really a test; it simply takes a stacktrace and prints to stdout,
+so we can see what a stacktrace actually contains and looks like when printed.
+*/
+
+#include <__config_site>
+#if _LIBCPP_HAS_LOCALIZATION
+
+# include <cassert>
+# include <iostream>
+# include <stacktrace>
+
+int main(int, char**) {
+ std::cout << std::stacktrace::current() << '\n';
+ return 0;
+}
+
+#else
+int main() { return 0; }
+#endif // _LIBCPP_HAS_LOCALIZATION
diff --git a/libcxx/test/std/diagnostics/stacktrace/test_allocs.h b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
new file mode 100644
index 0000000000000..682874dcdba25
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+/*
+Allocator class useful for testing various propagation, always-equal scenarios.
+*/
+
+#ifndef _LIBCPP_STACKTRACE_TEST_ALLOCS_H
+#define _LIBCPP_STACKTRACE_TEST_ALLOCS_H
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+
+template <typename T, bool _KNoExCtors, bool _KNoExAlloc, bool _KPropagate, bool _KAlwaysEqual>
+struct TestAlloc {
+ using size_type = size_t;
+ using value_type = T;
+ using pointer = T*;
+ using const_pointer = T const*;
+
+ using Self = TestAlloc<T, _KNoExCtors, _KNoExAlloc, _KPropagate, _KAlwaysEqual>;
+
+ template <typename U>
+ using Other = TestAlloc<U, _KNoExCtors, _KNoExAlloc, _KPropagate, _KAlwaysEqual>;
+
+ using propagate_on_container_copy_assignment = typename std::bool_constant<_KPropagate>;
+ using propagate_on_container_move_assignment = typename std::bool_constant<_KPropagate>;
+ using propagate_on_container_swap = typename std::bool_constant<_KPropagate>;
+ using is_always_equal = typename std::bool_constant<_KAlwaysEqual>;
+
+ auto select_on_container_copy_construction(this auto& self) { return _KPropagate ? self : Self(); }
+
+ template <typename U>
+ struct rebind {
+ using other = Other<U>;
+ };
+
+ static std::shared_ptr<std::allocator<std::byte>> new_alloc() {
+ return std::make_shared<std::allocator<std::byte>>();
+ }
+
+ static std::shared_ptr<std::allocator<std::byte>> global_alloc() {
+ static auto ret = new_alloc();
+ return ret;
+ }
+
+ /** Type-erased allocator used for servicing allocate and deallocate.
+ Two `TestAlloc`'s are equal IFF they contain the same alloc pointer.
+ Always-equal `TestAlloc`'s get a pointer to a shared `global_alloc`. */
+ std::shared_ptr<std::allocator<std::byte>> alloc_;
+
+ /** Instances are equal IFF they have the same alloc pointer (even if this is "always_equals",
+ since such instances point to the global alloc). */
+ bool operator==(auto const& rhs) const noexcept { return alloc_.get() == rhs.alloc_.get(); }
+
+ /** Construct with a new alloc, or, if always-equal, the global alloc. */
+ TestAlloc() noexcept(_KNoExCtors) : alloc_(_KAlwaysEqual ? global_alloc() : new_alloc()) {}
+
+ template <typename U>
+ TestAlloc(Other<U> const& rhs) : alloc_(rhs.alloc_) {}
+
+ template <typename U>
+ TestAlloc& operator=(Other<U> const& rhs) {
+ alloc_ = rhs.alloc_;
+ }
+
+ std::allocator<T>& alloc() { return *(std::allocator<T>*)alloc_.get(); }
+
+ T* allocate(size_t n) noexcept(_KNoExAlloc) { return alloc().allocate(n); }
+ auto allocate_at_least(size_t n) noexcept(_KNoExAlloc) { return alloc().allocate_at_least(n); }
+ void deallocate(T* ptr, size_t n) noexcept(_KNoExAlloc) { return alloc().deallocate(ptr, n); }
+};
+
+// For convenience and readability:
+
+template <typename T>
+using AllocPropagate =
+ TestAlloc<T,
+ /*_KNoExCtors=*/true,
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/true,
+ /*_KAlwaysEqual=*/false>;
+
+template <typename T>
+using AllocNoPropagate =
+ TestAlloc<T,
+ /*_KNoExCtors=*/true,
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/false,
+ /*_KAlwaysEqual=*/false>;
+
+template <typename T>
+using AllocAlwaysEqual =
+ TestAlloc<T,
+ /*_KNoExCtors=*/true,
+ /*_KNoExAlloc=*/true,
+ /*_KPropagate=*/true,
+ /*_KAlwaysEqual=*/true>;
+
+#endif
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..3ffc575cb225a
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// 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) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+# 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 when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE' is not met!"
+# 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) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE
+# 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 when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE' is not met!"
+# endif
+# endif
+
+#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 05af1fb0cf14b..ff323e06febf1 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
@@ -5961,7 +5961,7 @@
# error "__cpp_lib_sstream_from_string_view should not be defined before c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE
# ifndef __cpp_lib_stacktrace
# error "__cpp_lib_stacktrace should be defined in c++23"
# endif
@@ -5970,7 +5970,7 @@
# endif
# else
# ifdef __cpp_lib_stacktrace
-# error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
+# error "__cpp_lib_stacktrace should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE' is not met!"
# endif
# endif
@@ -7923,7 +7923,7 @@
# error "__cpp_lib_sstream_from_string_view should have the value 202306L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE
# ifndef __cpp_lib_stacktrace
# error "__cpp_lib_stacktrace should be defined in c++26"
# endif
@@ -7932,7 +7932,7 @@
# endif
# else
# ifdef __cpp_lib_stacktrace
-# error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
+# error "__cpp_lib_stacktrace should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE' is not met!"
# endif
# endif
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index c4e1600572456..5f00c1f994662 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -360,6 +360,24 @@ inline Tp const& DoNotOptimize(Tp const& value) {
#define TEST_NOINLINE
#endif
+// Disables tail-call optimization for "outbound" calls
+// performed in the function annotated with this attribute.
+#if __has_cpp_attribute(_Clang::__disable_tail_calls__)
+#define TEST_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
+#elif __has_cpp_attribute(__gnu__::__optimize__)
+#define TEST_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
+#else
+#define TEST_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 TEST_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
+#else
+#define TEST_NO_TAIL_CALLS_IN
+#endif
+
#ifdef _WIN32
#define TEST_NOT_WIN32(...) ((void)0)
#else
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f6f252751b3e3..bb549d2510b47 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1318,7 +1318,8 @@ def add_version_header(tc):
"name": "__cpp_lib_stacktrace",
"values": {"c++23": 202011},
"headers": ["stacktrace"],
- "unimplemented": True,
+ "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_STACKTRACE",
+ "libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_STACKTRACE",
},
{
"name": "__cpp_lib_starts_ends_with",
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index d06271a7908cc..410ffce8795a2 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",
"stdfloat",
"text_encoding",
]))
@@ -198,6 +197,7 @@ def __hash__(self) -> int:
"print": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14, c++17, c++20, availability-fp_to_chars-missing", # TODO PRINT investigate
"semaphore": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"shared_mutex": "// UNSUPPORTED: no-threads, c++03, c++11",
+ "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",
"thread": "// UNSUPPORTED: no-threads, c++03",
@@ -244,6 +244,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/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 7d6e78de343c5..55e1d258f0f6f 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -889,4 +889,12 @@ def check_gdb(cfg):
cfg.available_features,
),
),
+ # Tests that require std::[basic_]stacktrace in the built library
+ Feature(
+ name="availability-stacktrace-missing",
+ when=lambda cfg: BooleanExpression.evaluate(
+ "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-22)",
+ cfg.available_features,
+ ),
+ ),
]
>From e64e551789db21d091711c038de77f63521d8b0e Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Fri, 24 Oct 2025 09:00:04 -0400
Subject: [PATCH 02/11] Support Windows 32-bit (and other smaller fixes)
---
libcxx/include/__stacktrace/basic_stacktrace.h | 6 +++---
libcxx/include/__stacktrace/stacktrace_entry.h | 6 +++---
libcxx/src/stacktrace/impl_windows.cpp | 6 +++++-
3 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 291ca672631b1..94ba81d43b9ca 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_STACKTRACE_BASIC
-#define _LIBCPP_STACKTRACE_BASIC
+#ifndef _LIBCPP_BASIC_STACKTRACE_H
+#define _LIBCPP_BASIC_STACKTRACE_H
#include <__config>
@@ -339,4 +339,4 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
-#endif // _LIBCPP_STACKTRACE_BASIC
+#endif // _LIBCPP_BASIC_STACKTRACE_H
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index 48daf14ccccc6..1168195a56de0 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_STACKTRACE_ENTRY
-#define _LIBCPP_STACKTRACE_ENTRY
+#ifndef _LIBCPP_STACKTRACE_ENTRY_H
+#define _LIBCPP_STACKTRACE_ENTRY_H
#include <__config>
@@ -208,4 +208,4 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
-#endif // _LIBCPP_STACKTRACE_ENTRY
+#endif // _LIBCPP_STACKTRACE_ENTRY_H
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/impl_windows.cpp
index c739f3731c175..9cb9b3508a3a5 100644
--- a/libcxx/src/stacktrace/impl_windows.cpp
+++ b/libcxx/src/stacktrace/impl_windows.cpp
@@ -171,8 +171,12 @@ base::current_impl(size_t skip, size_t max_depth) {
frame.AddrPC.Offset = ccx.Pc;
frame.AddrStack.Offset = ccx.Sp;
frame.AddrFrame.Offset = ccx.Fp;
+#elif defined(_M_IX86)
+ frame.AddrPC.Offset = ccx.Eip;
+ frame.AddrStack.Offset = ccx.Esp;
+ frame.AddrFrame.Offset = ccx.Ebp;
#else
-# warning stacktrace requires x86-64 or ARM64; returned stacktraces will be empty
+# warning unrecognized architecture; returned stacktraces will be empty
return;
#endif
>From 93f24b42f08e69365644b55f2ef002160ca62469 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Fri, 24 Oct 2025 00:13:27 -0400
Subject: [PATCH 03/11] Formatting, fix generated files, etc.
---
...bcxxabi.v1.stable.exceptions.nonew.abilist | 98 ++++++++++++++++---
libcxx/test/support/test_macros.h | 16 +--
2 files changed, 92 insertions(+), 22 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 f6d454ccb2f23..56fcfc55d7fa2 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
@@ -35,6 +35,7 @@
{'is_defined': False, 'name': '_ZTVSt13runtime_error', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTVSt14overflow_error', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTVSt16invalid_argument', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': False, 'name': '_ZTVSt9exception', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZdaPv', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZdaPvSt11align_val_t', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZdlPv', 'type': 'FUNC'}
@@ -59,6 +60,7 @@
{'is_defined': False, 'name': '__cxa_rethrow', 'type': 'FUNC'}
{'is_defined': False, 'name': '__cxa_rethrow_primary_exception', 'type': 'FUNC'}
{'is_defined': False, 'name': '__cxa_throw', 'type': 'FUNC'}
+{'is_defined': False, 'name': '__cxa_throw_bad_array_new_length', 'type': 'FUNC'}
{'is_defined': False, 'name': '__cxa_uncaught_exceptions', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt12bad_any_cast4whatEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv', 'type': 'FUNC'}
@@ -597,6 +599,22 @@
{'is_defined': True, 'name': '_ZNSt3__110to_wstringEx', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__110to_wstringEy', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__111__call_onceERVmPvPFvS2_E', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPaLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPcLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPdLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPeLb0EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPfLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPhLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPiLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPjLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPlLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPmLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPsLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPtLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPwLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPxLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPyLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyERNS_6__lessIvvEEPNS_12__stacktrace5imageELb0EEEvT1_S8_T0_NS_15iterator_traitsIS8_E15difference_typeEb', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri', 'type': 'FUNC'}
@@ -627,6 +645,7 @@
{'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__stacktrace12unwind_addrsERNS0_4baseEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12current_implEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
@@ -781,6 +800,8 @@
{'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__MIN_BLOCK_2E', 'size': 69, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__113__POW10_SPLITE', 'size': 29376, 'type': 'OBJECT'}
{'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'}
@@ -962,6 +983,7 @@
{'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__114__POW10_OFFSETE', 'size': 128, 'type': 'OBJECT'}
{'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'}
@@ -1014,10 +1036,12 @@
{'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__114error_categoryC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114error_categoryC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114error_categoryD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114error_categoryD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114error_categoryD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__115__POW10_SPLIT_2E', 'size': 75192, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__115__get_classnameEPKcb', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE', 'type': 'FUNC'}
@@ -1137,14 +1161,19 @@
{'is_defined': True, 'name': '_ZNSt3__115recursive_mutexD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__115recursive_mutexD2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__115system_categoryEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__116__POW10_OFFSET_2E', 'size': 138, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__116__d2s_buffered_nEPcS0_dNS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__116__f2s_buffered_nEPcS0_fNS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED0Ev', 'type': 'FUNC'}
{'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__116__parse_exponentEPKcmmc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116generic_categoryEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__117__append_n_digitsEjjPc', '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'}
@@ -1155,6 +1184,7 @@
{'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state4waitEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state9__executeEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state9set_valueEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__117__merge_exponentsElli', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED2Ev', 'type': 'FUNC'}
@@ -1169,11 +1199,19 @@
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__FLOAT_POW5_SPLITE', 'size': 376, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__118__calculate_resultIdmEENS_19__from_chars_resultIT_EET0_ibS3_', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__calculate_resultIfjEENS_19__from_chars_resultIT_EET0_ibS3_', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__d2exp_buffered_nEPcS0_dj', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC2EPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwE4initERKNS_5ctypeIwEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwE9__analyzeEcRKNS_5ctypeIwEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC1EPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC2EPKc', 'type': 'FUNC'}
@@ -1193,6 +1231,7 @@
{'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutex8try_lockEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutexC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutexC2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__119__DOUBLE_POW5_SPLITE', 'size': 5216, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__119__is_posix_terminalEP8_IO_FILE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__119__shared_mutex_base11lock_sharedEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__119__shared_mutex_base13unlock_sharedEv', 'type': 'FUNC'}
@@ -1213,10 +1252,15 @@
{'is_defined': True, 'name': '_ZNSt3__119__thread_local_dataEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__120__append_nine_digitsEjPc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__120__d2fixed_buffered_nEPcS0_dj', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__120__get_collation_nameEPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEEi', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKvi', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__120__throw_system_errorEiPKc', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEE11set_pointerEPS1_', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEEC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEEC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121__throw_runtime_errorEPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutex4lockEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutex6unlockEv', 'type': 'FUNC'}
@@ -1225,19 +1269,43 @@
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__122_Floating_to_chars_ryuIdEENS_15to_chars_resultEPcS2_T_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__122_Floating_to_chars_ryuIfEENS_15to_chars_resultEPcS2_T_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__122__FLOAT_POW5_INV_SPLITE', 'size': 248, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__122__libcpp_verbose_abortEPKcz', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__123__DOUBLE_POW5_INV_SPLITE', 'size': 4672, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_allEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_oneEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE16_Special_X_tableE', 'size': 1560, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE17_Ordinary_X_tableE', 'size': 2512, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE6_Max_PE', 'size': 4, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE16_Special_X_tableE', 'size': 252, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE17_Ordinary_X_tableE', 'size': 176, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE6_Max_PE', 'size': 4, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_hexIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_hexIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_infIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_infIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_nanIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_nanIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__parse_fractional_hex_constantIjEENS_28__fractional_constant_resultIT_EEPKcmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__131__parse_fractional_hex_constantImEENS_28__fractional_constant_resultIT_EEPKcmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__132__from_chars_floating_point_implIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__132__from_chars_floating_point_implIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__135__from_chars_floating_point_decimalIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatES5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__135__from_chars_floating_point_decimalIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatES5_b', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__135__parse_fractional_decimal_constantIjEENS_28__fractional_constant_resultIT_EEPKcll', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__135__parse_fractional_decimal_constantImEENS_28__fractional_constant_resultIT_EEPKcll', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 280, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
@@ -1246,6 +1314,8 @@
{'is_defined': True, 'name': '_ZNSt3__13pmr20get_default_resourceEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr20null_memory_resourceEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__13pmr25__try_allocate_from_chunkILb0ENS0_25monotonic_buffer_resource14__chunk_footerEEEPvRT0_mm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__13pmr25__try_allocate_from_chunkILb1ENS0_25monotonic_buffer_resource20__initial_descriptorEEEPvRT0_mm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm', 'type': 'FUNC'}
@@ -1302,6 +1372,7 @@
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path8iterator11__decrementEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path8iterator11__incrementEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem6detail14FileDescriptor14refresh_statusERNS_10error_codeE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE', 'type': 'FUNC'}
@@ -1346,6 +1417,13 @@
{'is_defined': True, 'name': '_ZNSt3__15wclogE', 'size': 264, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 264, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa10__pow10_32E', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa10__pow10_64E', 'size': 160, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa12__base_2_lutE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa12__base_8_lutE', 'size': 128, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa13__base_16_lutE', 'size': 512, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa16_Charconv_digitsE', 'size': 36, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt3__16__itoa16__digits_base_10E', 'size': 200, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
@@ -1558,6 +1636,10 @@
{'is_defined': True, 'name': '_ZNSt3__18valarrayImEC2Em', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__18valarrayImED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__18valarrayImED2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitC2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitD1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitD2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_', 'type': 'FUNC'}
@@ -1603,20 +1685,6 @@
{'is_defined': True, 'name': '_ZSt18uncaught_exceptionv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZSt19uncaught_exceptionsv', 'type': 'FUNC'}
{'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__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'}
-{'is_defined': True, 'name': '_ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE', 'size': 120, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE', 'size': 120, 'type': 'OBJECT'}
-{'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__time_getE', 'size': 16, 'type': 'OBJECT'}
@@ -1967,6 +2035,8 @@
{'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IDiEE', 'size': 96, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IDsEE', 'size': 96, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IwEE', 'size': 96, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__120__time_get_c_storageIcEE', 'size': 72, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__120__time_get_c_storageIwEE', 'size': 72, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__13pmr15memory_resourceE', 'size': 56, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__13pmr25monotonic_buffer_resourceE', 'size': 56, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__13pmr26synchronized_pool_resourceE', 'size': 56, 'type': 'OBJECT'}
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 5f00c1f994662..2699d5371a7fb 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -363,19 +363,19 @@ inline Tp const& DoNotOptimize(Tp const& value) {
// Disables tail-call optimization for "outbound" calls
// performed in the function annotated with this attribute.
#if __has_cpp_attribute(_Clang::__disable_tail_calls__)
-#define TEST_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
+# define TEST_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
#elif __has_cpp_attribute(__gnu__::__optimize__)
-#define TEST_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
+# define TEST_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
#else
-#define TEST_NO_TAIL_CALLS_OUT
+# define TEST_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 TEST_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
+# define TEST_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
#else
-#define TEST_NO_TAIL_CALLS_IN
+# define TEST_NO_TAIL_CALLS_IN
#endif
#ifdef _WIN32
@@ -395,8 +395,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
//
// The same goes on IBM zOS.
// The same goes on AIX.
-#define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
-#define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
+# define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) ((void)(__VA_ARGS__))
+# define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 0
#else
#define ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(...) assert(__VA_ARGS__)
#define TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS 1
@@ -417,7 +417,7 @@ inline Tp const& DoNotOptimize(Tp const& value) {
// the end user executable, and these fallbacks work even in DLL configurations.
// In MinGW configurations when built as a DLL, and on zOS, these fallbacks
// don't work though.
-#define ASSERT_WITH_OPERATOR_NEW_FALLBACKS(...) ((void)(__VA_ARGS__))
+# define ASSERT_WITH_OPERATOR_NEW_FALLBACKS(...) ((void)(__VA_ARGS__))
#else
#define ASSERT_WITH_OPERATOR_NEW_FALLBACKS(...) assert(__VA_ARGS__)
#endif
>From a3f8c5d7e102aa885d3718697dfc03aab5525867 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sat, 25 Oct 2025 16:28:56 -0400
Subject: [PATCH 04/11] De-uglify issue links
---
libcxx/docs/ReleaseNotes/22.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index e1b252b696fef..b628a10350036 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -46,8 +46,8 @@ Implemented Papers
- P2835R7: Expose ``std::atomic_ref``'s object address (`Github <https://llvm.org/PR118377>`__)
- P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://llvm.org/PR105424>`__)
- P3168R2: Give ``std::optional`` Range Support (`Github <https://llvm.org/PR105430>`__)
-- 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>`__)
+- P0881R7: A Proposal to add stacktrace library (`Github <https://llvm.org/PR105131>`__)
+- P2301R1: Add a `pmr` alias for `std::stacktrace` (`Github <https://llvm.org/PR105167>`__)
Improvements and New Features
-----------------------------
>From 82d49388a265bfe6ef22bf5f9eb7e3672ba67f62 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sat, 25 Oct 2025 16:20:49 -0400
Subject: [PATCH 05/11] Update ABI list, WTF edition. Hopefully fixed
---
...bcxxabi.v1.stable.exceptions.nonew.abilist | 98 +++----------------
1 file changed, 14 insertions(+), 84 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 56fcfc55d7fa2..f6d454ccb2f23 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
@@ -35,7 +35,6 @@
{'is_defined': False, 'name': '_ZTVSt13runtime_error', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTVSt14overflow_error', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZTVSt16invalid_argument', 'size': 0, 'type': 'OBJECT'}
-{'is_defined': False, 'name': '_ZTVSt9exception', 'size': 0, 'type': 'OBJECT'}
{'is_defined': False, 'name': '_ZdaPv', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZdaPvSt11align_val_t', 'type': 'FUNC'}
{'is_defined': False, 'name': '_ZdlPv', 'type': 'FUNC'}
@@ -60,7 +59,6 @@
{'is_defined': False, 'name': '__cxa_rethrow', 'type': 'FUNC'}
{'is_defined': False, 'name': '__cxa_rethrow_primary_exception', 'type': 'FUNC'}
{'is_defined': False, 'name': '__cxa_throw', 'type': 'FUNC'}
-{'is_defined': False, 'name': '__cxa_throw_bad_array_new_length', 'type': 'FUNC'}
{'is_defined': False, 'name': '__cxa_uncaught_exceptions', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt12bad_any_cast4whatEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt12experimental15fundamentals_v112bad_any_cast4whatEv', 'type': 'FUNC'}
@@ -599,22 +597,6 @@
{'is_defined': True, 'name': '_ZNSt3__110to_wstringEx', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__110to_wstringEy', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__111__call_onceERVmPvPFvS2_E', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPaLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPcLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPdLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPeLb0EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPfLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPhLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPiLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPjLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPlLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPmLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPsLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPtLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPwLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPxLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyENS_6ranges4lessEPyLb1EEEvT1_S5_T0_NS_15iterator_traitsIS5_E15difference_typeEb', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__111__introsortINS_17_ClassicAlgPolicyERNS_6__lessIvvEEPNS_12__stacktrace5imageELb0EEEvT1_S8_T0_NS_15iterator_traitsIS8_E15difference_typeEb', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__111__money_getIcE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_SF_Ri', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__111__money_getIwE13__gather_infoEbRKNS_6localeERNS_10money_base7patternERwS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS9_IwNSA_IwEENSC_IwEEEESJ_SJ_Ri', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__111__money_putIcE13__gather_infoEbbRKNS_6localeERNS_10money_base7patternERcS8_RNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEESF_SF_Ri', 'type': 'FUNC'}
@@ -645,7 +627,6 @@
{'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__stacktrace12unwind_addrsERNS0_4baseEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12current_implEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
@@ -800,8 +781,6 @@
{'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__MIN_BLOCK_2E', 'size': 69, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__113__POW10_SPLITE', 'size': 29376, 'type': 'OBJECT'}
{'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'}
@@ -983,7 +962,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__114__POW10_OFFSETE', 'size': 128, 'type': 'OBJECT'}
{'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'}
@@ -1036,12 +1014,10 @@
{'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114collate_bynameIwED2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__114error_categoryC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114error_categoryC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114error_categoryD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114error_categoryD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__114error_categoryD2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__115__POW10_SPLIT_2E', 'size': 75192, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__115__get_classnameEPKcb', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__115__thread_struct25notify_all_at_thread_exitEPNS_18condition_variableEPNS_5mutexE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__115__thread_struct27__make_ready_at_thread_exitEPNS_17__assoc_sub_stateE', 'type': 'FUNC'}
@@ -1161,19 +1137,14 @@
{'is_defined': True, 'name': '_ZNSt3__115recursive_mutexD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__115recursive_mutexD2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__115system_categoryEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__116__POW10_OFFSET_2E', 'size': 138, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__116__check_groupingERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjS8_Rj', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__116__d2s_buffered_nEPcS0_dNS_12chars_formatE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__116__f2s_buffered_nEPcS0_fNS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm16EED2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116__narrow_to_utf8ILm32EED0Ev', 'type': 'FUNC'}
{'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__116__parse_exponentEPKcmmc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__116generic_categoryEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__117__append_n_digitsEjjPc', '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'}
@@ -1184,7 +1155,6 @@
{'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state4waitEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state9__executeEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__assoc_sub_state9set_valueEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__117__merge_exponentsElli', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117__widen_from_utf8ILm16EED2Ev', 'type': 'FUNC'}
@@ -1199,19 +1169,11 @@
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb0EE4initEPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__117moneypunct_bynameIwLb1EE4initEPKc', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__FLOAT_POW5_SPLITE', 'size': 376, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__118__calculate_resultIdmEENS_19__from_chars_resultIT_EET0_ibS3_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__calculate_resultIfjEENS_19__from_chars_resultIT_EET0_ibS3_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__d2exp_buffered_nEPcS0_dj', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__get_ostream_fileERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE4initERKNS_5ctypeIcEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcE9__analyzeEcRKNS_5ctypeIcEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1EPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC2EPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIcEC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwE4initERKNS_5ctypeIwEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwE9__analyzeEcRKNS_5ctypeIwEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC1EPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC1ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118__time_get_storageIwEC2EPKc', 'type': 'FUNC'}
@@ -1231,7 +1193,6 @@
{'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutex8try_lockEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutexC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__118shared_timed_mutexC2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__119__DOUBLE_POW5_SPLITE', 'size': 5216, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__119__is_posix_terminalEP8_IO_FILE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__119__shared_mutex_base11lock_sharedEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__119__shared_mutex_base13unlock_sharedEv', 'type': 'FUNC'}
@@ -1252,15 +1213,10 @@
{'is_defined': True, 'name': '_ZNSt3__119__thread_local_dataEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__120__append_nine_digitsEjPc', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__120__d2fixed_buffered_nEPcS0_dj', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__120__get_collation_nameEPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEEi', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKvi', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__120__throw_system_errorEiPKc', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEE11set_pointerEPS1_', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEEC1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__121__thread_specific_ptrINS_15__thread_structEEC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121__throw_runtime_errorEPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutex4lockEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutex6unlockEv', 'type': 'FUNC'}
@@ -1269,43 +1225,19 @@
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__121recursive_timed_mutexD2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__122_Floating_to_chars_ryuIdEENS_15to_chars_resultEPcS2_T_NS_12chars_formatE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__122_Floating_to_chars_ryuIfEENS_15to_chars_resultEPcS2_T_NS_12chars_formatE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__122__FLOAT_POW5_INV_SPLITE', 'size': 248, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__122__libcpp_verbose_abortEPKcz', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__123__DOUBLE_POW5_INV_SPLITE', 'size': 4672, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_allEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_allEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_oneEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_oneEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE16_Special_X_tableE', 'size': 1560, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE17_Ordinary_X_tableE', 'size': 2512, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIdE6_Max_PE', 'size': 4, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE16_Special_X_tableE', 'size': 252, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE17_Ordinary_X_tableE', 'size': 176, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__125_General_precision_tablesIfE6_Max_PE', 'size': 4, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_hexIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_hexIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_infIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_infIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_nanIdEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__from_chars_floating_point_nanIfEENS_19__from_chars_resultIT_EEPKcS5_S5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__parse_fractional_hex_constantIjEENS_28__fractional_constant_resultIT_EEPKcmm', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__131__parse_fractional_hex_constantImEENS_28__fractional_constant_resultIT_EEPKcmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__132__from_chars_floating_point_implIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__132__from_chars_floating_point_implIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__135__from_chars_floating_point_decimalIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatES5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__135__from_chars_floating_point_decimalIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatES5_b', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__135__parse_fractional_decimal_constantIjEENS_28__fractional_constant_resultIT_EEPKcll', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__135__parse_fractional_decimal_constantImEENS_28__fractional_constant_resultIT_EEPKcll', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 280, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
@@ -1314,8 +1246,6 @@
{'is_defined': True, 'name': '_ZNSt3__13pmr20get_default_resourceEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr20null_memory_resourceEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr20set_default_resourceEPNS0_15memory_resourceE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__13pmr25__try_allocate_from_chunkILb0ENS0_25monotonic_buffer_resource14__chunk_footerEEEPvRT0_mm', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__13pmr25__try_allocate_from_chunkILb1ENS0_25monotonic_buffer_resource20__initial_descriptorEEEPvRT0_mm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr25monotonic_buffer_resource11do_allocateEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr28unsynchronized_pool_resource11do_allocateEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__13pmr28unsynchronized_pool_resource12__adhoc_pool13__do_allocateEPNS0_15memory_resourceEmm', 'type': 'FUNC'}
@@ -1372,7 +1302,6 @@
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path8iterator11__decrementEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path8iterator11__incrementEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem6__copyERKNS1_4pathES4_NS1_12copy_optionsEPNS_10error_codeE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem6detail14FileDescriptor14refresh_statusERNS_10error_codeE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem7__spaceERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE', 'type': 'FUNC'}
@@ -1417,13 +1346,6 @@
{'is_defined': True, 'name': '_ZNSt3__15wclogE', 'size': 264, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 264, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa10__pow10_32E', 'size': 40, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa10__pow10_64E', 'size': 160, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa12__base_2_lutE', 'size': 64, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa12__base_8_lutE', 'size': 128, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa13__base_16_lutE', 'size': 512, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa16_Charconv_digitsE', 'size': 36, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__16__itoa16__digits_base_10E', 'size': 200, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
@@ -1636,10 +1558,6 @@
{'is_defined': True, 'name': '_ZNSt3__18valarrayImEC2Em', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__18valarrayImED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__18valarrayImED2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitC1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitC2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitD1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__19DoIOSInitD2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19__num_getIcE17__stage2_int_loopEciPcRS2_RjcRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSD_S2_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19__num_getIcE17__stage2_int_prepERNS_8ios_baseEPcRc', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19__num_getIcE19__stage2_float_loopEcRbRcPcRS4_ccRKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPjRSE_RjS4_', 'type': 'FUNC'}
@@ -1685,6 +1603,20 @@
{'is_defined': True, 'name': '_ZSt18uncaught_exceptionv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZSt19uncaught_exceptionsv', 'type': 'FUNC'}
{'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__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'}
+{'is_defined': True, 'name': '_ZTCNSt3__114basic_ofstreamIcNS_11char_traitsIcEEEE0_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE', 'size': 120, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE0_NS_14basic_iostreamIcNS_11char_traitsIcEEEE', 'size': 120, 'type': 'OBJECT'}
+{'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__time_getE', 'size': 16, 'type': 'OBJECT'}
@@ -2035,8 +1967,6 @@
{'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IDiEE', 'size': 96, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IDsEE', 'size': 96, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__120__codecvt_utf8_utf16IwEE', 'size': 96, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__120__time_get_c_storageIcEE', 'size': 72, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__120__time_get_c_storageIwEE', 'size': 72, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__13pmr15memory_resourceE', 'size': 56, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__13pmr25monotonic_buffer_resourceE', 'size': 56, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__13pmr26synchronized_pool_resourceE', 'size': 56, 'type': 'OBJECT'}
>From 3cd7feed3203fcf315a8547aa0c6fc6a75834a41 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sun, 26 Oct 2025 11:51:56 -0400
Subject: [PATCH 06/11] Impl stack-height-sensitive code in headers, remove
some macros
---
libcxx/include/__config | 27 -----
.../include/__stacktrace/basic_stacktrace.h | 89 +++++++++++++--
...bcxxabi.v1.stable.exceptions.nonew.abilist | 4 +-
...bcxxabi.v1.stable.exceptions.nonew.abilist | 4 +-
libcxx/src/CMakeLists.txt | 1 -
libcxx/src/stacktrace/images.h | 10 +-
libcxx/src/stacktrace/impl_generic.cpp | 56 +--------
libcxx/src/stacktrace/impl_windows.cpp | 4 +-
libcxx/src/stacktrace/unwinding.h | 108 ------------------
.../assert.current.no_overflow.pass.cpp | 1 +
.../stacktrace/basic.cons/copy.pass.cpp | 3 +-
.../basic.cons/ctor_no_args.pass.cpp | 1 +
.../basic.cons/ctor_with_alloc.pass.cpp | 1 +
.../basic.cons/current_no_args.pass.cpp | 10 +-
.../basic.cons/current_skip.pass.cpp | 5 +-
.../basic.cons/current_skip_depth.pass.cpp | 5 +-
.../stacktrace/basic.cons/move.pass.cpp | 3 +-
.../stacktrace/basic.obs/at.pass.cpp | 10 +-
.../stacktrace/basic.obs/begin_end.pass.cpp | 10 +-
.../stacktrace/basic.obs/cbegin_cend.pass.cpp | 10 +-
.../basic.obs/crbegin_crend.pass.cpp | 10 +-
.../stacktrace/basic.obs/empty.pass.cpp | 10 +-
.../stacktrace/basic.obs/max_size.pass.cpp | 10 +-
.../basic.obs/operator_index.pass.cpp | 10 +-
.../stacktrace/basic.obs/rbegin_rend.pass.cpp | 10 +-
.../stacktrace/basic.obs/size.pass.cpp | 10 +-
.../entry.cons/copy_assign.pass.cpp | 4 +-
.../entry.cons/copy_construct.pass.cpp | 4 +-
.../stacktrace/only_uses_allocator.pass.cpp | 3 +-
libcxx/test/support/test_macros.h | 2 +
30 files changed, 182 insertions(+), 253 deletions(-)
delete mode 100644 libcxx/src/stacktrace/unwinding.h
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 4d32f1fba03c7..b4c081dcdff1b 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -965,33 +965,6 @@ 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/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 94ba81d43b9ca..3e110e23056ca 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -40,6 +40,10 @@ _LIBCPP_PUSH_MACROS
# include <__fwd/ostream.h>
#endif // _LIBCPP_HAS_LOCALIZATION
+#if !defined(_WIN32)
+# include <unwind.h>
+#endif
+
#if _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
# include <__stacktrace/stacktrace_entry.h>
@@ -76,11 +80,30 @@ struct base {
__entry_iters_(__entry_iters),
__entry_append_(__entry_append) {}
- _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void current_impl(size_t __skip, size_t __max_depth);
+# ifndef _WIN32
+ // Use <unwind.h> or <libunwind.h> to collect addresses in the stack.
+ // Defined in this header so it can be reliably force-inlined.
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void unwind_addrs(size_t __skip, size_t __depth);
+# endif
+
+ // On Windows, there are DLLs which take care of all this (dbghelp, psapi), with
+ // zero overlap with any other OS, so it's in its own file, impl_windows.cpp.
+ // For all other platforms we implement these "find" methods.
+
+ // Resolve instruction addresses to their respective images, dealing with possible ASLR.
+ _LIBCPP_EXPORTED_FROM_ABI void find_images();
- _LIBCPP_HIDE_FROM_ABI void find_images();
- _LIBCPP_HIDE_FROM_ABI void find_symbols();
- _LIBCPP_HIDE_FROM_ABI void find_source_locs();
+ // Resolve addresses to symbols if possible.
+ _LIBCPP_EXPORTED_FROM_ABI void find_symbols();
+
+ // Resolve addresses to source locations if possible.
+ _LIBCPP_EXPORTED_FROM_ABI void find_source_locs();
+
+ _LIBCPP_HIDE_FROM_ABI void finish_stacktrace() {
+ find_images();
+ find_symbols();
+ find_source_locs();
+ }
_LIBCPP_EXPORTED_FROM_ABI ostream& write_to(ostream& __os) const;
_LIBCPP_EXPORTED_FROM_ABI string to_string() const;
@@ -147,7 +170,8 @@ class basic_stacktrace : private __stacktrace::base {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
basic_stacktrace __ret{__caller_alloc};
- __ret.current_impl(__skip, __max_depth);
+ __ret.unwind_addrs(__skip, __max_depth);
+ __ret.finish_stacktrace();
return __ret;
}
@@ -157,7 +181,8 @@ class basic_stacktrace : private __stacktrace::base {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
basic_stacktrace __ret{__caller_alloc};
- __ret.current_impl(__skip, __max_depth);
+ __ret.unwind_addrs(__skip, __max_depth);
+ __ret.finish_stacktrace();
return __ret;
}
@@ -169,7 +194,8 @@ class basic_stacktrace : private __stacktrace::base {
__skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
basic_stacktrace __ret{__caller_alloc};
if (__max_depth) [[likely]] {
- __ret.current_impl(__skip, __max_depth);
+ __ret.unwind_addrs(__skip, __max_depth);
+ __ret.finish_stacktrace();
}
return __ret;
}
@@ -333,6 +359,55 @@ struct hash<basic_stacktrace<_Allocator>> {
}
};
+namespace __stacktrace {
+
+# if defined(_WIN32)
+
+_LIBCPP_EXPORTED_FROM_ABI void base::windows_impl(size_t skip, size_t max_depth)
+
+# else
+
+struct unwind_backtrace {
+ base& base_;
+ size_t skip_;
+ size_t maxDepth_;
+
+ _LIBCPP_HIDE_FROM_ABI _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 __ip_before{0};
+ auto __ip = _Unwind_GetIPInfo(__ucx, &__ip_before);
+ if (!__ip) {
+ return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+ }
+ auto& __entry = base_.__entry_append_();
+ auto& __eb = (entry_base&)__entry;
+ __eb.__addr_ = (__ip_before ? __ip : __ip - 1);
+ return _Unwind_Reason_Code::_URC_NO_REASON;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI static _Unwind_Reason_Code callback(_Unwind_Context* __cx, void* __self) {
+ return ((unwind_backtrace*)__self)->callback(__cx);
+ }
+};
+
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void base::unwind_addrs(size_t __skip, size_t __depth) {
+ if (!__depth) {
+ return;
+ }
+ unwind_backtrace __bt{*this, __skip, __depth};
+ _Unwind_Backtrace(unwind_backtrace::callback, &__bt);
+}
+
+# endif // _WIN32
+
+} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_STD_VER >= 23 && _LIBCPP_AVAILABILITY_HAS_STACKTRACE
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 0998827ebf058..ba952095d16a1 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
@@ -979,7 +979,9 @@
{'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__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base11find_imagesEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base12find_symbolsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base16find_source_locsEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
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 f6d454ccb2f23..04c2115f96d7d 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
@@ -627,7 +627,9 @@
{'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__stacktrace4base12current_implEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base11find_imagesEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12find_symbolsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base16find_source_locsEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index aca66727d6442..6e930022ede4a 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -44,7 +44,6 @@ set(LIBCXX_SOURCES
stacktrace/impl_generic.cpp
stacktrace/impl_windows.cpp
stacktrace/impl.cpp
- stacktrace/unwinding.h
stdexcept.cpp
string.cpp
support/runtime/exception_fallback.ipp
diff --git a/libcxx/src/stacktrace/images.h b/libcxx/src/stacktrace/images.h
index 5c7af94de31d6..476d684734878 100644
--- a/libcxx/src/stacktrace/images.h
+++ b/libcxx/src/stacktrace/images.h
@@ -19,11 +19,9 @@
_LIBCPP_PUSH_MACROS
#include <__undef_macros>
-#if _LIBCPP_STD_VER >= 23
-
-# include <__stacktrace/stacktrace_entry.h>
-# include <array>
-# include <cstdint>
+#include <__stacktrace/stacktrace_entry.h>
+#include <array>
+#include <cstdint>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
@@ -112,8 +110,6 @@ struct images {
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_STD_VER >= 23
-
_LIBCPP_POP_MACROS
#endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/stacktrace/impl_generic.cpp b/libcxx/src/stacktrace/impl_generic.cpp
index 315e0f21c30aa..ae3624def0816 100644
--- a/libcxx/src/stacktrace/impl_generic.cpp
+++ b/libcxx/src/stacktrace/impl_generic.cpp
@@ -6,65 +6,21 @@
//
//===----------------------------------------------------------------------===//
-/*
-"Generic" implementation for any platform that doesn't have its own special implementation.
-(Currently this means any platform other than Windows)
-*/
+// "Generic" implementation for any platform that doesn't have its own special implementation.
+// (Currently this means any platform other than Windows)
#if !defined(_WIN32)
# include <__config>
+# include <__stacktrace/basic_stacktrace.h>
# include <__stacktrace/stacktrace_entry.h>
# include "stacktrace/images.h"
-# include "stacktrace/unwinding.h"
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
-base::current_impl(size_t skip, size_t max_depth) {
- if (!max_depth) [[unlikely]] {
- return;
- }
-
- /*
- Build up the stacktrace entries and fill out their fields the best we can.
- An `entry_base` structure looks more or less like:
-
- __addr_ uintptr_t Instruction address (of a call instruction, faulting insn, ...)
- __desc_ string "Description" which we'll say is synonymous with "symbol"
- __file_ string Source filename, or if that's not available, program/library path
- __line_ int Line number within the source file, or failing that, zero
- __image_ image* The image, loaded in by the OS, containing that address (program, library)
-
- On Windows, there are DLLs which take care of all this (dbghelp, psapi), with essentially
- zero overlap with any other OS, so it's in its own file (impl_windows). Otherwise, i.e.
- on non-Windows platforms, taking a stacktrace looks like:
-
- 0. Create the basic_stacktrace, its internal vector, using provided allocator.
- (This was handled by the `current` member functions inside `basic_stacktrace`.)
- 1. Collect instruction addresses to build up the entries, observing skip and max_depth.
- The unwinding library we have available to us should take care of walking the call stack
- and finding addresses.
- 2. Resolve addresses into the program images (executable, libraries); normalize the addresses
- from step 1, as they were seen in the wild, into program-image-relative addresses
- (i.e. deal with ASLR)
- 3. Resolve adjusted addresses into their symbols; some environments provide this out of the box
- (MacOS) and others make this less easy (Linux)
- 4. To get the source file and line number, we have to dig through debug information (DWARF format);
- we might need the help of a library.
- */
-
- unwind_addrs(*this, skip + 1, max_depth);
- if (__entry_iters_().size()) {
- find_images();
- find_symbols();
- find_source_locs();
- }
-}
-
-void base::find_images() {
+_LIBCPP_EXPORTED_FROM_ABI void base::find_images() {
images images;
size_t i = 0;
for (auto& entry : __entry_iters_()) {
@@ -77,11 +33,11 @@ void base::find_images() {
}
}
-void base::find_symbols() {
+_LIBCPP_EXPORTED_FROM_ABI void base::find_symbols() {
// TODO
}
-void base::find_source_locs() {
+_LIBCPP_EXPORTED_FROM_ABI void base::find_source_locs() {
// TODO
}
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/impl_windows.cpp
index 9cb9b3508a3a5..b31ecee8f1ee6 100644
--- a/libcxx/src/stacktrace/impl_windows.cpp
+++ b/libcxx/src/stacktrace/impl_windows.cpp
@@ -109,8 +109,8 @@ struct sym_init_scope {
} // namespace
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
-base::current_impl(size_t skip, size_t max_depth) {
+_LIBCPP_EXPORTED_FROM_ABI void
+base::windows_impl(size_t skip, size_t max_depth) {
if (!max_depth) [[unlikely]] {
return;
}
diff --git a/libcxx/src/stacktrace/unwinding.h b/libcxx/src/stacktrace/unwinding.h
deleted file mode 100644
index d363ea1bc5a15..0000000000000
--- a/libcxx/src/stacktrace/unwinding.h
+++ /dev/null
@@ -1,108 +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_ADDRS_H
-#define __LIBCPP_STACKTRACE_UNWIND_ADDRS_H
-
-#include <__stacktrace/basic_stacktrace.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-void unwind_addrs(std::__stacktrace::base& base, size_t skip, size_t depth);
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#ifndef _WIN32
-
-# if __has_include(<libunwind.h>)
-# include <libunwind.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE inline void unwind_addrs(base& base, size_t skip, size_t depth) {
- if (!depth) {
- return;
- }
- unw_context_t cx;
- unw_getcontext(&cx);
- unw_cursor_t cur;
- unw_init_local(&cur, &cx);
- while (unw_step(&cur) > 0) {
- if (skip && skip--) {
- continue;
- }
- if (!depth--) {
- break;
- }
- auto& entry = base.__entry_append_();
- auto& eb = (__stacktrace::entry_base&)entry;
- unw_get_reg(&cur, UNW_REG_IP, &eb.__addr_);
- if (!unw_is_signal_frame(&cur)) {
- --eb.__addr_;
- }
- }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-# elif __has_include(<unwind.h>)
-# include <unwind.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct unwind_backtrace {
- base& base_;
- 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{0};
- auto ip = _Unwind_GetIPInfo(ucx, &ipBefore);
- if (!ip) {
- return _Unwind_Reason_Code::_URC_NORMAL_STOP;
- }
- auto& entry = base_.__entry_append_();
- auto& eb = (entry_base&)entry;
- eb.__addr_ = (ipBefore ? ip : ip - 1);
- 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 inline void unwind_addrs(base& base, size_t skip, size_t depth) {
- if (!depth) {
- return;
- }
- unwind_backtrace bt{base, skip + 1, depth}; // skip this call as well
- _Unwind_Backtrace(unwind_backtrace::callback, &bt);
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-# else
-# error need <libunwind.h> or <unwind.h>
-# endif
-
-#endif // _WIN32
-
-#endif // __LIBCPP_STACKTRACE_UNWIND_ADDRS_H
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
index 50866ff5902e3..8d4efaac0442f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/assert.current.no_overflow.pass.cpp
@@ -21,6 +21,7 @@
#include <stacktrace>
#include "check_assertion.h"
+#include "test_macros.h"
int main(int, char**) {
TEST_LIBCPP_ASSERT_FAILURE(
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
index 2b3baffefeb50..94151583e4753 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
@@ -17,6 +17,7 @@
#include <cassert>
#include <stacktrace>
+#include "test_macros.h"
#include "../test_allocs.h"
void test_copy_construct() {
@@ -73,7 +74,7 @@ void test_copy_assign() {
}
}
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
int main(int, char**) {
test_copy_construct();
test_copy_assign();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
index 131e6ca2ad29b..129f8aa04bd80 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
@@ -16,6 +16,7 @@
#include <stacktrace>
#include <type_traits>
+#include "test_macros.h"
#include "../test_allocs.h"
void test_default_construct() {
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
index f28def7e70b6e..3d7a94165ad23 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
@@ -20,6 +20,7 @@
#include <stacktrace>
#include <type_traits>
+#include "test_macros.h"
#include "../test_allocs.h"
void test_construct_with_alloc() {
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
index bee38f57ffad0..750d6c695f3c7 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
@@ -21,23 +21,25 @@
#include <cstdint>
#include <stacktrace>
+#include "test_macros.h"
+
uint32_t test1_line;
uint32_t test2_line;
uint32_t main_line;
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS 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() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS 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() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS void test_current() {
main_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
auto st = test2();
@@ -59,7 +61,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
// assert(st[2].source_line() == main_line);
}
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
int main(int, char**) {
static_assert(noexcept(std::stacktrace::current()));
test_current();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
index d0bf282d5fccd..c291d60d59c0c 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.pass.cpp
@@ -19,6 +19,7 @@
#include <cassert>
#include <stacktrace>
+#include "test_macros.h"
/*
Let t be a stacktrace as-if obtained via basic_stacktrace::current(alloc). Let n be t.size().
@@ -26,7 +27,7 @@
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() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS 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);
@@ -38,7 +39,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
assert(st_skip_many.empty());
}
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
int main(int, char**) {
static_assert(noexcept(std::stacktrace::current(0)));
test_current_with_skip();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
index 300f95e8e97b3..a2cbee1f866b9 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
@@ -24,8 +24,9 @@
# include <cassert>
# include <iostream>
# include <stacktrace>
+# include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS void test_current_with_skip_depth() {
// current stack is: [this function, main, (possibly something else, such as libc _start)]
// so it's probably 3 functions deep -- but certainly at least 2 deep.
std::stacktrace_entry entry;
@@ -46,7 +47,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
assert(*it2 == entry);
}
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
int main(int, char**) {
static_assert(noexcept(std::stacktrace::current(0, 0)));
test_current_with_skip_depth();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
index a659a515231f4..482842da3ddf8 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
@@ -24,6 +24,7 @@
#include <stacktrace>
#include <utility>
+#include "test_macros.h"
#include "../test_allocs.h"
void test_move_construct() {
@@ -81,7 +82,7 @@ void test_move_assign() {
}
}
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
int main(int, char**) {
test_move_construct();
test_move_assign();
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
index 6084f3995b2c0..34562973bfc01 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
@@ -18,11 +18,13 @@
#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(); }
+#include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
auto st = test3();
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
index 3206879964134..64080931a2306 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
@@ -20,11 +20,13 @@
#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(); }
+#include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
std::stacktrace st;
static_assert(noexcept(st.begin()));
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
index a3628e454b9bc..6381858846ecf 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
@@ -20,11 +20,13 @@
#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(); }
+#include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
std::stacktrace st;
static_assert(noexcept(st.cbegin()));
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
index b211fb753a688..277e22c180b0e 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
@@ -20,11 +20,13 @@
#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(); }
+#include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
std::stacktrace st;
static_assert(noexcept(st.crbegin()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
index 837e0bbf80acf..112e645f2c254 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
@@ -18,11 +18,13 @@
#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(); }
+#include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
std::stacktrace st;
static_assert(noexcept(st.empty()));
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
index 3ba11885eff0b..bc31d63da9b21 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
@@ -19,11 +19,13 @@
#include <stacktrace>
#include <vector>
-_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(); }
+#include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
std::stacktrace st;
static_assert(noexcept(st.max_size()));
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
index bce77a7047f40..0a616bf2540b9 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
@@ -18,11 +18,13 @@
#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(); }
+#include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
auto st = test3();
assert(st[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
index 471367e19436f..bfb7b907c5bfa 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
@@ -20,11 +20,13 @@
#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(); }
+#include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
std::stacktrace st;
static_assert(noexcept(st.rbegin()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
index 03e003c6d2c0c..5afa6d957d653 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
@@ -18,11 +18,13 @@
#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(); }
+#include "test_macros.h"
-_LIBCPP_NO_TAIL_CALLS
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test2() { return test1(); }
+_LIBCPP_NOINLINE TEST_NO_TAIL_CALLS std::stacktrace test3() { return test2(); }
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
std::stacktrace st;
static_assert(noexcept(st.size()));
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
index 93198a4ac64a4..3065897428a11 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.pass.cpp
@@ -23,7 +23,9 @@ namespace std {
#include <stacktrace>
#include <type_traits>
-_LIBCPP_NO_TAIL_CALLS
+#include "test_macros.h"
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
index 822047c65d44b..2d6c63bb56903 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
@@ -23,7 +23,9 @@ namespace std {
#include <stacktrace>
#include <type_traits>
-_LIBCPP_NO_TAIL_CALLS
+#include "test_macros.h"
+
+TEST_NO_TAIL_CALLS
int main(int, char**) {
static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
diff --git a/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
index f733ae5786881..0d101f9d07a9a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/only_uses_allocator.pass.cpp
@@ -18,6 +18,7 @@
#include <stacktrace>
#include "test_allocs.h"
+#include "test_macros.h"
/*
* This file includes tests which ensure any allocations performed by `basic_stacktrace`
@@ -90,7 +91,7 @@ struct test_alloc : std::allocator<T> {
}
};
-_LIBCPP_NO_TAIL_CALLS
+TEST_NO_TAIL_CALLS
int main(int, char**) {
(void)std::stacktrace::current();
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 2699d5371a7fb..06f3b9ee5e64a 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -378,6 +378,8 @@ inline Tp const& DoNotOptimize(Tp const& value) {
# define TEST_NO_TAIL_CALLS_IN
#endif
+#define TEST_NO_TAIL_CALLS TEST_NO_TAIL_CALLS_IN TEST_NO_TAIL_CALLS_OUT
+
#ifdef _WIN32
#define TEST_NOT_WIN32(...) ((void)0)
#else
>From b683fe0a8192f1e69b8f317ba6c3cb4ccd98cb2b Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sun, 26 Oct 2025 23:36:23 -0400
Subject: [PATCH 07/11] _Ugly_Names
---
.../include/__stacktrace/basic_stacktrace.h | 54 +++++++++---------
.../include/__stacktrace/stacktrace_entry.h | 56 +++++++++----------
...bcxxabi.v1.stable.exceptions.nonew.abilist | 18 +++---
...bcxxabi.v1.stable.exceptions.nonew.abilist | 18 +++---
libcxx/src/stacktrace/images.cpp | 12 ++--
libcxx/src/stacktrace/images.h | 22 ++++----
libcxx/src/stacktrace/impl.cpp | 10 ++--
libcxx/src/stacktrace/impl_generic.cpp | 8 +--
libcxx/src/stacktrace/impl_windows.cpp | 30 +++++-----
9 files changed, 114 insertions(+), 114 deletions(-)
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 3e110e23056ca..1d343f95a6938 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -53,7 +53,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
template <typename _Tp, typename _Bp = _Tp>
-struct iters {
+struct _Iters {
_Tp* __data_{};
size_t __size_{};
@@ -63,20 +63,20 @@ struct iters {
_Bp* end() { return data() + size(); }
};
-struct base {
+struct _Trace {
constexpr static size_t __default_max_depth = 64;
constexpr static size_t __absolute_max_depth = 256;
- str_alloc<char> __string_alloc_;
+ _Str_Alloc<char> __string_alloc_;
- using _EntryIters _LIBCPP_NODEBUG = iters<stacktrace_entry, entry_base>;
+ using _EntryIters _LIBCPP_NODEBUG = _Iters<stacktrace_entry, _Entry>;
function<_EntryIters()> __entry_iters_;
- function<entry_base&()> __entry_append_;
+ function<_Entry&()> __entry_append_;
template <class _Allocator>
_LIBCPP_HIDE_FROM_ABI
- base(_Allocator const& __alloc, function<_EntryIters()> __entry_iters, function<entry_base&()> __entry_append)
- : __string_alloc_(std::move(str_alloc<char>::make(__alloc))),
+ _Trace(_Allocator const& __alloc, function<_EntryIters()> __entry_iters, function<_Entry&()> __entry_append)
+ : __string_alloc_(std::move(_Str_Alloc<char>::make(__alloc))),
__entry_iters_(__entry_iters),
__entry_append_(__entry_append) {}
@@ -108,7 +108,7 @@ struct base {
_LIBCPP_EXPORTED_FROM_ABI ostream& write_to(ostream& __os) const;
_LIBCPP_EXPORTED_FROM_ABI string to_string() const;
- _LIBCPP_HIDE_FROM_ABI str __create_str() { return str(__string_alloc_); }
+ _LIBCPP_HIDE_FROM_ABI _Str __create_str() { return _Str(__string_alloc_); }
};
} // namespace __stacktrace
@@ -119,9 +119,9 @@ struct base {
class stacktrace_entry;
template <class _Allocator>
-class basic_stacktrace : private __stacktrace::base {
+class basic_stacktrace : private __stacktrace::_Trace {
friend struct hash<basic_stacktrace<_Allocator>>;
- friend struct __stacktrace::base;
+ friend struct __stacktrace::_Trace;
using _ATraits _LIBCPP_NODEBUG = allocator_traits<_Allocator>;
constexpr static bool __kPropOnCopyAssign = _ATraits::propagate_on_container_copy_assignment::value;
@@ -134,15 +134,15 @@ class basic_stacktrace : private __stacktrace::base {
vector<stacktrace_entry, _Allocator> __entries_;
_LIBCPP_HIDE_FROM_ABI _EntryIters entry_iters() { return {__entries_.data(), __entries_.size()}; }
- _LIBCPP_HIDE_FROM_ABI __stacktrace::entry_base& entry_append() {
- return (__stacktrace::entry_base&)__entries_.emplace_back();
+ _LIBCPP_HIDE_FROM_ABI __stacktrace::_Entry& entry_append() {
+ return (__stacktrace::_Entry&)__entries_.emplace_back();
}
_LIBCPP_HIDE_FROM_ABI auto entry_iters_fn() {
return [this] -> _EntryIters { return entry_iters(); };
}
_LIBCPP_HIDE_FROM_ABI auto entry_append_fn() {
- return [this] -> __stacktrace::entry_base& { return entry_append(); };
+ return [this] -> __stacktrace::_Entry& { return entry_append(); };
}
public:
@@ -202,16 +202,16 @@ class basic_stacktrace : private __stacktrace::base {
_LIBCPP_HIDE_FROM_ABI constexpr ~basic_stacktrace() = default;
- static_assert(sizeof(__stacktrace::entry_base) == sizeof(stacktrace_entry));
+ static_assert(sizeof(__stacktrace::_Entry) == sizeof(stacktrace_entry));
_LIBCPP_HIDE_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc)
- : base(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__alloc_) {}
+ : _Trace(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__alloc_) {}
_LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
- : base(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__other.__entries_) {}
+ : _Trace(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__other.__entries_) {}
_LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
- : base(__alloc, entry_iters_fn(), entry_append_fn()),
+ : _Trace(__alloc, entry_iters_fn(), entry_append_fn()),
__alloc_(__alloc),
__entries_(std::move(__other.__entries_)) {}
@@ -337,11 +337,11 @@ swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) noexc
# if _LIBCPP_HAS_LOCALIZATION
template <class _Allocator>
_LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __stacktrace) {
- return ((__stacktrace::base const&)__stacktrace).write_to(__os);
+ return ((__stacktrace::_Trace const&)__stacktrace).write_to(__os);
}
template <class _Allocator>
_LIBCPP_HIDE_FROM_ABI inline string to_string(const basic_stacktrace<_Allocator>& __stacktrace) {
- return ((__stacktrace::base const&)__stacktrace).to_string();
+ return ((__stacktrace::_Trace const&)__stacktrace).to_string();
}
# endif // _LIBCPP_HAS_LOCALIZATION
@@ -363,12 +363,12 @@ namespace __stacktrace {
# if defined(_WIN32)
-_LIBCPP_EXPORTED_FROM_ABI void base::windows_impl(size_t skip, size_t max_depth)
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::windows_impl(size_t skip, size_t max_depth)
# else
-struct unwind_backtrace {
- base& base_;
+struct _Unwind_Wrapper {
+ _Trace& base_;
size_t skip_;
size_t maxDepth_;
@@ -387,22 +387,22 @@ struct unwind_backtrace {
return _Unwind_Reason_Code::_URC_NORMAL_STOP;
}
auto& __entry = base_.__entry_append_();
- auto& __eb = (entry_base&)__entry;
+ auto& __eb = (_Entry&)__entry;
__eb.__addr_ = (__ip_before ? __ip : __ip - 1);
return _Unwind_Reason_Code::_URC_NO_REASON;
}
_LIBCPP_HIDE_FROM_ABI static _Unwind_Reason_Code callback(_Unwind_Context* __cx, void* __self) {
- return ((unwind_backtrace*)__self)->callback(__cx);
+ return ((_Unwind_Wrapper*)__self)->callback(__cx);
}
};
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void base::unwind_addrs(size_t __skip, size_t __depth) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void _Trace::unwind_addrs(size_t __skip, size_t __depth) {
if (!__depth) {
return;
}
- unwind_backtrace __bt{*this, __skip, __depth};
- _Unwind_Backtrace(unwind_backtrace::callback, &__bt);
+ _Unwind_Wrapper __bt{*this, __skip, __depth};
+ _Unwind_Backtrace(_Unwind_Wrapper::callback, &__bt);
}
# endif // _WIN32
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index 1168195a56de0..6dc88bcf40a1e 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -40,10 +40,10 @@ class stacktrace_entry;
namespace __stacktrace {
-struct str;
+struct _Str;
template <class _Tp>
-struct str_alloc {
+struct _Str_Alloc {
using result_t _LIBCPP_NODEBUG = allocation_result<_Tp*, size_t>;
// Lambdas wrap the caller's allocator, re-bound so we can deal with `chars`.
@@ -58,22 +58,22 @@ struct str_alloc {
template <class _Tp2>
struct rebind {
- using other = str_alloc<_Tp2>;
+ using other = _Str_Alloc<_Tp2>;
};
- str_alloc(const str_alloc&) = default;
- str_alloc(str_alloc&&) = default;
- str_alloc& operator=(const str_alloc&) = default;
- str_alloc& operator=(str_alloc&&) = default;
+ _Str_Alloc(const _Str_Alloc&) = default;
+ _Str_Alloc(_Str_Alloc&&) = default;
+ _Str_Alloc& operator=(const _Str_Alloc&) = default;
+ _Str_Alloc& operator=(_Str_Alloc&&) = default;
- str_alloc(function<char*(size_t)> __alloc, function<void(char*, size_t)> __dealloc)
+ _Str_Alloc(function<char*(size_t)> __alloc, function<void(char*, size_t)> __dealloc)
: __alloc_(std::move(__alloc)), __dealloc_(std::move(__dealloc)) {}
template <class _A0, // some allocator; can be of any type
class _AT = allocator_traits<_A0>,
class _CA = typename _AT::template rebind_alloc<char>>
requires __is_allocator_v<_A0>
- static str_alloc make(_A0 __a) {
+ static _Str_Alloc make(_A0 __a) {
auto __ca = _CA(__a);
return {[__ca](size_t __n) mutable -> char* { return __ca.allocate(__n); },
[__ca](char* __p, size_t __n) mutable { __ca.deallocate(__p, __n); }};
@@ -81,18 +81,18 @@ struct str_alloc {
_Tp* allocate(size_t __n) { return __alloc_(__n); }
void deallocate(_Tp* __p, size_t __n) { __dealloc_(__p, __n); }
- bool operator==(str_alloc<_Tp> const& __rhs) const { return std::addressof(__rhs) == this; }
+ bool operator==(_Str_Alloc<_Tp> const& __rhs) const { return std::addressof(__rhs) == this; }
};
-struct str : basic_string<char, char_traits<char>, str_alloc<char>> {
- using base = basic_string<char, char_traits<char>, str_alloc<char>>;
- _LIBCPP_HIDE_FROM_ABI str(str_alloc<char> const& __alloc) : base(__alloc) {}
+struct _Str : basic_string<char, char_traits<char>, _Str_Alloc<char>> {
+ using base = basic_string<char, char_traits<char>, _Str_Alloc<char>>;
+ _LIBCPP_HIDE_FROM_ABI _Str(_Str_Alloc<char> const& __alloc) : base(__alloc) {}
_LIBCPP_HIDE_FROM_ABI string_view view() const { return {this->data(), this->size()}; }
};
-struct image;
+struct _Image;
-struct entry_base {
+struct _Entry {
constexpr static size_t __max_sym_len = 512;
# if defined(PATH_MAX)
constexpr static size_t __max_file_len = PATH_MAX;
@@ -103,13 +103,13 @@ struct entry_base {
# endif
uintptr_t __addr_{};
- optional<str> __desc_{};
- optional<str> __file_{};
+ optional<_Str> __desc_{};
+ optional<_Str> __file_{};
uint_least32_t __line_{};
- image const* __image_{};
+ _Image const* __image_{};
- _LIBCPP_HIDE_FROM_ABI str& assign_desc(str&& __s) { return *(__desc_ = std::move(__s)); }
- _LIBCPP_HIDE_FROM_ABI str& assign_file(str&& __s) { return *(__file_ = std::move(__s)); }
+ _LIBCPP_HIDE_FROM_ABI _Str& assign_desc(_Str&& __s) { return *(__desc_ = std::move(__s)); }
+ _LIBCPP_HIDE_FROM_ABI _Str& assign_file(_Str&& __s) { return *(__file_ = std::move(__s)); }
# if _LIBCPP_HAS_LOCALIZATION
_LIBCPP_EXPORTED_FROM_ABI std::ostream& write_to(std::ostream& __os) const;
@@ -118,14 +118,14 @@ struct entry_base {
_LIBCPP_HIDE_FROM_ABI uintptr_t adjusted_addr() const;
- _LIBCPP_HIDE_FROM_ABI constexpr static entry_base* of(auto& __s) { return static_cast<entry_base*>(__s); }
+ _LIBCPP_HIDE_FROM_ABI constexpr static _Entry* of(auto& __s) { return static_cast<_Entry*>(__s); }
- _LIBCPP_HIDE_FROM_ABI ~entry_base() = default;
- _LIBCPP_HIDE_FROM_ABI constexpr entry_base() = default;
- _LIBCPP_HIDE_FROM_ABI constexpr entry_base(const entry_base&) = default;
- _LIBCPP_HIDE_FROM_ABI constexpr entry_base& operator=(const entry_base&) = default;
- _LIBCPP_HIDE_FROM_ABI constexpr entry_base(entry_base&&) = default;
- _LIBCPP_HIDE_FROM_ABI constexpr entry_base& operator=(entry_base&&) = default;
+ _LIBCPP_HIDE_FROM_ABI ~_Entry() = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr _Entry() = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr _Entry(const _Entry&) = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr _Entry& operator=(const _Entry&) = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr _Entry(_Entry&&) = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr _Entry& operator=(_Entry&&) = default;
};
} // namespace __stacktrace
@@ -134,7 +134,7 @@ class stacktrace_entry {
friend _LIBCPP_HIDE_FROM_ABI inline ostream& operator<<(ostream& __os, std::stacktrace_entry const& __entry);
friend _LIBCPP_HIDE_FROM_ABI inline string to_string(std::stacktrace_entry const& __entry);
- __stacktrace::entry_base __base_{};
+ __stacktrace::_Entry __base_{};
public:
// (19.6.3.1) Overview [stacktrace.entry.overview]
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 ba952095d16a1..b11c50254906c 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
@@ -413,10 +413,10 @@
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Trace8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Trace9to_stringEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -979,11 +979,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__stacktrace4base11find_imagesEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base12find_symbolsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base16find_source_locsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace11find_imagesEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace12find_symbolsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace16find_source_locsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC2Ev', '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'}
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 04c2115f96d7d..8b8fde6b28d92 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
@@ -104,10 +104,10 @@
{'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace10entry_base9to_stringEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace4base9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace6_Entry8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace6_Entry9to_stringEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace6_Trace8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__112__stacktrace6_Trace9to_stringEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__112bad_weak_ptr4whatEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE12find_last_ofEPKcmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13find_first_ofEPKcmm', 'type': 'FUNC'}
@@ -627,11 +627,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__stacktrace4base11find_imagesEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base12find_symbolsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base16find_source_locsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC1Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6imagesC2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace11find_imagesEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace12find_symbolsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace16find_source_locsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace7_ImagesC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace7_ImagesC2Ev', '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'}
diff --git a/libcxx/src/stacktrace/images.cpp b/libcxx/src/stacktrace/images.cpp
index 4120ff409bb3c..391d3304523a3 100644
--- a/libcxx/src/stacktrace/images.cpp
+++ b/libcxx/src/stacktrace/images.cpp
@@ -24,7 +24,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-images::images() {
+_Images::_Images() {
images_[count_++] = {0uz, 0}; // sentinel at low end
images_[count_++] = {~0uz, 0}; // sentinel at high end
auto dyld_count = _dyld_image_count();
@@ -55,8 +55,8 @@ namespace __stacktrace {
namespace {
int add_image(dl_phdr_info* info, size_t, void* images_v) {
- auto& imgs = *(images*)images_v;
- if (imgs.count_ == images::k_max_images) {
+ auto& imgs = *(_Images*)images_v;
+ if (imgs.count_ == _Images::k_max_images) {
return 0;
}
auto is_first = (imgs.count_ == 0);
@@ -69,17 +69,17 @@ int add_image(dl_phdr_info* info, size_t, void* images_v) {
// `dl_iterate_phdr` gives us the main program image first
image.is_main_prog_ = is_first;
if (!image.name_[0] && is_first) {
- char buf[entry_base::__max_file_len]{0};
+ char buf[_Entry::__max_file_len]{0};
if (readlink("/proc/self/exe", buf, sizeof(buf)) != -1) { // Ignores errno if error
strncpy(image.name_, buf, sizeof(image.name_));
}
}
// If we're at the limit, return nonzero to stop iterating
- return imgs.count_ == images::k_max_images;
+ return imgs.count_ == _Images::k_max_images;
}
} // namespace
-images::images() {
+_Images::_Images() {
dl_iterate_phdr(add_image, this);
images_[count_++] = {0uz, 0}; // sentinel at low end
images_[count_++] = {~0uz, 0}; // sentinel at high end
diff --git a/libcxx/src/stacktrace/images.h b/libcxx/src/stacktrace/images.h
index 476d684734878..1af64fae6f9c8 100644
--- a/libcxx/src/stacktrace/images.h
+++ b/libcxx/src/stacktrace/images.h
@@ -26,16 +26,16 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-struct image;
-struct images;
+struct _Image;
+struct _Images;
-struct image {
+struct _Image {
uintptr_t loaded_at_{};
uintptr_t slide_{};
- char name_[__stacktrace::entry_base::__max_file_len]{0};
+ char name_[__stacktrace::_Entry::__max_file_len]{0};
bool is_main_prog_{};
- _LIBCPP_HIDE_FROM_ABI bool operator<(image const& __rhs) const {
+ _LIBCPP_HIDE_FROM_ABI bool operator<(_Image const& __rhs) const {
if (loaded_at_ < __rhs.loaded_at_) {
return true;
}
@@ -58,22 +58,22 @@ struct image {
* (sentinel) foo.exe libc++so.1 libc.so.6 (sentinel)
* 0x000000000000 0x000100000000 0x633b00000000 0x7c5500000000 0xffffffffffff
*/
-struct images {
+struct _Images {
constexpr static size_t k_max_images = 256;
- std::array<image, k_max_images + 2> images_{}; // space for the L/R sentinels
- unsigned count_{0}; // image count, including sentinels
+ std::array<_Image, k_max_images + 2> images_{}; // space for the L/R sentinels
+ unsigned count_{0}; // image count, including sentinels
/** An OS-specific constructor is defined. */
- _LIBCPP_EXPORTED_FROM_ABI images();
+ _LIBCPP_EXPORTED_FROM_ABI _Images();
/** Get prog_image by index (0 <= index < count_) */
- _LIBCPP_HIDE_FROM_ABI image& operator[](size_t __index) {
+ _LIBCPP_HIDE_FROM_ABI _Image& operator[](size_t __index) {
_LIBCPP_ASSERT(__index < count_, "index out of range");
return images_.at(__index);
}
/** Image representing the main program, or nullptr if we couldn't find it */
- _LIBCPP_HIDE_FROM_ABI image* main_prog_image() {
+ _LIBCPP_HIDE_FROM_ABI _Image* main_prog_image() {
for (size_t __i = 1; __i < count_ - 1; __i++) {
auto& __image = images_[__i];
if (__image.is_main_prog_) {
diff --git a/libcxx/src/stacktrace/impl.cpp b/libcxx/src/stacktrace/impl.cpp
index c3b9f906a4cf3..15f3d3bb86a4c 100644
--- a/libcxx/src/stacktrace/impl.cpp
+++ b/libcxx/src/stacktrace/impl.cpp
@@ -25,7 +25,7 @@ namespace __stacktrace {
#if _LIBCPP_HAS_LOCALIZATION
-_LIBCPP_EXPORTED_FROM_ABI ostream& entry_base::write_to(ostream& __os) const {
+_LIBCPP_EXPORTED_FROM_ABI ostream& _Entry::write_to(ostream& __os) const {
// 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;
@@ -42,7 +42,7 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& entry_base::write_to(ostream& __os) const {
return __os;
}
-_LIBCPP_EXPORTED_FROM_ABI ostream& base::write_to(std::ostream& __os) const {
+_LIBCPP_EXPORTED_FROM_ABI ostream& _Trace::write_to(std::ostream& __os) const {
auto iters = __entry_iters_();
auto count = iters.size();
if (!count) {
@@ -60,13 +60,13 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& base::write_to(std::ostream& __os) const {
return __os;
}
-_LIBCPP_EXPORTED_FROM_ABI string entry_base::to_string() const {
+_LIBCPP_EXPORTED_FROM_ABI string _Entry::to_string() const {
stringstream __ss;
write_to(__ss);
return __ss.str();
}
-_LIBCPP_EXPORTED_FROM_ABI string base::to_string() const {
+_LIBCPP_EXPORTED_FROM_ABI string _Trace::to_string() const {
stringstream __ss;
write_to(__ss);
return __ss.str();
@@ -74,7 +74,7 @@ _LIBCPP_EXPORTED_FROM_ABI string base::to_string() const {
#endif // _LIBCPP_HAS_LOCALIZATION
-_LIBCPP_HIDE_FROM_ABI uintptr_t entry_base::adjusted_addr() const {
+_LIBCPP_HIDE_FROM_ABI uintptr_t _Entry::adjusted_addr() const {
auto sub = __image_ ? __image_->slide_ : 0;
return __addr_ - sub;
}
diff --git a/libcxx/src/stacktrace/impl_generic.cpp b/libcxx/src/stacktrace/impl_generic.cpp
index ae3624def0816..8b8fc66c20e90 100644
--- a/libcxx/src/stacktrace/impl_generic.cpp
+++ b/libcxx/src/stacktrace/impl_generic.cpp
@@ -20,8 +20,8 @@
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-_LIBCPP_EXPORTED_FROM_ABI void base::find_images() {
- images images;
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_images() {
+ _Images images;
size_t i = 0;
for (auto& entry : __entry_iters_()) {
images.find(&i, entry.__addr_);
@@ -33,11 +33,11 @@ _LIBCPP_EXPORTED_FROM_ABI void base::find_images() {
}
}
-_LIBCPP_EXPORTED_FROM_ABI void base::find_symbols() {
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_symbols() {
// TODO
}
-_LIBCPP_EXPORTED_FROM_ABI void base::find_source_locs() {
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_source_locs() {
// TODO
}
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/impl_windows.cpp
index b31ecee8f1ee6..10258c664d0a1 100644
--- a/libcxx/src/stacktrace/impl_windows.cpp
+++ b/libcxx/src/stacktrace/impl_windows.cpp
@@ -24,13 +24,13 @@ namespace __stacktrace {
namespace {
-struct dll {
+struct _DLL {
HMODULE module_{};
bool loaded_{};
- explicit dll(char const* name) : module_(LoadLibraryA(name)) {}
+ explicit _DLL(char const* name) : module_(LoadLibraryA(name)) {}
- ~dll() {
+ ~_DLL() {
if (module_) {
FreeLibrary(module_);
}
@@ -44,7 +44,7 @@ struct dll {
// clang-format off
-struct dbghelp_dll final : dll {
+struct _Dbghelp_DLL final : _DLL {
IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
bool (WINAPI *SymCleanup) (HANDLE);
DWORD (WINAPI *SymGetOptions) ();
@@ -60,7 +60,7 @@ struct dbghelp_dll final : dll {
bool (WINAPI *SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
DWORD64 (WINAPI *SymLoadModule64) (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
- dbghelp_dll() : dll("dbghelp.dll") {
+ _Dbghelp_DLL() : _DLL("dbghelp.dll") {
loaded_ = true
&& get_func(&ImageNtHeader, "ImageNtHeader")
&& get_func(&SymCleanup, "SymCleanup")
@@ -80,12 +80,12 @@ struct dbghelp_dll final : dll {
}
};
-struct psapi_dll final : dll {
+struct _Psapi_DLL final : _DLL {
bool (WINAPI *EnumProcessModules) (HANDLE, HMODULE*, DWORD, DWORD*);
bool (WINAPI *GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
DWORD (WINAPI *GetModuleBaseName) (HANDLE, HMODULE, char**, DWORD);
- psapi_dll() : dll("psapi.dll") {
+ _Psapi_DLL() : _DLL("psapi.dll") {
loaded_ = true
&& get_func(&EnumProcessModules, "EnumProcessModules")
&& get_func(&GetModuleInformation, "GetModuleInformation")
@@ -94,15 +94,15 @@ struct psapi_dll final : dll {
}
};
-struct sym_init_scope {
- dbghelp_dll& dbghelp_;
+struct _Sym_Init_Scope {
+ _Dbghelp_DLL& dbghelp_;
HANDLE proc_;
- sym_init_scope(dbghelp_dll& dbghelp, HANDLE proc)
+ _Sym_Init_Scope(_Dbghelp_DLL& dbghelp, HANDLE proc)
: dbghelp_(dbghelp), proc_(proc) {
(*dbghelp_.SymInitialize)(proc_, nullptr, true);
}
- ~sym_init_scope() {
+ ~_Sym_Init_Scope() {
(*dbghelp_.SymCleanup)(proc_);
}
};
@@ -110,13 +110,13 @@ struct sym_init_scope {
} // namespace
_LIBCPP_EXPORTED_FROM_ABI void
-base::windows_impl(size_t skip, size_t max_depth) {
+_Trace::windows_impl(size_t skip, size_t max_depth) {
if (!max_depth) [[unlikely]] {
return;
}
- static psapi_dll psapi;
- static dbghelp_dll dbghelp;
+ static _Psapi_DLL psapi;
+ static _Dbghelp_DLL dbghelp;
if (!psapi.loaded_ || !dbghelp.loaded_) { return; }
// Not thread-safe according to docs
@@ -127,7 +127,7 @@ base::windows_impl(size_t skip, size_t max_depth) {
HMODULE exe = GetModuleHandle(nullptr);
if (!exe) { return; }
- sym_init_scope symscope(dbghelp, proc);
+ _Sym_Init_Scope symscope(dbghelp, proc);
char sym_path[MAX_PATH * 4]; // arbitrary
if (!(*dbghelp.SymGetSearchPath)(proc, sym_path, sizeof(sym_path))) { return; }
>From 4f800883bcd78598513513a4ff339ff06ebefdbd Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 27 Oct 2025 10:05:18 -0400
Subject: [PATCH 08/11] Split up impl's to make more readable. Simplify things
considerably
---
.../include/__stacktrace/basic_stacktrace.h | 167 +++++-------------
.../include/__stacktrace/stacktrace_entry.h | 89 +++-------
...bcxxabi.v1.stable.exceptions.nonew.abilist | 6 +-
...bcxxabi.v1.stable.exceptions.nonew.abilist | 4 +-
libcxx/src/CMakeLists.txt | 10 +-
.../{images.cpp => default_impl.cpp} | 57 ++----
libcxx/src/stacktrace/{impl.cpp => entry.cpp} | 32 +---
.../{impl_generic.cpp => macos_impl.cpp} | 36 ++--
libcxx/src/stacktrace/trace.cpp | 54 ++++++
.../{impl_windows.cpp => windows_impl.cpp} | 6 +-
.../test/libcxx/transitive_includes/cxx23.csv | 2 -
.../test/libcxx/transitive_includes/cxx26.csv | 2 -
12 files changed, 184 insertions(+), 281 deletions(-)
rename libcxx/src/stacktrace/{images.cpp => default_impl.cpp} (61%)
rename libcxx/src/stacktrace/{impl.cpp => entry.cpp} (65%)
rename libcxx/src/stacktrace/{impl_generic.cpp => macos_impl.cpp} (52%)
create mode 100644 libcxx/src/stacktrace/trace.cpp
rename libcxx/src/stacktrace/{impl_windows.cpp => windows_impl.cpp} (97%)
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 1d343f95a6938..dbc6f7f4b0708 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -67,48 +67,24 @@ struct _Trace {
constexpr static size_t __default_max_depth = 64;
constexpr static size_t __absolute_max_depth = 256;
- _Str_Alloc<char> __string_alloc_;
-
using _EntryIters _LIBCPP_NODEBUG = _Iters<stacktrace_entry, _Entry>;
function<_EntryIters()> __entry_iters_;
function<_Entry&()> __entry_append_;
- template <class _Allocator>
- _LIBCPP_HIDE_FROM_ABI
- _Trace(_Allocator const& __alloc, function<_EntryIters()> __entry_iters, function<_Entry&()> __entry_append)
- : __string_alloc_(std::move(_Str_Alloc<char>::make(__alloc))),
- __entry_iters_(__entry_iters),
- __entry_append_(__entry_append) {}
-
-# ifndef _WIN32
- // Use <unwind.h> or <libunwind.h> to collect addresses in the stack.
- // Defined in this header so it can be reliably force-inlined.
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void unwind_addrs(size_t __skip, size_t __depth);
-# endif
-
- // On Windows, there are DLLs which take care of all this (dbghelp, psapi), with
- // zero overlap with any other OS, so it's in its own file, impl_windows.cpp.
- // For all other platforms we implement these "find" methods.
-
- // Resolve instruction addresses to their respective images, dealing with possible ASLR.
- _LIBCPP_EXPORTED_FROM_ABI void find_images();
-
- // Resolve addresses to symbols if possible.
- _LIBCPP_EXPORTED_FROM_ABI void find_symbols();
-
- // Resolve addresses to source locations if possible.
- _LIBCPP_EXPORTED_FROM_ABI void find_source_locs();
-
- _LIBCPP_HIDE_FROM_ABI void finish_stacktrace() {
- find_images();
- find_symbols();
- find_source_locs();
- }
+ _LIBCPP_HIDE_FROM_ABI _Trace(function<_EntryIters()> __entry_iters, function<_Entry&()> __entry_append)
+ : __entry_iters_(__entry_iters), __entry_append_(__entry_append) {}
_LIBCPP_EXPORTED_FROM_ABI ostream& write_to(ostream& __os) const;
_LIBCPP_EXPORTED_FROM_ABI string to_string() const;
- _LIBCPP_HIDE_FROM_ABI _Str __create_str() { return _Str(__string_alloc_); }
+# ifdef _WIN32
+ // Windows impl uses dbghelp and psapi DLLs to do the full stacktrace operation.
+ _LIBCPP_EXPORTED_FROM_ABI void windows_impl(size_t skip, size_t max_depth);
+# else
+ // Non-windows: impl separated out into several smaller platform-dependent parts.
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void populate_addrs(size_t __skip, size_t __depth);
+ _LIBCPP_EXPORTED_FROM_ABI void populate_images();
+# endif
};
} // namespace __stacktrace
@@ -123,15 +99,6 @@ class basic_stacktrace : private __stacktrace::_Trace {
friend struct hash<basic_stacktrace<_Allocator>>;
friend struct __stacktrace::_Trace;
- using _ATraits _LIBCPP_NODEBUG = allocator_traits<_Allocator>;
- constexpr static bool __kPropOnCopyAssign = _ATraits::propagate_on_container_copy_assignment::value;
- constexpr static bool __kPropOnMoveAssign = _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 __kNoThrowAlloc = noexcept(noexcept(_Allocator().allocate(1)));
-
- _LIBCPP_NO_UNIQUE_ADDRESS _Allocator __alloc_;
-
vector<stacktrace_entry, _Allocator> __entries_;
_LIBCPP_HIDE_FROM_ABI _EntryIters entry_iters() { return {__entries_.data(), __entries_.size()}; }
_LIBCPP_HIDE_FROM_ABI __stacktrace::_Entry& entry_append() {
@@ -152,106 +119,71 @@ class basic_stacktrace : private __stacktrace::_Trace {
using value_type = stacktrace_entry;
using const_reference = value_type const&;
using reference = value_type&;
+ using const_iterator = decltype(__entries_.cbegin());
+ using iterator = const_iterator;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using difference_type = ptrdiff_t;
using size_type = size_t;
using allocator_type = _Allocator;
- using iterator = decltype(__entries_.begin());
- using const_iterator = decltype(__entries_.cbegin());
- using reverse_iterator = decltype(__entries_.rbegin());
- using const_reverse_iterator = decltype(__entries_.crbegin());
// (19.6.4.2)
// Creation and assignment [stacktrace.basic.cons]
- _LIBCPP_ALWAYS_INLINE static basic_stacktrace
- current(const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
- size_type __skip = 0;
- size_type __max_depth = __default_max_depth;
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
- basic_stacktrace __ret{__caller_alloc};
- __ret.unwind_addrs(__skip, __max_depth);
- __ret.finish_stacktrace();
- return __ret;
+ _LIBCPP_ALWAYS_INLINE // Omit this function from the trace
+ static basic_stacktrace current(const allocator_type& __alloc = allocator_type()) noexcept {
+ return current(0, __default_max_depth, __alloc);
}
- _LIBCPP_ALWAYS_INLINE static basic_stacktrace
- current(size_type __skip, const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
- size_type __max_depth = __default_max_depth;
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
- __skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
- basic_stacktrace __ret{__caller_alloc};
- __ret.unwind_addrs(__skip, __max_depth);
- __ret.finish_stacktrace();
- return __ret;
+ _LIBCPP_ALWAYS_INLINE // Omit this function from the trace
+ static basic_stacktrace current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept {
+ return current(__skip, __default_max_depth, __alloc);
}
- _LIBCPP_ALWAYS_INLINE static basic_stacktrace
- current(size_type __skip,
- size_type __max_depth,
- const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ _LIBCPP_ALWAYS_INLINE // Omit this function from the trace
+ static basic_stacktrace
+ current(size_type __skip, size_type __max_depth, const allocator_type& __alloc = allocator_type()) noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__skip <= __skip + __max_depth, "sum of skip and max_depth overflows size_type");
- basic_stacktrace __ret{__caller_alloc};
- if (__max_depth) [[likely]] {
- __ret.unwind_addrs(__skip, __max_depth);
- __ret.finish_stacktrace();
+ basic_stacktrace __ret{__alloc};
+
+ if (__max_depth) {
+# if defined(WIN32)
+ __ret.windows_impl(__skip, __max_depth);
+# else
+ __ret.populate_addrs(__skip, __max_depth);
+ __ret.populate_images();
+# endif
}
+
return __ret;
}
- _LIBCPP_HIDE_FROM_ABI constexpr ~basic_stacktrace() = default;
-
- static_assert(sizeof(__stacktrace::_Entry) == sizeof(stacktrace_entry));
-
_LIBCPP_HIDE_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc)
- : _Trace(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__alloc_) {}
+ : _Trace(entry_iters_fn(), entry_append_fn()), __entries_(__alloc) {}
_LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
- : _Trace(__alloc, entry_iters_fn(), entry_append_fn()), __alloc_(__alloc), __entries_(__other.__entries_) {}
+ : _Trace(entry_iters_fn(), entry_append_fn()), __entries_(__other.__entries_, __alloc) {}
_LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
- : _Trace(__alloc, entry_iters_fn(), entry_append_fn()),
- __alloc_(__alloc),
- __entries_(std::move(__other.__entries_)) {}
+ : _Trace(entry_iters_fn(), entry_append_fn()), __entries_(std::move(__other.__entries_), __alloc) {}
_LIBCPP_HIDE_FROM_ABI basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>)
: basic_stacktrace(allocator_type()) {}
- _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) noexcept
- : basic_stacktrace(__other, _ATraits::select_on_container_copy_construction(__other.__alloc_)) {}
-
- _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept
- : basic_stacktrace(std::move(__other), __other.__alloc_) {}
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other) = default;
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(basic_stacktrace&& __other) noexcept = default;
- _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other) {
- if (std::addressof(__other) != this) {
- if (__kPropOnCopyAssign) {
- new (this) basic_stacktrace(__other, __other.__alloc_);
- } else {
- new (this) basic_stacktrace(__other);
- }
- }
- return *this;
- }
-
- _LIBCPP_HIDE_FROM_ABI basic_stacktrace&
- operator=(basic_stacktrace&& __other) noexcept(__kPropOnMoveAssign || __kAlwaysEqual) {
- if (std::addressof(__other) != this) {
- if (__kPropOnMoveAssign) {
- auto __alloc = __other.__alloc_;
- new (this) basic_stacktrace(std::move(__other), __alloc);
- } else {
- new (this) basic_stacktrace(std::move(__other));
- }
- }
- return *this;
- }
+ _LIBCPP_HIDE_FROM_ABI ~basic_stacktrace() = default;
// (19.6.4.3)
// [stacktrace.basic.obs], observers
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __alloc_; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept {
+ return __entries_.get_allocator();
+ }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const noexcept { return __entries_.begin(); }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const noexcept { return __entries_.end(); }
@@ -313,9 +245,6 @@ class basic_stacktrace : private __stacktrace::_Trace {
allocator_traits<_Allocator>::propagate_on_container_swap::value ||
allocator_traits<_Allocator>::is_always_equal::value) {
std::swap(__entries_, __other.__entries_);
- if (__kPropOnSwap) {
- std::swap(__alloc_, __other.__alloc_);
- }
}
};
@@ -361,11 +290,7 @@ struct hash<basic_stacktrace<_Allocator>> {
namespace __stacktrace {
-# if defined(_WIN32)
-
-_LIBCPP_EXPORTED_FROM_ABI void _Trace::windows_impl(size_t skip, size_t max_depth)
-
-# else
+# if !defined(_WIN32)
struct _Unwind_Wrapper {
_Trace& base_;
@@ -397,7 +322,7 @@ struct _Unwind_Wrapper {
}
};
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void _Trace::unwind_addrs(size_t __skip, size_t __depth) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE inline void _Trace::populate_addrs(size_t __skip, size_t __depth) {
if (!__depth) {
return;
}
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index 6dc88bcf40a1e..b3f34a245b512 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -11,6 +11,7 @@
#define _LIBCPP_STACKTRACE_ENTRY_H
#include <__config>
+#include <cstring>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -23,9 +24,8 @@ _LIBCPP_PUSH_MACROS
#include <__functional/function.h>
#include <cstddef>
#include <cstdint>
-#include <memory>
-#include <optional>
#include <string>
+#include <string_view>
#if _LIBCPP_HAS_LOCALIZATION
# include <__fwd/format.h>
@@ -40,58 +40,30 @@ class stacktrace_entry;
namespace __stacktrace {
-struct _Str;
-
-template <class _Tp>
-struct _Str_Alloc {
- using result_t _LIBCPP_NODEBUG = allocation_result<_Tp*, size_t>;
-
- // Lambdas wrap the caller's allocator, re-bound so we can deal with `chars`.
- function<char*(size_t)> __alloc_;
- function<void(char*, size_t)> __dealloc_;
-
- // This only works with chars or other 1-byte things.
- static_assert(sizeof(_Tp) == 1);
-
- using value_type = _Tp;
- using pointer = _Tp*;
-
- template <class _Tp2>
- struct rebind {
- using other = _Str_Alloc<_Tp2>;
- };
-
- _Str_Alloc(const _Str_Alloc&) = default;
- _Str_Alloc(_Str_Alloc&&) = default;
- _Str_Alloc& operator=(const _Str_Alloc&) = default;
- _Str_Alloc& operator=(_Str_Alloc&&) = default;
-
- _Str_Alloc(function<char*(size_t)> __alloc, function<void(char*, size_t)> __dealloc)
- : __alloc_(std::move(__alloc)), __dealloc_(std::move(__dealloc)) {}
+struct _Image;
- template <class _A0, // some allocator; can be of any type
- class _AT = allocator_traits<_A0>,
- class _CA = typename _AT::template rebind_alloc<char>>
- requires __is_allocator_v<_A0>
- static _Str_Alloc make(_A0 __a) {
- auto __ca = _CA(__a);
- return {[__ca](size_t __n) mutable -> char* { return __ca.allocate(__n); },
- [__ca](char* __p, size_t __n) mutable { __ca.deallocate(__p, __n); }};
+struct _StringWrapper {
+ // XXX FIXME TODO:
+ // Figure out a solution for creating strings while respecting
+ // the caller's allocator they provided:
+ // 1. properly type-erase basic_strings' allocator types
+ // 2. move all code into headers (seems like a bad idea)
+ // 3. leave these as oversized char arrays, seems suboptimal
+ // 4. just use std::string, which is just plain wrong
+ // 5. ...?
+
+ char __chars_[1024];
+
+ _LIBCPP_EXPORTED_FROM_ABI std::string_view view() const { return __chars_; }
+
+ _LIBCPP_EXPORTED_FROM_ABI _StringWrapper& assign(std::string_view __view) {
+ size_t __size = std::min(__view.size(), sizeof(__chars_) - 1);
+ memcpy(__chars_, __view.data(), __size);
+ __chars_[__size] = 0;
+ return *this;
}
-
- _Tp* allocate(size_t __n) { return __alloc_(__n); }
- void deallocate(_Tp* __p, size_t __n) { __dealloc_(__p, __n); }
- bool operator==(_Str_Alloc<_Tp> const& __rhs) const { return std::addressof(__rhs) == this; }
};
-struct _Str : basic_string<char, char_traits<char>, _Str_Alloc<char>> {
- using base = basic_string<char, char_traits<char>, _Str_Alloc<char>>;
- _LIBCPP_HIDE_FROM_ABI _Str(_Str_Alloc<char> const& __alloc) : base(__alloc) {}
- _LIBCPP_HIDE_FROM_ABI string_view view() const { return {this->data(), this->size()}; }
-};
-
-struct _Image;
-
struct _Entry {
constexpr static size_t __max_sym_len = 512;
# if defined(PATH_MAX)
@@ -103,14 +75,11 @@ struct _Entry {
# endif
uintptr_t __addr_{};
- optional<_Str> __desc_{};
- optional<_Str> __file_{};
+ _StringWrapper __desc_{};
+ _StringWrapper __file_{};
uint_least32_t __line_{};
_Image const* __image_{};
- _LIBCPP_HIDE_FROM_ABI _Str& assign_desc(_Str&& __s) { return *(__desc_ = std::move(__s)); }
- _LIBCPP_HIDE_FROM_ABI _Str& assign_file(_Str&& __s) { return *(__file_ = std::move(__s)); }
-
# if _LIBCPP_HAS_LOCALIZATION
_LIBCPP_EXPORTED_FROM_ABI std::ostream& write_to(std::ostream& __os) const;
_LIBCPP_EXPORTED_FROM_ABI string to_string() const;
@@ -118,8 +87,6 @@ struct _Entry {
_LIBCPP_HIDE_FROM_ABI uintptr_t adjusted_addr() const;
- _LIBCPP_HIDE_FROM_ABI constexpr static _Entry* of(auto& __s) { return static_cast<_Entry*>(__s); }
-
_LIBCPP_HIDE_FROM_ABI ~_Entry() = default;
_LIBCPP_HIDE_FROM_ABI constexpr _Entry() = default;
_LIBCPP_HIDE_FROM_ABI constexpr _Entry(const _Entry&) = default;
@@ -153,12 +120,8 @@ class stacktrace_entry {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return native_handle() != 0; }
// (19.6.3.4) [stacktrace.entry.query], query
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string description() const {
- return __base_.__desc_ ? string(*__base_.__desc_) : string();
- }
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string source_file() const {
- return __base_.__file_ ? string(*__base_.__file_) : string();
- }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string description() const { return string(__base_.__desc_.view()); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string source_file() const { return string(__base_.__file_.view()); }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI uint_least32_t source_line() const { return __base_.__line_; }
// (19.6.3.5) [stacktrace.entry.cmp], comparison
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 b11c50254906c..2ce5ffdc1ccd3 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
@@ -413,6 +413,7 @@
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace14_StringWrapper4viewEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry9to_stringEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Trace8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
@@ -979,9 +980,8 @@
{'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__stacktrace6_Trace11find_imagesEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace12find_symbolsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace16find_source_locsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace14_StringWrapper6assignENS_17basic_string_viewIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace15populate_imagesEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
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 8b8fde6b28d92..c6923aadb05db 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
@@ -627,9 +627,7 @@
{'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__stacktrace6_Trace11find_imagesEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace12find_symbolsEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace16find_source_locsEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace6_Trace15populate_imagesEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace7_ImagesC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace7_ImagesC2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 6e930022ede4a..41fb1fab1f9e4 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,10 +40,12 @@ set(LIBCXX_SOURCES
ryu/d2fixed.cpp
ryu/d2s.cpp
ryu/f2s.cpp
- stacktrace/images.cpp
- stacktrace/impl_generic.cpp
- stacktrace/impl_windows.cpp
- stacktrace/impl.cpp
+ stacktrace/default_impl.cpp
+ stacktrace/entry.cpp
+ stacktrace/images.h
+ stacktrace/macos_impl.cpp
+ stacktrace/trace.cpp
+ stacktrace/windows_impl.cpp
stdexcept.cpp
string.cpp
support/runtime/exception_fallback.ipp
diff --git a/libcxx/src/stacktrace/images.cpp b/libcxx/src/stacktrace/default_impl.cpp
similarity index 61%
rename from libcxx/src/stacktrace/images.cpp
rename to libcxx/src/stacktrace/default_impl.cpp
index 391d3304523a3..ef6d067a54351 100644
--- a/libcxx/src/stacktrace/images.cpp
+++ b/libcxx/src/stacktrace/default_impl.cpp
@@ -6,50 +6,18 @@
//
//===----------------------------------------------------------------------===//
-//
-// OS-specific construction
-//
+#if !defined(_WIN32) && !defined(__APPLE__)
-#include "__config"
-
-#if defined(__APPLE__)
-// MacOS-specific: use the `dyld` loader to access info about loaded Mach-O images.
-# include "stacktrace/images.h"
+# include <__config>
+# include <__stacktrace/basic_stacktrace.h>
+# include <__stacktrace/stacktrace_entry.h>
# include <algorithm>
-# include <cstdlib>
-# include <dlfcn.h>
-# include <mach-o/dyld.h>
-# include <mach-o/loader.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-_Images::_Images() {
- images_[count_++] = {0uz, 0}; // sentinel at low end
- images_[count_++] = {~0uz, 0}; // sentinel at high end
- auto dyld_count = _dyld_image_count();
- for (unsigned i = 0; i < dyld_count && count_ < k_max_images; i++) {
- auto& image = images_[count_++];
- image.slide_ = uintptr_t(_dyld_get_image_vmaddr_slide(i));
- image.loaded_at_ = uintptr_t(_dyld_get_image_header(i));
- image.is_main_prog_ = (i == 0);
- strncpy(image.name_, _dyld_get_image_name(i), sizeof(image.name_));
- }
- std::sort(images_.begin(), images_.begin() + count_);
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#elif !defined(_WIN32)
-// Non-MacOS and non-Windows, including Linux: assume environment has these headers.
-# include "stacktrace/images.h"
-# include <algorithm>
-# include <cstdlib>
# include <dlfcn.h>
# include <link.h>
# include <unistd.h>
+# include "stacktrace/images.h"
+
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
@@ -86,6 +54,19 @@ _Images::_Images() {
std::sort(images_.begin(), images_.begin() + count_);
}
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::populate_images() {
+ _Images images;
+ size_t i = 0;
+ for (auto& entry : __entry_iters_()) {
+ images.find(&i, entry.__addr_);
+ if (auto& image = images[i]) {
+ entry.__image_ = ℑ
+ // While we're in this loop, get the executable's path, and tentatively use this for source file.
+ entry.__file_.assign(image.name_);
+ }
+ }
+}
+
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/impl.cpp b/libcxx/src/stacktrace/entry.cpp
similarity index 65%
rename from libcxx/src/stacktrace/impl.cpp
rename to libcxx/src/stacktrace/entry.cpp
index 15f3d3bb86a4c..f29b0df0c655e 100644
--- a/libcxx/src/stacktrace/impl.cpp
+++ b/libcxx/src/stacktrace/entry.cpp
@@ -30,11 +30,11 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& _Entry::write_to(ostream& __os) const {
constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
__os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << __addr_;
- if (__desc_) {
- __os << ": " << __desc_->view();
+ if (__desc_.view().size()) {
+ __os << ": " << __desc_.view();
}
- if (__file_) {
- __os << ": " << __file_->view();
+ if (__file_.view().size()) {
+ __os << ": " << __file_.view();
}
if (__line_) {
__os << ":" << std::dec << __line_;
@@ -42,36 +42,12 @@ _LIBCPP_EXPORTED_FROM_ABI ostream& _Entry::write_to(ostream& __os) const {
return __os;
}
-_LIBCPP_EXPORTED_FROM_ABI ostream& _Trace::write_to(std::ostream& __os) const {
- auto iters = __entry_iters_();
- auto count = iters.size();
- if (!count) {
- __os << "(empty stacktrace)";
- } else {
- for (size_t __i = 0; __i < count; __i++) {
- // Insert newlines between entries (but not before the first or after the last)
- if (__i) {
- __os << '\n';
- }
- __os << " frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": "
- << *(stacktrace_entry const*)(iters.data() + __i);
- }
- }
- return __os;
-}
-
_LIBCPP_EXPORTED_FROM_ABI string _Entry::to_string() const {
stringstream __ss;
write_to(__ss);
return __ss.str();
}
-_LIBCPP_EXPORTED_FROM_ABI string _Trace::to_string() const {
- stringstream __ss;
- write_to(__ss);
- return __ss.str();
-}
-
#endif // _LIBCPP_HAS_LOCALIZATION
_LIBCPP_HIDE_FROM_ABI uintptr_t _Entry::adjusted_addr() const {
diff --git a/libcxx/src/stacktrace/impl_generic.cpp b/libcxx/src/stacktrace/macos_impl.cpp
similarity index 52%
rename from libcxx/src/stacktrace/impl_generic.cpp
rename to libcxx/src/stacktrace/macos_impl.cpp
index 8b8fc66c20e90..431486008dd9b 100644
--- a/libcxx/src/stacktrace/impl_generic.cpp
+++ b/libcxx/src/stacktrace/macos_impl.cpp
@@ -6,21 +6,37 @@
//
//===----------------------------------------------------------------------===//
-// "Generic" implementation for any platform that doesn't have its own special implementation.
-// (Currently this means any platform other than Windows)
-
-#if !defined(_WIN32)
+#if defined(__APPLE__)
# include <__config>
# include <__stacktrace/basic_stacktrace.h>
# include <__stacktrace/stacktrace_entry.h>
+# include <algorithm>
+# include <cstdlib>
+# include <dlfcn.h>
+# include <mach-o/dyld.h>
+# include <mach-o/loader.h>
# include "stacktrace/images.h"
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_images() {
+_Images::_Images() {
+ images_[count_++] = {0uz, 0}; // sentinel at low end
+ images_[count_++] = {~0uz, 0}; // sentinel at high end
+ auto dyld_count = _dyld_image_count();
+ for (unsigned i = 0; i < dyld_count && count_ < k_max_images; i++) {
+ auto& image = images_[count_++];
+ image.slide_ = uintptr_t(_dyld_get_image_vmaddr_slide(i));
+ image.loaded_at_ = uintptr_t(_dyld_get_image_header(i));
+ image.is_main_prog_ = (i == 0);
+ strncpy(image.name_, _dyld_get_image_name(i), sizeof(image.name_));
+ }
+ std::sort(images_.begin(), images_.begin() + count_);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::populate_images() {
_Images images;
size_t i = 0;
for (auto& entry : __entry_iters_()) {
@@ -28,19 +44,11 @@ _LIBCPP_EXPORTED_FROM_ABI void _Trace::find_images() {
if (auto& image = images[i]) {
entry.__image_ = ℑ
// While we're in this loop, get the executable's path, and tentatively use this for source file.
- entry.assign_file(__create_str()).assign(image.name_);
+ entry.__file_.assign(image.name_);
}
}
}
-_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_symbols() {
- // TODO
-}
-
-_LIBCPP_EXPORTED_FROM_ABI void _Trace::find_source_locs() {
- // TODO
-}
-
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/trace.cpp b/libcxx/src/stacktrace/trace.cpp
new file mode 100644
index 0000000000000..0c50617759ec9
--- /dev/null
+++ b/libcxx/src/stacktrace/trace.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
+#include <string>
+
+#if _LIBCPP_HAS_LOCALIZATION
+# include <iomanip>
+# include <iostream>
+# include <sstream>
+#endif //_LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+#if _LIBCPP_HAS_LOCALIZATION
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& _Trace::write_to(std::ostream& __os) const {
+ auto iters = __entry_iters_();
+ auto count = iters.size();
+ if (!count) {
+ __os << "(empty stacktrace)";
+ } else {
+ for (size_t __i = 0; __i < count; __i++) {
+ // Insert newlines between entries (but not before the first or after the last)
+ if (__i) {
+ __os << '\n';
+ }
+ __os << " frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": "
+ << *(stacktrace_entry const*)(iters.data() + __i);
+ }
+ }
+ return __os;
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string _Trace::to_string() const {
+ stringstream __ss;
+ write_to(__ss);
+ return __ss.str();
+}
+
+#endif // _LIBCPP_HAS_LOCALIZATION
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/impl_windows.cpp b/libcxx/src/stacktrace/windows_impl.cpp
similarity index 97%
rename from libcxx/src/stacktrace/impl_windows.cpp
rename to libcxx/src/stacktrace/windows_impl.cpp
index 10258c664d0a1..7653b39557fc6 100644
--- a/libcxx/src/stacktrace/impl_windows.cpp
+++ b/libcxx/src/stacktrace/windows_impl.cpp
@@ -211,7 +211,7 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
memset(&mod_info, 0, sizeof(mod_info));
mod_info.SizeOfStruct = sizeof(mod_info);
if ((*dbghelp.SymGetModuleInfo64)(proc, frame.AddrPC.Offset, &mod_info)) {
- entry.assign_file(__create_str()).assign(mod_info.LoadedImageName);
+ entry.__file_.assign(mod_info.LoadedImageName);
}
--max_depth;
@@ -236,11 +236,11 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
DWORD linedisp{0};
IMAGEHLP_LINE64 line;
if ((*dbghelp.SymGetSymFromAddr64)(proc, entry.__addr_, &symdisp, sym)) {
- entry.assign_desc(__create_str()).assign(sym->Name);
+ entry.__desc_.assign(sym->Name);
}
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if ((*dbghelp.SymGetLineFromAddr64)(proc, entry.__addr_, &linedisp, &line)) {
- entry.assign_file(__create_str()).assign(line.FileName);
+ entry.__file_.assign(line.FileName);
entry.__line_ = line.LineNumber;
}
}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index e33d1f07e3fc3..b2faf316721db 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -938,8 +938,6 @@ stacktrace cwctype
stacktrace initializer_list
stacktrace iosfwd
stacktrace limits
-stacktrace memory
-stacktrace optional
stacktrace stdexcept
stacktrace string
stacktrace string_view
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index c89c6d50b3a4f..01a6b5e9b0039 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -920,8 +920,6 @@ stacktrace cwctype
stacktrace initializer_list
stacktrace iosfwd
stacktrace limits
-stacktrace memory
-stacktrace optional
stacktrace stdexcept
stacktrace string
stacktrace string_view
>From a901e3c1889a20f9e819e793de78d0124ac88ab5 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 27 Oct 2025 17:30:53 -0400
Subject: [PATCH 09/11] Clean up windows impl
---
.../include/__stacktrace/basic_stacktrace.h | 8 +-
libcxx/src/stacktrace/windows_impl.cpp | 218 ++++++------------
2 files changed, 80 insertions(+), 146 deletions(-)
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index dbc6f7f4b0708..e4ae7b297e391 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -131,12 +131,14 @@ class basic_stacktrace : private __stacktrace::_Trace {
// Creation and assignment [stacktrace.basic.cons]
_LIBCPP_ALWAYS_INLINE // Omit this function from the trace
- static basic_stacktrace current(const allocator_type& __alloc = allocator_type()) noexcept {
+ static basic_stacktrace
+ current(const allocator_type& __alloc = allocator_type()) noexcept {
return current(0, __default_max_depth, __alloc);
}
_LIBCPP_ALWAYS_INLINE // Omit this function from the trace
- static basic_stacktrace current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept {
+ static basic_stacktrace
+ current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept {
return current(__skip, __default_max_depth, __alloc);
}
@@ -148,7 +150,7 @@ class basic_stacktrace : private __stacktrace::_Trace {
basic_stacktrace __ret{__alloc};
if (__max_depth) {
-# if defined(WIN32)
+# if defined(_WIN32)
__ret.windows_impl(__skip, __max_depth);
# else
__ret.populate_addrs(__skip, __max_depth);
diff --git a/libcxx/src/stacktrace/windows_impl.cpp b/libcxx/src/stacktrace/windows_impl.cpp
index 7653b39557fc6..3af62a8ffe194 100644
--- a/libcxx/src/stacktrace/windows_impl.cpp
+++ b/libcxx/src/stacktrace/windows_impl.cpp
@@ -15,188 +15,124 @@
# include <psapi.h>
//
# include <cstring>
-# include <iostream>
# include <mutex>
# include <stacktrace>
+# pragma comment(lib, "dbghelp.lib")
+# pragma comment(lib, "psapi.lib")
+
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
namespace {
-struct _DLL {
- HMODULE module_{};
- bool loaded_{};
-
- explicit _DLL(char const* name) : module_(LoadLibraryA(name)) {}
-
- ~_DLL() {
- if (module_) {
- FreeLibrary(module_);
- }
- }
-
- template <typename F>
- bool get_func(F** func, char const* name) {
- return ((*func = (F*)(void*)GetProcAddress(module_, name)) != nullptr);
- }
-};
-
-// clang-format off
-
-struct _Dbghelp_DLL final : _DLL {
- IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
- bool (WINAPI *SymCleanup) (HANDLE);
- DWORD (WINAPI *SymGetOptions) ();
- bool (WINAPI *SymGetSearchPath) (HANDLE, char const*, DWORD);
- bool (WINAPI *SymInitialize) (HANDLE, char const*, bool);
- DWORD (WINAPI *SymSetOptions) (DWORD);
- bool (WINAPI *SymSetSearchPath) (HANDLE, char const*);
- bool (WINAPI *StackWalk64) (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
- void* (WINAPI *SymFunctionTableAccess64)(HANDLE, DWORD64);
- bool (WINAPI *SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
- DWORD64 (WINAPI *SymGetModuleBase64) (HANDLE, DWORD64);
- bool (WINAPI *SymGetModuleInfo64) (HANDLE, DWORD64, IMAGEHLP_MODULE64*);
- bool (WINAPI *SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
- DWORD64 (WINAPI *SymLoadModule64) (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
-
- _Dbghelp_DLL() : _DLL("dbghelp.dll") {
- loaded_ = true
- && get_func(&ImageNtHeader, "ImageNtHeader")
- && get_func(&SymCleanup, "SymCleanup")
- && get_func(&SymGetOptions, "SymGetOptions")
- && get_func(&SymGetSearchPath, "SymGetSearchPath")
- && get_func(&SymInitialize, "SymInitialize")
- && get_func(&SymSetOptions, "SymSetOptions")
- && get_func(&SymSetSearchPath, "SymSetSearchPath")
- && get_func(&StackWalk64, "StackWalk64")
- && get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")
- && get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")
- && get_func(&SymGetModuleBase64, "SymGetModuleBase64")
- && get_func(&SymGetModuleInfo64, "SymGetModuleInfo64")
- && get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")
- && get_func(&SymLoadModule64, "SymLoadModule64")
- ;
- }
-};
-
-struct _Psapi_DLL final : _DLL {
- bool (WINAPI *EnumProcessModules) (HANDLE, HMODULE*, DWORD, DWORD*);
- bool (WINAPI *GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
- DWORD (WINAPI *GetModuleBaseName) (HANDLE, HMODULE, char**, DWORD);
-
- _Psapi_DLL() : _DLL("psapi.dll") {
- loaded_ = true
- && get_func(&EnumProcessModules, "EnumProcessModules")
- && get_func(&GetModuleInformation, "GetModuleInformation")
- && get_func(&GetModuleBaseName, "GetModuleBaseNameA")
- ;
- }
-};
-
struct _Sym_Init_Scope {
- _Dbghelp_DLL& dbghelp_;
HANDLE proc_;
- _Sym_Init_Scope(_Dbghelp_DLL& dbghelp, HANDLE proc)
- : dbghelp_(dbghelp), proc_(proc) {
- (*dbghelp_.SymInitialize)(proc_, nullptr, true);
- }
- ~_Sym_Init_Scope() {
- (*dbghelp_.SymCleanup)(proc_);
- }
+ explicit _Sym_Init_Scope(HANDLE proc) : proc_(proc) { SymInitialize(proc_, nullptr, true); }
+ ~_Sym_Init_Scope() { SymCleanup(proc_); }
};
-} // namespace
+} // namespace
-_LIBCPP_EXPORTED_FROM_ABI void
-_Trace::windows_impl(size_t skip, size_t max_depth) {
- if (!max_depth) [[unlikely]] {
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::windows_impl(size_t skip, size_t max_depth) {
+ if (!max_depth) {
return;
}
- static _Psapi_DLL psapi;
- static _Dbghelp_DLL dbghelp;
- if (!psapi.loaded_ || !dbghelp.loaded_) { return; }
+ // Use the Windows Debug Help and Process Status libraries to get a stacktrace.
+ // https://learn.microsoft.com/en-us/windows/win32/debug/debug-help-library
+ // https://learn.microsoft.com/en-us/windows/win32/psapi/process-status-helper
- // Not thread-safe according to docs
+ // These APIs are not thread-safe, according to docs.
static std::mutex api_mutex;
std::lock_guard<std::mutex> api_guard(api_mutex);
HANDLE proc = GetCurrentProcess();
HMODULE exe = GetModuleHandle(nullptr);
- if (!exe) { return; }
+ if (!exe) {
+ return;
+ }
- _Sym_Init_Scope symscope(dbghelp, proc);
+ _Sym_Init_Scope symscope(proc);
- char sym_path[MAX_PATH * 4]; // arbitrary
- if (!(*dbghelp.SymGetSearchPath)(proc, sym_path, sizeof(sym_path))) { return; }
+ // Allow space for a handful of paths
+ char sym_path[MAX_PATH * 4];
+ if (!SymGetSearchPath(proc, sym_path, sizeof(sym_path))) {
+ return;
+ }
char exe_dir[MAX_PATH];
- if (!GetModuleFileNameA(nullptr, exe_dir, sizeof(exe_dir))) { return; }
+ if (!GetModuleFileNameA(nullptr, exe_dir, sizeof(exe_dir))) {
+ return;
+ }
size_t exe_dir_len = strlen(exe_dir);
- while (exe_dir_len > 0 && exe_dir[exe_dir_len - 1] != '\\') { exe_dir[--exe_dir_len] = 0; }
- if (exe_dir_len > 0) { exe_dir[--exe_dir_len] = 0; } // strip last backslash
+ while (exe_dir_len > 0 && exe_dir[exe_dir_len - 1] != '\\') {
+ exe_dir[--exe_dir_len] = 0;
+ }
+ if (exe_dir_len > 0) {
+ exe_dir[--exe_dir_len] = 0;
+ } // strip last backslash
if (!strstr(sym_path, exe_dir)) {
- (void) strncat(sym_path, ";", sizeof(sym_path) - 1);
- (void) strncat(sym_path, exe_dir, sizeof(sym_path) - 1);
- if (!(*dbghelp.SymSetSearchPath)(proc, sym_path)) { return; }
+ (void)strncat(sym_path, ";", sizeof(sym_path) - 1);
+ (void)strncat(sym_path, exe_dir, sizeof(sym_path) - 1);
+ if (!SymSetSearchPath(proc, sym_path)) {
+ return;
+ }
}
IMAGE_NT_HEADERS* nt_headers;
- if (!(nt_headers = (*dbghelp.ImageNtHeader)(exe))) { return; }
+ if (!(nt_headers = ImageNtHeader(exe))) {
+ return;
+ }
- (*dbghelp.SymSetOptions)(
- (*dbghelp.SymGetOptions)()
- | SYMOPT_LOAD_LINES
- | SYMOPT_UNDNAME);
+ SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
- auto thread = GetCurrentThread();
- auto machine = nt_headers->FileHeader.Machine;
+ HANDLE thread = GetCurrentThread();
+ int machine = nt_headers->FileHeader.Machine;
CONTEXT ccx;
RtlCaptureContext(&ccx);
- STACKFRAME64 frame;
+ STACKFRAME frame;
memset(&frame, 0, sizeof(frame));
- frame.AddrPC.Mode = AddrModeFlat;
- frame.AddrStack.Mode = AddrModeFlat;
- frame.AddrFrame.Mode = AddrModeFlat;
-#if defined(_M_AMD64)
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrStack.Mode = AddrModeFlat;
+ frame.AddrFrame.Mode = AddrModeFlat;
+# if defined(_M_AMD64)
frame.AddrPC.Offset = ccx.Rip;
frame.AddrStack.Offset = ccx.Rsp;
frame.AddrFrame.Offset = ccx.Rbp;
-#elif defined(_M_ARM64)
+# elif defined(_M_ARM64)
frame.AddrPC.Offset = ccx.Pc;
frame.AddrStack.Offset = ccx.Sp;
frame.AddrFrame.Offset = ccx.Fp;
-#elif defined(_M_IX86)
+# elif defined(_M_IX86)
frame.AddrPC.Offset = ccx.Eip;
frame.AddrStack.Offset = ccx.Esp;
frame.AddrFrame.Offset = ccx.Ebp;
-#else
-# warning unrecognized architecture; returned stacktraces will be empty
+# else
+# warning unrecognized architecture; returned stacktraces will be empty
return;
-#endif
+# endif
// Skip call to this `current_impl` func
++skip;
-
- while (max_depth) {
- if (!(*dbghelp.StackWalk64)(
- machine, proc, thread, &frame, &ccx, nullptr,
- (void*) dbghelp.SymFunctionTableAccess64,
- (void*) dbghelp.SymGetModuleBase64,
- nullptr)) {
+ while (max_depth) {
+ if (!StackWalk(machine, proc, thread, &frame, &ccx, nullptr, SymFunctionTableAccess, SymGetModuleBase, nullptr)) {
break;
}
- if (skip && skip--) { continue; }
- if (!frame.AddrPC.Offset) { break; }
+ if (skip && skip--) {
+ continue;
+ }
+ if (!frame.AddrPC.Offset) {
+ break;
+ }
- auto& entry = this->__entry_append_();
+ _Entry& entry = this->__entry_append_();
// Note: can't differentiate between a signal / exception, or a normal function call.
// This assumes the more common (presumably) case of normal function calls, so we'll
@@ -207,10 +143,10 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
// itself or a DLL. This is used in place of the source filename, if the source filename
// cannot be found (missing PDB, etc.). If the source file can be determined this will
// be overwritten.
- IMAGEHLP_MODULE64 mod_info;
+ IMAGEHLP_MODULE mod_info;
memset(&mod_info, 0, sizeof(mod_info));
mod_info.SizeOfStruct = sizeof(mod_info);
- if ((*dbghelp.SymGetModuleInfo64)(proc, frame.AddrPC.Offset, &mod_info)) {
+ if (SymGetModuleInfo(proc, frame.AddrPC.Offset, &mod_info)) {
entry.__file_.assign(mod_info.LoadedImageName);
}
@@ -218,28 +154,24 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
}
DWORD need_bytes = 0;
- HMODULE module_handles[1024] {0};
- if (!(*psapi.EnumProcessModules)(
- proc, module_handles, sizeof(module_handles), LPDWORD(&need_bytes))) {
+ HMODULE module_handles[1024]{0};
+ if (!EnumProcessModules(proc, module_handles, sizeof(module_handles), LPDWORD(&need_bytes))) {
return;
}
- // Symbols longer than this will be truncated.
- static constexpr size_t kMaxSymName = 256;
-
- for (auto& entry : __entry_iters_()) {
- char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName + 1];
- auto* sym = (IMAGEHLP_SYMBOL64*)space;
- sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
- sym->MaxNameLength = kMaxSymName;
- uint64_t symdisp{0};
+ for (_Entry& entry : __entry_iters_()) {
+ char space[sizeof(IMAGEHLP_SYMBOL) + _Entry::__max_sym_len + 1];
+ IMAGEHLP_SYMBOL* sym = reinterpret_cast<IMAGEHLP_SYMBOL*>(space);
+ sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
+ sym->MaxNameLength = _Entry::__max_sym_len;
+ uintptr_t symdisp{0};
DWORD linedisp{0};
- IMAGEHLP_LINE64 line;
- if ((*dbghelp.SymGetSymFromAddr64)(proc, entry.__addr_, &symdisp, sym)) {
+ IMAGEHLP_LINE line;
+ if (SymGetSymFromAddr(proc, entry.__addr_, &symdisp, sym)) {
entry.__desc_.assign(sym->Name);
}
- line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
- if ((*dbghelp.SymGetLineFromAddr64)(proc, entry.__addr_, &linedisp, &line)) {
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
+ if (SymGetLineFromAddr(proc, entry.__addr_, &linedisp, &line)) {
entry.__file_.assign(line.FileName);
entry.__line_ = line.LineNumber;
}
@@ -249,4 +181,4 @@ _Trace::windows_impl(size_t skip, size_t max_depth) {
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#endif // _WIN32
+#endif // _WIN32
>From 004ee57037d8802642592402d2dc51d3dad0efe7 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Tue, 28 Oct 2025 11:27:11 -0400
Subject: [PATCH 10/11] More formatting and generated-file fixes
---
libcxx/include/__stacktrace/basic_stacktrace.h | 6 ++----
libcxx/include/__stacktrace/stacktrace_entry.h | 4 ++--
...pple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 --
3 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index e4ae7b297e391..b43538f00af91 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -131,14 +131,12 @@ class basic_stacktrace : private __stacktrace::_Trace {
// Creation and assignment [stacktrace.basic.cons]
_LIBCPP_ALWAYS_INLINE // Omit this function from the trace
- static basic_stacktrace
- current(const allocator_type& __alloc = allocator_type()) noexcept {
+ static basic_stacktrace current(const allocator_type& __alloc = allocator_type()) noexcept {
return current(0, __default_max_depth, __alloc);
}
_LIBCPP_ALWAYS_INLINE // Omit this function from the trace
- static basic_stacktrace
- current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept {
+ static basic_stacktrace current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept {
return current(__skip, __default_max_depth, __alloc);
}
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index b3f34a245b512..ffa94b20455a2 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -54,9 +54,9 @@ struct _StringWrapper {
char __chars_[1024];
- _LIBCPP_EXPORTED_FROM_ABI std::string_view view() const { return __chars_; }
+ _LIBCPP_HIDE_FROM_ABI std::string_view view() const { return __chars_; }
- _LIBCPP_EXPORTED_FROM_ABI _StringWrapper& assign(std::string_view __view) {
+ _LIBCPP_HIDE_FROM_ABI _StringWrapper& assign(std::string_view __view) {
size_t __size = std::min(__view.size(), sizeof(__chars_) - 1);
memcpy(__chars_, __view.data(), __size);
__chars_[__size] = 0;
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 2ce5ffdc1ccd3..66e17882a8491 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
@@ -413,7 +413,6 @@
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_negative_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_positive_signEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__110moneypunctIwLb1EE16do_thousands_sepEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace14_StringWrapper4viewEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Entry9to_stringEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNKSt3__112__stacktrace6_Trace8write_toERNS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
@@ -980,7 +979,6 @@
{'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__stacktrace14_StringWrapper6assignENS_17basic_string_viewIcNS_11char_traitsIcEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace6_Trace15populate_imagesEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7_ImagesC2Ev', 'type': 'FUNC'}
>From 934a4300bfe2b0bb5f4ef266f034661bc32160f0 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Tue, 28 Oct 2025 13:00:03 -0400
Subject: [PATCH 11/11] Try to fix windows mingw build
---
libcxx/src/CMakeLists.txt | 5 +++++
libcxx/src/stacktrace/windows_impl.cpp | 13 +++++++------
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 41fb1fab1f9e4..e30fcef2658de 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -254,6 +254,11 @@ if (LIBCXX_ENABLE_SHARED)
list(APPEND LIBCXX_BUILD_TARGETS "cxx_shared")
endif()
+if(WIN32 AND NOT (${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} STREQUAL "MSVC"))
+ string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}dbghelp")
+ string(APPEND link_libraries " ${CMAKE_LINK_LIBRARY_FLAG}psapi")
+endif()
+
if(WIN32 AND NOT MINGW AND NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")
# Since we most likely do not have a mt.exe replacement, disable the
# manifest bundling. This allows a normal cmake invocation to pass which
diff --git a/libcxx/src/stacktrace/windows_impl.cpp b/libcxx/src/stacktrace/windows_impl.cpp
index 3af62a8ffe194..d70f29d8263f4 100644
--- a/libcxx/src/stacktrace/windows_impl.cpp
+++ b/libcxx/src/stacktrace/windows_impl.cpp
@@ -10,16 +10,17 @@
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
-//
-# include <dbghelp.h>
-# include <psapi.h>
-//
+
# include <cstring>
+# include <dbghelp.h>
# include <mutex>
+# include <psapi.h>
# include <stacktrace>
-# pragma comment(lib, "dbghelp.lib")
-# pragma comment(lib, "psapi.lib")
+# if defined(_MSC_VER)
+# pragma comment(lib, "dbghelp.lib")
+# pragma comment(lib, "psapi.lib")
+# endif
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
More information about the libcxx-commits
mailing list