[libcxx-commits] [libcxx] Add C++23 stacktrace (P0881R7) (PR #136528)
Steve O'Brien via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jul 15 07:01:32 PDT 2025
https://github.com/elsteveogrande updated https://github.com/llvm/llvm-project/pull/136528
>From 6e91f8e021ce281e43aff09ed061d076d8df5683 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/14] Add C++23 stacktrace
---
libcxx/CMakeLists.txt | 6 +
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/docs/ReleaseNotes/21.rst | 2 +
libcxx/docs/Status/Cxx23Issues.csv | 2 +-
libcxx/docs/Status/Cxx23Papers.csv | 6 +-
libcxx/docs/VendorDocumentation.rst | 8 +
libcxx/include/CMakeLists.txt | 7 +
libcxx/include/__config | 27 ++
libcxx/include/__config_site.in | 1 +
libcxx/include/__stacktrace/base.h | 133 ++++++
libcxx/include/__stacktrace/basic.h | 247 +++++++++++
libcxx/include/__stacktrace/entry.h | 113 +++++
libcxx/include/__stacktrace/hash.h | 47 ++
libcxx/include/__stacktrace/nonmem.h | 55 +++
libcxx/include/__stacktrace/to_string.h | 53 +++
libcxx/include/module.modulemap.in | 37 +-
libcxx/include/stacktrace | 191 +++++++++
libcxx/include/version | 2 +-
...bcxxabi.v1.stable.exceptions.nonew.abilist | 24 +-
...bcxxabi.v1.stable.exceptions.nonew.abilist | 47 +-
libcxx/modules/std.compat.cppm.in | 3 -
libcxx/modules/std.cppm.in | 4 +-
libcxx/modules/std/stacktrace.inc | 6 +-
libcxx/src/CMakeLists.txt | 9 +
libcxx/src/stacktrace/README.md | 6 +
libcxx/src/stacktrace/builder.cpp | 70 +++
libcxx/src/stacktrace/config.h | 26 ++
libcxx/src/stacktrace/linux/impl.cpp | 112 +++++
libcxx/src/stacktrace/linux/impl.h | 401 ++++++++++++++++++
libcxx/src/stacktrace/macos/impl.cpp | 99 +++++
libcxx/src/stacktrace/macos/impl.h | 40 ++
libcxx/src/stacktrace/to_string.cpp | 93 ++++
libcxx/src/stacktrace/tools/tools.cpp | 382 +++++++++++++++++
libcxx/src/stacktrace/tools/tools.h | 197 +++++++++
libcxx/src/stacktrace/unwind/impl.cpp | 61 +++
libcxx/src/stacktrace/unwind/impl.h | 32 ++
libcxx/src/stacktrace/utils/debug.h | 55 +++
libcxx/src/stacktrace/utils/failed.h | 29 ++
libcxx/src/stacktrace/utils/fd.h | 131 ++++++
libcxx/src/stacktrace/utils/image.h | 33 ++
libcxx/src/stacktrace/windows/dll.cpp | 247 +++++++++++
libcxx/src/stacktrace/windows/dll.h | 121 ++++++
libcxx/src/stacktrace/windows/impl.cpp | 200 +++++++++
libcxx/src/stacktrace/windows/impl.h | 50 +++
.../stacktrace/simple.o0.nodebug.pass.cpp | 32 ++
.../stacktrace/simple.o0.nosplit.pass.cpp | 29 ++
.../stacktrace/simple.o0.split.pass.cpp | 29 ++
.../stacktrace/simple.o3.nodebug.pass.cpp | 32 ++
.../stacktrace/simple.o3.nosplit.pass.cpp | 29 ++
.../stacktrace/simple.o3.split.pass.cpp | 29 ++
.../test/libcxx/transitive_includes/cxx23.csv | 21 +
.../test/libcxx/transitive_includes/cxx26.csv | 21 +
.../stacktrace/basic.cmp/equality.pass.cpp | 51 +++
.../basic.cmp/strong_ordering.pass.cpp | 81 ++++
.../basic.cons/copy_and_move.pass.cpp | 82 ++++
.../basic.cons/ctor_no_args.pass.cpp | 48 +++
.../basic.cons/ctor_with_alloc.pass.cpp | 79 ++++
.../basic.cons/current_no_args.pass.cpp | 67 +++
.../basic.cons/current_skip.pass.cpp | 45 ++
.../basic.cons/current_skip_depth.pass.cpp | 42 ++
.../basic.cons/only_uses_allocator.pass.cpp | 128 ++++++
.../stacktrace/basic.hash.pass.cpp | 37 ++
.../stacktrace/basic.mod/swap.pass.cpp | 41 ++
.../basic.nonmem/operator_left_shift.pass.cpp | 35 ++
.../stacktrace/basic.nonmem/swap.pass.cpp | 36 ++
.../basic.nonmem/to_string.pass.cpp | 33 ++
.../stacktrace/basic.obs/at.pass.cpp | 49 +++
.../stacktrace/basic.obs/begin_end.pass.cpp | 41 ++
.../stacktrace/basic.obs/cbegin_cend.pass.cpp | 36 ++
.../basic.obs/crbegin_crend.pass.cpp | 36 ++
.../stacktrace/basic.obs/empty.pass.cpp | 32 ++
.../basic.obs/get_allocator.pass.cpp | 25 ++
.../stacktrace/basic.obs/max_size.pass.cpp | 32 ++
.../basic.obs/operator_index.pass.cpp | 49 +++
.../stacktrace/basic.obs/rbegin_rend.pass.cpp | 37 ++
.../stacktrace/basic.obs/size.pass.cpp | 32 ++
.../std/diagnostics/stacktrace/basic.pass.cpp | 143 +++++++
.../stacktrace/entry.cmp/equality.pass.cpp | 42 ++
.../entry.cmp/strong_ordering.pass.cpp | 52 +++
.../entry.cons/copy_assign.pass.cpp | 34 ++
.../entry.cons/copy_construct.pass.cpp | 33 ++
.../stacktrace/entry.cons/default.pass.cpp | 35 ++
.../entry.obs/native_handle.pass.cpp | 28 ++
.../entry.obs/operator_bool.pass.cpp | 34 ++
.../std/diagnostics/stacktrace/entry.pass.cpp | 61 +++
.../entry.query/description.pass.cpp | 32 ++
.../entry.query/source_file.pass.cpp | 32 ++
.../entry.query/source_line.pass.cpp | 30 ++
.../formatter_basic_stacktrace.pass.cpp | 31 ++
.../formatter_stacktrace_entry.pass.cpp | 37 ++
.../stacktrace.version.compile.pass.cpp | 108 +++++
.../version.version.compile.pass.cpp | 32 +-
.../generate_feature_test_macro_components.py | 1 -
libcxx/utils/libcxx/header_information.py | 3 +-
94 files changed, 5537 insertions(+), 44 deletions(-)
create mode 100644 libcxx/include/__stacktrace/base.h
create mode 100644 libcxx/include/__stacktrace/basic.h
create mode 100644 libcxx/include/__stacktrace/entry.h
create mode 100644 libcxx/include/__stacktrace/hash.h
create mode 100644 libcxx/include/__stacktrace/nonmem.h
create mode 100644 libcxx/include/__stacktrace/to_string.h
create mode 100644 libcxx/include/stacktrace
create mode 100644 libcxx/src/stacktrace/README.md
create mode 100644 libcxx/src/stacktrace/builder.cpp
create mode 100644 libcxx/src/stacktrace/config.h
create mode 100644 libcxx/src/stacktrace/linux/impl.cpp
create mode 100644 libcxx/src/stacktrace/linux/impl.h
create mode 100644 libcxx/src/stacktrace/macos/impl.cpp
create mode 100644 libcxx/src/stacktrace/macos/impl.h
create mode 100644 libcxx/src/stacktrace/to_string.cpp
create mode 100644 libcxx/src/stacktrace/tools/tools.cpp
create mode 100644 libcxx/src/stacktrace/tools/tools.h
create mode 100644 libcxx/src/stacktrace/unwind/impl.cpp
create mode 100644 libcxx/src/stacktrace/unwind/impl.h
create mode 100644 libcxx/src/stacktrace/utils/debug.h
create mode 100644 libcxx/src/stacktrace/utils/failed.h
create mode 100644 libcxx/src/stacktrace/utils/fd.h
create mode 100644 libcxx/src/stacktrace/utils/image.h
create mode 100644 libcxx/src/stacktrace/windows/dll.cpp
create mode 100644 libcxx/src/stacktrace/windows/dll.h
create mode 100644 libcxx/src/stacktrace/windows/impl.cpp
create mode 100644 libcxx/src/stacktrace/windows/impl.h
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cmp/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/copy_and_move.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/only_uses_allocator.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/format/formatter_basic_stacktrace.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.pass.cpp
create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 85514cc7547a9..6e4624326100e 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -131,6 +131,11 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
the shared library they shipped should turn this on and see `include/__configuration/availability.h`
for more details." OFF)
+option(LIBCXX_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
+ "For C++23 <stacktrace>: whether to allow invocation of `addr2line`, `llvm-addr2line`, or `atos`
+ at runtime (if it's available in `PATH`) to resolve call-chain addresses in the stacktrace
+ into source locations, if other methods are not available." ON)
+
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
elseif(MINGW)
@@ -750,6 +755,7 @@ config_define(${LIBCXX_ENABLE_UNICODE} _LIBCPP_HAS_UNICODE)
config_define(${LIBCXX_ENABLE_WIDE_CHARACTERS} _LIBCPP_HAS_WIDE_CHARACTERS)
config_define(${LIBCXX_ENABLE_TIME_ZONE_DATABASE} _LIBCPP_HAS_TIME_ZONE_DATABASE)
config_define(${LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS} _LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS)
+config_define(${LIBCXX_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME} _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME)
# TODO: Remove in LLVM 21. We're leaving an error to make this fail explicitly.
if (LIBCXX_ENABLE_ASSERTIONS)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 61805726a4ff0..949d6dc468f21 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/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index 6f18b61284f49..c0ae4d80198d8 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -53,6 +53,8 @@ Implemented Papers
- P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github <https://github.com/llvm/llvm-project/issues/105252>`__)
- P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github <https://github.com/llvm/llvm-project/issues/105250>`__)
- P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github <https://github.com/llvm/llvm-project/issues/105260>`__)
+- 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 e5b2dcf8c1a5b..cd25c80dace2e 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -189,7 +189,7 @@
"`LWG3028 <https://wg21.link/LWG3028>`__","Container requirements tables should distinguish ``const`` and non-``const`` variables","2022-11 (Kona)","","",""
"`LWG3118 <https://wg21.link/LWG3118>`__","``fpos`` equality comparison unspecified","2022-11 (Kona)","","",""
"`LWG3177 <https://wg21.link/LWG3177>`__","Limit permission to specialize variable templates to program-defined types","2022-11 (Kona)","|Nothing To Do|","",""
-"`LWG3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized","2022-11 (Kona)","","",""
+"`LWG3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized","2022-11 (Kona)","|Nothing To Do|","",""
"`LWG3545 <https://wg21.link/LWG3545>`__","``std::pointer_traits`` should be SFINAE-friendly","2022-11 (Kona)","|Complete|","18",""
"`LWG3569 <https://wg21.link/LWG3569>`__","``join_view`` fails to support ranges of ranges with non-default_initializable iterators","2022-11 (Kona)","","",""
"`LWG3594 <https://wg21.link/LWG3594>`__","``inout_ptr`` — inconsistent ``release()`` in destructor","2022-11 (Kona)","|Complete|","19",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e4fa07d82289d..b5559f8485162 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -1,5 +1,5 @@
"Paper #","Paper Name","Meeting","Status","First released version","Notes"
-"`P0881R7 <https://wg21.link/P0881R7>`__","A Proposal to add stacktrace library","2020-11 (Virtual)","","",""
+"`P0881R7 <https://wg21.link/P0881R7>`__","A Proposal to add stacktrace library","2020-11 (Virtual)","|Complete|","21",""
"`P0943R6 <https://wg21.link/P0943R6>`__","Support C atomics in C++","2020-11 (Virtual)","|Complete|","15",""
"`P1048R1 <https://wg21.link/P1048R1>`__","A proposal for a type trait to detect scoped enumerations","2020-11 (Virtual)","|Complete|","12",""
"`P1679R3 <https://wg21.link/P1679R3>`__","string contains function","2020-11 (Virtual)","|Complete|","12",""
@@ -32,7 +32,7 @@
"`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","",""
"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","","",""
"`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14",""
-"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","",""
+"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","|Complete|","21",""
"`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|In Progress|","",""
"`P2340R1 <https://wg21.link/P2340R1>`__","Clarifying the status of the 'C headers'","2021-10 (Virtual)","|Nothing To Do|","",""
"`P2393R1 <https://wg21.link/P2393R1>`__","Cleaning up ``integer``-class types","2021-10 (Virtual)","","",""
@@ -110,7 +110,7 @@
"`P2713R1 <https://wg21.link/P2713R1>`__","Escaping improvements in ``std::format``","2023-02 (Issaquah)","|Complete|","19",""
"`P2675R1 <https://wg21.link/P2675R1>`__","``format``'s width estimation is too approximate and not forward compatible","2023-02 (Issaquah)","|Complete|","17",""
"`P2572R1 <https://wg21.link/P2572R1>`__","``std::format`` fill character allowances","2023-02 (Issaquah)","|Complete|","17",""
-"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","2023-02 (Issaquah)","|Partial|","","The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is not implemented yet"
+"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``","2023-02 (Issaquah)","|Partial|","","The formatter for ``stacktrace`` is not implemented yet"
"`P2679R2 <https://wg21.link/P2679R2>`__","Fixing ``std::start_lifetime_as`` for arrays","2023-02 (Issaquah)","","",""
"`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20",""
"`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","The paper is implemented as a DR to C++20"
diff --git a/libcxx/docs/VendorDocumentation.rst b/libcxx/docs/VendorDocumentation.rst
index aede8f9a81dd2..971925bb8a7d0 100644
--- a/libcxx/docs/VendorDocumentation.rst
+++ b/libcxx/docs/VendorDocumentation.rst
@@ -185,6 +185,14 @@ General purpose options
ship the IANA time zone database. When time zones are not supported,
time zone support in <chrono> will be disabled.
+.. option:: LIBCXX_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME:BOOL
+
+ **Default**: ``ON``
+
+ For C++23 <stacktrace>: whether to allow invocation of ``addr2line``, ``llvm-addr2line``, or ``atos``
+ at runtime (if it's available in ``PATH``) to resolve call-chain addresses in the stacktrace
+ into source locations, if other methods are not available.
+
.. option:: LIBCXX_INSTALL_LIBRARY_DIR:PATH
**Default**: ``lib${LIBCXX_LIBDIR_SUFFIX}``
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4f2a8dddad92c..397c0a320dffc 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -739,6 +739,12 @@ set(files
__ranges/views.h
__ranges/zip_view.h
__split_buffer
+ __stacktrace/base.h
+ __stacktrace/basic.h
+ __stacktrace/entry.h
+ __stacktrace/hash.h
+ __stacktrace/nonmem.h
+ __stacktrace/to_string.h
__std_mbstate_t.h
__stop_token/atomic_unique_lock.h
__stop_token/intrusive_list_view.h
@@ -1058,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 d940461c30234..6b226a5bea091 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -924,6 +924,33 @@ typedef __char32_t char32_t;
# define _LIBCPP_NOINLINE
# endif
+// Some functions, e.g. std::stacktrace::current, need to avoid being
+// tail-called by (and tail-calling other) functions, for proper enumeration of
+// call-stack frames.
+// clang-format off
+
+// Disables tail-call optimization for "outbound" calls
+// performed in the function annotated with this attribute.
+# if __has_cpp_attribute(_Clang::__disable_tail_calls__)
+# define _LIBCPP_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
+# elif __has_cpp_attribute(__gnu__::__optimize__)
+# define _LIBCPP_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
+# else
+# define _LIBCPP_NO_TAIL_CALLS_OUT
+# endif
+
+// Disables tail-call optimization for "inbound" calls -- that is,
+// calls from some other function calling the one having this attribute.
+# if __has_cpp_attribute(_Clang::__not_tail_called__)
+# define _LIBCPP_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
+# else
+# define _LIBCPP_NO_TAIL_CALLS_IN
+# endif
+
+// Disable TCO for calls into, and out from, the annotated function.
+# define _LIBCPP_NO_TAIL_CALLS _LIBCPP_NO_TAIL_CALLS_IN _LIBCPP_NO_TAIL_CALLS_OUT
+// clang-format on
+
// We often repeat things just for handling wide characters in the library.
// When wide characters are disabled, it can be useful to have a quick way of
// disabling it without having to resort to #if-#endif, which has a larger
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index fc01aaf2d8746..4e25ed040c9d0 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -33,6 +33,7 @@
#cmakedefine _LIBCPP_HAS_NO_STD_MODULES
#cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
#cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
+#cmakedefine01 _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
// PSTL backends
#cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
diff --git a/libcxx/include/__stacktrace/base.h b/libcxx/include/__stacktrace/base.h
new file mode 100644
index 0000000000000..52e6d17e4e5be
--- /dev/null
+++ b/libcxx/include/__stacktrace/base.h
@@ -0,0 +1,133 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_BUILDER
+#define _LIBCPP_STACKTRACE_BUILDER
+
+#include <__config>
+#include <__cstddef/byte.h>
+#include <__cstddef/size_t.h>
+#include <__functional/function.h>
+#include <__fwd/format.h>
+#include <__fwd/ostream.h>
+#include <__memory/allocator.h>
+#include <__memory/allocator_traits.h>
+#include <__new/allocate.h>
+#include <__vector/vector.h>
+#include <cstddef>
+#include <cstdint>
+#include <list>
+#include <optional>
+#include <string>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry;
+
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI alloc final {
+ function<byte*(size_t)> __alloc_bytes_;
+ function<void(byte*, size_t)> __dealloc_bytes_;
+
+ template <class _Allocator>
+ _LIBCPP_HIDE_FROM_ABI alloc(_Allocator __alloc) {
+ using _AT = allocator_traits<_Allocator>;
+ using _BA = typename _AT::template rebind_alloc<byte>;
+ auto __ba = _BA(__alloc);
+ __alloc_bytes_ = [__ba](size_t __sz) mutable { return __ba.allocate(__sz); };
+ __dealloc_bytes_ = [__ba](void* __ptr, size_t __sz) mutable { __ba.deallocate((byte*)__ptr, __sz); };
+ }
+
+ template <typename _Tp>
+ struct _LIBCPP_HIDE_FROM_ABI Alloc {
+ function<byte*(size_t)> __alloc_bytes_;
+ function<void(byte*, size_t)> __dealloc_bytes_;
+
+ Alloc(function<byte*(size_t)> __alloc_bytes, function<void(byte*, size_t)> __dealloc_bytes)
+ : __alloc_bytes_(__alloc_bytes), __dealloc_bytes_(__dealloc_bytes) {}
+
+ template <typename _T2 = _Tp>
+ Alloc(Alloc<_T2> const& __rhs) : Alloc(__rhs.__alloc_bytes_, __rhs.__dealloc_bytes_) {}
+
+ using value_type = _Tp;
+ [[nodiscard]] _Tp* allocate(size_t __sz) { return (_Tp*)__alloc_bytes_(__sz * sizeof(_Tp)); }
+ void deallocate(_Tp* __ptr, size_t __sz) { __dealloc_bytes_((byte*)__ptr, __sz * sizeof(_Tp)); }
+
+ template <typename _A2>
+ bool operator==(_A2 const& __rhs) const {
+ return &__rhs == this;
+ }
+ };
+
+ template <typename _Tp>
+ Alloc<_Tp> _LIBCPP_HIDE_FROM_ABI make_alloc() {
+ return {__alloc_bytes_, __dealloc_bytes_};
+ }
+
+ using str = basic_string<char, char_traits<char>, Alloc<char>>;
+
+ template <typename... _Args>
+ str _LIBCPP_HIDE_FROM_ABI make_str(_Args... __args) {
+ return str(std::forward<_Args>(__args)..., make_alloc<char>());
+ }
+
+ template <typename _Tp>
+ using vec = vector<_Tp, Alloc<_Tp>>;
+
+ template <typename _Tp, typename... _Args>
+ _LIBCPP_HIDE_FROM_ABI vec<_Tp> make_vec(_Args... __args) {
+ return vec(std::forward<_Args>(__args)..., make_alloc<_Tp>());
+ }
+
+ template <typename _Tp>
+ using list = ::std::list<_Tp, Alloc<_Tp>>;
+
+ template <typename _Tp, typename... _Args>
+ _LIBCPP_HIDE_FROM_ABI list<_Tp> make_list(_Args... __args) {
+ return list(std::forward<_Args>(__args)..., make_alloc<_Tp>());
+ }
+};
+
+struct _LIBCPP_HIDE_FROM_ABI entry_base {
+ uintptr_t __addr_actual_{}; // this address, as observed in this current process
+ uintptr_t __addr_unslid_{}; // address adjusted for ASLR
+ optional<__stacktrace::alloc::str> __desc_{}; // uses wrapped _Allocator from caller
+ optional<__stacktrace::alloc::str> __file_{}; // uses wrapped _Allocator from caller
+ uint_least32_t __line_{};
+
+ _LIBCPP_HIDE_FROM_ABI stacktrace_entry to_stacktrace_entry() const;
+};
+
+struct _LIBCPP_EXPORTED_FROM_ABI builder final {
+ alloc __alloc_; // wraps the caller-provided allocator
+ alloc::vec<entry_base> __entries_;
+ alloc::str __main_prog_path_;
+
+ template <class _Allocator>
+ explicit _LIBCPP_EXPORTED_FROM_ABI builder(_Allocator __alloc)
+ : __alloc_(__alloc), __entries_(__alloc_.make_vec<entry_base>()), __main_prog_path_(__alloc_.make_str()) {}
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+ build_stacktrace(size_t __skip, size_t __max_depth);
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_BUILDER
diff --git a/libcxx/include/__stacktrace/basic.h b/libcxx/include/__stacktrace/basic.h
new file mode 100644
index 0000000000000..a73c8d56f6c21
--- /dev/null
+++ b/libcxx/include/__stacktrace/basic.h
@@ -0,0 +1,247 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_BASIC_STACKTRACE
+#define _LIBCPP_BASIC_STACKTRACE
+
+#include <__config>
+#include <__functional/hash.h>
+#include <__fwd/format.h>
+#include <__iterator/iterator.h>
+#include <__iterator/reverse_iterator.h>
+#include <__memory/allocator_traits.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__vector/vector.h>
+#include <utility>
+
+#include <__stacktrace/base.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/to_string.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// (19.6.4)
+// Class template basic_stacktrace [stacktrace.basic]
+
+class stacktrace_entry;
+
+template <class _Allocator>
+class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
+ friend struct hash<basic_stacktrace<_Allocator>>;
+ friend struct __stacktrace::__to_string;
+
+ using _ATraits _LIBCPP_NODEBUG = allocator_traits<_Allocator>;
+ constexpr static bool __kPropOnCopy = _ATraits::propagate_on_container_copy_assignment::value;
+ constexpr static bool __kPropOnMove = _ATraits::propagate_on_container_move_assignment::value;
+ constexpr static bool __kPropOnSwap = _ATraits::propagate_on_container_swap::value;
+ constexpr static bool __kAlwaysEqual = _ATraits::is_always_equal::value;
+ constexpr static bool __kNoThrowDflConstruct = is_nothrow_default_constructible_v<_Allocator>;
+ constexpr static bool __kNoThrowAlloc =
+ noexcept(noexcept(_Allocator().allocate(1)) && noexcept(_Allocator().allocate_at_least(1)));
+
+ [[no_unique_address]] _Allocator __alloc_;
+
+ using __entry_vec = vector<stacktrace_entry, _Allocator>;
+ __entry_vec __entries_;
+
+public:
+ // (19.6.4.1)
+ // Overview [stacktrace.basic.overview]
+
+ using value_type = stacktrace_entry;
+ using const_reference = const value_type&;
+ using reference = value_type&;
+ using difference_type = ptrdiff_t;
+ using size_type = size_t;
+ using allocator_type = _Allocator;
+ using const_iterator = __entry_vec::const_iterator;
+ using iterator = const_iterator;
+
+ using reverse_iterator = std::reverse_iterator<basic_stacktrace::iterator>;
+ using const_reverse_iterator = std::reverse_iterator<basic_stacktrace::const_iterator>;
+
+ // (19.6.4.2)
+ // Creation and assignment [stacktrace.basic.cons]
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
+ current(const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ return current(1, /* no __max_depth */ ~0, __caller_alloc);
+ }
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
+ current(size_type __skip, const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ return current(__skip + 1, /* no __max_depth */ ~0, __caller_alloc);
+ }
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
+ current(size_type __skip,
+ size_type __max_depth,
+ const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ __stacktrace::builder __builder(__caller_alloc);
+ __builder.build_stacktrace(__skip + 1, __max_depth);
+ basic_stacktrace<_Allocator> __ret{__caller_alloc};
+ __ret.__entries_.reserve(__builder.__entries_.size());
+ for (auto& __base : __builder.__entries_) {
+ __ret.__entries_.emplace_back(__base.to_stacktrace_entry());
+ }
+ return __ret;
+ }
+
+ _LIBCPP_EXPORTED_FROM_ABI constexpr ~basic_stacktrace() = default;
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace() noexcept(__kNoThrowDflConstruct) : basic_stacktrace(allocator_type()) {}
+
+ _LIBCPP_EXPORTED_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc) noexcept
+ : __alloc_(__alloc), __entries_(__alloc_) {}
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) = default;
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept = default;
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
+ : __alloc_(__alloc), __entries_(__other.__entries_, __alloc) {}
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
+ : __alloc_(__alloc) {
+ if (__kAlwaysEqual || __alloc_ == __other.__alloc_) {
+ __entries_ = std::move(__other.__entries_);
+ } else {
+ // "moving" from a container with a different allocator; we're forced to copy items instead
+ for (auto const& __entry : __other.__entries_) {
+ __entries_.push_back(__entry);
+ }
+ }
+ }
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other) {
+ if (this == std::addressof(__other)) {
+ return *this;
+ }
+ if (__kPropOnCopy) {
+ __alloc_ = __other.__alloc_;
+ }
+ __entries_ = {__other.__entries_, __alloc_};
+ return *this;
+ }
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace&
+ operator=(basic_stacktrace&& __other) noexcept(__kPropOnMove || __kAlwaysEqual) {
+ if (this == std::addressof(__other)) {
+ return *this;
+ }
+ if (__kPropOnMove) {
+ __alloc_ = __other.__alloc_;
+ __entries_ = std::move(__other.__entries_);
+ } else {
+ auto __allocs_eq = __kAlwaysEqual || __alloc_ == __other.__alloc_;
+ if (__allocs_eq) {
+ __entries_ = std::move(__other.__entries_);
+ } else {
+ // "moving" from a container with a different allocator;
+ // we're forced to copy items instead
+ for (auto const& __entry : __other.__entries_) {
+ __entries_.push_back(__entry);
+ }
+ }
+ }
+ return *this;
+ }
+
+ // clang-format on
+
+ // (19.6.4.3)
+ // [stacktrace.basic.obs], observers
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI allocator_type get_allocator() const noexcept { return __alloc_; }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator begin() const noexcept { return __entries_.begin(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator end() const noexcept { return __entries_.end(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator rbegin() const noexcept { return __entries_.rbegin(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator rend() const noexcept { return __entries_.rend(); }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator cbegin() const noexcept { return __entries_.cbegin(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator cend() const noexcept { return __entries_.cend(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator crbegin() const noexcept {
+ return __entries_.crbegin();
+ }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator crend() const noexcept { return __entries_.crend(); }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI bool empty() const noexcept { return __entries_.empty(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI size_type size() const noexcept { return __entries_.size(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI size_type max_size() const noexcept { return __entries_.max_size(); }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reference operator[](size_type __i) const { return __entries_[__i]; }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reference at(size_type __i) const { return __entries_.at(__i); }
+
+ // (19.6.4.4)
+ // [stacktrace.basic.cmp], comparisons
+
+ template <class _Allocator2>
+ _LIBCPP_EXPORTED_FROM_ABI friend bool
+ operator==(const basic_stacktrace& __x, const basic_stacktrace<_Allocator2>& __y) noexcept {
+ if (__x.size() != __y.size()) {
+ return false;
+ }
+ auto __xi = __x.begin();
+ auto __yi = __y.begin();
+ auto __xe = __x.end();
+ while (__xi != __xe) {
+ if (*__xi++ != *__yi++) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ template <class _Allocator2>
+ _LIBCPP_EXPORTED_FROM_ABI friend strong_ordering
+ operator<=>(const basic_stacktrace& __x, const basic_stacktrace<_Allocator2>& __y) noexcept {
+ auto __ret = __x.size() <=> __y.size();
+ if (__ret != std::strong_ordering::equal) {
+ return __ret;
+ }
+ auto __xi = __x.begin();
+ auto __yi = __y.begin();
+ auto __xe = __x.end();
+ while ((__ret == std::strong_ordering::equal) && __xi != __xe) {
+ __ret = *__xi++ <=> *__yi++;
+ }
+ return __ret;
+ }
+
+ // (19.6.4.5)
+ // [stacktrace.basic.mod], modifiers
+
+ _LIBCPP_EXPORTED_FROM_ABI void swap(basic_stacktrace<_Allocator>& __other) noexcept {
+ std::swap(__entries_, __other.__entries_);
+ if (__kPropOnSwap) {
+ std::swap(__alloc_, __other.__alloc_);
+ }
+ }
+};
+
+using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+
+namespace pmr {
+using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
+} // namespace pmr
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_BASIC_STACKTRACE
diff --git a/libcxx/include/__stacktrace/entry.h b/libcxx/include/__stacktrace/entry.h
new file mode 100644
index 0000000000000..6290ebea9bc85
--- /dev/null
+++ b/libcxx/include/__stacktrace/entry.h
@@ -0,0 +1,113 @@
+// -*- 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>
+#include <__fwd/format.h>
+#include <__fwd/ostream.h>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <string>
+
+#include <__stacktrace/base.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry : private __stacktrace::entry_base {
+ friend struct __stacktrace::entry_base;
+ _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry(entry_base const& __base) : 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_EXPORTED_FROM_ABI ~stacktrace_entry() noexcept = default;
+ _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry() noexcept = default;
+ _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry(const stacktrace_entry&) noexcept = default;
+ _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry& operator=(const stacktrace_entry&) noexcept = default;
+
+ // (19.6.3.3) [stacktrace.entry.obs], observers
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr native_handle_type native_handle() const noexcept {
+ return __addr_actual_;
+ }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr explicit operator bool() const noexcept {
+ return native_handle() != 0;
+ }
+
+ // (19.6.3.4) [stacktrace.entry.query], query
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string description() const {
+ if (__desc_->empty()) {
+ return "";
+ }
+ return {__desc_->data(), __desc_->size()};
+ }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string source_file() const {
+ if (__desc_->empty()) {
+ return "";
+ }
+ return {__file_->data(), __file_->size()};
+ }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI uint_least32_t source_line() const { return __line_; }
+
+ // (19.6.3.5) [stacktrace.entry.cmp], comparison
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr bool
+ operator==(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+ return __x.native_handle() == __y.native_handle();
+ }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr strong_ordering
+ operator<=>(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+ return __x.native_handle() <=> __y.native_handle();
+ }
+};
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry);
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry);
+
+// (19.6.5)
+// Formatting support [stacktrace.format]
+
+// TODO: stacktrace formatter: https://github.com/llvm/llvm-project/issues/105257
+template <>
+struct _LIBCPP_EXPORTED_FROM_ABI formatter<stacktrace_entry>;
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <>
+struct _LIBCPP_EXPORTED_FROM_ABI hash<stacktrace_entry> {
+ [[nodiscard]] size_t operator()(stacktrace_entry const& __entry) const noexcept {
+ auto __addr = __entry.native_handle();
+ return hash<uintptr_t>()(__addr);
+ }
+};
+
+namespace __stacktrace {
+_LIBCPP_HIDE_FROM_ABI inline stacktrace_entry entry_base::to_stacktrace_entry() const { return {*this}; }
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_ENTRY
diff --git a/libcxx/include/__stacktrace/hash.h b/libcxx/include/__stacktrace/hash.h
new file mode 100644
index 0000000000000..297507f5415fa
--- /dev/null
+++ b/libcxx/include/__stacktrace/hash.h
@@ -0,0 +1,47 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_BASIC_STACKTRACE_HASH
+#define _LIBCPP_BASIC_STACKTRACE_HASH
+
+#include <__config>
+#include <__functional/hash.h>
+#include <cstdint>
+
+#include <__stacktrace/base.h>
+#include <__stacktrace/to_string.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <class _Allocator>
+struct _LIBCPP_EXPORTED_FROM_ABI hash<basic_stacktrace<_Allocator>> {
+ [[nodiscard]] size_t operator()(basic_stacktrace<_Allocator> const& __context) const noexcept {
+ size_t __ret = 1;
+ for (auto const& __entry : __context.__entries_) {
+ __ret += hash<uintptr_t>()(__entry.native_handle());
+ }
+ return __ret;
+ }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_BASIC_STACKTRACE_HASH
diff --git a/libcxx/include/__stacktrace/nonmem.h b/libcxx/include/__stacktrace/nonmem.h
new file mode 100644
index 0000000000000..570237e08ecd5
--- /dev/null
+++ b/libcxx/include/__stacktrace/nonmem.h
@@ -0,0 +1,55 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_BASIC_STACKTRACE_NONMEM
+#define _LIBCPP_BASIC_STACKTRACE_NONMEM
+
+#include <__config>
+#include <__memory/allocator_traits.h>
+#include <__utility/swap.h>
+#include <__vector/vector.h>
+#include <string>
+
+#include <__stacktrace/base.h>
+#include <__stacktrace/to_string.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI inline void
+swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) noexcept(noexcept(__a.swap(__b))) {
+ __a.swap(__b);
+}
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI inline string to_string(const basic_stacktrace<_Allocator>& __stacktrace) {
+ return __stacktrace::__to_string()(__stacktrace);
+}
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI inline ostream& operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __stacktrace) {
+ auto __str = __stacktrace::__to_string()(__stacktrace);
+ return __os << __str;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_BASIC_STACKTRACE_NONMEM
diff --git a/libcxx/include/__stacktrace/to_string.h b/libcxx/include/__stacktrace/to_string.h
new file mode 100644
index 0000000000000..920c41133d5df
--- /dev/null
+++ b/libcxx/include/__stacktrace/to_string.h
@@ -0,0 +1,53 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_TO_STRING
+#define _LIBCPP_STACKTRACE_TO_STRING
+
+#include <__config>
+#include <__fwd/ostream.h>
+#include <string>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Allocator>
+class basic_stacktrace;
+
+class stacktrace_entry;
+
+namespace __stacktrace {
+
+struct __to_string {
+ _LIBCPP_EXPORTED_FROM_ABI string operator()(stacktrace_entry const& __entry);
+
+ _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
+
+ _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
+
+ _LIBCPP_EXPORTED_FROM_ABI string operator()(std::stacktrace_entry const* __entries, size_t __count);
+
+ template <class _Allocator>
+ _LIBCPP_EXPORTED_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
+ return (*this)(__st.__entries_.data(), __st.__entries_.size());
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 61ba1c381b2b3..e485ad46d0d2d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1336,7 +1336,10 @@ module std [system] {
module concepts { header "__format/concepts.h" }
module container_adaptor { header "__format/container_adaptor.h" }
module enable_insertable { header "__format/enable_insertable.h" }
- module escaped_output_table { header "__format/escaped_output_table.h" }
+ module escaped_output_table {
+ header "__format/escaped_output_table.h"
+ export std.format.escaped_output_table // TODO: Workaround for https://github.com/llvm/llvm-project/issues/120108
+ }
module extended_grapheme_cluster_table { header "__format/extended_grapheme_cluster_table.h" }
module format_arg { header "__format/format_arg.h" }
module format_arg_store { header "__format/format_arg_store.h" }
@@ -1371,7 +1374,10 @@ module std [system] {
module range_default_formatter { header "__format/range_default_formatter.h" }
module range_formatter { header "__format/range_formatter.h" }
module unicode { header "__format/unicode.h" }
- module width_estimation_table { header "__format/width_estimation_table.h" }
+ module width_estimation_table {
+ header "__format/width_estimation_table.h"
+ export std.format.width_estimation_table // TODO: Workaround for https://github.com/llvm/llvm-project/issues/120108
+ }
module write_escaped { header "__format/write_escaped.h" }
header "format"
@@ -2008,6 +2014,33 @@ module std [system] {
export *
}
+ module stacktrace {
+ module base { header "__stacktrace/base.h" }
+ module basic { header "__stacktrace/basic.h" }
+ module entry { header "__stacktrace/entry.h" }
+ module hash { header "__stacktrace/hash.h" }
+ module nonmem { header "__stacktrace/nonmem.h" }
+ module to_string { header "__stacktrace/to_string.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.formatter
+ 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..1fbdb6b4fe293
--- /dev/null
+++ b/libcxx/include/stacktrace
@@ -0,0 +1,191 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_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
+ 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
+ };
+}
+
+*/
+
+#include <__config>
+
+#if _LIBCPP_STD_VER >= 23
+
+# include <compare> // according to [stacktrace.syn]
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
+
+_LIBCPP_PUSH_MACROS
+# include <__undef_macros>
+
+# include <__stacktrace/base.h>
+# include <__stacktrace/basic.h>
+# include <__stacktrace/entry.h>
+# include <__stacktrace/hash.h>
+# include <__stacktrace/nonmem.h>
+# include <__stacktrace/to_string.h>
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_STD_VER >= 23
+
+#endif // _LIBCPP_STACKTRACE
diff --git a/libcxx/include/version b/libcxx/include/version
index d98049bd57046..abb000267b5a4 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -531,7 +531,7 @@ __cpp_lib_void_t 201411L <type_traits>
// # define __cpp_lib_ranges_zip 202110L
// # define __cpp_lib_reference_from_temporary 202202L
// # define __cpp_lib_spanstream 202106L
-// # define __cpp_lib_stacktrace 202011L
+# define __cpp_lib_stacktrace 202011L
# define __cpp_lib_stdatomic_h 202011L
# define __cpp_lib_string_contains 202011L
# define __cpp_lib_string_resize_and_overwrite 202110L
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 162757c7e37ec..114f3beb42a71 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -534,6 +534,11 @@
{'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__116stacktrace_entry11descriptionEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__116stacktrace_entry11source_fileEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__116stacktrace_entry11source_lineEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNKSt3__116stacktrace_entry13native_handleEv', '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,11 @@
{'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclERKNS_16stacktrace_entryE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7builder16build_stacktraceEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112bad_weak_ptrD2Ev', 'type': 'FUNC'}
@@ -1125,6 +1135,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 +1316,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 +1518,6 @@
{'is_defined': True, 'name': '__ZNSt3__117bad_function_callD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__117bad_function_callD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__117bad_function_callD2Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNKSt3__117bad_function_call4whatEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__117iostream_categoryEv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb0EE4initEPKc', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__117moneypunct_bynameIcLb1EE4initEPKc', 'type': 'FUNC'}
@@ -1935,6 +1944,7 @@
{'is_defined': True, 'name': '__ZNSt3__19strstreamD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__19strstreamD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__19strstreamD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__19to_stringERKNS_16stacktrace_entryE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__19to_stringEd', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__19to_stringEe', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__19to_stringEf', 'type': 'FUNC'}
@@ -1944,6 +1954,7 @@
{'is_defined': True, 'name': '__ZNSt3__19to_stringEm', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__19to_stringEx', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__19to_stringEy', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__1lsERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt8bad_castC1Ev', 'type': 'I'}
{'is_defined': True, 'name': '__ZNSt8bad_castC2Ev', 'type': 'I'}
@@ -2011,6 +2022,9 @@
{'is_defined': True, 'name': '__ZTINSt3__110moneypunctIwLb1EEE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTINSt3__110ostrstreamE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTINSt3__111regex_errorE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTINSt3__112__stacktrace15llvm_symbolizerE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTINSt3__112__stacktrace4atosE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTINSt3__112__stacktrace9addr2lineE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTINSt3__112bad_weak_ptrE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTINSt3__112ctype_bynameIcEE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTINSt3__112ctype_bynameIwEE', 'size': 0, 'type': 'OBJECT'}
@@ -2225,6 +2239,9 @@
{'is_defined': True, 'name': '__ZTSNSt3__110moneypunctIwLb1EEE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTSNSt3__110ostrstreamE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTSNSt3__111regex_errorE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTSNSt3__112__stacktrace15llvm_symbolizerE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTSNSt3__112__stacktrace4atosE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTSNSt3__112__stacktrace9addr2lineE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTSNSt3__112bad_weak_ptrE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTSNSt3__112ctype_bynameIcEE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTSNSt3__112ctype_bynameIwEE', 'size': 0, 'type': 'OBJECT'}
@@ -2447,6 +2464,9 @@
{'is_defined': True, 'name': '__ZTVNSt3__110moneypunctIwLb1EEE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTVNSt3__110ostrstreamE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTVNSt3__111regex_errorE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTVNSt3__112__stacktrace15llvm_symbolizerE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTVNSt3__112__stacktrace4atosE', 'size': 0, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '__ZTVNSt3__112__stacktrace9addr2lineE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTVNSt3__112bad_weak_ptrE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTVNSt3__112ctype_bynameIcEE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZTVNSt3__112ctype_bynameIwEE', 'size': 0, 'type': 'OBJECT'}
diff --git a/libcxx/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 8c55c4385f6f6..d954b1926115e 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'}
@@ -225,6 +226,11 @@
{'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__116stacktrace_entry11descriptionEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__116stacktrace_entry11source_fileEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__116stacktrace_entry11source_lineEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__116stacktrace_entry13native_handleEv', '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 +628,11 @@
{'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__rs_defaultD2Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__rs_defaultclEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclERKNS_16stacktrace_entryE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace7builder16build_stacktraceEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112bad_weak_ptrD2Ev', 'type': 'FUNC'}
@@ -773,6 +784,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 +965,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 +1167,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'}
@@ -1584,6 +1594,7 @@
{'is_defined': True, 'name': '_ZNSt3__19strstreamD0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19strstreamD1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19strstreamD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__19to_stringERKNS_16stacktrace_entryE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19to_stringEd', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19to_stringEe', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19to_stringEf', 'type': 'FUNC'}
@@ -1593,6 +1604,7 @@
{'is_defined': True, 'name': '_ZNSt3__19to_stringEm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19to_stringEx', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__19to_stringEy', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__1lsERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__1plIcNS_11char_traitsIcEENS_9allocatorIcEEEENS_12basic_stringIT_T0_T1_EEPKS6_RKS9_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZSt17__throw_bad_allocv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZSt17current_exceptionv', 'type': 'FUNC'}
@@ -1602,6 +1614,7 @@
{'is_defined': True, 'name': '_ZSt7nothrow', 'size': 1, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTCNSt3__110istrstreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTCNSt3__110ostrstreamE0_NS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTCNSt3__112__stacktrace10fd_istreamE0_NS_13basic_istreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTCNSt3__114basic_iostreamIcNS_11char_traitsIcEEEE16_NS_13basic_ostreamIcS2_EE', 'size': 80, 'type': 'OBJECT'}
@@ -1616,6 +1629,9 @@
{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt12experimental15fundamentals_v112bad_any_castE', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt12experimental19bad_optional_accessE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace3elf6SymbolEEEE', 'size': 16, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 16, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace4toolEEEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__time_getE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__time_putE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110ctype_baseE', 'size': 16, 'type': 'OBJECT'}
@@ -1631,6 +1647,12 @@
{'is_defined': True, 'name': '_ZTINSt3__111__money_putIcEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__111__money_putIwEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__111regex_errorE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace10fd_istreamE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace15llvm_symbolizerE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace4atosE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace4toolE', 'size': 16, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace6failedE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__112__stacktrace9addr2lineE', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__112bad_weak_ptrE', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__112codecvt_baseE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__112ctype_bynameIcEE', 'size': 24, 'type': 'OBJECT'}
@@ -1749,6 +1771,9 @@
{'is_defined': True, 'name': '_ZTISt19bad_optional_access', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt12experimental15fundamentals_v112bad_any_castE', 'size': 50, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt12experimental19bad_optional_accessE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf6SymbolEEEE', 'size': 64, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 65, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace4toolEEEE', 'size': 58, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__time_getE', 'size': 21, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__time_putE', 'size': 21, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110ctype_baseE', 'size': 21, 'type': 'OBJECT'}
@@ -1764,6 +1789,12 @@
{'is_defined': True, 'name': '_ZTSNSt3__111__money_putIcEE', 'size': 25, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__111__money_putIwEE', 'size': 25, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__111regex_errorE', 'size': 22, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace10fd_istreamE', 'size': 35, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace15llvm_symbolizerE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace4atosE', 'size': 28, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace4toolE', 'size': 28, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace6failedE', 'size': 30, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__112__stacktrace9addr2lineE', 'size': 33, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__112bad_weak_ptrE', 'size': 23, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__112codecvt_baseE', 'size': 23, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__112ctype_bynameIcEE', 'size': 26, 'type': 'OBJECT'}
@@ -1882,6 +1913,7 @@
{'is_defined': True, 'name': '_ZTSSt19bad_optional_access', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__110istrstreamE', 'size': 32, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__110ostrstreamE', 'size': 32, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTTNSt3__112__stacktrace10fd_istreamE', 'size': 32, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__113basic_istreamIcNS_11char_traitsIcEEEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__113basic_istreamIwNS_11char_traitsIwEEEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__113basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 16, 'type': 'OBJECT'}
@@ -1895,6 +1927,9 @@
{'is_defined': True, 'name': '_ZTTNSt3__19strstreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt12experimental15fundamentals_v112bad_any_castE', 'size': 40, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt12experimental19bad_optional_accessE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf6SymbolEEEE', 'size': 88, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 88, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFbRKNS_12__stacktrace4toolEEEE', 'size': 88, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110istrstreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIcLb0EEE', 'size': 112, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIcLb1EEE', 'size': 112, 'type': 'OBJECT'}
@@ -1902,6 +1937,12 @@
{'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIwLb1EEE', 'size': 112, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110ostrstreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__111regex_errorE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace10fd_istreamE', 'size': 80, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace15llvm_symbolizerE', 'size': 48, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace4atosE', 'size': 48, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace4toolE', 'size': 48, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace6failedE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace9addr2lineE', 'size': 48, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112bad_weak_ptrE', 'size': 40, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112ctype_bynameIcEE', 'size': 104, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112ctype_bynameIwEE', 'size': 136, 'type': 'OBJECT'}
@@ -1964,6 +2005,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/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..e7b31fd29b3a6 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;
@@ -31,5 +32,6 @@ export namespace std {
// [stacktrace.basic.hash], hash support
using std::hash;
-#endif
+
+#endif // _LIBCPP_STD_VER >= 23
} // namespace std
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 97fe57a5f24f8..118771d4b5a5c 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,6 +40,14 @@ set(LIBCXX_SOURCES
ryu/d2fixed.cpp
ryu/d2s.cpp
ryu/f2s.cpp
+ stacktrace/builder.cpp
+ stacktrace/linux/impl.cpp
+ stacktrace/macos/impl.cpp
+ stacktrace/to_string.cpp
+ stacktrace/tools/tools.cpp
+ stacktrace/unwind/impl.cpp
+ stacktrace/windows/dll.cpp
+ stacktrace/windows/impl.cpp
stdexcept.cpp
string.cpp
support/runtime/exception_fallback.ipp
@@ -334,6 +342,7 @@ endif()
add_library(cxx_experimental STATIC ${LIBCXX_EXPERIMENTAL_SOURCES})
target_link_libraries(cxx_experimental PUBLIC cxx-headers)
+
if (LIBCXX_ENABLE_SHARED)
target_link_libraries(cxx_experimental PRIVATE cxx_shared)
else()
diff --git a/libcxx/src/stacktrace/README.md b/libcxx/src/stacktrace/README.md
new file mode 100644
index 0000000000000..9f25eb4ad6e22
--- /dev/null
+++ b/libcxx/src/stacktrace/README.md
@@ -0,0 +1,6 @@
+# C++23 `<stacktrace>`
+
+
+# References
+
+1. https://eel.is/c++draft/stacktrace
diff --git a/libcxx/src/stacktrace/builder.cpp b/libcxx/src/stacktrace/builder.cpp
new file mode 100644
index 0000000000000..000135b4ccc38
--- /dev/null
+++ b/libcxx/src/stacktrace/builder.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <__stacktrace/base.h>
+
+#include "stacktrace/linux/impl.h"
+#include "stacktrace/macos/impl.h"
+#include "stacktrace/tools/tools.h"
+#include "stacktrace/unwind/impl.h"
+#include "stacktrace/windows/impl.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+builder::build_stacktrace(size_t skip, size_t max_depth) {
+#if defined(_LIBCPP_WIN32API)
+ // Windows implementation
+ win_impl dbghelp{*this};
+ dbghelp.collect(skip + 1, max_depth);
+ if (!__entries_.size()) {
+ return;
+ }
+ dbghelp.ident_modules();
+ dbghelp.resolve_lines();
+ dbghelp.symbolize();
+
+#else
+ // Non-Windows; assume Unix-like.
+
+ // For spawning `addr2line` or similar external process
+ spawner pspawn{*this};
+
+ // `Unwind.h` or `libunwind.h` often available on Linux/OSX etc.
+ unwind unwind{*this};
+ unwind.collect(skip + 1, max_depth);
+ if (!__entries_.size()) {
+ return;
+ }
+
+# if defined(__APPLE__)
+ // Specific to MacOS and other Apple SDKs
+ macos macos{*this};
+ macos.ident_modules();
+ pspawn.resolve_lines();
+ macos.symbolize();
+
+# else
+ // Linux and other other platforms
+ linux linux{*this};
+ linux.ident_modules();
+ pspawn.resolve_lines();
+ linux.symbolize();
+
+# endif
+#endif
+}
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/config.h b/libcxx/src/stacktrace/config.h
new file mode 100644
index 0000000000000..c4c742d868b69
--- /dev/null
+++ b/libcxx/src/stacktrace/config.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_CONFIG_H
+#define _LIBCPP_STACKTRACE_CONFIG_H
+
+#include <__config>
+#include <__config_site>
+
+// Check for unwind.h -- could exist on any OS (in theory), but it (or `libunwind`) is likely on Linux systems, and also
+// comes with XCode tools on MacOS.
+#if __has_include(<unwind.h>)
+# define _LIBCPP_STACKTRACE_UNWIND_IMPL
+#endif
+
+// Whether we can invoke external processes via `posix_spawn`
+#if __has_include(<spawn.h>) && _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
+# define _LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS
+#endif
+
+#endif // _LIBCPP_STACKTRACE_CONFIG_H
diff --git a/libcxx/src/stacktrace/linux/impl.cpp b/libcxx/src/stacktrace/linux/impl.cpp
new file mode 100644
index 0000000000000..050eb1f0088e7
--- /dev/null
+++ b/libcxx/src/stacktrace/linux/impl.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__linux__)
+
+# include <cassert>
+# include <dlfcn.h>
+# include <link.h>
+# include <stacktrace>
+# include <unistd.h>
+
+# include "stacktrace/config.h"
+# include "stacktrace/linux/impl.h"
+# include "stacktrace/utils/fd.h"
+# include "stacktrace/utils/image.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+void linux::ident_modules() {
+ auto& images = images::get();
+
+ // Aside from the left/right sentinels in the array (hence the 2),
+ // are there any other real images?
+ if (images.count_ <= 2) {
+ return;
+ }
+
+ auto mainProg = images.mainProg();
+ if (mainProg) {
+ builder_.__main_prog_path_ = mainProg->name_;
+ }
+
+ unsigned index = 1; // Starts at one, and is moved around in this loop
+ for (auto& entry : builder_.__entries_) {
+ while (images[index].loaded_at_ > entry.__addr_actual_) {
+ --index;
+ }
+ while (images[index + 1].loaded_at_ <= entry.__addr_actual_) {
+ ++index;
+ }
+ entry.__addr_unslid_ = entry.__addr_actual_ - images[index].slide_;
+ entry.__file_ = builder_.__alloc_.make_str(images[index].name_);
+ }
+}
+
+/**
+When trying to collect a stacktrace under Linux, there are narrow (but still quite common) cases where we will fail
+to resolve symbols. Linux's `dl` doesn't want to read symbols from the non-exported symbol table at runtime,
+and older versions of `addr2line` or `llvm-symbolizer` will also not resolve these.
+
+This implementation the minimum necessary to resolve symbols. It can identify this as an ELF (32 or 64 bits), locate
+the symbol and symbol-string table, and fill in any remaining missing symbols.
+*/
+void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
+ // We can statically initialize these, because main_elf_name should be the same every time.
+ static fd_mmap _mm(main_elf_name);
+ if (_mm) {
+ static elf::ELF _this_elf(_mm.addr_);
+ if (_this_elf) {
+ for (auto& entry : builder_.__entries_) {
+ if (entry.__desc_->empty() && entry.__file_ == main_elf_name) {
+ auto name = _this_elf.getSym(entry.__addr_unslid_).name();
+ entry.__desc_ = builder_.__alloc_.make_str(name);
+ }
+ }
+ }
+ }
+}
+
+bool symbolize_entry(alloc& alloc, entry_base& entry) {
+ bool ret = false;
+ Dl_info info;
+ if (dladdr((void*)entry.__addr_actual_, &info)) {
+ ret = true; // at least partially successful
+ if (info.dli_fname && entry.__file_->empty()) {
+ // provide at least the binary filename in case we cannot lookup source location
+ entry.__file_ = alloc.make_str(info.dli_fname);
+ }
+ if (info.dli_sname && entry.__desc_->empty()) {
+ // provide at least the mangled name; try to unmangle in a later step
+ entry.__desc_ = alloc.make_str(info.dli_sname);
+ }
+ }
+ return ret;
+}
+
+// NOTE: We can use `dlfcn` to resolve addresses to symbols, which works great --
+// except for symbols in the main program. If addr2line-style tools are enabled, that step
+// might also be able to get symbols directly from the binary's debug info.
+void linux::symbolize() {
+ for (auto& entry : builder_.__entries_) {
+ symbolize_entry(builder_.__alloc_, entry);
+ }
+ // Symbols might be missing, because both (1) Linux's `dladdr` won't try to resolve non-exported symbols,
+ // which can be the case for the main program executable; and (2) debug info was not preserved.
+ // As a last resort, this function (see `linux-elf.cpp`) can still access symbol table directly.
+ image* mainELF = images::get().mainProg();
+ if (mainELF && !mainELF->name_.empty()) {
+ resolve_main_elf_syms(mainELF->name_);
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // __linux__
diff --git a/libcxx/src/stacktrace/linux/impl.h b/libcxx/src/stacktrace/linux/impl.h
new file mode 100644
index 0000000000000..cd028d070835f
--- /dev/null
+++ b/libcxx/src/stacktrace/linux/impl.h
@@ -0,0 +1,401 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_LINUX_IMPL
+#define _LIBCPP_STACKTRACE_LINUX_IMPL
+
+#include <__stacktrace/base.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct linux {
+ builder& builder_;
+
+#if defined(__linux__)
+ // defined in linux.cpp
+ void ident_modules();
+ void symbolize();
+
+private:
+ void resolve_main_elf_syms(std::string_view elf_name);
+#else
+ // inline-able dummy definitions
+ void ident_modules() {}
+ void symbolize() {}
+#endif
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#include "stacktrace/config.h"
+
+#if defined(__linux__)
+
+# include <algorithm>
+# include <array>
+# include <cassert>
+# include <cstddef>
+# include <cstdlib>
+# include <functional>
+# include <link.h>
+# include <string_view>
+# include <unistd.h>
+
+# include "stacktrace/utils/image.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct images {
+ // How many images this contains, including the left/right sentinels.
+ unsigned count_{0};
+ std::array<image, image::kMaxImages + 2> images_{};
+
+ int add(dl_phdr_info& info) {
+ assert(count_ < image::kMaxImages);
+ auto isFirst = (count_ == 0);
+ auto& image = images_.at(count_++);
+ image.loaded_at_ = info.dlpi_addr;
+ image.slide_ = info.dlpi_addr;
+ image.name_ = info.dlpi_name;
+ image.is_main_prog_ = isFirst; // first one is the main ELF
+ if (image.name_.empty() && isFirst) {
+ static char buffer[PATH_MAX + 1];
+ uint32_t length = sizeof(buffer);
+ if (readlink("/proc/self/exe", buffer, length) > 0) {
+ image.name_ = buffer;
+ }
+ }
+ return count_ == image::kMaxImages; // return nonzero if we're at the limit
+ }
+
+ static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
+
+ images() {
+ dl_iterate_phdr(images::callback, this);
+ images_[count_++] = {0uz, 0}; // sentinel at low end
+ images_[count_++] = {~0uz, 0}; // sentinel at high end
+ std::sort(images_.begin(), images_.begin() + count_ - 1);
+ }
+
+ image& operator[](size_t index) {
+ assert(index < count_);
+ return images_.at(index);
+ }
+
+ image* mainProg() {
+ for (auto& image : images_) {
+ if (image.is_main_prog_) {
+ return ℑ
+ }
+ }
+ return nullptr;
+ }
+
+ static images& get() {
+ static images images;
+ return images;
+ }
+};
+
+// Includes ELF constants and structs copied from <elf.h>, with a few changes.
+
+namespace elf {
+
+struct Header32 final {
+ uint8_t ident[16]; /* Magic number and other info */
+ uint16_t type; /* Object file type */
+ uint16_t machine; /* Architecture */
+ uint32_t version; /* Object file version */
+ uint32_t entry; /* Entry point virtual address */
+ uint32_t phoff; /* Program header table file offset */
+ uint32_t shoff; /* Section header table file offset */
+ uint32_t flags; /* Processor-specific flags */
+ uint16_t ehsize; /* ELF header size in bytes */
+ uint16_t phentsize; /* Program header table entry size */
+ uint16_t phnum; /* Program header table entry count */
+ uint16_t shentsize; /* Section header table entry size */
+ uint16_t shnum; /* Section header table entry count */
+ uint16_t shstrndx; /* Section header string table index */
+};
+
+struct Section32 final {
+ uint32_t name; /* Section name (string tbl index) */
+ uint32_t type; /* Section type */
+ uint32_t flags; /* Section flags */
+ uint32_t addr; /* Section virtual addr at execution */
+ uint32_t offset; /* Section file offset */
+ uint32_t size; /* Section size in bytes */
+ uint32_t link; /* Link to another section */
+ uint32_t info; /* Additional section information */
+ uint32_t addralign; /* Section alignment */
+ uint32_t entsize; /* Entry size if section holds table */
+};
+
+struct Symbol32 final {
+ uint32_t name; /* Symbol name (string tbl index) */
+ uint32_t value; /* Symbol value */
+ uint32_t size; /* Symbol size */
+ uint8_t info; /* Symbol type and binding */
+ uint8_t other; /* Symbol visibility */
+ uint16_t shndx; /* Section index */
+};
+
+struct Header64 final {
+ uint8_t ident[16]; /* Magic number and other info */
+ uint16_t type; /* Object file type */
+ uint16_t machine; /* Architecture */
+ uint32_t version; /* Object file version */
+ uint64_t entry; /* Entry point virtual address */
+ uint64_t phoff; /* Program header table file offset */
+ uint64_t shoff; /* Section header table file offset */
+ uint32_t flags; /* Processor-specific flags */
+ uint16_t ehsize; /* ELF header size in bytes */
+ uint16_t phentsize; /* Program header table entry size */
+ uint16_t phnum; /* Program header table entry count */
+ uint16_t shentsize; /* Section header table entry size */
+ uint16_t shnum; /* Section header table entry count */
+ uint16_t shstrndx; /* Section header string table index */
+};
+
+struct Section64 final {
+ uint32_t name; /* Section name (string tbl index) */
+ uint32_t type; /* Section type */
+ uint64_t flags; /* Section flags */
+ uint64_t addr; /* Section virtual addr at execution */
+ uint64_t offset; /* Section file offset */
+ uint64_t size; /* Section size in bytes */
+ uint32_t link; /* Link to another section */
+ uint32_t info; /* Additional section information */
+ uint64_t addralign; /* Section alignment */
+ uint64_t entsize; /* Entry size if section holds table */
+};
+
+struct Symbol64 final {
+ uint32_t name; /* Symbol name (string tbl index) */
+ uint8_t info; /* Symbol type and binding */
+ uint8_t other; /* Symbol visibility */
+ uint16_t shndx; /* Section index */
+ uint64_t value; /* Symbol value */
+ uint64_t size; /* Symbol size */
+};
+
+/** Represents an ELF header. Supports the minimum needed to navigate an ELF file's sections and get at the symbol and
+ * string tables. */
+struct Header final {
+ std::byte const* ptr_{};
+ uintptr_t shoff_{};
+ size_t shnum_{};
+ size_t shstrndx_{};
+
+ Header() = default;
+ Header(Header const&) = default;
+ Header& operator=(Header const& rhs) { return *new (this) Header(rhs); }
+
+ operator bool() { return ptr_; }
+
+ template <class H>
+ explicit Header(H* h)
+ : ptr_((std::byte const*)h),
+ shoff_(uintptr_t(h->shoff)),
+ shnum_(size_t(h->shnum)),
+ shstrndx_(size_t(h->shstrndx)) {}
+};
+
+struct ELF;
+struct StringTable;
+
+struct Section final {
+ constexpr static uint32_t kSymTab = 2; // symbol table
+ constexpr static uint32_t kStrTab = 3; // name table for symbols or sections
+
+ ELF* elf_{};
+ std::byte const* ptr_{};
+ uintptr_t nameIndex_{};
+ uint32_t type_{};
+ uintptr_t offset_{};
+ size_t size_{};
+
+ Section() = default;
+
+ template <class S>
+ Section(ELF* elf, S* sec)
+ : elf_(elf),
+ ptr_((std::byte const*)sec),
+ nameIndex_(sec->name),
+ type_(sec->type),
+ offset_(sec->offset),
+ size_(sec->size) {}
+
+ operator bool() const { return ptr_; }
+
+ template <class T = std::byte>
+ T const* data() const {
+ return (T const*)(elfBase() + offset_);
+ }
+
+ std::byte const* elfBase() const;
+ std::string_view name() const;
+};
+
+struct Symbol final {
+ constexpr static uint8_t kFunc = 0x02; // STT_FUNC (code object)
+
+ ELF* elf_{};
+ std::byte const* ptr_{};
+ uintptr_t nameIndex_{};
+ uint32_t type_{};
+ uintptr_t value_{};
+
+ Symbol() = default;
+ Symbol(Symbol const&) = default;
+ Symbol& operator=(Symbol const& rhs) { return *new (this) Symbol(rhs); }
+
+ operator bool() { return ptr_; }
+
+ bool isCode() const { return type_ == kFunc; }
+
+ template <class S>
+ Symbol(ELF* elf, S* sym)
+ : elf_(elf), ptr_((std::byte const*)sym), nameIndex_(sym->name), type_(0x0f & sym->info), value_(sym->value) {}
+
+ std::byte const* elfBase() const;
+ std::string_view name() const;
+};
+
+/** Represents one of the ELF's `strtab`s. This is a block of string data, with strings appended one after another, and
+ * NUL-terminated. Strings are indexed according to their starting offset. At offset 0 is typically an empty string.
+ */
+struct StringTable {
+ std::string_view data_{};
+
+ StringTable() = default;
+
+ /* implicit */ StringTable(Section const& sec) : data_(sec.data<char>(), sec.size_) {}
+
+ operator bool() { return data_.size(); }
+
+ std::string_view at(size_t index) {
+ auto* ret = data_.data() + index;
+ return {ret, strlen(ret)};
+ }
+};
+
+/** Encapsulates an ELF image specified by byte-address (e.g. from an mmapped file or a program image or shared object
+ * in memory). If given a supported ELF image, this will test true with `operator bool` to indicate it is supported and
+ * was able to parse some basic information from the header. */
+struct ELF {
+ Header header_{};
+ Section (*makeSection_)(ELF*, std::byte const*){};
+ Symbol (*makeSymbol_)(ELF*, std::byte const*){};
+ size_t secSize_{};
+ size_t symSize_{};
+ StringTable nametab_{};
+ Section symtab_{};
+ StringTable strtab_{};
+ size_t symCount_{};
+
+ static Section makeSection32(ELF* elf, std::byte const* ptr) { return Section(elf, (Section32 const*)ptr); }
+ static Section makeSection64(ELF* elf, std::byte const* ptr) { return Section(elf, (Section64 const*)ptr); }
+ static Symbol makeSymbol32(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol32 const*)ptr); }
+ static Symbol makeSymbol64(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol64 const*)ptr); }
+
+ operator bool() { return header_; }
+
+ explicit ELF(std::byte const* image) {
+ auto* p = (uint8_t const*)image;
+ // Bytes 0..3: magic bytes: 0x7F, 'E', 'L', 'F'
+ if (*p++ == 0x7f && *p++ == 0x45 && *p++ == 0x4c && *p++ == 0x46) {
+ auto klass = *p++; // Byte 4 (EI_CLASS): ELF class, 32- or 64-bit (0x01 or 0x02)
+ auto dataFormat = *p++; // Byte 5 (EI_DATA): (0x01) little- or (0x02) big-endian
+ auto fileVersion = *p++; // Byte 6 (EI_VERSION): ELF version: expect 1 (latest ELF version)
+ constexpr static uint16_t kEndianTestWord{0x0201};
+ auto hostEndianness = *(uint8_t const*)&kEndianTestWord;
+ if (dataFormat == hostEndianness && fileVersion == 1) {
+ if (klass == 0x01) {
+ header_ = Header((Header32 const*)image);
+ makeSection_ = makeSection32;
+ makeSymbol_ = makeSymbol32;
+ secSize_ = sizeof(Section32);
+ symSize_ = sizeof(Symbol32);
+ } else if (klass == 0x02) {
+ header_ = Header((Header64 const*)image);
+ makeSection_ = makeSection64;
+ makeSymbol_ = makeSymbol64;
+ secSize_ = sizeof(Section64);
+ symSize_ = sizeof(Symbol64);
+ }
+ }
+ }
+ if (*this) {
+ nametab_ = section(header_.shstrndx_);
+ eachSection([&](auto& sec) mutable -> bool {
+ if (sec.type_ == Section::kSymTab && sec.name() == ".symtab") {
+ symtab_ = sec;
+ } else if (sec.type_ == Section::kStrTab && sec.name() == ".strtab") {
+ strtab_ = sec;
+ }
+ return !symtab_ || !strtab_;
+ });
+ }
+ if (symtab_) {
+ symCount_ = symtab_.size_ / symSize_;
+ }
+ }
+
+ Section section(size_t index) {
+ auto* addr = header_.ptr_ + header_.shoff_ + (index * secSize_);
+ return makeSection_(this, addr);
+ }
+
+ Symbol symbol(size_t index) {
+ auto* addr = symtab_.data() + (index * symSize_);
+ return makeSymbol_(this, addr);
+ }
+
+ template <class T>
+ using CB = std::function<bool(T const&)>;
+
+ void eachSection(CB<Section> cb) {
+ for (size_t i = 0; i < header_.shnum_ && cb(section(i)); i++)
+ ;
+ }
+
+ void eachSymbol(CB<Symbol> cb) {
+ for (size_t i = 0; i < symCount_ && cb(symbol(i)); i++)
+ ;
+ }
+
+ Symbol getSym(uintptr_t addr) {
+ Symbol ret{};
+ eachSymbol([&](auto& sym) -> bool {
+ if (sym.value_ <= addr && sym.value_ > ret.value_) {
+ ret = sym;
+ }
+ return true;
+ });
+ return ret;
+ }
+};
+
+inline std::byte const* Section::elfBase() const { return elf_->header_.ptr_; }
+inline std::byte const* Symbol::elfBase() const { return elf_->header_.ptr_; }
+
+inline std::string_view Section::name() const { return elf_->nametab_.at(nameIndex_); }
+inline std::string_view Symbol::name() const { return elf_->strtab_.at(nameIndex_); }
+
+} // namespace elf
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // __linux__
+
+#endif // _LIBCPP_STACKTRACE_LINUX_IMPL
diff --git a/libcxx/src/stacktrace/macos/impl.cpp b/libcxx/src/stacktrace/macos/impl.cpp
new file mode 100644
index 0000000000000..575a2927a78ca
--- /dev/null
+++ b/libcxx/src/stacktrace/macos/impl.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache 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(__APPLE__)
+
+# include <algorithm>
+# include <array>
+# include <dlfcn.h>
+# include <mach-o/dyld.h>
+# include <mach-o/loader.h>
+
+# include <__stacktrace/base.h>
+
+# include "stacktrace/macos/impl.h"
+# include "stacktrace/utils/image.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+void ident_module(alloc& alloc, entry_base& entry, unsigned& index, image* images) {
+ if (entry.__addr_actual_) {
+ while (images[index].loaded_at_ > entry.__addr_actual_) {
+ --index;
+ }
+ while (images[index + 1].loaded_at_ <= entry.__addr_actual_) {
+ ++index;
+ }
+ auto& image = images[index];
+ if (image) {
+ entry.__addr_unslid_ = entry.__addr_actual_ - images[index].slide_;
+ entry.__file_ = alloc.make_str(images[index].name_);
+ }
+ }
+}
+
+bool enum_modules(unsigned& count, auto& images) {
+ count = std::min(image::kMaxImages, size_t(_dyld_image_count()));
+ for (size_t i = 0; i < count; i++) {
+ auto& image = images[i];
+ image.slide_ = _dyld_get_image_vmaddr_slide(i);
+ image.loaded_at_ = uintptr_t(_dyld_get_image_header(i));
+ image.name_ = _dyld_get_image_name(i);
+ }
+ images[count++] = {0uz, 0}; // sentinel at low end
+ images[count++] = {~0uz, 0}; // sentinel at high end
+ std::sort(images.begin(), images.begin() + count - 1);
+ return true;
+}
+
+void macos::ident_modules() {
+ static unsigned imageCount;
+ static std::array<image, image::kMaxImages + 2> images;
+ static bool atomicInitialized = enum_modules(imageCount, images);
+ (void)atomicInitialized;
+
+ // Aside from the left/right sentinels in the array (hence the 2),
+ // are there any other real images?
+ if (imageCount <= 2) {
+ return;
+ }
+
+ // First image (the main program) is at index 1
+ builder_.__main_prog_path_ = builder_.__alloc_.make_str(images.at(1).name_);
+
+ unsigned index = 1; // Starts at one, and is moved by 'ident_module'
+ for (auto& entry : builder_.__entries_) {
+ ident_module(builder_.__alloc_, (entry_base&)entry, index, images.data());
+ }
+}
+
+void symbolize_entry(alloc& alloc, entry_base& entry) {
+ Dl_info info;
+ if (dladdr((void*)entry.__addr_actual_, &info)) {
+ if (info.dli_fname && entry.__file_->empty()) {
+ // provide at least the binary filename in case we cannot lookup source location
+ entry.__file_ = alloc.make_str(info.dli_fname);
+ }
+ if (info.dli_sname && entry.__desc_->empty()) {
+ // provide at least the mangled name; try to unmangle in a later step
+ entry.__desc_ = alloc.make_str(info.dli_sname);
+ }
+ }
+}
+
+void macos::symbolize() {
+ for (auto& entry : builder_.__entries_) {
+ symbolize_entry(builder_.__alloc_, (entry_base&)entry);
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // __APPLE__
diff --git a/libcxx/src/stacktrace/macos/impl.h b/libcxx/src/stacktrace/macos/impl.h
new file mode 100644
index 0000000000000..42b8b89d7bbb9
--- /dev/null
+++ b/libcxx/src/stacktrace/macos/impl.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_MACOS_IMPL
+#define _LIBCPP_STACKTRACE_MACOS_IMPL
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+
+#include "stacktrace/config.h"
+#include <__stacktrace/base.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct macos {
+ builder& builder_;
+
+#if defined(__APPLE__)
+ // defined in macos.cpp
+ void ident_modules();
+ void symbolize();
+#else
+ // inline-able dummy definitions
+ void ident_modules() {}
+ void symbolize() {}
+#endif // __APPLE__
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_MACOS_IMPL
diff --git a/libcxx/src/stacktrace/to_string.cpp b/libcxx/src/stacktrace/to_string.cpp
new file mode 100644
index 0000000000000..6d02b1c973344
--- /dev/null
+++ b/libcxx/src/stacktrace/to_string.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <__stacktrace/basic.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/to_string.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// `to_string`-related non-member functions
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
+ return __stacktrace::__to_string()(__entry);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
+ __stacktrace::__to_string()(__os, __entry);
+ return __os;
+}
+
+namespace __stacktrace {
+
+/*
+ * `to_string` Helpers
+ */
+
+_LIBCPP_EXPORTED_FROM_ABI void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
+ // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
+ constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
+
+ __os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << entry.native_handle();
+ if (!entry.description().empty()) {
+ __os << ": " << entry.description();
+ }
+ if (!entry.source_file().empty()) {
+ __os << ": " << entry.source_file();
+ }
+ if (entry.source_line()) {
+ __os << ":" << std::dec << entry.source_line();
+ }
+}
+
+_LIBCPP_EXPORTED_FROM_ABI void
+__to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
+ /*
+ * Print each entry as a line, as per `operator()`, with additional whitespace
+ * at the start of the line, and only a newline added at the end:
+ *
+ * frame 1: 0xbeefbeefbeef: _symbol_name: /path/to/file.cc:123
+ */
+ if (!__count) {
+ __os << "(empty stacktrace)";
+ } else {
+ for (size_t __i = 0; __i < __count; __i++) {
+ if (__i) {
+ // Insert newlines between entries (but not before the first or after the last)
+ __os << std::endl;
+ }
+ __os << " frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": ";
+ (*this)(__os, __entries[__i]);
+ }
+ }
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const& entry) {
+ stringstream __ss;
+ (*this)(__ss, entry);
+ return __ss.str();
+}
+
+_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
+ stringstream __ss;
+ (*this)(__ss, __entries, __count);
+ return __ss.str();
+}
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/tools.cpp b/libcxx/src/stacktrace/tools/tools.cpp
new file mode 100644
index 0000000000000..aa573567377e8
--- /dev/null
+++ b/libcxx/src/stacktrace/tools/tools.cpp
@@ -0,0 +1,382 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "stacktrace/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+
+# include <__config>
+# include <__config_site>
+# include <cassert>
+# include <cerrno>
+# include <csignal>
+# include <cstddef>
+# include <cstdlib>
+# include <spawn.h>
+# include <sys/fcntl.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+
+# include <__stacktrace/base.h>
+# include <__stacktrace/basic.h>
+# include <__stacktrace/entry.h>
+
+# include "stacktrace/tools/tools.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+_LIBCPP_HIDE_FROM_ABI alloc::str hex_string(alloc& alloc, uintptr_t __addr) {
+ char __ret[19]; // "0x" + 16 digits + NUL
+ auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
+ return alloc.make_str(__ret, size_t(__size));
+}
+
+_LIBCPP_HIDE_FROM_ABI alloc::str u64_string(alloc& alloc, uintptr_t __val) {
+ char __ret[21]; // 20 digits max + NUL
+ auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
+ return alloc.make_str(__ret, size_t(__size));
+}
+
+# define STRINGIFY0(x) #x
+# define STRINGIFY(x) STRINGIFY0(x)
+
+void try_tools(alloc& alloc, function<bool(tool const&)> cb) {
+ char const* prog_name;
+
+ if ((prog_name = getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH"))) {
+ if (cb(llvm_symbolizer{alloc, prog_name})) {
+ return;
+ }
+ } else {
+# if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
+ if (cb(llvm_symbolizer{alloc, STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)})) {
+ return;
+ }
+# else
+ if (cb(llvm_symbolizer{alloc})) {
+ return;
+ }
+# endif
+ }
+
+ if ((prog_name = getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH"))) {
+ if (cb(addr2line{alloc, prog_name})) {
+ return;
+ }
+ } else {
+# if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
+ if (cb(addr2line{alloc, STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)})) {
+ return;
+ }
+# else
+ if (cb(addr2line{alloc})) {
+ return;
+ }
+# endif
+ }
+
+ if ((prog_name = getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH"))) {
+ if (cb(atos{alloc, prog_name})) {
+ return;
+ }
+ } else {
+# if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
+ if (cb(atos{alloc, STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)})) {
+ return;
+ }
+# else
+ if (cb(atos{alloc})) {
+ return;
+ }
+# endif
+ }
+}
+
+} // namespace
+
+void spawner::resolve_lines() {
+ try_tools(builder_.__alloc_, [&](tool const& prog) {
+ char buf[512];
+ pspawn_tool proc(prog, builder_, buf, sizeof(buf));
+ try {
+ proc.run();
+ return true;
+ } catch (failed const& failed) {
+ debug() << failed.what();
+ if (failed.errno_) {
+ debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
+ }
+ debug() << '\n';
+ }
+ return false;
+ });
+}
+
+alloc::list<alloc::str> llvm_symbolizer::buildArgs(builder& builder) const {
+ auto ret = alloc_.make_list<alloc::str>();
+ ret.push_back(alloc_.make_str(progName_));
+ ret.push_back(alloc_.make_str("--demangle"));
+ ret.push_back(alloc_.make_str("--no-inlines"));
+ ret.push_back(alloc_.make_str("--verbose"));
+ ret.push_back(alloc_.make_str("--relativenames"));
+ ret.push_back(alloc_.make_str("--functions=short"));
+ for (auto& st_entry : builder.__entries_) {
+ auto& entry = (entry_base&)st_entry;
+ auto addr_string = hex_string(alloc_, entry.__addr_unslid_);
+ if (entry.__file_) {
+ auto arg = alloc_.make_str();
+ arg.reserve(entry.__file_->size() + 40);
+ arg += "FILE:";
+ arg += *entry.__file_;
+ arg += " ";
+ arg += addr_string;
+ ret.push_back(arg);
+ } else {
+ ret.push_back(addr_string);
+ }
+ }
+ return ret;
+}
+
+void llvm_symbolizer::parseOutput(builder& builder, __stacktrace::entry_base& entry, std::istream& output) const {
+ // clang-format off
+/*
+With "--verbose", parsing is a little easier, or at least, more reliable;
+probably the best solution (until we have a JSON parser).
+Example output, verbatim, between the '---' lines:
+---
+test1<test_alloc<std::__1::stackbuilder_entry> >
+ Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+ Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+ Function start line: 114
+ Function start address: 0x8dd0
+ Line: 116
+ Column: 14
+
+---
+Note that this includes an extra empty line as a terminator.
+*/
+ // clang-format on
+
+ auto& alloc = builder.__alloc_;
+ auto line = alloc.make_str();
+ line.reserve(512);
+ std::string_view tmp;
+ while (true) {
+ std::getline(output, line);
+ while (!line.empty() && isspace(line.back())) {
+ line.pop_back();
+ }
+ if (line.empty()) {
+ return;
+ }
+ if (!line.starts_with(" ")) {
+ // The symbol has no leading whitespace, while the other
+ // lines with "fields" like line, column, filename, etc.
+ // start with two spaces.
+ if (line != "??") {
+ entry.__desc_ = line;
+ }
+ } else if (line.starts_with(" Filename:")) {
+ tmp = line;
+ tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+ if (tmp != "??") {
+ entry.__file_ = alloc.make_str(tmp);
+ }
+ } else if (line.starts_with(" Line:")) {
+ tmp = line;
+ tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+ if (tmp != "??" && tmp != "0") {
+ uint32_t lineno = 0;
+ auto pos = 0;
+ while (isdigit(tmp[pos])) {
+ lineno = lineno * 10 + (tmp[pos++] - '0');
+ }
+ entry.__line_ = lineno;
+ }
+ }
+ }
+}
+
+alloc::list<alloc::str> addr2line::buildArgs(builder& builder) const {
+ auto& alloc = builder.__alloc_;
+ auto ret = alloc.make_list<alloc::str>();
+ if (builder.__main_prog_path_.empty()) {
+ // Should not have reached here but be graceful anyway
+ ret.push_back(alloc.make_str("/bin/false"));
+ return ret;
+ }
+
+ ret.push_back(alloc.make_str(progName_));
+ ret.push_back(alloc.make_str("--functions"));
+ ret.push_back(alloc.make_str("--demangle"));
+ ret.push_back(alloc.make_str("--basenames"));
+ ret.push_back(alloc.make_str("--pretty-print")); // This "human-readable form" is easier to parse
+ ret.push_back(alloc.make_str("-e"));
+ ret.push_back(builder.__main_prog_path_);
+ for (auto& entry : builder.__entries_) {
+ ret.push_back(hex_string(alloc, ((entry_base&)entry).__addr_unslid_));
+ }
+ return ret;
+}
+
+void addr2line::parseOutput(builder& builder, entry_base& entry, std::istream& output) const {
+ // clang-format off
+/*
+Example:
+--
+llvm-addr2line -e foo --functions --demangle --basenames --pretty-print --no-inlines 0x11a0 0x1120 0x3d58 0x1284
+
+Output: (1 line per input address)
+--
+main at foo.cc:15
+register_tm_clones at crtstuff.c:0
+GCC_except_table2 at foo.cc:0
+test::Foo::Foo(int) at foo.cc:11
+*/
+ // clang-format on
+
+ auto& alloc = builder.__alloc_;
+ auto line = alloc.make_str();
+ line.reserve(512);
+ std::getline(output, line);
+ while (!line.empty() && isspace(line.back())) {
+ line.pop_back();
+ }
+ if (line.empty()) {
+ return;
+ }
+ // Split at the sequence " at ". Barring weird symbols
+ // having " at " in them, this should work.
+ auto sepIndex = line.find(" at ");
+ if (sepIndex == std::string::npos) {
+ return;
+ }
+ if (sepIndex > 0) {
+ entry.__desc_ = alloc.make_str(string_view(line).substr(0, sepIndex));
+ }
+ auto fileBegin = sepIndex + 4;
+ if (fileBegin >= line.size()) {
+ return;
+ }
+ auto fileline = alloc.make_str(string_view(line).substr(fileBegin));
+ auto colon = fileline.find_last_of(":");
+ if (colon > 0 && !fileline.starts_with("?")) {
+ entry.__file_ = alloc.make_str(string_view(fileline).substr(0, colon));
+ }
+
+ if (colon == std::string::npos) {
+ return;
+ }
+ uint32_t lineno = 0;
+ auto pos = colon;
+ while (isdigit(fileline[++pos])) {
+ lineno = lineno * 10 + (fileline[pos] - '0');
+ }
+ entry.__line_ = lineno;
+}
+
+alloc::list<alloc::str> atos::buildArgs(builder& builder) const {
+ auto& alloc = builder.__alloc_;
+ auto ret = alloc.make_list<alloc::str>();
+ ret.push_back(alloc.make_str(progName_));
+ ret.push_back(alloc.make_str("-p"));
+ ret.push_back(u64_string(alloc, getpid()));
+ // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
+ // ret.push_back("--fullPath");
+ for (auto& entry : builder.__entries_) {
+ ret.push_back(hex_string(alloc, ((entry_base&)entry).__addr_actual_));
+ }
+ return ret;
+}
+
+void atos::parseOutput(builder& builder, entry_base& entry, std::istream& output) const {
+ // Simple example:
+ //
+ // main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+ //
+ // Assuming this is always atos's format (except when it returns empty lines)
+ // we can split the string like so:
+ //
+ // main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+ // ^^^^-----^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^-
+ // sym module filename line
+ //
+ // Note that very strange filenames or module names can confuse this.
+ // We'll do the best we can for a decent result, while definitely ensuring safety
+ // (i.e. careful with our bound-checking).
+ //
+ // Another more interesting example (with an added newline for legibility):
+ //
+ // std::__1::basic_ios<char, std::__1::char_traits<char>>::fill[abi:ne190107]() const (in testprog)
+ // (/opt/homebrew/Cellar/llvm/19.1.7_1/bin/../include/c++/v1/ios:0
+ //
+ // If this more or less fits our expected format we'll take these data,
+ // even if the line number is 0.
+
+ auto& alloc = builder.__alloc_;
+ auto line = alloc.make_str();
+ line.reserve(512);
+ std::getline(output, line);
+ while (!line.empty() && isspace(line.back())) {
+ line.pop_back();
+ }
+ if (line.empty()) {
+ return;
+ }
+ auto buf = line.data();
+ auto size = line.size();
+
+ auto* end = buf + size;
+ auto* symEnd = strstr(buf, " (in ");
+ if (!symEnd) {
+ return;
+ }
+ auto* modBegin = symEnd + 5;
+ auto* modEnd = strstr(modBegin, ") (");
+ if (!modEnd) {
+ return;
+ }
+ auto* fileBegin = modEnd + 3; // filename starts just after that
+ if (fileBegin >= end) {
+ return;
+ }
+ auto const* lastColon = fileBegin; // we'll search for last colon after filename
+ char const* nextColon;
+ while ((nextColon = strstr(lastColon + 1, ":"))) { // skip colons in filename (e.g. in "C:\foo.cpp")
+ lastColon = nextColon;
+ }
+
+ std::string_view sym{buf, size_t(symEnd - buf)};
+ // In case a previous step could not obtain the symbol name,
+ // we have the name provided by atos; only use that if we have no symbol
+ // (no need to copy more strings otherwise).
+ if (entry.__desc_->empty() && !sym.empty()) {
+ entry.__desc_ = alloc.make_str(sym);
+ }
+
+ std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
+ if (file != "?" && file != "??" && !file.empty()) {
+ entry.__file_ = alloc.make_str(file);
+ }
+
+ unsigned lineno = 0;
+ for (auto* digit = lastColon + 1; digit < end && isdigit(*digit); ++digit) {
+ lineno = (lineno * 10) + unsigned(*digit - '0');
+ }
+ entry.__line_ = lineno;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/stacktrace/tools/tools.h b/libcxx/src/stacktrace/tools/tools.h
new file mode 100644
index 0000000000000..620a1e2f4f9a9
--- /dev/null
+++ b/libcxx/src/stacktrace/tools/tools.h
@@ -0,0 +1,197 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_TOOLS_H
+#define _LIBCPP_STACKTRACE_TOOLS_H
+
+#include <__config>
+#include <__config_site>
+#include <cassert>
+#include <cerrno>
+#include <csignal>
+#include <cstddef>
+#include <cstdlib>
+#include <spawn.h>
+#include <string>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <__stacktrace/base.h>
+#include <__stacktrace/basic.h>
+#include <__stacktrace/entry.h>
+
+#include "stacktrace/utils/debug.h"
+#include "stacktrace/utils/failed.h"
+#include "stacktrace/utils/fd.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct tool {
+ alloc& alloc_;
+ char const* progName_;
+
+ tool(alloc& alloc, char const* progName) : alloc_(alloc), progName_(progName) {}
+ virtual ~tool() = default;
+
+ /** Construct complete `argv` for the spawned process.
+ Includes the program name at argv[0], followed by flags */
+ virtual alloc::list<alloc::str> buildArgs(builder& trace) const = 0;
+
+ /** Parse line(s) output by the tool, and modify `entry`. */
+ virtual void parseOutput(builder& trace, entry_base& entry, std::istream& output) const = 0;
+};
+
+struct llvm_symbolizer : tool {
+ virtual ~llvm_symbolizer() = default;
+ explicit llvm_symbolizer(alloc& alloc) : llvm_symbolizer(alloc, "llvm_symbolizer") {}
+ llvm_symbolizer(alloc& alloc, char const* progName) : tool{alloc, progName} {}
+ alloc::list<alloc::str> buildArgs(builder& trace) const override;
+ void parseOutput(builder& trace, entry_base& entry, std::istream& output) const override;
+};
+
+struct addr2line : tool {
+ virtual ~addr2line() = default;
+ explicit addr2line(alloc& alloc) : addr2line(alloc, "addr2line") {}
+ addr2line(alloc& alloc, char const* progName) : tool{alloc, progName} {}
+ alloc::list<alloc::str> buildArgs(builder& trace) const override;
+ void parseOutput(builder& trace, entry_base& entry, std::istream& stream) const override;
+};
+
+struct atos : tool {
+ virtual ~atos() = default;
+ explicit atos(alloc& alloc) : atos(alloc, "atos") {}
+ atos(alloc& alloc, char const* progName) : tool{alloc, progName} {}
+ alloc::list<alloc::str> buildArgs(builder& trace) const override;
+ void parseOutput(builder& trace, entry_base& entry, std::istream& output) const override;
+};
+
+struct file_actions {
+ posix_spawn_file_actions_t fa_;
+
+ file_actions() {
+ if (posix_spawn_file_actions_init(&fa_)) {
+ throw failed("posix_spawn_file_actions_init", errno);
+ }
+ }
+
+ ~file_actions() { posix_spawn_file_actions_destroy(&fa_); }
+
+ void addClose(int fd) {
+ if (posix_spawn_file_actions_addclose(&fa_, fd)) {
+ throw failed("posix_spawn_file_actions_addclose", errno);
+ }
+ }
+ void addDup2(int fd, int stdfd) {
+ if (posix_spawn_file_actions_adddup2(&fa_, fd, stdfd)) {
+ throw failed("posix_spawn_file_actions_adddup2", errno);
+ }
+ }
+
+ fd redirectOutFD() {
+ int fds[2];
+ if (::pipe(fds)) {
+ throw failed("pipe", errno);
+ }
+ addClose(fds[0]);
+ addDup2(fds[1], 1);
+ return {fds[0]};
+ }
+
+ void redirectInNull() { addDup2(fd::null_fd(), 0); }
+ void redirectOutNull() { addDup2(fd::null_fd(), 1); }
+ void redirectErrNull() { addDup2(fd::null_fd(), 2); }
+};
+
+struct pspawn {
+ tool const& tool_;
+ pid_t pid_{0};
+ file_actions fa_{};
+
+ // TODO(stacktrace23): ignore SIGCHLD for spawned subprocess
+
+ ~pspawn() {
+ if (pid_) {
+ kill(pid_, SIGTERM);
+ wait();
+ }
+ }
+
+ void spawn(alloc::list<alloc::str> const& argStrings) {
+ alloc::vec<char const*> argv = tool_.alloc_.make_vec<char const*>();
+ argv.reserve(argStrings.size() + 1);
+ for (auto const& str : argStrings) {
+ argv.push_back(str.data());
+ }
+ argv.push_back(nullptr);
+ int err;
+ if ((err = posix_spawnp(&pid_, argv[0], &fa_.fa_, nullptr, const_cast<char**>(argv.data()), nullptr))) {
+ throw failed("posix_spawnp", err);
+ }
+ }
+
+ int wait() {
+ int status;
+ waitpid(pid_, &status, 0);
+ return status;
+ }
+};
+
+struct pspawn_tool : pspawn {
+ builder& builder_;
+ fd fd_;
+ fd_streambuf buf_;
+ fd_istream stream_;
+
+ pspawn_tool(tool const& a2l, builder& trace, char* buf, size_t size)
+ : pspawn{a2l}, builder_(trace), fd_(fa_.redirectOutFD()), buf_(fd_, buf, size), stream_(buf_) {
+ if (!debug::enabled()) {
+ fa_.redirectErrNull();
+ }
+ fa_.redirectInNull();
+ }
+
+ void run() {
+ // Cannot run "addr2line" or similar without addresses, since we
+ // provide them in argv, and if there are none passed in argv, the
+ // tool will try to read from stdin and hang.
+ if (builder_.__entries_.empty()) {
+ return;
+ }
+
+ auto argStrings = tool_.buildArgs(builder_);
+ if (debug::enabled()) {
+ debug() << "Trying to get stacktrace using:";
+ for (auto& str : argStrings) {
+ debug() << " \"" << str << '"';
+ }
+ debug() << '\n';
+ }
+
+ spawn(argStrings);
+
+ auto end = builder_.__entries_.end();
+ auto it = builder_.__entries_.begin();
+ while (it != end) {
+ auto& entry = (entry_base&)(*it++);
+ tool_.parseOutput(builder_, entry, stream_);
+ }
+ }
+};
+
+struct spawner {
+ builder& builder_;
+ void resolve_lines();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_TOOLS_H
diff --git a/libcxx/src/stacktrace/unwind/impl.cpp b/libcxx/src/stacktrace/unwind/impl.cpp
new file mode 100644
index 0000000000000..90603e0422b9b
--- /dev/null
+++ b/libcxx/src/stacktrace/unwind/impl.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "stacktrace/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_UNWIND_IMPL)
+
+# include <unwind.h>
+
+# include "stacktrace/unwind/impl.h"
+# include <__stacktrace/basic.h>
+# include <__stacktrace/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct unwind_backtrace {
+ builder& builder_;
+ size_t skip_;
+ size_t maxDepth_;
+
+ _Unwind_Reason_Code callback(_Unwind_Context* ucx) {
+ if (skip_) {
+ --skip_;
+ return _Unwind_Reason_Code::_URC_NO_REASON;
+ }
+ if (!maxDepth_) {
+ return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+ }
+ --maxDepth_;
+ int ipBefore;
+ auto ip = _Unwind_GetIPInfo(ucx, &ipBefore);
+ if (!ip) {
+ return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+ }
+ auto& entry = builder_.__entries_.emplace_back();
+ auto& eb = (entry_base&)entry;
+ eb.__addr_actual_ = (ipBefore ? ip : ip - 1);
+ eb.__addr_unslid_ = eb.__addr_actual_; // in case we can't un-slide
+ return _Unwind_Reason_Code::_URC_NO_REASON;
+ }
+
+ static _Unwind_Reason_Code callback(_Unwind_Context* cx, void* self) {
+ return ((unwind_backtrace*)self)->callback(cx);
+ }
+};
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void unwind::collect(size_t skip, size_t max_depth) {
+ unwind_backtrace bt{builder_, skip + 1, max_depth}; // skip this call as well
+ _Unwind_Backtrace(unwind_backtrace::callback, &bt);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/stacktrace/unwind/impl.h b/libcxx/src/stacktrace/unwind/impl.h
new file mode 100644
index 0000000000000..4b552349d75bc
--- /dev/null
+++ b/libcxx/src/stacktrace/unwind/impl.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_UNWIND_H
+#define _LIBCPP_STACKTRACE_UNWIND_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+
+#include <__stacktrace/base.h>
+#include <__stacktrace/basic.h>
+#include <__stacktrace/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct unwind {
+ builder& builder_;
+ void collect(size_t skip, size_t max_depth);
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_UNWIND_H
diff --git a/libcxx/src/stacktrace/utils/debug.h b/libcxx/src/stacktrace/utils/debug.h
new file mode 100644
index 0000000000000..cc88224942f6f
--- /dev/null
+++ b/libcxx/src/stacktrace/utils/debug.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_UTILS_DEBUG
+#define _LIBCPP_STACKTRACE_UTILS_DEBUG
+
+#include <__config>
+#include <iostream>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/** Debug-message output stream. If `LIBCXX_STACKTRACE_DEBUG` is defined in the environment
+or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
+otherwise its does nothing by returning a dummy stream. */
+struct _LIBCPP_HIDE_FROM_ABI debug : std::ostream {
+ _LIBCPP_HIDE_FROM_ABI virtual ~debug() = default;
+
+ _LIBCPP_HIDE_FROM_ABI static bool enabled() {
+#if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
+ return true;
+#else
+ static bool ret = [] {
+ auto const* val = getenv("LIBCXX_STACKTRACE_DEBUG");
+ return val && !strncmp(val, "1", 1);
+ }();
+ return ret;
+#endif
+ }
+
+ /** No-op output stream. */
+ struct _LIBCPP_HIDE_FROM_ABI dummy_ostream final : std::ostream {
+ _LIBCPP_HIDE_FROM_ABI virtual ~dummy_ostream() = default;
+ friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
+ };
+
+ friend std::ostream& operator<<(debug& dp, auto const& val) {
+ static dummy_ostream kdummy;
+ if (!enabled()) {
+ return kdummy;
+ }
+ std::cerr << val;
+ return std::cerr;
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_UTILS_DEBUG
diff --git a/libcxx/src/stacktrace/utils/failed.h b/libcxx/src/stacktrace/utils/failed.h
new file mode 100644
index 0000000000000..3f8a24dd88eb6
--- /dev/null
+++ b/libcxx/src/stacktrace/utils/failed.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_UTILS_FAILED
+#define _LIBCPP_STACKTRACE_UTILS_FAILED
+
+#include <__config>
+#include <cerrno>
+#include <stdexcept>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct failed : std::runtime_error {
+ virtual ~failed() = default;
+ int errno_{0};
+ failed() : std::runtime_error({}) {}
+ failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_UTILS_FAILED
diff --git a/libcxx/src/stacktrace/utils/fd.h b/libcxx/src/stacktrace/utils/fd.h
new file mode 100644
index 0000000000000..f4be1876e5d37
--- /dev/null
+++ b/libcxx/src/stacktrace/utils/fd.h
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache 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_UTILS_FD
+#define _LIBCPP_STACKTRACE_UTILS_FD
+
+#include <__config>
+#include <cerrno>
+#include <cstdio>
+#include <iostream>
+#include <string_view>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <utility>
+
+#include "stacktrace/utils/failed.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/** Encapsulates a plain old file descriptor `int`. Avoids copies in order to
+force some component to "own" this, although it's freely convertible back to
+integer form. Default-constructed, closed, and moved-out-of instances will have
+the invalid fd `-1`. */
+class _LIBCPP_HIDE_FROM_ABI fd {
+ int fd_{-1};
+
+public:
+ fd() : fd(-1) {}
+ fd(int fdint) : fd_(fdint) {}
+
+ fd(fd const&) = delete;
+ fd& operator=(fd const&) = delete;
+
+ fd(fd&& rhs) {
+ if (&rhs != this) {
+ std::exchange(fd_, rhs.fd_);
+ }
+ }
+
+ fd& operator=(fd&& rhs) { return *new (this) fd(std::move(rhs)); }
+
+ ~fd() { close(); }
+
+ /** Returns true IFF fd is above zero */
+ bool valid() const { return fd_ > 0; }
+
+ /* implicit */ operator int() const { return fd_; }
+
+ void close() {
+ int fd_old = -1;
+ std::exchange(fd_old, fd_);
+ if (fd_old != -1) {
+ ::close(fd_old);
+ }
+ }
+
+ static fd& null_fd() {
+ static fd ret = {::open("/dev/null", O_RDWR)};
+ return ret;
+ }
+
+ static fd open(std::string_view path) {
+ fd ret = {::open(path.data(), O_RDONLY)};
+ return ret;
+ }
+};
+
+/** Wraps a readable fd using the `streambuf` interface. I/O errors arising
+from reading the provided fd will result in a `Failed` being thrown. */
+struct _LIBCPP_HIDE_FROM_ABI fd_streambuf final : std::streambuf {
+ fd& fd_;
+ char* buf_;
+ size_t size_;
+ _LIBCPP_HIDE_FROM_ABI fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
+ _LIBCPP_HIDE_FROM_ABI virtual ~fd_streambuf() = default;
+
+ _LIBCPP_HIDE_FROM_ABI int underflow() override {
+ int bytesRead = ::read(fd_, buf_, size_);
+ if (bytesRead < 0) {
+ throw ::std::__stacktrace::failed("I/O error reading from child process", errno);
+ }
+ if (bytesRead == 0) {
+ return traits_type::eof();
+ }
+ setg(buf_, buf_, buf_ + bytesRead);
+ return int(*buf_);
+ }
+};
+
+/** Wraps an `FDInStreamBuffer` in an `istream` */
+struct fd_istream final : std::istream {
+ fd_streambuf& buf_;
+ _LIBCPP_HIDE_FROM_ABI virtual ~fd_istream() = default;
+ _LIBCPP_HIDE_FROM_ABI explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
+};
+
+struct fd_mmap final {
+ fd fd_{};
+ size_t size_{0};
+ std::byte const* addr_{nullptr};
+
+ _LIBCPP_HIDE_FROM_ABI explicit fd_mmap(std::string_view path) : fd_mmap(fd::open(path)) {}
+
+ _LIBCPP_HIDE_FROM_ABI explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
+ if (fd_) {
+ if ((size_ = ::lseek(fd, 0, SEEK_END))) {
+ addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
+ }
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI operator bool() const { return addr_; }
+
+ _LIBCPP_HIDE_FROM_ABI ~fd_mmap() {
+ if (addr_) {
+ ::munmap(const_cast<void*>((void const*)addr_), size_);
+ }
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_UTILS_FD
diff --git a/libcxx/src/stacktrace/utils/image.h b/libcxx/src/stacktrace/utils/image.h
new file mode 100644
index 0000000000000..21119d7a66ec9
--- /dev/null
+++ b/libcxx/src/stacktrace/utils/image.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_UTILS_IMAGE
+#define _LIBCPP_STACKTRACE_UTILS_IMAGE
+
+#include <__config>
+#include <__stacktrace/base.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct image {
+ constexpr static size_t kMaxImages = 256;
+
+ uintptr_t loaded_at_{};
+ intptr_t slide_{};
+ std::string_view name_{};
+ bool is_main_prog_{};
+
+ bool operator<(image const& rhs) const { return loaded_at_ < rhs.loaded_at_; }
+ operator bool() const { return !name_.empty(); }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_UTILS_IMAGE
diff --git a/libcxx/src/stacktrace/windows/dll.cpp b/libcxx/src/stacktrace/windows/dll.cpp
new file mode 100644
index 0000000000000..d48bead956041
--- /dev/null
+++ b/libcxx/src/stacktrace/windows/dll.cpp
@@ -0,0 +1,247 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache 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>
+
+#if defined(_LIBCPP_WIN32API)
+
+# include <__stacktrace/base.h>
+
+# include "stacktrace/alloc.h"
+# include "stacktrace/config.h"
+# include "stacktrace/utils.h"
+# include "stacktrace/windows/dll.h"
+# include "stacktrace/windows/impl.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/*
+Global objects, shared among all threads and among all stacktrace operations.
+The `dbghelp` APIs are not safe to call concurrently (according to their docs)
+so we claim a lock in the `WinDebugAPIs` constructor.
+*/
+
+// Statically-initialized
+std::mutex gWindowsAPILock;
+DbgHelpDLL dbg;
+PSAPIDLL ps;
+
+// Initialized once, in first WinDebugAPIs construction;
+// protected by the above mutex.
+HANDLE proc;
+HMODULE exe;
+IMAGE_NT_HEADERS* ntHeaders;
+bool globalInitialized{false};
+
+// Globals used across invocations of the functions below.
+// Also guarded by the above mutex.
+bool symsInitialized{false};
+HMODULE moduleHandles[1024];
+size_t moduleCount; // 0 IFF module enumeration failed
+
+} // namespace
+
+win_impl::WinDebugAPIs(builder& trace) : builder_(trace), guard_(gWindowsAPILock) {
+ if (!globalInitialized) {
+ // Cannot proceed without these DLLs:
+ if (!dbg) {
+ return;
+ }
+ if (!ps) {
+ return;
+ }
+ proc = GetCurrentProcess();
+ if (!(exe = GetModuleHandle(nullptr))) {
+ return;
+ }
+ if (!(ntHeaders = (*dbg.ImageNtHeader)(exe))) {
+ return;
+ }
+
+ globalInitialized = true;
+ }
+
+ // Initialize symbol machinery.
+ // Presumably the symbols in this process's space can change between
+ // stacktraces, so we'll do this each time we take a trace.
+ // The final `true` means we want the runtime to enumerate all this
+ // process's modules' symbol tables.
+ symsInitialized = (*dbg.SymInitialize)(proc, nullptr, true);
+ DWORD symOptions = (*dbg.SymGetOptions)();
+ symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
+ (*dbg.SymSetOptions)(symOptions);
+}
+
+win_impl::~WinDebugAPIs() {
+ if (symsInitialized) {
+ (*dbg.SymCleanup)(proc);
+ symsInitialized = false;
+ }
+}
+
+void win_impl::ident_modules() {
+ if (!globalInitialized) {
+ return;
+ }
+ DWORD needBytes;
+ auto enumMods = (*ps.EnumProcessModules)(proc, moduleHandles, sizeof(moduleHandles), LPDWORD(&needBytes));
+ if (enumMods) {
+ moduleCount = needBytes / sizeof(HMODULE);
+ } else {
+ moduleCount = 0;
+ Debug() << "EnumProcessModules failed: " << GetLastError() << '\n';
+ }
+}
+
+void win_impl::symbolize() {
+ // Very long symbols longer than this amount will be truncated.
+ static constexpr size_t kMaxSymName = 256;
+ if (!globalInitialized) {
+ return;
+ }
+
+ for (auto& entry : builder_.__entries_) {
+ char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName];
+ auto* sym = (IMAGEHLP_SYMBOL64*)space;
+ sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ sym->MaxNameLength = kMaxSymName;
+ uint64_t disp{0};
+ if ((*dbg.SymGetSymFromAddr64)(proc, entry.__addr_actual_, &disp, sym)) {
+ // Copy chars into the destination string which uses the caller-provided allocator.
+ ((entry_base&)entry).__desc_ = {sym->Name};
+ } else {
+ Debug() << "SymGetSymFromAddr64 failed: " << GetLastError() << '\n';
+ }
+ }
+}
+
+void win_impl::resolve_lines() {
+ if (!globalInitialized) {
+ return;
+ }
+
+ for (auto& entry : builder_.__entries_) {
+ DWORD disp{0};
+ IMAGEHLP_LINE64 line;
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ if ((*dbg.SymGetLineFromAddr64)(proc, entry.__addr_actual_, &disp, &line)) {
+ // Copy chars into the destination string which uses the caller-provided allocator.
+ entry.__file_ = line.FileName;
+ entry.__line_ = line.LineNumber;
+ } else {
+ Debug() << "SymGetLineFromAddr64 failed: " << GetLastError() << '\n';
+ }
+ }
+}
+
+/*
+Inlining is disabled from here on;
+this is to ensure `collect` below doesn't get merged into its caller
+and mess around with the top of the stack (making `skip` inaccurate).
+*/
+# pragma auto_inline(off)
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_t max_depth) {
+ if (!globalInitialized) {
+ return;
+ }
+
+ auto thread = GetCurrentThread();
+ auto machine = ntHeaders->FileHeader.Machine;
+
+ CONTEXT ccx;
+ RtlCaptureContext(&ccx);
+
+ STACKFRAME64 frame;
+ memset(&frame, 0, sizeof(frame));
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrStack.Mode = AddrModeFlat;
+ frame.AddrFrame.Mode = AddrModeFlat;
+ frame.AddrPC.Offset = ctrace.Rip;
+ frame.AddrStack.Offset = ctrace.Rsp;
+ frame.AddrFrame.Offset = ctrace.Rbp;
+
+ while (max_depth &&
+ (*dbg.StackWalk64)(
+ machine,
+ proc,
+ thread,
+ &frame,
+ &ccx,
+ nullptr,
+ dbg.SymFunctionTableAccess64,
+ dbg.SymGetModuleBase64,
+ nullptr)) {
+ if (skip) {
+ --skip;
+ continue;
+ }
+ --max_depth;
+ auto& entry = builder_.__entries_.emplace_back();
+ // We don't need to compute the un-slid addr; windbg only needs the actual addresses.
+ // Assume address is of the instruction after a call instruction, since we can't
+ // differentiate between a signal, SEH exception handler, or a normal function call.
+ entry.__addr_actual_ = frame.AddrPC.Offset - 1; // Back up 1 byte to get into prev insn range
+ }
+}
+
+dll::~dll() { FreeLibrary(module_); }
+
+dll::dll(char const* name) : name_(name), module_(LoadLibrary(name)) {
+ if (!module_) {
+ debug() << "LoadLibrary failed: " << name_ << ": " << GetLastError() << '\n';
+ }
+}
+
+dbghelp_dll::~dbghelp_dll() = default;
+
+dbghelp_dll& dbghelp_dll::get() {
+ dbghelp_dll ret;
+ return ret;
+}
+
+dbghelp_dll::dbghelp_dll() : dll("dbghelp.dll") {
+ // clang-format off
+if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+ if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+ if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+ if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+ if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+ if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+ if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+ if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+ if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+ if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+ if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+ valid_ = true;
+ // clang-format on
+}
+
+psapi_dll::~psapi_dll() = default;
+
+psapi_dll& psapi_dll::get() {
+ psapi_dll ret;
+ return ret;
+}
+
+psapi_dll() : dll("psapi.dll") {
+ // clang-format off
+if (!getFunc(&EnumProcessModules, "EnumProcessModules")) { return; }
+ if (!getFunc(&GetModuleInformation, "GetModuleInformation")) { return; }
+ if (!getFunc(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+ valid_ = true;
+ // clang-format on
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_WIN32API
diff --git a/libcxx/src/stacktrace/windows/dll.h b/libcxx/src/stacktrace/windows/dll.h
new file mode 100644
index 0000000000000..13ed9e42768d5
--- /dev/null
+++ b/libcxx/src/stacktrace/windows/dll.h
@@ -0,0 +1,121 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_WIN_DLL
+#define _LIBCPP_STACKTRACE_WIN_DLL
+
+#include <__config>
+#if defined(_LIBCPP_WIN32API)
+
+// windows.h must be first
+# include <windows.h>
+// other windows-specific headers
+# include <dbghelp.h>
+# define PSAPI_VERSION 1
+# include <psapi.h>
+
+# include <__config_site>
+# include <cstddef>
+# include <cstdlib>
+# include <mutex>
+# include <stacktrace>
+
+# include "stacktrace/utils/debug.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+# if defined(_LIBCPP_WIN32API)
+
+// clang-format off
+
+struct dll {
+ char const* name_;
+ HMODULE module_;
+ /** Set to true in subclass's ctor if initialized successfully. */
+ bool valid_{false};
+
+ operator bool() const { return valid_; }
+
+ explicit dll(char const* name)
+ : name_(name), module_(LoadLibrary(name)) {
+ if (!module_) {
+ debug() << "LoadLibrary failed: "
+ << name_ << ": " << GetLastError() << '\n';
+ }
+ }
+
+ virtual ~dll() { FreeLibrary(module_); }
+
+ template <typename F>
+ bool get_func(F* func, char const* name) {
+ if (!(*func = (F)GetProcAddress(module_, name))) {
+ debug() << "GetProcAddress failed: "
+ << name << "' (" << name_ << "): "
+ << GetLastError() << "\n";
+ return false;
+ }
+ return true;
+ }
+};
+
+struct dbghelp_dll final : dll {
+ virtual ~dbghelp_dll() = default;
+ static dbghelp_dll& get() { static dbghelp_dll ret; return ret; }
+
+ IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
+ bool (*StackWalk64) (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
+ bool (*SymCleanup) (HANDLE);
+ void* (*SymFunctionTableAccess64)(HANDLE, DWORD64);
+ bool (*SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
+ DWORD64 (*SymGetModuleBase64) (HANDLE, DWORD64);
+ DWORD (*SymGetOptions) ();
+ bool (*SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
+ bool (*SymInitialize) (HANDLE, char const*, bool);
+ DWORD64 (*SymLoadModule64) (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
+ DWORD (*SymSetOptions) (DWORD);
+
+ dbghelp_dll() : dll("dbghelp.dll") {
+ if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+ if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+ if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+ if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+ if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+ if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+ if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+ if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+ if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+ if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+ if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+ valid_ = true;
+ }
+};
+
+struct psapi_dll final : dll {
+ virtual ~psapi_dll() = default;
+ static psapi_dll& get() { static psapi_dll ret; return ret; }
+
+ bool (*EnumProcessModules) (HANDLE, HMODULE*, DWORD, DWORD*);
+ bool (*GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
+ DWORD (*GetModuleBaseName) (HANDLE, HMODULE, char**, DWORD);
+
+ psapi_dll() : dll("psapi.dll") {
+ if (!get_func(&EnumProcessModules, "EnumProcessModules")) { return; }
+ if (!get_func(&GetModuleInformation, "GetModuleInformation")) { return; }
+ if (!get_func(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+ valid_ = true;
+ }
+};
+
+#endif
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_WIN32API
+#endif // _LIBCPP_STACKTRACE_WIN_DLL
diff --git a/libcxx/src/stacktrace/windows/impl.cpp b/libcxx/src/stacktrace/windows/impl.cpp
new file mode 100644
index 0000000000000..e8901e469e768
--- /dev/null
+++ b/libcxx/src/stacktrace/windows/impl.cpp
@@ -0,0 +1,200 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache 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(_LIBCPP_WIN32API)
+// windows.h must be first
+# include <windows.h>
+// other windows-specific headers
+# include <dbghelp.h>
+# define PSAPI_VERSION 1
+# include <psapi.h>
+
+# include <stacktrace>
+
+# include "stacktrace/utils/debug.h"
+# include "stacktrace/windows/dll.h"
+# include "stacktrace/windows/impl.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/*
+Global objects, shared among all threads and among all stacktrace operations.
+The `dbghelp` APIs are not safe to call concurrently (according to their docs)
+so we claim a lock in the `WinDebugAPIs` constructor.
+*/
+
+// Statically-initialized
+DbgHelpDLL dbg;
+PSAPIDLL ps;
+
+// Initialized once, in first WinDebugAPIs construction;
+// protected by the above mutex.
+HANDLE proc;
+HMODULE exe;
+IMAGE_NT_HEADERS* ntHeaders;
+bool globalInitialized{false};
+
+// Globals used across invocations of the functions below.
+// Also guarded by the above mutex.
+bool symsInitialized{false};
+HMODULE moduleHandles[1024];
+size_t moduleCount; // 0 IFF module enumeration failed
+
+} // namespace
+
+win_impl::global_init() {
+ if (!globalInitialized) {
+ // Cannot proceed without these DLLs:
+ if (!dbg) {
+ return;
+ }
+ if (!ps) {
+ return;
+ }
+ proc = GetCurrentProcess();
+ if (!(exe = GetModuleHandle(nullptr))) {
+ return;
+ }
+ if (!(ntHeaders = (*dbg.ImageNtHeader)(exe))) {
+ return;
+ }
+
+ globalInitialized = true;
+ }
+
+ // Initialize symbol machinery.
+ // Presumably the symbols in this process's space can change between
+ // stacktraces, so we'll do this each time we take a trace.
+ // The final `true` means we want the runtime to enumerate all this
+ // process's modules' symbol tables.
+ symsInitialized = (*dbg.SymInitialize)(proc, nullptr, true);
+ DWORD symOptions = (*dbg.SymGetOptions)();
+ symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
+ (*dbg.SymSetOptions)(symOptions);
+}
+
+win_impl::~win_impl() {
+ if (symsInitialized) {
+ (*dbg.SymCleanup)(proc);
+ symsInitialized = false;
+ }
+}
+
+void win_impl::ident_modules() {
+ if (!globalInitialized) {
+ return;
+ }
+ DWORD needBytes;
+ auto enumMods = (*ps.EnumProcessModules)(proc, moduleHandles, sizeof(moduleHandles), LPDWORD(&needBytes));
+ if (enumMods) {
+ moduleCount = needBytes / sizeof(HMODULE);
+ } else {
+ moduleCount = 0;
+ Debug() << "EnumProcessModules failed: " << GetLastError() << '\n';
+ }
+}
+
+void win_impl::symbolize() {
+ // Very long symbols longer than this amount will be truncated.
+ static constexpr size_t kMaxSymName = 256;
+ if (!globalInitialized) {
+ return;
+ }
+
+ for (auto& entry : builder_.__entries_) {
+ char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName];
+ auto* sym = (IMAGEHLP_SYMBOL64*)space;
+ sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ sym->MaxNameLength = kMaxSymName;
+ uint64_t disp{0};
+ if ((*dbg.SymGetSymFromAddr64)(proc, entry.__addr_actual_, &disp, sym)) {
+ // Copy chars into the destination string which uses the caller-provided allocator.
+ ((entry_base&)entry).__desc_ = {sym->Name};
+ } else {
+ Debug() << "SymGetSymFromAddr64 failed: " << GetLastError() << '\n';
+ }
+ }
+}
+
+void win_impl::resolve_lines() {
+ if (!globalInitialized) {
+ return;
+ }
+
+ for (auto& entry : builder_.__entries_) {
+ DWORD disp{0};
+ IMAGEHLP_LINE64 line;
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ if ((*dbg.SymGetLineFromAddr64)(proc, entry.__addr_actual_, &disp, &line)) {
+ // Copy chars into the destination string which uses the caller-provided allocator.
+ entry.__file_ = line.FileName;
+ entry.__line_ = line.LineNumber;
+ } else {
+ Debug() << "SymGetLineFromAddr64 failed: " << GetLastError() << '\n';
+ }
+ }
+}
+
+/*
+Inlining is disabled from here on;
+this is to ensure `collect` below doesn't get merged into its caller
+and mess around with the top of the stack (making `skip` inaccurate).
+*/
+# pragma auto_inline(off)
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_t max_depth) {
+ if (!globalInitialized) {
+ return;
+ }
+
+ auto thread = GetCurrentThread();
+ auto machine = ntHeaders->FileHeader.Machine;
+
+ CONTEXT ccx;
+ RtlCaptureContext(&ccx);
+
+ STACKFRAME64 frame;
+ memset(&frame, 0, sizeof(frame));
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrStack.Mode = AddrModeFlat;
+ frame.AddrFrame.Mode = AddrModeFlat;
+ frame.AddrPC.Offset = ctrace.Rip;
+ frame.AddrStack.Offset = ctrace.Rsp;
+ frame.AddrFrame.Offset = ctrace.Rbp;
+
+ while (max_depth &&
+ (*dbg.StackWalk64)(
+ machine,
+ proc,
+ thread,
+ &frame,
+ &ccx,
+ nullptr,
+ dbg.SymFunctionTableAccess64,
+ dbg.SymGetModuleBase64,
+ nullptr)) {
+ if (skip) {
+ --skip;
+ continue;
+ }
+ --max_depth;
+ auto& entry = builder_.__entries_.emplace_back();
+ // We don't need to compute the un-slid addr; windbg only needs the actual addresses.
+ // Assume address is of the instruction after a call instruction, since we can't
+ // differentiate between a signal, SEH exception handler, or a normal function call.
+ entry.__addr_actual_ = frame.AddrPC.Offset - 1; // Back up 1 byte to get into prev insn range
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/stacktrace/windows/impl.h b/libcxx/src/stacktrace/windows/impl.h
new file mode 100644
index 0000000000000..b88a3f9d8a3a1
--- /dev/null
+++ b/libcxx/src/stacktrace/windows/impl.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_WIN_IMPL_H
+#define _LIBCPP_STACKTRACE_WIN_IMPL_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+#include <mutex>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct builder;
+
+struct win_impl {
+ builder& builder_;
+
+#if defined(_LIBCPP_WIN32API)
+ static std::mutex mutex_;
+ std::lock_guard<std::mutex> guard_;
+
+ explicit win_impl(builder& builder) : builder_(builder), guard_(mutex_) { global_init(); }
+ ~win_impl();
+
+ void global_init();
+ void collect(size_t skip, size_t max_depth);
+ void ident_modules();
+ void symbolize();
+ void resolve_lines();
+#else
+ void global_init() {}
+ void collect(size_t, size_t) {}
+ void ident_modules() {}
+ void symbolize() {}
+ void resolve_lines() {}
+#endif // _LIBCPP_WIN32API
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WIN_IMPL_H
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
new file mode 100644
index 0000000000000..f40037d7e46e6
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g0
+
+#include <cassert>
+#include <stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // This is 'nodebug', so we cannot get the source file and line:
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ // But this should at least produce the executable filename
+ assert(entry.source_file().contains("simple.o0.nodebug.pass.cpp"));
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
new file mode 100644
index 0000000000000..2c7fa9416f31f
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
+#include <cassert>
+#include <stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
new file mode 100644
index 0000000000000..1f6e2bd557460
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
+
+#include <cassert>
+#include <iostream>
+#include <stacktrace>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cerr << trace << '\n';
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
new file mode 100644
index 0000000000000..fafb0d618b908
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O3 -g0
+
+#include <cassert>
+#include <stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // This is 'nodebug', so we cannot get the source file and line:
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ // But this should at least produce the executable filename
+ assert(entry.source_file().contains("simple.o3.nodebug.pass.cpp"));
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
new file mode 100644
index 0000000000000..3690499ae7c25
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O3 -g
+
+#include <cassert>
+#include <stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
new file mode 100644
index 0000000000000..4b32e33110d1a
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O3 -g -gsplit-dwarf
+
+#include <cassert>
+#include <stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ return 0;
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index cb23c7a98de1b..e67db3ac47cf9 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 list
+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 5f906338f4b7c..fa29ac86a2f7e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -911,6 +911,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 list
+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..5864d241abf35
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.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
+
+/*
+ (19.6.4.4) Comparisons [stacktrace.basic.cmp]
+
+ template<class Allocator2>
+ friend bool operator==(const basic_stacktrace& x,
+ const basic_stacktrace<Allocator2>& y) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2a() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2b() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ auto st1a = test1(); // [test1, main, ...]
+ assert(st1a == st1a);
+
+ auto st1b = st1a;
+ assert(st1a == st1b);
+
+ auto st2a = test2a(); // [test1, test2a, main, ...]
+ assert(st1a != st2a);
+
+ std::stacktrace empty; // []
+ assert(st1a != empty);
+ assert(st2a != empty);
+
+ assert(st2a.size() > st1a.size());
+ assert(st1a.size() > empty.size());
+
+ auto st2b = test2b(); // [test1, test2b, main, ...]
+ assert(st2a.size() == st2b.size());
+ assert(st2a != st2b);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp
new file mode 100644
index 0000000000000..5664e9bc41db2
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.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
+
+/*
+ (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;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2a() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2b() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ /*
+ Returns: x.size() <=> y.size() if x.size() != y.size();
+ lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end()) otherwise.
+ */
+
+ auto st1a = test1(); // [test1, main, ...]
+ auto st1b = st1a;
+ assert(st1a == st1b);
+ auto st2a = test2a(); // [test1, test2a, main, ...]
+ assert(st1a != st2a);
+ std::stacktrace empty; // []
+ auto st2b = test2b(); // [test1, test2b, main, ...]
+ assert(st2a != st2b);
+
+ // empty: []
+ // st1a: [test1, main, ...]
+ // st1b: [test1, main, ...] (copy of st1a)
+ // st2a: [test1, test2a, main:X, ...]
+ // st2b: [test1, test2b, main:Y, ...], Y > X
+
+ assert(std::strong_ordering::equal == empty <=> empty);
+ assert(std::strong_ordering::less == empty <=> st1a);
+ assert(std::strong_ordering::less == empty <=> st1b);
+ assert(std::strong_ordering::less == empty <=> st2a);
+ assert(std::strong_ordering::less == empty <=> st2b);
+
+ assert(std::strong_ordering::greater == st1a <=> empty);
+ assert(std::strong_ordering::equal == st1a <=> st1a);
+ assert(std::strong_ordering::equal == st1a <=> st1b);
+ assert(std::strong_ordering::less == st1a <=> st2a);
+ assert(std::strong_ordering::less == st1a <=> st2b);
+
+ assert(std::strong_ordering::greater == st1b <=> empty);
+ assert(std::strong_ordering::equal == st1b <=> st1a);
+ assert(std::strong_ordering::equal == st1b <=> st1b);
+ assert(std::strong_ordering::less == st1b <=> st2a);
+ assert(std::strong_ordering::less == st1b <=> st2b);
+
+ assert(std::strong_ordering::greater == st2a <=> empty);
+ assert(std::strong_ordering::greater == st2a <=> st1a);
+ assert(std::strong_ordering::greater == st2a <=> st1b);
+ assert(std::strong_ordering::equal == st2a <=> st2a);
+ assert(std::strong_ordering::less == st2a <=> st2b);
+
+ assert(std::strong_ordering::greater == st2b <=> empty);
+ assert(std::strong_ordering::greater == st2b <=> st1a);
+ assert(std::strong_ordering::greater == st2b <=> st1b);
+ assert(std::strong_ordering::greater == st2b <=> st2a);
+ assert(std::strong_ordering::equal == st2b <=> st2b);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.pass.cpp
new file mode 100644
index 0000000000000..48da1d0bd30b6
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ basic_stacktrace(const basic_stacktrace& other);
+ basic_stacktrace(basic_stacktrace&& other) noexcept;
+ basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+ basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+ basic_stacktrace& operator=(const basic_stacktrace& other);
+ basic_stacktrace& operator=(basic_stacktrace&& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+ allocator_traits<Allocator>::is_always_equal::value);
+*/
+
+#include <cassert>
+#include <cstdint>
+#include <stacktrace>
+
+// clang-format off
+uint32_t test1_line;
+uint32_t test2_line;
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test1(A& alloc) {
+ test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = std::basic_stacktrace<A>::current(alloc);
+ return ret;
+}
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test2(A& alloc) {
+ test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = test1(alloc);
+ return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_copy_move_ctors() {
+ using A = std::allocator<std::stacktrace_entry>;
+ A alloc;
+ auto st = std::basic_stacktrace<A>::current(alloc);
+
+ auto copy_constr = std::basic_stacktrace<A>(st);
+ assert(st == copy_constr);
+
+ std::basic_stacktrace<A> copy_assign;
+ copy_assign = std::basic_stacktrace<A>(st);
+ assert(st == copy_assign);
+
+ auto st2 = test2(alloc);
+ assert(st2.size());
+ std::basic_stacktrace<A> move_constr(std::move(st2));
+ assert(move_constr.size());
+ assert(!st2.size());
+
+ auto st3 = test2(alloc);
+ assert(st3.size());
+ std::basic_stacktrace<A> move_assign;
+ move_assign = std::move(st3);
+ assert(move_assign.size());
+ assert(!st3.size());
+
+ // TODO(stacktrace23): should we add test cases with `select_on_container_copy_construction`?
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ test_copy_move_ctors();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
new file mode 100644
index 0000000000000..3b406f1b16a61
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.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
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+uint32_t test1_line;
+uint32_t test2_line;
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test1(A& alloc) {
+ test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = std::basic_stacktrace<A>::current(alloc);
+ return ret;
+}
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test2(A& alloc) {
+ test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = test1(alloc);
+ return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_default_construct() {
+ std::stacktrace st;
+ assert(st.empty());
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ test_default_construct();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
new file mode 100644
index 0000000000000..a49f45245f7d2
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_with_alloc.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+uint32_t test1_line;
+uint32_t test2_line;
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test1(A& alloc) {
+ test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = std::basic_stacktrace<A>::current(alloc);
+ return ret;
+}
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test2(A& alloc) {
+ test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = test1(alloc);
+ return ret;
+}
+
+template <typename T>
+struct test_alloc {
+ using size_type = size_t;
+ using value_type = T;
+ using pointer = T*;
+ using const_pointer = T const*;
+
+ template <typename U>
+ struct rebind {
+ using other = test_alloc<U>;
+ };
+
+ std::allocator<T> wrapped_{};
+
+ test_alloc() = default;
+
+ template <typename U>
+ test_alloc(test_alloc<U> const& rhs) : wrapped_(rhs.wrapped_) {}
+
+ bool operator==(auto const& rhs) const { return &rhs == this; }
+ bool operator==(test_alloc const&) const { return true; }
+
+ T* allocate(size_t n) { return wrapped_.allocate(n); }
+ auto allocate_at_least(size_t n) { return wrapped_.allocate_at_least(n); }
+ void deallocate(T* ptr, size_t n) { return wrapped_.deallocate(ptr, n); }
+};
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_construct_with_allocator() {
+ test_alloc<std::stacktrace_entry> alloc;
+ std::basic_stacktrace<decltype(alloc)> st(alloc);
+ assert(st.empty());
+
+ st = std::basic_stacktrace<decltype(alloc)>::current(alloc);
+ assert(!st.empty());
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ test_construct_with_allocator();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_no_args.pass.cpp
new file mode 100644
index 0000000000000..c1c9b3ea70496
--- /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: -g
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+uint32_t test1_line;
+uint32_t test2_line;
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() {
+ test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = std::stacktrace::current();
+ return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() {
+ test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = test1();
+ return ret;
+}
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
+ auto st = test2();
+ assert(st.size() >= 3);
+ assert(st[0]);
+ assert(st[0].native_handle());
+ assert(st[0].description().contains("test1"));
+ assert(st[0].source_file().contains("current_no_args.pass.cpp"));
+ assert(st[1]);
+ assert(st[1].native_handle());
+ assert(st[1].description().contains("test2"));
+ assert(st[1].source_file().contains("current_no_args.pass.cpp"));
+ assert(st[2]);
+ assert(st[2].native_handle());
+ assert(st[2].description().contains("test_current"));
+ assert(st[2].source_file().contains("current_no_args.pass.cpp"));
+
+ // We unfortunately cannot guarantee the following; in CI, and possibly on users' build machines,
+ // there may not be an up-to-date version of e.g. `addr2line`.
+ // assert(st[0].source_file().ends_with("current_no_args.pass.cpp"));
+ // assert(st[0].source_line() == test1_line);
+ // assert(st[1].source_file().ends_with("current_no_args.pass.cpp"));
+ // assert(st[1].source_line() == test2_line);
+ // 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**) {
+ 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..55877f0a49785
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip.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
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ static basic_stacktrace current(size_type skip,
+ const allocator_type& alloc = allocator_type()) noexcept; [2]
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+/*
+ Let t be a stacktrace as-if obtained via basic_stacktrace::current(alloc). Let n be t.size().
+ Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
+ t.begin() + min(n, skip), t.end(), and alloc, or an empty basic_stacktrace object if the
+ initialization of frames_ failed.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
+ // Use default allocator for simplicity; alloc is covered above
+ auto st_skip0 = std::stacktrace::current();
+ assert(st_skip0.size() >= 2);
+ auto st_skip1 = std::stacktrace::current(1);
+ assert(st_skip1.size() >= 1);
+ assert(st_skip0.size() == st_skip1.size() + 1);
+ assert(st_skip0[1] == st_skip1[0]);
+ auto st_skip_many = std::stacktrace::current(1 << 20);
+ assert(st_skip_many.empty());
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ test_current_with_skip();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
new file mode 100644
index 0000000000000..a999845e92e01
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/current_skip_depth.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ static basic_stacktrace current(size_type skip, size_type max_depth,
+ const allocator_type& alloc = allocator_type()) noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
+ // current stack is: [this function, main, (possibly something else, e.g. `_start` from libc)]
+ // so it's probably 3 functions deep -- but certainly at least 2 deep.
+ auto st = std::stacktrace::current();
+ assert(st.size() >= 2);
+ auto it = st.begin();
+ auto entry1 = *(it++); // represents this function
+ auto entry2 = *(it++); // represents our caller, `main`
+
+ // get current trace again, but skip the 1st
+ st = std::stacktrace::current(1, 1);
+ assert(st.size() >= 1);
+ assert(*st.begin() == entry2);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ test_current_with_skip_depth();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.pass.cpp
new file mode 100644
index 0000000000000..ff6a3404a676a
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.pass.cpp
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g -O0
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept; [1]
+ static basic_stacktrace current(size_type skip,
+ const allocator_type& alloc = allocator_type()) noexcept; [2]
+ static basic_stacktrace current(size_type skip, size_type max_depth,
+ const allocator_type& alloc = allocator_type()) noexcept; [3]
+
+ basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>); [4]
+ explicit basic_stacktrace(const allocator_type& alloc) noexcept; [5]
+
+ basic_stacktrace(const basic_stacktrace& other); [6]
+ basic_stacktrace(basic_stacktrace&& other) noexcept; [7]
+ basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc); [8]
+ basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc); [9]
+ basic_stacktrace& operator=(const basic_stacktrace& other); [10]
+ basic_stacktrace& operator=(basic_stacktrace&& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+ allocator_traits<Allocator>::is_always_equal::value); [11]
+
+ ~basic_stacktrace(); [12]
+*/
+
+#include <cassert>
+#include <cstdlib>
+#include <stacktrace>
+
+/*
+ * 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.
+ */
+
+unsigned custom_alloc;
+unsigned custom_dealloc;
+
+void* operator new(size_t size) { return malloc(size); }
+void* operator new[](size_t size) { return malloc(size); }
+void operator delete(void* ptr) noexcept { free(ptr); }
+void operator delete(void* ptr, size_t) noexcept { free(ptr); }
+void operator delete[](void* ptr) noexcept { free(ptr); }
+void operator delete[](void* ptr, size_t) noexcept { free(ptr); }
+
+template <typename T>
+struct test_alloc {
+ using size_type = size_t;
+ using value_type = T;
+ using pointer = T*;
+ using const_pointer = T const*;
+
+ template <typename U>
+ struct rebind {
+ using other = test_alloc<U>;
+ };
+
+ std::allocator<T> wrapped_{};
+
+ test_alloc() = default;
+
+ template <typename U>
+ test_alloc(test_alloc<U> const& rhs) : wrapped_(rhs.wrapped_) {}
+
+ bool operator==(auto const& rhs) const { return &rhs == this; }
+ bool operator==(test_alloc const&) const { return true; }
+
+ T* allocate(size_t n) {
+ ++custom_alloc;
+ return wrapped_.allocate(n);
+ }
+
+ auto allocate_at_least(size_t n) {
+ ++custom_alloc;
+ return wrapped_.allocate_at_least(n);
+ }
+
+ void deallocate(T* ptr, size_t n) {
+ ++custom_dealloc;
+ wrapped_.deallocate(ptr, n);
+ }
+};
+
+/*
+ (19.6.4.2) [stacktrace.basic.cons], creation and assignment,
+ only exercising usage of caller-provided allocator.
+
+ static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept; [1]
+ static basic_stacktrace current(size_type skip,
+ const allocator_type& alloc = allocator_type()) noexcept; [2]
+ static basic_stacktrace current(size_type skip, size_type max_depth,
+ const allocator_type& alloc = allocator_type()) noexcept; [3]
+
+ explicit basic_stacktrace(const allocator_type& alloc) noexcept; [5]
+ basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc); [8]
+ basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc); [9]
+
+ basic_stacktrace& operator=(const basic_stacktrace& other); [10]
+ basic_stacktrace& operator=(basic_stacktrace&& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+ allocator_traits<Allocator>::is_always_equal::value); [11]
+*/
+
+void do_current_stacktrace() {
+ using A = test_alloc<std::stacktrace_entry>;
+ (void)std::basic_stacktrace<A>::current(A());
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ {
+ do_current_stacktrace();
+ assert(custom_alloc > 0);
+ }
+ assert(custom_dealloc == custom_alloc);
+ 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..ccdd195e2980f
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.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..147ce1f65810d
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.5) Modifiers [stacktrace.basic.mod]
+
+ template<class Allocator>
+ void swap(basic_stacktrace& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+ allocator_traits<Allocator>::is_always_equal::value);
+
+ Effects: Exchanges the contents of *this and other.
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) {
+ std::stacktrace empty;
+ auto current = std::stacktrace::current();
+
+ std::stacktrace a(empty);
+ std::stacktrace b(current);
+ assert(a == empty);
+ assert(b == current);
+
+ a.swap(b);
+ assert(a == current);
+ assert(b == empty);
+
+ // TODO(stacktrace23): should we also test swap w/ `select_on_container_swap` case
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.cpp
new file mode 100644
index 0000000000000..def734fa56697
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/operator_left_shift.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.6) Non-member functions
+
+ ostream& operator<<(ostream& os, const stacktrace_entry& f);
+ template<class Allocator>
+ ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+*/
+
+#include <cassert>
+#include <sstream>
+#include <stacktrace>
+
+int main(int, char**) {
+ auto a = std::stacktrace::current();
+
+ std::stringstream entry_os;
+ entry_os << a[0];
+ assert(entry_os.str() == std::to_string(a[0]));
+
+ std::stringstream trace_os;
+ trace_os << a;
+ assert(trace_os.str() == std::to_string(a));
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
new file mode 100644
index 0000000000000..281af1c245245
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.6) Non-member functions
+
+ template<class Allocator>
+ void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+ noexcept(noexcept(a.swap(b)));
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) {
+ std::stacktrace empty;
+ auto current = std::stacktrace::current();
+
+ std::stacktrace a(empty);
+ std::stacktrace b(current);
+ assert(a == empty);
+ assert(b == current);
+
+ std::swap(a, b);
+ assert(a == current);
+ assert(b == empty);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.pass.cpp
new file mode 100644
index 0000000000000..ca68883f6a115
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/to_string.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
+
+/*
+ (19.6.4.6) Non-member functions
+
+ string to_string(const stacktrace_entry& f);
+
+ template<class Allocator>
+ string to_string(const basic_stacktrace<Allocator>& st);
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) {
+ auto a = std::stacktrace::current();
+
+ assert(std::to_string(a[0]).contains("main"));
+ assert(std::to_string(a[0]).contains("to_string.pass"));
+
+ assert(std::to_string(a).contains("main"));
+ assert(std::to_string(a).contains("to_string.pass"));
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
new file mode 100644
index 0000000000000..4562f33af2687
--- /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
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ const_reference at(size_type) const;
+*/
+
+#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**) {
+ auto st = test3();
+
+ assert(st.at(0));
+ assert(st.at(1));
+ assert(st.at(2));
+ assert(st.at(3));
+
+ auto f0 = st.at(0);
+ auto f1 = st.at(1);
+ auto f2 = st.at(2);
+ auto f3 = st.at(3);
+
+ auto it = st.begin();
+ assert(*it++ == f0);
+ assert(it != st.end());
+ assert(*it++ == f1);
+ assert(it != st.end());
+ assert(*it++ == f2);
+ assert(it != st.end());
+ assert(*it++ == f3);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
new file mode 100644
index 0000000000000..db3189d7cb007
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/begin_end.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ std::stacktrace st;
+ static_assert(std::random_access_iterator<decltype(st.begin())>);
+ assert(st.begin() == st.end());
+ // no longer empty:
+ st = test3();
+ assert(!st.empty());
+ assert(st.begin() != st.end());
+ auto f0 = st[0];
+ auto it = st.begin();
+ assert(*it == f0);
+ assert(it != st.end());
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
new file mode 100644
index 0000000000000..3d084d45ef7b2
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/cbegin_cend.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ std::stacktrace st;
+ static_assert(std::random_access_iterator<decltype(st.cbegin())>);
+ assert(st.cbegin() == st.cend());
+ // no longer empty:
+ st = test3();
+ assert(st.cbegin() != st.cend());
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
new file mode 100644
index 0000000000000..5d4d6fc6737f9
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/crbegin_crend.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ const_reverse_iterator crbegin() const noexcept;
+ const_reverse_iterator crend() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ std::stacktrace st;
+ static_assert(std::random_access_iterator<decltype(st.crbegin())>);
+ assert(st.crbegin() == st.crend());
+ // no longer empty:
+ st = test3();
+ assert(st.crbegin() != st.crend());
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
new file mode 100644
index 0000000000000..dc3c6baa8b978
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ bool empty() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ std::stacktrace st;
+ assert(st.empty());
+ st = test3();
+ assert(!st.empty());
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
new file mode 100644
index 0000000000000..2b1004f38051d
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ allocator_type get_allocator() const noexcept;
+*/
+
+#include <cassert>
+#include <concepts>
+#include <stacktrace>
+
+int main(int, char**) {
+ std::stacktrace st;
+ static_assert(std::same_as<decltype(st.get_allocator()), std::stacktrace::allocator_type>);
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
new file mode 100644
index 0000000000000..81768ec965cff
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/max_size.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ size_type max_size() const noexcept;
+*/
+
+#include <cassert>
+#include <memory>
+#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;
+ assert(st.max_size() == (std::vector<std::stacktrace_entry, std::allocator<std::stacktrace_entry>>().max_size()));
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
new file mode 100644
index 0000000000000..aab086b47dd32
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.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
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ const_reference operator[](size_type) const;
+*/
+
+#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**) {
+ auto st = test3();
+
+ assert(st[0]);
+ assert(st[1]);
+ assert(st[2]);
+ assert(st[3]);
+
+ auto f0 = st[0];
+ auto f1 = st[1];
+ auto f2 = st[2];
+ auto f3 = st[3];
+
+ auto it = st.begin();
+ assert(*it++ == f0);
+ assert(it != st.end());
+ assert(*it++ == f1);
+ assert(it != st.end());
+ assert(*it++ == f2);
+ assert(it != st.end());
+ assert(*it++ == f3);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
new file mode 100644
index 0000000000000..f8446d42b5f03
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/rbegin_rend.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ const_reverse_iterator rbegin() const noexcept;
+ const_reverse_iterator rend() const noexcept;
+*/
+
+#include <cassert>
+#include <iterator>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ std::stacktrace st;
+ static_assert(std::random_access_iterator<decltype(st.rbegin())>);
+ assert(st.rbegin() == st.rend());
+ // no longer empty:
+ st = test3();
+ auto it = st.rbegin();
+ assert(it != st.rend());
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
new file mode 100644
index 0000000000000..cbfac43b4beaf
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ size_type size() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ std::stacktrace st;
+ assert(st.size() == 0);
+ st = test3();
+ assert(st.size() > 0);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
new file mode 100644
index 0000000000000..39a52fd9fd856
--- /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
+
+/*
+ (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()).crbegin())>);
+ using RI = S::reverse_iterator;
+ static_assert(std::is_same_v<RI, decltype(S(A()).rbegin())>);
+
+ using IterT = S::iterator;
+ using DiffT = S::difference_type;
+ static_assert(std::is_same_v<IterT, decltype(IterT() + DiffT())>);
+
+ static_assert(std::is_integral_v<S::size_type>);
+ static_assert(std::is_same_v<S::allocator_type, A>);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
new file mode 100644
index 0000000000000..4ddd27ed388c6
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (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);
+ 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..edefe70af1fc9
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.3.5) Comparison [stacktrace.entry.cmp]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ // [stacktrace.entry.cmp], comparison
+ friend constexpr 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);
+
+ 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..c993a6df64508
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_assign.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
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+ (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+ class stacktrace_entry {
+ // [stacktrace.entry.cons], constructors
+ constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+}
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) noexcept {
+ static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
+ std::stacktrace_entry entry_t2;
+ std::stacktrace_entry entry_t4;
+ entry_t4 = entry_t2;
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.pass.cpp
new file mode 100644
index 0000000000000..85dd6fb7853cc
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/copy_construct.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: -g
+
+/*
+ (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+ class stacktrace_entry {
+ // [stacktrace.entry.cons], constructors
+ constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+}
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ std::stacktrace_entry entry_t2;
+ static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
+ std::stacktrace_entry entry_t3(entry_t2);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp
new file mode 100644
index 0000000000000..03bd82cfd9c73
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+ (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+ class stacktrace_entry {
+ // [stacktrace.entry.cons], constructors
+ constexpr stacktrace_entry() noexcept;
+}
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <type_traits>
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) noexcept {
+ // "Postconditions: *this is empty."
+ static_assert(std::is_default_constructible_v<std::stacktrace_entry>);
+ static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
+ std::stacktrace_entry entry_t2;
+ assert(!entry_t2);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
new file mode 100644
index 0000000000000..bf1a23080f307
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.3.3) Observers [stacktrace.entry.obs]
+
+namespace std {
+ class stacktrace_entry {
+ // [stacktrace.entry.obs], observers
+ constexpr native_handle_type native_handle() const noexcept;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) noexcept {
+ std::stacktrace_entry entry_t6;
+ assert(entry_t6.native_handle() == 0);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
new file mode 100644
index 0000000000000..f1a685324b22d
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.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
+
+/*
+ (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 entry_t6;
+ // "Returns: false if and only if *this is empty."
+ assert(!entry_t6);
+ // Now set addr to something nonzero
+ *(uintptr_t*)(&entry_t6) = uintptr_t(&main);
+ assert(entry_t6.native_handle() == uintptr_t(&main));
+ assert(entry_t6);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
new file mode 100644
index 0000000000000..39bc5845e9718
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+#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..2493e2bd8b490
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query/description.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
+/*
+ (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ // [stacktrace.entry.query], query
+ string description() const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <string>
+
+int main(int, char**) {
+ std::stacktrace_entry e;
+ auto desc = e.description();
+ assert(desc.empty());
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp
new file mode 100644
index 0000000000000..fc2d5efb6a31e
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_file.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
+/*
+ (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ // [stacktrace.entry.query], query
+ string source_file() const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+#include <string>
+
+int main(int, char**) {
+ std::stacktrace_entry e;
+ auto src = e.source_file();
+ assert(src.empty());
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp
new file mode 100644
index 0000000000000..e601b74f032f0
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query/source_line.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
+/*
+ (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ // [stacktrace.entry.query], query
+ uint_least32_t source_line() const;
+*/
+
+#include <cassert>
+#include <stacktrace>
+
+int main(int, char**) {
+ std::stacktrace_entry e;
+ assert(e.source_line() == 0);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp
new file mode 100644
index 0000000000000..bda374e0b2d90
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/format/formatter_basic_stacktrace.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.5) Formatting support [stacktrace.format]
+
+ template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+*/
+
+#include <cassert>
+// #include <stacktrace>
+
+int main(int, char**) {
+ /*
+ For formatter<basic_stacktrace<Allocator>>, format-spec is empty.
+
+ A basic_stacktrace<Allocator> object s is formatted as if by copying to_string(s) through the
+ output iterator of the context.
+ */
+
+ // TODO: stacktrace formatter: https://github.com/llvm/llvm-project/issues/105257
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.pass.cpp
new file mode 100644
index 0000000000000..acd04ae33dfc3
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/format/formatter_stacktrace_entry.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+/*
+ (19.6.5) Formatting support [stacktrace.format]
+
+ template<> struct formatter<stacktrace_entry>;
+*/
+
+#include <cassert>
+// #include <stacktrace>
+
+int main(int, char**) {
+ /*
+ formatter<stacktrace_entry> interprets format-spec as a stacktrace-entry-format-spec.
+ The syntax of format specifications is as follows:
+
+ stacktrace-entry-format-spec :
+ fill-and-align_[opt] width_[opt]
+
+ [Note 1: The productions fill-and-align and width are described in [format.string.std]. - end note]
+
+ A stacktrace_entry object se is formatted as if by copying to_string(se) through the output iterator
+ of the context with additional padding and adjustments as specified by the format specifiers.
+ */
+
+ // TODO: stacktrace formatter: https://github.com/llvm/llvm-project/issues/105257
+
+ return 0;
+}
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..461087467a270
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// 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
+
+# ifndef __cpp_lib_stacktrace
+# error "__cpp_lib_stacktrace should be defined in c++23"
+# endif
+# if __cpp_lib_stacktrace != 202011L
+# error "__cpp_lib_stacktrace should have the value 202011L in c++23"
+# endif
+
+#elif TEST_STD_VER > 23
+
+# 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
+
+# ifndef __cpp_lib_stacktrace
+# error "__cpp_lib_stacktrace should be defined in c++26"
+# endif
+# if __cpp_lib_stacktrace != 202011L
+# error "__cpp_lib_stacktrace should have the value 202011L in c++26"
+# endif
+
+#endif // TEST_STD_VER > 23
+
+// 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 222d562a19d63..6b6d7b9629820 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
@@ -5937,17 +5937,11 @@
# error "__cpp_lib_sstream_from_string_view should not be defined before c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_stacktrace
-# error "__cpp_lib_stacktrace should be defined in c++23"
-# endif
-# if __cpp_lib_stacktrace != 202011L
-# error "__cpp_lib_stacktrace should have the value 202011L in c++23"
-# endif
-# else
-# ifdef __cpp_lib_stacktrace
-# error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_stacktrace
+# error "__cpp_lib_stacktrace should be defined in c++23"
+# endif
+# if __cpp_lib_stacktrace != 202011L
+# error "__cpp_lib_stacktrace should have the value 202011L in c++23"
# endif
# ifndef __cpp_lib_starts_ends_with
@@ -7906,17 +7900,11 @@
# error "__cpp_lib_sstream_from_string_view should have the value 202306L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_stacktrace
-# error "__cpp_lib_stacktrace should be defined in c++26"
-# endif
-# if __cpp_lib_stacktrace != 202011L
-# error "__cpp_lib_stacktrace should have the value 202011L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_stacktrace
-# error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_stacktrace
+# error "__cpp_lib_stacktrace should be defined in c++26"
+# endif
+# if __cpp_lib_stacktrace != 202011L
+# error "__cpp_lib_stacktrace should have the value 202011L in c++26"
# endif
# ifndef __cpp_lib_starts_ends_with
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 18eb8a8623748..66e35ef2864f5 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1315,7 +1315,6 @@ def add_version_header(tc):
"name": "__cpp_lib_stacktrace",
"values": {"c++23": 202011},
"headers": ["stacktrace"],
- "unimplemented": True,
},
{
"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"],
>From acf0a9c4deefd5ceda1e6282d5d2263ca9108965 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Tue, 8 Jul 2025 20:48:04 -0400
Subject: [PATCH 02/14] Address formatting and other issues
---
.../support.limits.general/stacktrace.version.compile.pass.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 461087467a270..8d8ce5665565b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,4 +105,3 @@
#endif // TEST_STD_VER > 23
// clang-format on
-
>From c3a1c4897bc5363ae038702d982d775f0f3fbe5f Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Tue, 8 Jul 2025 22:32:32 -0400
Subject: [PATCH 03/14] update headers and modules
---
libcxx/include/module.modulemap.in | 10 ++--------
libcxx/src/stacktrace/linux/impl.h | 3 ++-
libcxx/src/stacktrace/macos/impl.h | 3 ++-
libcxx/src/stacktrace/tools/tools.cpp | 7 +++----
libcxx/src/stacktrace/unwind/impl.cpp | 5 +++--
5 files changed, 12 insertions(+), 16 deletions(-)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index e485ad46d0d2d..a997602c9950a 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1336,10 +1336,7 @@ module std [system] {
module concepts { header "__format/concepts.h" }
module container_adaptor { header "__format/container_adaptor.h" }
module enable_insertable { header "__format/enable_insertable.h" }
- module escaped_output_table {
- header "__format/escaped_output_table.h"
- export std.format.escaped_output_table // TODO: Workaround for https://github.com/llvm/llvm-project/issues/120108
- }
+ module escaped_output_table { header "__format/escaped_output_table.h" }
module extended_grapheme_cluster_table { header "__format/extended_grapheme_cluster_table.h" }
module format_arg { header "__format/format_arg.h" }
module format_arg_store { header "__format/format_arg_store.h" }
@@ -1374,10 +1371,7 @@ module std [system] {
module range_default_formatter { header "__format/range_default_formatter.h" }
module range_formatter { header "__format/range_formatter.h" }
module unicode { header "__format/unicode.h" }
- module width_estimation_table {
- header "__format/width_estimation_table.h"
- export std.format.width_estimation_table // TODO: Workaround for https://github.com/llvm/llvm-project/issues/120108
- }
+ module width_estimation_table { header "__format/width_estimation_table.h" }
module write_escaped { header "__format/write_escaped.h" }
header "format"
diff --git a/libcxx/src/stacktrace/linux/impl.h b/libcxx/src/stacktrace/linux/impl.h
index cd028d070835f..1f6ca6ef2b413 100644
--- a/libcxx/src/stacktrace/linux/impl.h
+++ b/libcxx/src/stacktrace/linux/impl.h
@@ -34,7 +34,8 @@ struct linux {
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#include "stacktrace/config.h"
+#include <__config>
+#include <__config_site>
#if defined(__linux__)
diff --git a/libcxx/src/stacktrace/macos/impl.h b/libcxx/src/stacktrace/macos/impl.h
index 42b8b89d7bbb9..e0a24228a6ade 100644
--- a/libcxx/src/stacktrace/macos/impl.h
+++ b/libcxx/src/stacktrace/macos/impl.h
@@ -14,7 +14,8 @@
#include <cstddef>
#include <cstdlib>
-#include "stacktrace/config.h"
+#include <__config>
+#include <__config_site>
#include <__stacktrace/base.h>
_LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/tools.cpp b/libcxx/src/stacktrace/tools/tools.cpp
index aa573567377e8..8f04a1806bbc7 100644
--- a/libcxx/src/stacktrace/tools/tools.cpp
+++ b/libcxx/src/stacktrace/tools/tools.cpp
@@ -6,12 +6,11 @@
//
//===----------------------------------------------------------------------===//
-#include "stacktrace/config.h"
+#include <__config>
+#include <__config_site>
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+#if __has_include(<spawn.h>) && _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
-# include <__config>
-# include <__config_site>
# include <cassert>
# include <cerrno>
# include <csignal>
diff --git a/libcxx/src/stacktrace/unwind/impl.cpp b/libcxx/src/stacktrace/unwind/impl.cpp
index 90603e0422b9b..4742988dac64c 100644
--- a/libcxx/src/stacktrace/unwind/impl.cpp
+++ b/libcxx/src/stacktrace/unwind/impl.cpp
@@ -6,9 +6,10 @@
//
//===----------------------------------------------------------------------===//
-#include "stacktrace/config.h"
+#include <__config>
+#include <__config_site>
-#if defined(_LIBCPP_STACKTRACE_UNWIND_IMPL)
+#if __has_include(<unwind.h>)
# include <unwind.h>
>From 54ea52e453d8bb4ea4dfa621346c7cb434ad1d16 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Tue, 8 Jul 2025 23:18:48 -0400
Subject: [PATCH 04/14] Break up large linux impl
---
libcxx/include/__stacktrace/base.h | 2 +-
libcxx/include/__stacktrace/basic.h | 2 +-
libcxx/include/__stacktrace/hash.h | 1 +
libcxx/include/module.modulemap.in | 2 +
libcxx/src/CMakeLists.txt | 2 +
libcxx/src/stacktrace/linux/elf.cpp | 98 +++++
libcxx/src/stacktrace/linux/elf.h | 248 ++++++++++++
libcxx/src/stacktrace/linux/images.cpp | 72 ++++
libcxx/src/stacktrace/linux/images.h | 50 +++
libcxx/src/stacktrace/linux/impl.cpp | 6 +-
libcxx/src/stacktrace/linux/impl.h | 365 ------------------
.../stacktrace.version.compile.pass.cpp | 1 +
12 files changed, 480 insertions(+), 369 deletions(-)
create mode 100644 libcxx/src/stacktrace/linux/elf.cpp
create mode 100644 libcxx/src/stacktrace/linux/elf.h
create mode 100644 libcxx/src/stacktrace/linux/images.cpp
create mode 100644 libcxx/src/stacktrace/linux/images.h
diff --git a/libcxx/include/__stacktrace/base.h b/libcxx/include/__stacktrace/base.h
index 52e6d17e4e5be..854071001a8b5 100644
--- a/libcxx/include/__stacktrace/base.h
+++ b/libcxx/include/__stacktrace/base.h
@@ -69,7 +69,7 @@ struct _LIBCPP_HIDE_FROM_ABI alloc final {
template <typename _A2>
bool operator==(_A2 const& __rhs) const {
- return &__rhs == this;
+ return std::addressof(__rhs) == this;
}
};
diff --git a/libcxx/include/__stacktrace/basic.h b/libcxx/include/__stacktrace/basic.h
index a73c8d56f6c21..ca958e55f0614 100644
--- a/libcxx/include/__stacktrace/basic.h
+++ b/libcxx/include/__stacktrace/basic.h
@@ -55,7 +55,7 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
[[no_unique_address]] _Allocator __alloc_;
- using __entry_vec = vector<stacktrace_entry, _Allocator>;
+ using __entry_vec _LIBCPP_NODEBUG = vector<stacktrace_entry, _Allocator>;
__entry_vec __entries_;
public:
diff --git a/libcxx/include/__stacktrace/hash.h b/libcxx/include/__stacktrace/hash.h
index 297507f5415fa..a507c0b68db3f 100644
--- a/libcxx/include/__stacktrace/hash.h
+++ b/libcxx/include/__stacktrace/hash.h
@@ -12,6 +12,7 @@
#include <__config>
#include <__functional/hash.h>
+#include <cstddef>
#include <cstdint>
#include <__stacktrace/base.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index a997602c9950a..bfe41f432601b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2021,7 +2021,9 @@ module std [system] {
// 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
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 118771d4b5a5c..5e12aec47467d 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -41,6 +41,8 @@ set(LIBCXX_SOURCES
ryu/d2s.cpp
ryu/f2s.cpp
stacktrace/builder.cpp
+ stacktrace/linux/elf.cpp
+ stacktrace/linux/images.cpp
stacktrace/linux/impl.cpp
stacktrace/macos/impl.cpp
stacktrace/to_string.cpp
diff --git a/libcxx/src/stacktrace/linux/elf.cpp b/libcxx/src/stacktrace/linux/elf.cpp
new file mode 100644
index 0000000000000..b6d05f33a9624
--- /dev/null
+++ b/libcxx/src/stacktrace/linux/elf.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__linux__)
+
+# include "stacktrace/linux/elf.h"
+
+# include <__stacktrace/base.h>
+# include <cassert>
+# include <cstddef>
+# include <cstdlib>
+# include <functional>
+# include <unistd.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace::elf {
+
+ELF::ELF(std::byte const* image) {
+ auto* p = (uint8_t const*)image;
+ // Bytes 0..3: magic bytes: 0x7F, 'E', 'L', 'F'
+ if (*p++ == 0x7f && *p++ == 0x45 && *p++ == 0x4c && *p++ == 0x46) {
+ auto klass = *p++; // Byte 4 (EI_CLASS): ELF class, 32- or 64-bit (0x01 or 0x02)
+ auto dataFormat = *p++; // Byte 5 (EI_DATA): (0x01) little- or (0x02) big-endian
+ auto fileVersion = *p++; // Byte 6 (EI_VERSION): ELF version: expect 1 (latest ELF version)
+ constexpr static uint16_t kEndianTestWord{0x0201};
+ auto hostEndianness = *(uint8_t const*)&kEndianTestWord;
+ if (dataFormat == hostEndianness && fileVersion == 1) {
+ if (klass == 0x01) {
+ header_ = Header((Header32 const*)image);
+ makeSection_ = makeSection32;
+ makeSymbol_ = makeSymbol32;
+ secSize_ = sizeof(Section32);
+ symSize_ = sizeof(Symbol32);
+ } else if (klass == 0x02) {
+ header_ = Header((Header64 const*)image);
+ makeSection_ = makeSection64;
+ makeSymbol_ = makeSymbol64;
+ secSize_ = sizeof(Section64);
+ symSize_ = sizeof(Symbol64);
+ }
+ }
+ }
+ if (*this) {
+ nametab_ = section(header_.shstrndx_);
+ eachSection([&](auto& sec) mutable -> bool {
+ if (sec.type_ == Section::kSymTab && sec.name() == ".symtab") {
+ symtab_ = sec;
+ } else if (sec.type_ == Section::kStrTab && sec.name() == ".strtab") {
+ strtab_ = sec;
+ }
+ return !symtab_ || !strtab_;
+ });
+ }
+ if (symtab_) {
+ symCount_ = symtab_.size_ / symSize_;
+ }
+}
+
+Section ELF::section(size_t index) {
+ auto* addr = header_.ptr_ + header_.shoff_ + (index * secSize_);
+ return makeSection_(this, addr);
+}
+
+Symbol ELF::symbol(size_t index) {
+ auto* addr = symtab_.data() + (index * symSize_);
+ return makeSymbol_(this, addr);
+}
+
+void ELF::eachSection(CB<Section> cb) {
+ for (size_t i = 0; i < header_.shnum_ && cb(section(i)); i++)
+ ;
+}
+
+void ELF::eachSymbol(CB<Symbol> cb) {
+ for (size_t i = 0; i < symCount_ && cb(symbol(i)); i++)
+ ;
+}
+
+Symbol ELF::getSym(uintptr_t addr) {
+ Symbol ret{};
+ eachSymbol([&](auto& sym) -> bool {
+ if (sym.value_ <= addr && sym.value_ > ret.value_) {
+ ret = sym;
+ }
+ return true;
+ });
+ return ret;
+}
+
+} // namespace __stacktrace::elf
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // defined(__linux__)
diff --git a/libcxx/src/stacktrace/linux/elf.h b/libcxx/src/stacktrace/linux/elf.h
new file mode 100644
index 0000000000000..ebf47ed964b97
--- /dev/null
+++ b/libcxx/src/stacktrace/linux/elf.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_LINUX_ELF
+#define _LIBCPP_STACKTRACE_LINUX_ELF
+
+#include <__stacktrace/base.h>
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <functional>
+#include <string_view>
+#include <unistd.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace::elf {
+
+// Includes ELF constants and structs copied from <elf.h>, with a few changes.
+
+struct Header32 final {
+ uint8_t ident[16]; /* Magic number and other info */
+ uint16_t type; /* Object file type */
+ uint16_t machine; /* Architecture */
+ uint32_t version; /* Object file version */
+ uint32_t entry; /* Entry point virtual address */
+ uint32_t phoff; /* Program header table file offset */
+ uint32_t shoff; /* Section header table file offset */
+ uint32_t flags; /* Processor-specific flags */
+ uint16_t ehsize; /* ELF header size in bytes */
+ uint16_t phentsize; /* Program header table entry size */
+ uint16_t phnum; /* Program header table entry count */
+ uint16_t shentsize; /* Section header table entry size */
+ uint16_t shnum; /* Section header table entry count */
+ uint16_t shstrndx; /* Section header string table index */
+};
+
+struct Section32 final {
+ uint32_t name; /* Section name (string tbl index) */
+ uint32_t type; /* Section type */
+ uint32_t flags; /* Section flags */
+ uint32_t addr; /* Section virtual addr at execution */
+ uint32_t offset; /* Section file offset */
+ uint32_t size; /* Section size in bytes */
+ uint32_t link; /* Link to another section */
+ uint32_t info; /* Additional section information */
+ uint32_t addralign; /* Section alignment */
+ uint32_t entsize; /* Entry size if section holds table */
+};
+
+struct Symbol32 final {
+ uint32_t name; /* Symbol name (string tbl index) */
+ uint32_t value; /* Symbol value */
+ uint32_t size; /* Symbol size */
+ uint8_t info; /* Symbol type and binding */
+ uint8_t other; /* Symbol visibility */
+ uint16_t shndx; /* Section index */
+};
+
+struct Header64 final {
+ uint8_t ident[16]; /* Magic number and other info */
+ uint16_t type; /* Object file type */
+ uint16_t machine; /* Architecture */
+ uint32_t version; /* Object file version */
+ uint64_t entry; /* Entry point virtual address */
+ uint64_t phoff; /* Program header table file offset */
+ uint64_t shoff; /* Section header table file offset */
+ uint32_t flags; /* Processor-specific flags */
+ uint16_t ehsize; /* ELF header size in bytes */
+ uint16_t phentsize; /* Program header table entry size */
+ uint16_t phnum; /* Program header table entry count */
+ uint16_t shentsize; /* Section header table entry size */
+ uint16_t shnum; /* Section header table entry count */
+ uint16_t shstrndx; /* Section header string table index */
+};
+
+struct Section64 final {
+ uint32_t name; /* Section name (string tbl index) */
+ uint32_t type; /* Section type */
+ uint64_t flags; /* Section flags */
+ uint64_t addr; /* Section virtual addr at execution */
+ uint64_t offset; /* Section file offset */
+ uint64_t size; /* Section size in bytes */
+ uint32_t link; /* Link to another section */
+ uint32_t info; /* Additional section information */
+ uint64_t addralign; /* Section alignment */
+ uint64_t entsize; /* Entry size if section holds table */
+};
+
+struct Symbol64 final {
+ uint32_t name; /* Symbol name (string tbl index) */
+ uint8_t info; /* Symbol type and binding */
+ uint8_t other; /* Symbol visibility */
+ uint16_t shndx; /* Section index */
+ uint64_t value; /* Symbol value */
+ uint64_t size; /* Symbol size */
+};
+
+/** Represents an ELF header. Supports the minimum needed to navigate an ELF file's sections and get at the symbol and
+ * string tables. */
+struct Header final {
+ std::byte const* ptr_{};
+ uintptr_t shoff_{};
+ size_t shnum_{};
+ size_t shstrndx_{};
+
+ Header() = default;
+ Header(Header const&) = default;
+ Header& operator=(Header const& rhs) { return *new (this) Header(rhs); }
+
+ operator bool() { return ptr_; }
+
+ template <class H>
+ explicit Header(H* h)
+ : ptr_((std::byte const*)h),
+ shoff_(uintptr_t(h->shoff)),
+ shnum_(size_t(h->shnum)),
+ shstrndx_(size_t(h->shstrndx)) {}
+};
+
+struct ELF;
+struct StringTable;
+
+struct Section final {
+ constexpr static uint32_t kSymTab = 2; // symbol table
+ constexpr static uint32_t kStrTab = 3; // name table for symbols or sections
+
+ ELF* elf_{};
+ std::byte const* ptr_{};
+ uintptr_t nameIndex_{};
+ uint32_t type_{};
+ uintptr_t offset_{};
+ size_t size_{};
+
+ Section() = default;
+
+ template <class S>
+ Section(ELF* elf, S* sec)
+ : elf_(elf),
+ ptr_((std::byte const*)sec),
+ nameIndex_(sec->name),
+ type_(sec->type),
+ offset_(sec->offset),
+ size_(sec->size) {}
+
+ operator bool() const { return ptr_; }
+
+ template <class T = std::byte>
+ T const* data() const {
+ return (T const*)(elfBase() + offset_);
+ }
+
+ std::byte const* elfBase() const;
+ std::string_view name() const;
+};
+
+struct Symbol final {
+ constexpr static uint8_t kFunc = 0x02; // STT_FUNC (code object)
+
+ ELF* elf_{};
+ std::byte const* ptr_{};
+ uintptr_t nameIndex_{};
+ uint32_t type_{};
+ uintptr_t value_{};
+
+ Symbol() = default;
+ Symbol(Symbol const&) = default;
+ Symbol& operator=(Symbol const& rhs) { return *new (this) Symbol(rhs); }
+
+ operator bool() { return ptr_; }
+
+ bool isCode() const { return type_ == kFunc; }
+
+ template <class S>
+ Symbol(ELF* elf, S* sym)
+ : elf_(elf), ptr_((std::byte const*)sym), nameIndex_(sym->name), type_(0x0f & sym->info), value_(sym->value) {}
+
+ std::byte const* elfBase() const;
+ std::string_view name() const;
+};
+
+/** Represents one of the ELF's `strtab`s. This is a block of string data, with strings appended one after another, and
+ * NUL-terminated. Strings are indexed according to their starting offset. At offset 0 is typically an empty string.
+ */
+struct StringTable {
+ std::string_view data_{};
+
+ StringTable() = default;
+
+ /* implicit */ StringTable(Section const& sec) : data_(sec.data<char>(), sec.size_) {}
+
+ operator bool() { return data_.size(); }
+
+ std::string_view at(size_t index) {
+ auto* ret = data_.data() + index;
+ return {ret, strlen(ret)};
+ }
+};
+
+/** Encapsulates an ELF image specified by byte-address (e.g. from an mmapped file or a program image or shared object
+ * in memory). If given a supported ELF image, this will test true with `operator bool` to indicate it is supported and
+ * was able to parse some basic information from the header. */
+struct ELF {
+ Header header_{};
+ Section (*makeSection_)(ELF*, std::byte const*){};
+ Symbol (*makeSymbol_)(ELF*, std::byte const*){};
+ size_t secSize_{};
+ size_t symSize_{};
+ StringTable nametab_{};
+ Section symtab_{};
+ StringTable strtab_{};
+ size_t symCount_{};
+
+ static Section makeSection32(ELF* elf, std::byte const* ptr) { return Section(elf, (Section32 const*)ptr); }
+ static Section makeSection64(ELF* elf, std::byte const* ptr) { return Section(elf, (Section64 const*)ptr); }
+ static Symbol makeSymbol32(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol32 const*)ptr); }
+ static Symbol makeSymbol64(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol64 const*)ptr); }
+
+ operator bool() { return header_; }
+
+ explicit ELF(std::byte const* image);
+
+ Section section(size_t index);
+ Symbol symbol(size_t index);
+
+ template <class T>
+ using CB = std::function<bool(T const&)>;
+
+ void eachSection(CB<Section> cb);
+ void eachSymbol(CB<Symbol> cb);
+
+ Symbol getSym(uintptr_t addr);
+};
+
+inline std::byte const* Section::elfBase() const { return elf_->header_.ptr_; }
+inline std::byte const* Symbol::elfBase() const { return elf_->header_.ptr_; }
+
+inline std::string_view Section::name() const { return elf_->nametab_.at(nameIndex_); }
+inline std::string_view Symbol::name() const { return elf_->strtab_.at(nameIndex_); }
+
+} // namespace __stacktrace::elf
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX_ELF
diff --git a/libcxx/src/stacktrace/linux/images.cpp b/libcxx/src/stacktrace/linux/images.cpp
new file mode 100644
index 0000000000000..1ff3dc80253e4
--- /dev/null
+++ b/libcxx/src/stacktrace/linux/images.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache 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(__linux__)
+
+# include <__stacktrace/base.h>
+
+# include "stacktrace/linux/images.h"
+# include "stacktrace/utils/image.h"
+
+# include <algorithm>
+# include <cassert>
+# include <cstddef>
+# include <cstdlib>
+# include <link.h>
+# include <unistd.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+images images::instance;
+
+int images::add(dl_phdr_info& info) {
+ assert(count_ < image::kMaxImages);
+ auto isFirst = (count_ == 0);
+ auto& image = images_.at(count_++);
+ image.loaded_at_ = info.dlpi_addr;
+ image.slide_ = info.dlpi_addr;
+ image.name_ = info.dlpi_name;
+ image.is_main_prog_ = isFirst; // first one is the main ELF
+ if (image.name_.empty() && isFirst) {
+ static char buffer[PATH_MAX + 1];
+ uint32_t length = sizeof(buffer);
+ if (readlink("/proc/self/exe", buffer, length) > 0) {
+ image.name_ = buffer;
+ }
+ }
+ return count_ == image::kMaxImages; // return nonzero if we're at the limit
+}
+
+int images::callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
+
+images::images() {
+ dl_iterate_phdr(images::callback, this);
+ images_[count_++] = {0uz, 0}; // sentinel at low end
+ images_[count_++] = {~0uz, 0}; // sentinel at high end
+ std::sort(images_.begin(), images_.begin() + count_ - 1);
+}
+
+image& images::operator[](size_t index) {
+ assert(index < count_);
+ return images_.at(index);
+}
+
+image* images::mainProg() {
+ for (auto& image : images_) {
+ if (image.is_main_prog_) {
+ return ℑ
+ }
+ }
+ return nullptr;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // __linux__
diff --git a/libcxx/src/stacktrace/linux/images.h b/libcxx/src/stacktrace/linux/images.h
new file mode 100644
index 0000000000000..bd7ac8ff77bf0
--- /dev/null
+++ b/libcxx/src/stacktrace/linux/images.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_LINUX_IMAGES
+#define _LIBCPP_STACKTRACE_LINUX_IMAGES
+
+#if defined(__linux__)
+
+# include <__stacktrace/base.h>
+
+# include <array>
+# include <cassert>
+# include <cstddef>
+# include <cstdlib>
+# include <link.h>
+# include <unistd.h>
+
+# include "stacktrace/utils/image.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct images {
+ // How many images this contains, including the left/right sentinels.
+ unsigned count_{0};
+ std::array<image, image::kMaxImages + 2> images_{};
+
+ images();
+
+ int add(dl_phdr_info& info);
+ static int callback(dl_phdr_info* info, size_t, void* self);
+
+ image& operator[](size_t index);
+
+ image* mainProg();
+
+ static images instance;
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // __linux__
+
+#endif // _LIBCPP_STACKTRACE_LINUX_IMAGES
diff --git a/libcxx/src/stacktrace/linux/impl.cpp b/libcxx/src/stacktrace/linux/impl.cpp
index 050eb1f0088e7..3f18d015aa93f 100644
--- a/libcxx/src/stacktrace/linux/impl.cpp
+++ b/libcxx/src/stacktrace/linux/impl.cpp
@@ -15,6 +15,8 @@
# include <unistd.h>
# include "stacktrace/config.h"
+# include "stacktrace/linux/elf.h"
+# include "stacktrace/linux/images.h"
# include "stacktrace/linux/impl.h"
# include "stacktrace/utils/fd.h"
# include "stacktrace/utils/image.h"
@@ -23,7 +25,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
void linux::ident_modules() {
- auto& images = images::get();
+ auto& images = images::instance;
// Aside from the left/right sentinels in the array (hence the 2),
// are there any other real images?
@@ -100,7 +102,7 @@ void linux::symbolize() {
// Symbols might be missing, because both (1) Linux's `dladdr` won't try to resolve non-exported symbols,
// which can be the case for the main program executable; and (2) debug info was not preserved.
// As a last resort, this function (see `linux-elf.cpp`) can still access symbol table directly.
- image* mainELF = images::get().mainProg();
+ image* mainELF = images::instance.mainProg();
if (mainELF && !mainELF->name_.empty()) {
resolve_main_elf_syms(mainELF->name_);
}
diff --git a/libcxx/src/stacktrace/linux/impl.h b/libcxx/src/stacktrace/linux/impl.h
index 1f6ca6ef2b413..0a91bf88fcf9b 100644
--- a/libcxx/src/stacktrace/linux/impl.h
+++ b/libcxx/src/stacktrace/linux/impl.h
@@ -34,369 +34,4 @@ struct linux {
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#include <__config>
-#include <__config_site>
-
-#if defined(__linux__)
-
-# include <algorithm>
-# include <array>
-# include <cassert>
-# include <cstddef>
-# include <cstdlib>
-# include <functional>
-# include <link.h>
-# include <string_view>
-# include <unistd.h>
-
-# include "stacktrace/utils/image.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct images {
- // How many images this contains, including the left/right sentinels.
- unsigned count_{0};
- std::array<image, image::kMaxImages + 2> images_{};
-
- int add(dl_phdr_info& info) {
- assert(count_ < image::kMaxImages);
- auto isFirst = (count_ == 0);
- auto& image = images_.at(count_++);
- image.loaded_at_ = info.dlpi_addr;
- image.slide_ = info.dlpi_addr;
- image.name_ = info.dlpi_name;
- image.is_main_prog_ = isFirst; // first one is the main ELF
- if (image.name_.empty() && isFirst) {
- static char buffer[PATH_MAX + 1];
- uint32_t length = sizeof(buffer);
- if (readlink("/proc/self/exe", buffer, length) > 0) {
- image.name_ = buffer;
- }
- }
- return count_ == image::kMaxImages; // return nonzero if we're at the limit
- }
-
- static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
-
- images() {
- dl_iterate_phdr(images::callback, this);
- images_[count_++] = {0uz, 0}; // sentinel at low end
- images_[count_++] = {~0uz, 0}; // sentinel at high end
- std::sort(images_.begin(), images_.begin() + count_ - 1);
- }
-
- image& operator[](size_t index) {
- assert(index < count_);
- return images_.at(index);
- }
-
- image* mainProg() {
- for (auto& image : images_) {
- if (image.is_main_prog_) {
- return ℑ
- }
- }
- return nullptr;
- }
-
- static images& get() {
- static images images;
- return images;
- }
-};
-
-// Includes ELF constants and structs copied from <elf.h>, with a few changes.
-
-namespace elf {
-
-struct Header32 final {
- uint8_t ident[16]; /* Magic number and other info */
- uint16_t type; /* Object file type */
- uint16_t machine; /* Architecture */
- uint32_t version; /* Object file version */
- uint32_t entry; /* Entry point virtual address */
- uint32_t phoff; /* Program header table file offset */
- uint32_t shoff; /* Section header table file offset */
- uint32_t flags; /* Processor-specific flags */
- uint16_t ehsize; /* ELF header size in bytes */
- uint16_t phentsize; /* Program header table entry size */
- uint16_t phnum; /* Program header table entry count */
- uint16_t shentsize; /* Section header table entry size */
- uint16_t shnum; /* Section header table entry count */
- uint16_t shstrndx; /* Section header string table index */
-};
-
-struct Section32 final {
- uint32_t name; /* Section name (string tbl index) */
- uint32_t type; /* Section type */
- uint32_t flags; /* Section flags */
- uint32_t addr; /* Section virtual addr at execution */
- uint32_t offset; /* Section file offset */
- uint32_t size; /* Section size in bytes */
- uint32_t link; /* Link to another section */
- uint32_t info; /* Additional section information */
- uint32_t addralign; /* Section alignment */
- uint32_t entsize; /* Entry size if section holds table */
-};
-
-struct Symbol32 final {
- uint32_t name; /* Symbol name (string tbl index) */
- uint32_t value; /* Symbol value */
- uint32_t size; /* Symbol size */
- uint8_t info; /* Symbol type and binding */
- uint8_t other; /* Symbol visibility */
- uint16_t shndx; /* Section index */
-};
-
-struct Header64 final {
- uint8_t ident[16]; /* Magic number and other info */
- uint16_t type; /* Object file type */
- uint16_t machine; /* Architecture */
- uint32_t version; /* Object file version */
- uint64_t entry; /* Entry point virtual address */
- uint64_t phoff; /* Program header table file offset */
- uint64_t shoff; /* Section header table file offset */
- uint32_t flags; /* Processor-specific flags */
- uint16_t ehsize; /* ELF header size in bytes */
- uint16_t phentsize; /* Program header table entry size */
- uint16_t phnum; /* Program header table entry count */
- uint16_t shentsize; /* Section header table entry size */
- uint16_t shnum; /* Section header table entry count */
- uint16_t shstrndx; /* Section header string table index */
-};
-
-struct Section64 final {
- uint32_t name; /* Section name (string tbl index) */
- uint32_t type; /* Section type */
- uint64_t flags; /* Section flags */
- uint64_t addr; /* Section virtual addr at execution */
- uint64_t offset; /* Section file offset */
- uint64_t size; /* Section size in bytes */
- uint32_t link; /* Link to another section */
- uint32_t info; /* Additional section information */
- uint64_t addralign; /* Section alignment */
- uint64_t entsize; /* Entry size if section holds table */
-};
-
-struct Symbol64 final {
- uint32_t name; /* Symbol name (string tbl index) */
- uint8_t info; /* Symbol type and binding */
- uint8_t other; /* Symbol visibility */
- uint16_t shndx; /* Section index */
- uint64_t value; /* Symbol value */
- uint64_t size; /* Symbol size */
-};
-
-/** Represents an ELF header. Supports the minimum needed to navigate an ELF file's sections and get at the symbol and
- * string tables. */
-struct Header final {
- std::byte const* ptr_{};
- uintptr_t shoff_{};
- size_t shnum_{};
- size_t shstrndx_{};
-
- Header() = default;
- Header(Header const&) = default;
- Header& operator=(Header const& rhs) { return *new (this) Header(rhs); }
-
- operator bool() { return ptr_; }
-
- template <class H>
- explicit Header(H* h)
- : ptr_((std::byte const*)h),
- shoff_(uintptr_t(h->shoff)),
- shnum_(size_t(h->shnum)),
- shstrndx_(size_t(h->shstrndx)) {}
-};
-
-struct ELF;
-struct StringTable;
-
-struct Section final {
- constexpr static uint32_t kSymTab = 2; // symbol table
- constexpr static uint32_t kStrTab = 3; // name table for symbols or sections
-
- ELF* elf_{};
- std::byte const* ptr_{};
- uintptr_t nameIndex_{};
- uint32_t type_{};
- uintptr_t offset_{};
- size_t size_{};
-
- Section() = default;
-
- template <class S>
- Section(ELF* elf, S* sec)
- : elf_(elf),
- ptr_((std::byte const*)sec),
- nameIndex_(sec->name),
- type_(sec->type),
- offset_(sec->offset),
- size_(sec->size) {}
-
- operator bool() const { return ptr_; }
-
- template <class T = std::byte>
- T const* data() const {
- return (T const*)(elfBase() + offset_);
- }
-
- std::byte const* elfBase() const;
- std::string_view name() const;
-};
-
-struct Symbol final {
- constexpr static uint8_t kFunc = 0x02; // STT_FUNC (code object)
-
- ELF* elf_{};
- std::byte const* ptr_{};
- uintptr_t nameIndex_{};
- uint32_t type_{};
- uintptr_t value_{};
-
- Symbol() = default;
- Symbol(Symbol const&) = default;
- Symbol& operator=(Symbol const& rhs) { return *new (this) Symbol(rhs); }
-
- operator bool() { return ptr_; }
-
- bool isCode() const { return type_ == kFunc; }
-
- template <class S>
- Symbol(ELF* elf, S* sym)
- : elf_(elf), ptr_((std::byte const*)sym), nameIndex_(sym->name), type_(0x0f & sym->info), value_(sym->value) {}
-
- std::byte const* elfBase() const;
- std::string_view name() const;
-};
-
-/** Represents one of the ELF's `strtab`s. This is a block of string data, with strings appended one after another, and
- * NUL-terminated. Strings are indexed according to their starting offset. At offset 0 is typically an empty string.
- */
-struct StringTable {
- std::string_view data_{};
-
- StringTable() = default;
-
- /* implicit */ StringTable(Section const& sec) : data_(sec.data<char>(), sec.size_) {}
-
- operator bool() { return data_.size(); }
-
- std::string_view at(size_t index) {
- auto* ret = data_.data() + index;
- return {ret, strlen(ret)};
- }
-};
-
-/** Encapsulates an ELF image specified by byte-address (e.g. from an mmapped file or a program image or shared object
- * in memory). If given a supported ELF image, this will test true with `operator bool` to indicate it is supported and
- * was able to parse some basic information from the header. */
-struct ELF {
- Header header_{};
- Section (*makeSection_)(ELF*, std::byte const*){};
- Symbol (*makeSymbol_)(ELF*, std::byte const*){};
- size_t secSize_{};
- size_t symSize_{};
- StringTable nametab_{};
- Section symtab_{};
- StringTable strtab_{};
- size_t symCount_{};
-
- static Section makeSection32(ELF* elf, std::byte const* ptr) { return Section(elf, (Section32 const*)ptr); }
- static Section makeSection64(ELF* elf, std::byte const* ptr) { return Section(elf, (Section64 const*)ptr); }
- static Symbol makeSymbol32(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol32 const*)ptr); }
- static Symbol makeSymbol64(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol64 const*)ptr); }
-
- operator bool() { return header_; }
-
- explicit ELF(std::byte const* image) {
- auto* p = (uint8_t const*)image;
- // Bytes 0..3: magic bytes: 0x7F, 'E', 'L', 'F'
- if (*p++ == 0x7f && *p++ == 0x45 && *p++ == 0x4c && *p++ == 0x46) {
- auto klass = *p++; // Byte 4 (EI_CLASS): ELF class, 32- or 64-bit (0x01 or 0x02)
- auto dataFormat = *p++; // Byte 5 (EI_DATA): (0x01) little- or (0x02) big-endian
- auto fileVersion = *p++; // Byte 6 (EI_VERSION): ELF version: expect 1 (latest ELF version)
- constexpr static uint16_t kEndianTestWord{0x0201};
- auto hostEndianness = *(uint8_t const*)&kEndianTestWord;
- if (dataFormat == hostEndianness && fileVersion == 1) {
- if (klass == 0x01) {
- header_ = Header((Header32 const*)image);
- makeSection_ = makeSection32;
- makeSymbol_ = makeSymbol32;
- secSize_ = sizeof(Section32);
- symSize_ = sizeof(Symbol32);
- } else if (klass == 0x02) {
- header_ = Header((Header64 const*)image);
- makeSection_ = makeSection64;
- makeSymbol_ = makeSymbol64;
- secSize_ = sizeof(Section64);
- symSize_ = sizeof(Symbol64);
- }
- }
- }
- if (*this) {
- nametab_ = section(header_.shstrndx_);
- eachSection([&](auto& sec) mutable -> bool {
- if (sec.type_ == Section::kSymTab && sec.name() == ".symtab") {
- symtab_ = sec;
- } else if (sec.type_ == Section::kStrTab && sec.name() == ".strtab") {
- strtab_ = sec;
- }
- return !symtab_ || !strtab_;
- });
- }
- if (symtab_) {
- symCount_ = symtab_.size_ / symSize_;
- }
- }
-
- Section section(size_t index) {
- auto* addr = header_.ptr_ + header_.shoff_ + (index * secSize_);
- return makeSection_(this, addr);
- }
-
- Symbol symbol(size_t index) {
- auto* addr = symtab_.data() + (index * symSize_);
- return makeSymbol_(this, addr);
- }
-
- template <class T>
- using CB = std::function<bool(T const&)>;
-
- void eachSection(CB<Section> cb) {
- for (size_t i = 0; i < header_.shnum_ && cb(section(i)); i++)
- ;
- }
-
- void eachSymbol(CB<Symbol> cb) {
- for (size_t i = 0; i < symCount_ && cb(symbol(i)); i++)
- ;
- }
-
- Symbol getSym(uintptr_t addr) {
- Symbol ret{};
- eachSymbol([&](auto& sym) -> bool {
- if (sym.value_ <= addr && sym.value_ > ret.value_) {
- ret = sym;
- }
- return true;
- });
- return ret;
- }
-};
-
-inline std::byte const* Section::elfBase() const { return elf_->header_.ptr_; }
-inline std::byte const* Symbol::elfBase() const { return elf_->header_.ptr_; }
-
-inline std::string_view Section::name() const { return elf_->nametab_.at(nameIndex_); }
-inline std::string_view Symbol::name() const { return elf_->strtab_.at(nameIndex_); }
-
-} // namespace elf
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // __linux__
-
#endif // _LIBCPP_STACKTRACE_LINUX_IMPL
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 8d8ce5665565b..461087467a270 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,3 +105,4 @@
#endif // TEST_STD_VER > 23
// clang-format on
+
>From c5b741ee5e22ae33989d00142c1ba6daaf578d5e Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 9 Jul 2025 12:21:20 -0400
Subject: [PATCH 05/14] Fix ABI list for linux
---
...x-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 11 -----------
1 file changed, 11 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 d954b1926115e..8e8ae5bdd27da 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'}
@@ -226,10 +225,6 @@
{'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__116stacktrace_entry11descriptionEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__116stacktrace_entry11source_fileEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__116stacktrace_entry11source_lineEv', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNKSt3__116stacktrace_entry13native_handleEv', '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'}
@@ -1927,9 +1922,6 @@
{'is_defined': True, 'name': '_ZTTNSt3__19strstreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt12experimental15fundamentals_v112bad_any_castE', 'size': 40, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt12experimental19bad_optional_accessE', 'size': 40, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf6SymbolEEEE', 'size': 88, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 88, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFbRKNS_12__stacktrace4toolEEEE', 'size': 88, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110istrstreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIcLb0EEE', 'size': 112, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIcLb1EEE', 'size': 112, 'type': 'OBJECT'}
@@ -1940,7 +1932,6 @@
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace10fd_istreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace15llvm_symbolizerE', 'size': 48, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace4atosE', 'size': 48, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace4toolE', 'size': 48, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace6failedE', 'size': 40, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace9addr2lineE', 'size': 48, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112bad_weak_ptrE', 'size': 40, 'type': 'OBJECT'}
@@ -2005,8 +1996,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 b97367ff75cda7c8c4ccdd8ad08beb30d6e46c47 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 9 Jul 2025 13:20:08 -0400
Subject: [PATCH 06/14] Exclude stacktrace headers if C++ < 23
---
libcxx/include/__stacktrace/base.h | 47 ++++++++++---------
libcxx/include/__stacktrace/basic.h | 39 ++++++++-------
libcxx/include/__stacktrace/entry.h | 25 +++++-----
libcxx/include/__stacktrace/hash.h | 21 +++++----
libcxx/include/__stacktrace/nonmem.h | 23 +++++----
libcxx/include/__stacktrace/to_string.h | 15 +++---
.../stacktrace.version.compile.pass.cpp | 1 -
7 files changed, 94 insertions(+), 77 deletions(-)
diff --git a/libcxx/include/__stacktrace/base.h b/libcxx/include/__stacktrace/base.h
index 854071001a8b5..92070d35bf6e0 100644
--- a/libcxx/include/__stacktrace/base.h
+++ b/libcxx/include/__stacktrace/base.h
@@ -7,31 +7,33 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_STACKTRACE_BUILDER
-#define _LIBCPP_STACKTRACE_BUILDER
+#ifndef _LIBCPP_STACKTRACE_BASE
+#define _LIBCPP_STACKTRACE_BASE
#include <__config>
-#include <__cstddef/byte.h>
-#include <__cstddef/size_t.h>
-#include <__functional/function.h>
-#include <__fwd/format.h>
-#include <__fwd/ostream.h>
-#include <__memory/allocator.h>
-#include <__memory/allocator_traits.h>
-#include <__new/allocate.h>
-#include <__vector/vector.h>
-#include <cstddef>
-#include <cstdint>
-#include <list>
-#include <optional>
-#include <string>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
+#if _LIBCPP_STD_VER >= 23
+
+# include <__cstddef/byte.h>
+# include <__cstddef/size_t.h>
+# include <__functional/function.h>
+# include <__fwd/format.h>
+# include <__fwd/ostream.h>
+# include <__memory/allocator.h>
+# include <__memory/allocator_traits.h>
+# include <__new/allocate.h>
+# include <__vector/vector.h>
+# include <cstddef>
+# include <cstdint>
+# include <list>
+# include <optional>
+# include <string>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
+# include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -130,4 +132,5 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
-#endif // _LIBCPP_STACKTRACE_BUILDER
+#endif // _LIBCPP_STD_VER >= 23
+#endif // _LIBCPP_STACKTRACE_BASE
diff --git a/libcxx/include/__stacktrace/basic.h b/libcxx/include/__stacktrace/basic.h
index ca958e55f0614..6f56d653aa00d 100644
--- a/libcxx/include/__stacktrace/basic.h
+++ b/libcxx/include/__stacktrace/basic.h
@@ -11,26 +11,28 @@
#define _LIBCPP_BASIC_STACKTRACE
#include <__config>
-#include <__functional/hash.h>
-#include <__fwd/format.h>
-#include <__iterator/iterator.h>
-#include <__iterator/reverse_iterator.h>
-#include <__memory/allocator_traits.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <__type_traits/is_nothrow_constructible.h>
-#include <__vector/vector.h>
-#include <utility>
-
-#include <__stacktrace/base.h>
-#include <__stacktrace/entry.h>
-#include <__stacktrace/to_string.h>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
+#if _LIBCPP_STD_VER >= 23
+
+# include <__functional/hash.h>
+# include <__fwd/format.h>
+# include <__iterator/iterator.h>
+# include <__iterator/reverse_iterator.h>
+# include <__memory/allocator_traits.h>
+# include <__memory_resource/polymorphic_allocator.h>
+# include <__type_traits/is_nothrow_constructible.h>
+# include <__vector/vector.h>
+# include <utility>
+
+# include <__stacktrace/base.h>
+# include <__stacktrace/entry.h>
+# include <__stacktrace/to_string.h>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
+# include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -244,4 +246,5 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
+#endif // _LIBCPP_STD_VER >= 23
#endif // _LIBCPP_BASIC_STACKTRACE
diff --git a/libcxx/include/__stacktrace/entry.h b/libcxx/include/__stacktrace/entry.h
index 6290ebea9bc85..fa076eca76de5 100644
--- a/libcxx/include/__stacktrace/entry.h
+++ b/libcxx/include/__stacktrace/entry.h
@@ -11,21 +11,23 @@
#define _LIBCPP_STACKTRACE_ENTRY
#include <__config>
-#include <__fwd/format.h>
-#include <__fwd/ostream.h>
-#include <cstddef>
-#include <cstdint>
-#include <optional>
-#include <string>
+#if _LIBCPP_STD_VER >= 23
-#include <__stacktrace/base.h>
+# include <__fwd/format.h>
+# include <__fwd/ostream.h>
+# include <cstddef>
+# include <cstdint>
+# include <optional>
+# include <string>
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
+# include <__stacktrace/base.h>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
+# include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -110,4 +112,5 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
+#endif // _LIBCPP_STD_VER >= 23
#endif // _LIBCPP_STACKTRACE_ENTRY
diff --git a/libcxx/include/__stacktrace/hash.h b/libcxx/include/__stacktrace/hash.h
index a507c0b68db3f..122ad14144bc4 100644
--- a/libcxx/include/__stacktrace/hash.h
+++ b/libcxx/include/__stacktrace/hash.h
@@ -11,19 +11,21 @@
#define _LIBCPP_BASIC_STACKTRACE_HASH
#include <__config>
-#include <__functional/hash.h>
-#include <cstddef>
-#include <cstdint>
+#if _LIBCPP_STD_VER >= 23
-#include <__stacktrace/base.h>
-#include <__stacktrace/to_string.h>
+# include <__functional/hash.h>
+# include <cstddef>
+# include <cstdint>
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
+# include <__stacktrace/base.h>
+# include <__stacktrace/to_string.h>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
+# include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -45,4 +47,5 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
+#endif // _LIBCPP_STD_VER >= 23
#endif // _LIBCPP_BASIC_STACKTRACE_HASH
diff --git a/libcxx/include/__stacktrace/nonmem.h b/libcxx/include/__stacktrace/nonmem.h
index 570237e08ecd5..81f285b387531 100644
--- a/libcxx/include/__stacktrace/nonmem.h
+++ b/libcxx/include/__stacktrace/nonmem.h
@@ -11,20 +11,22 @@
#define _LIBCPP_BASIC_STACKTRACE_NONMEM
#include <__config>
-#include <__memory/allocator_traits.h>
-#include <__utility/swap.h>
-#include <__vector/vector.h>
-#include <string>
+#if _LIBCPP_STD_VER >= 23
-#include <__stacktrace/base.h>
-#include <__stacktrace/to_string.h>
+# include <__memory/allocator_traits.h>
+# include <__utility/swap.h>
+# include <__vector/vector.h>
+# include <string>
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
+# include <__stacktrace/base.h>
+# include <__stacktrace/to_string.h>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
+# include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -52,4 +54,5 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
+#endif // _LIBCPP_STD_VER >= 23
#endif // _LIBCPP_BASIC_STACKTRACE_NONMEM
diff --git a/libcxx/include/__stacktrace/to_string.h b/libcxx/include/__stacktrace/to_string.h
index 920c41133d5df..f6fcddf24c5fc 100644
--- a/libcxx/include/__stacktrace/to_string.h
+++ b/libcxx/include/__stacktrace/to_string.h
@@ -11,15 +11,17 @@
#define _LIBCPP_STACKTRACE_TO_STRING
#include <__config>
-#include <__fwd/ostream.h>
-#include <string>
+#if _LIBCPP_STD_VER >= 23
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
+# include <__fwd/ostream.h>
+# include <string>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
+# include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -50,4 +52,5 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
+#endif // _LIBCPP_STD_VER >= 23
#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 461087467a270..8d8ce5665565b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,4 +105,3 @@
#endif // TEST_STD_VER > 23
// clang-format on
-
>From 0781379b7bbde61b9ba14ee433cdab4e3d8349fd Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 9 Jul 2025 22:49:47 -0400
Subject: [PATCH 07/14] Use proper header format/layout
---
libcxx/include/__stacktrace/base.h | 19 ++++++++------
libcxx/include/__stacktrace/basic.h | 24 +++++++++---------
libcxx/include/__stacktrace/entry.h | 18 ++++++++------
libcxx/include/__stacktrace/hash.h | 24 +++++++++---------
libcxx/include/__stacktrace/nonmem.h | 24 +++++++++---------
libcxx/include/__stacktrace/to_string.h | 19 ++++++++------
libcxx/include/stacktrace | 33 ++++++++++++-------------
7 files changed, 87 insertions(+), 74 deletions(-)
diff --git a/libcxx/include/__stacktrace/base.h b/libcxx/include/__stacktrace/base.h
index 92070d35bf6e0..4583b2760f246 100644
--- a/libcxx/include/__stacktrace/base.h
+++ b/libcxx/include/__stacktrace/base.h
@@ -11,6 +11,14 @@
#define _LIBCPP_STACKTRACE_BASE
#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 <__cstddef/byte.h>
@@ -28,13 +36,6 @@
# include <optional>
# include <string>
-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-# endif
-
-_LIBCPP_PUSH_MACROS
-# include <__undef_macros>
-
_LIBCPP_BEGIN_NAMESPACE_STD
class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry;
@@ -128,9 +129,11 @@ struct _LIBCPP_EXPORTED_FROM_ABI builder final {
};
} // namespace __stacktrace
+
_LIBCPP_END_NAMESPACE_STD
+#endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_POP_MACROS
-#endif // _LIBCPP_STD_VER >= 23
#endif // _LIBCPP_STACKTRACE_BASE
diff --git a/libcxx/include/__stacktrace/basic.h b/libcxx/include/__stacktrace/basic.h
index 6f56d653aa00d..5798ac9bca628 100644
--- a/libcxx/include/__stacktrace/basic.h
+++ b/libcxx/include/__stacktrace/basic.h
@@ -7,10 +7,18 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_BASIC_STACKTRACE
-#define _LIBCPP_BASIC_STACKTRACE
+#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>
+
#if _LIBCPP_STD_VER >= 23
# include <__functional/hash.h>
@@ -27,13 +35,6 @@
# include <__stacktrace/entry.h>
# include <__stacktrace/to_string.h>
-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-# endif
-
-_LIBCPP_PUSH_MACROS
-# include <__undef_macros>
-
_LIBCPP_BEGIN_NAMESPACE_STD
// (19.6.4)
@@ -244,7 +245,8 @@ using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
_LIBCPP_END_NAMESPACE_STD
+#endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_POP_MACROS
-#endif // _LIBCPP_STD_VER >= 23
-#endif // _LIBCPP_BASIC_STACKTRACE
+#endif // _LIBCPP_STACKTRACE_BASIC
diff --git a/libcxx/include/__stacktrace/entry.h b/libcxx/include/__stacktrace/entry.h
index fa076eca76de5..b9d67bcd7adbc 100644
--- a/libcxx/include/__stacktrace/entry.h
+++ b/libcxx/include/__stacktrace/entry.h
@@ -11,6 +11,14 @@
#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>
+
#if _LIBCPP_STD_VER >= 23
# include <__fwd/format.h>
@@ -22,13 +30,6 @@
# include <__stacktrace/base.h>
-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-# endif
-
-_LIBCPP_PUSH_MACROS
-# include <__undef_macros>
-
_LIBCPP_BEGIN_NAMESPACE_STD
class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry : private __stacktrace::entry_base {
@@ -110,7 +111,8 @@ _LIBCPP_HIDE_FROM_ABI inline stacktrace_entry entry_base::to_stacktrace_entry()
_LIBCPP_END_NAMESPACE_STD
+#endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_POP_MACROS
-#endif // _LIBCPP_STD_VER >= 23
#endif // _LIBCPP_STACKTRACE_ENTRY
diff --git a/libcxx/include/__stacktrace/hash.h b/libcxx/include/__stacktrace/hash.h
index 122ad14144bc4..0830300ad81b9 100644
--- a/libcxx/include/__stacktrace/hash.h
+++ b/libcxx/include/__stacktrace/hash.h
@@ -7,10 +7,18 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_BASIC_STACKTRACE_HASH
-#define _LIBCPP_BASIC_STACKTRACE_HASH
+#ifndef _LIBCPP_STACKTRACE_HASH
+#define _LIBCPP_STACKTRACE_HASH
#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 <__functional/hash.h>
@@ -20,13 +28,6 @@
# include <__stacktrace/base.h>
# include <__stacktrace/to_string.h>
-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-# endif
-
-_LIBCPP_PUSH_MACROS
-# include <__undef_macros>
-
_LIBCPP_BEGIN_NAMESPACE_STD
// (19.6.6)
@@ -45,7 +46,8 @@ struct _LIBCPP_EXPORTED_FROM_ABI hash<basic_stacktrace<_Allocator>> {
_LIBCPP_END_NAMESPACE_STD
+#endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_POP_MACROS
-#endif // _LIBCPP_STD_VER >= 23
-#endif // _LIBCPP_BASIC_STACKTRACE_HASH
+#endif // _LIBCPP_STACKTRACE_HASH
diff --git a/libcxx/include/__stacktrace/nonmem.h b/libcxx/include/__stacktrace/nonmem.h
index 81f285b387531..e6ee621abbf46 100644
--- a/libcxx/include/__stacktrace/nonmem.h
+++ b/libcxx/include/__stacktrace/nonmem.h
@@ -7,10 +7,18 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_BASIC_STACKTRACE_NONMEM
-#define _LIBCPP_BASIC_STACKTRACE_NONMEM
+#ifndef _LIBCPP_STACKTRACE_NONMEM
+#define _LIBCPP_STACKTRACE_NONMEM
#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 <__memory/allocator_traits.h>
@@ -21,13 +29,6 @@
# include <__stacktrace/base.h>
# include <__stacktrace/to_string.h>
-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-# endif
-
-_LIBCPP_PUSH_MACROS
-# include <__undef_macros>
-
_LIBCPP_BEGIN_NAMESPACE_STD
// (19.6.4.6)
@@ -52,7 +53,8 @@ _LIBCPP_EXPORTED_FROM_ABI inline ostream& operator<<(ostream& __os, const basic_
_LIBCPP_END_NAMESPACE_STD
+#endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_POP_MACROS
-#endif // _LIBCPP_STD_VER >= 23
-#endif // _LIBCPP_BASIC_STACKTRACE_NONMEM
+#endif // _LIBCPP_STACKTRACE_NONMEM
diff --git a/libcxx/include/__stacktrace/to_string.h b/libcxx/include/__stacktrace/to_string.h
index f6fcddf24c5fc..442a6c1528228 100644
--- a/libcxx/include/__stacktrace/to_string.h
+++ b/libcxx/include/__stacktrace/to_string.h
@@ -11,18 +11,19 @@
#define _LIBCPP_STACKTRACE_TO_STRING
#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 <__fwd/ostream.h>
# include <string>
-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-# endif
-
-_LIBCPP_PUSH_MACROS
-# include <__undef_macros>
-
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Allocator>
@@ -48,9 +49,11 @@ struct __to_string {
};
} // namespace __stacktrace
+
_LIBCPP_END_NAMESPACE_STD
+#endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_POP_MACROS
-#endif // _LIBCPP_STD_VER >= 23
#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace
index 1fbdb6b4fe293..f7ec8c0deeca1 100644
--- a/libcxx/include/stacktrace
+++ b/libcxx/include/stacktrace
@@ -164,28 +164,27 @@ namespace std {
*/
-#include <__config>
-
-#if _LIBCPP_STD_VER >= 23
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+# include <__cxx03/__config>
+#else
+# include <__config>
+
+# if _LIBCPP_STD_VER >= 23
+# include <__stacktrace/base.h>
+# include <__stacktrace/basic.h>
+# include <__stacktrace/entry.h>
+# include <__stacktrace/hash.h>
+# include <__stacktrace/nonmem.h>
+# include <__stacktrace/to_string.h>
+# include <compare> // per [stacktrace.syn]
+# endif
-# include <compare> // according to [stacktrace.syn]
+# include <version>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
# endif
-_LIBCPP_PUSH_MACROS
-# include <__undef_macros>
-
-# include <__stacktrace/base.h>
-# include <__stacktrace/basic.h>
-# include <__stacktrace/entry.h>
-# include <__stacktrace/hash.h>
-# include <__stacktrace/nonmem.h>
-# include <__stacktrace/to_string.h>
-
-_LIBCPP_POP_MACROS
-
-#endif // _LIBCPP_STD_VER >= 23
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
#endif // _LIBCPP_STACKTRACE
>From 7724941ad10ad13952d460073356eed1d89e729b Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 9 Jul 2025 23:25:28 -0400
Subject: [PATCH 08/14] Fix stacktrace.inc
---
libcxx/modules/std/stacktrace.inc | 3 +++
.../support.limits.general/stacktrace.version.compile.pass.cpp | 1 +
2 files changed, 4 insertions(+)
diff --git a/libcxx/modules/std/stacktrace.inc b/libcxx/modules/std/stacktrace.inc
index e7b31fd29b3a6..8b71bed583022 100644
--- a/libcxx/modules/std/stacktrace.inc
+++ b/libcxx/modules/std/stacktrace.inc
@@ -33,5 +33,8 @@ export namespace std {
// [stacktrace.basic.hash], hash support
using std::hash;
+ // [stacktrace.format], formatting support
+ using std::formatter;
+
#endif // _LIBCPP_STD_VER >= 23
} // namespace std
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
index 8d8ce5665565b..461087467a270 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp
@@ -105,3 +105,4 @@
#endif // TEST_STD_VER > 23
// clang-format on
+
>From 31ddf32d4a0c742ad2fef3ea5f22aeba7a1ebde6 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Thu, 10 Jul 2025 00:25:54 -0400
Subject: [PATCH 09/14] Test for 'noexcept' (and related fixes)
---
libcxx/include/__stacktrace/basic.h | 57 ++++-----
.../stacktrace/basic.cmp/equality.pass.cpp | 2 +
.../stacktrace/basic.cons/copy.pass.cpp | 85 +++++++++++++
.../basic.cons/copy_and_move.pass.cpp | 82 -------------
.../basic.cons/ctor_no_args.pass.cpp | 32 +++--
.../basic.cons/ctor_with_alloc.pass.cpp | 63 +++-------
.../basic.cons/current_no_args.pass.cpp | 1 +
.../basic.cons/current_skip.pass.cpp | 1 +
.../basic.cons/current_skip_depth.pass.cpp | 1 +
.../stacktrace/basic.cons/move.pass.cpp | 43 +++++++
.../stacktrace/basic.mod/swap.pass.cpp | 41 +++++--
.../stacktrace/basic.nonmem/swap.pass.cpp | 37 ++++--
.../stacktrace/entry.cmp/equality.pass.cpp | 2 +
.../entry.cmp/strong_ordering.pass.cpp | 2 +
.../entry.cons/copy_assign.pass.cpp | 9 +-
.../entry.cons/copy_construct.pass.cpp | 6 +-
.../stacktrace/entry.cons/default.pass.cpp | 11 +-
.../entry.obs/native_handle.pass.cpp | 6 +-
.../entry.obs/operator_bool.pass.cpp | 12 +-
.../std/diagnostics/stacktrace/test_allocs.h | 114 ++++++++++++++++++
20 files changed, 388 insertions(+), 219 deletions(-)
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
delete mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/test_allocs.h
diff --git a/libcxx/include/__stacktrace/basic.h b/libcxx/include/__stacktrace/basic.h
index 5798ac9bca628..524bacd305a8f 100644
--- a/libcxx/include/__stacktrace/basic.h
+++ b/libcxx/include/__stacktrace/basic.h
@@ -48,8 +48,8 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
friend struct __stacktrace::__to_string;
using _ATraits _LIBCPP_NODEBUG = allocator_traits<_Allocator>;
- constexpr static bool __kPropOnCopy = _ATraits::propagate_on_container_copy_assignment::value;
- constexpr static bool __kPropOnMove = _ATraits::propagate_on_container_move_assignment::value;
+ constexpr static bool __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 __kNoThrowDflConstruct = is_nothrow_default_constructible_v<_Allocator>;
@@ -111,55 +111,38 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
_LIBCPP_EXPORTED_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc) noexcept
: __alloc_(__alloc), __entries_(__alloc_) {}
- _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) = default;
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other)
+ : __alloc_(_ATraits::select_on_container_copy_construction(__other.__alloc_)),
+ __entries_(__other.__entries_, __alloc_) {}
- _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept = default;
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept
+ : __alloc_(std::move(__other.__alloc_)), __entries_(std::move(__other.__entries_)) {}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
: __alloc_(__alloc), __entries_(__other.__entries_, __alloc) {}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
: __alloc_(__alloc) {
- if (__kAlwaysEqual || __alloc_ == __other.__alloc_) {
- __entries_ = std::move(__other.__entries_);
- } else {
- // "moving" from a container with a different allocator; we're forced to copy items instead
- for (auto const& __entry : __other.__entries_) {
- __entries_.push_back(__entry);
- }
- }
+ __entries_ = {std::move(__other.__entries_), __alloc_};
}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other) {
- if (this == std::addressof(__other)) {
- return *this;
- }
- if (__kPropOnCopy) {
- __alloc_ = __other.__alloc_;
+ if (this != std::addressof(__other)) {
+ if (__kPropOnCopyAssign) {
+ __alloc_ = __other.__alloc_;
+ }
+ __entries_ = {__other.__entries_, __alloc_};
}
- __entries_ = {__other.__entries_, __alloc_};
return *this;
}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace&
- operator=(basic_stacktrace&& __other) noexcept(__kPropOnMove || __kAlwaysEqual) {
- if (this == std::addressof(__other)) {
- return *this;
- }
- if (__kPropOnMove) {
- __alloc_ = __other.__alloc_;
- __entries_ = std::move(__other.__entries_);
- } else {
- auto __allocs_eq = __kAlwaysEqual || __alloc_ == __other.__alloc_;
- if (__allocs_eq) {
- __entries_ = std::move(__other.__entries_);
- } else {
- // "moving" from a container with a different allocator;
- // we're forced to copy items instead
- for (auto const& __entry : __other.__entries_) {
- __entries_.push_back(__entry);
- }
+ operator=(basic_stacktrace&& __other) noexcept(__kPropOnMoveAssign || __kAlwaysEqual) {
+ if (this != std::addressof(__other)) {
+ if (__kPropOnMoveAssign) {
+ __alloc_ = std::move(__other.__alloc_);
}
+ __entries_ = {std::move(__other.__entries_), __alloc_};
}
return *this;
}
@@ -229,7 +212,9 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
// (19.6.4.5)
// [stacktrace.basic.mod], modifiers
- _LIBCPP_EXPORTED_FROM_ABI void swap(basic_stacktrace<_Allocator>& __other) noexcept {
+ _LIBCPP_EXPORTED_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_);
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
index 5864d241abf35..19441ab2d3b48 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
@@ -47,5 +47,7 @@ int main(int, char**) {
assert(st2a.size() == st2b.size());
assert(st2a != st2b);
+ static_assert(noexcept(st2a.size() == st2b.size()));
+
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..db5d8960f05f3
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ basic_stacktrace(const basic_stacktrace& other);
+ basic_stacktrace(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() {
+ auto 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>;
+ auto s1 = std::basic_stacktrace<A>::current();
+ std::basic_stacktrace<A> s2{s1};
+ 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 s1 = std::basic_stacktrace<A>::current();
+ std::basic_stacktrace<A> s2{s1};
+ assert(s1 == s2);
+ 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 s1 = std::basic_stacktrace<A>::current();
+ std::basic_stacktrace<A> s2{s1};
+ assert(s1 == s2);
+ 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_copy_construct();
+ test_copy_assign();
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.pass.cpp
deleted file mode 100644
index 48da1d0bd30b6..0000000000000
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy_and_move.pass.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++23
-// ADDITIONAL_COMPILE_FLAGS: -g
-
-/*
- (19.6.4.2)
-
- // [stacktrace.basic.cons], creation and assignment
- basic_stacktrace(const basic_stacktrace& other);
- basic_stacktrace(basic_stacktrace&& other) noexcept;
- basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
- basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
- basic_stacktrace& operator=(const basic_stacktrace& other);
- basic_stacktrace& operator=(basic_stacktrace&& other)
- noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
- allocator_traits<Allocator>::is_always_equal::value);
-*/
-
-#include <cassert>
-#include <cstdint>
-#include <stacktrace>
-
-// clang-format off
-uint32_t test1_line;
-uint32_t test2_line;
-
-template <class A>
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
-std::basic_stacktrace<A> test1(A& alloc) {
- test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
- auto ret = std::basic_stacktrace<A>::current(alloc);
- return ret;
-}
-
-template <class A>
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
-std::basic_stacktrace<A> test2(A& alloc) {
- test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
- auto ret = test1(alloc);
- return ret;
-}
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_copy_move_ctors() {
- using A = std::allocator<std::stacktrace_entry>;
- A alloc;
- auto st = std::basic_stacktrace<A>::current(alloc);
-
- auto copy_constr = std::basic_stacktrace<A>(st);
- assert(st == copy_constr);
-
- std::basic_stacktrace<A> copy_assign;
- copy_assign = std::basic_stacktrace<A>(st);
- assert(st == copy_assign);
-
- auto st2 = test2(alloc);
- assert(st2.size());
- std::basic_stacktrace<A> move_constr(std::move(st2));
- assert(move_constr.size());
- assert(!st2.size());
-
- auto st3 = test2(alloc);
- assert(st3.size());
- std::basic_stacktrace<A> move_assign;
- move_assign = std::move(st3);
- assert(move_assign.size());
- assert(!st3.size());
-
- // TODO(stacktrace23): should we add test cases with `select_on_container_copy_construction`?
-}
-
-_LIBCPP_NO_TAIL_CALLS
-int main(int, char**) {
- test_copy_move_ctors();
- return 0;
-}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/ctor_no_args.pass.cpp
index 3b406f1b16a61..ed1aac95fc83e 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
@@ -18,31 +18,29 @@
#include <cassert>
#include <stacktrace>
+#include <type_traits>
-uint32_t test1_line;
-uint32_t test2_line;
+#include "../test_allocs.h"
-template <class A>
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test1(A& alloc) {
- test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
- auto ret = std::basic_stacktrace<A>::current(alloc);
- return ret;
+void test_default_construct() {
+ std::stacktrace st;
+ assert(st.empty());
}
-template <class A>
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test2(A& alloc) {
- test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
- auto ret = test1(alloc);
- return ret;
-}
+void test_default_construct_noexcept() {
+ static_assert(noexcept(std::stacktrace()));
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_default_construct() {
- std::stacktrace st;
- assert(st.empty());
+ using A1 = std::allocator<std::stacktrace_entry>;
+ static_assert(std::is_nothrow_default_constructible_v<A1>);
+ static_assert(noexcept(std::basic_stacktrace<A1>()));
+
+ using A2 = TestAlloc<std::stacktrace_entry, false, true, true, true>;
+ static_assert(!std::is_nothrow_default_constructible_v<A2>);
+ static_assert(!noexcept(std::basic_stacktrace<A2>()));
}
-_LIBCPP_NO_TAIL_CALLS
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
index a49f45245f7d2..baf2ac17fe89e 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
@@ -18,62 +18,29 @@
#include <cassert>
#include <stacktrace>
+#include <type_traits>
-uint32_t test1_line;
-uint32_t test2_line;
+#include "../test_allocs.h"
-template <class A>
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test1(A& alloc) {
- test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
- auto ret = std::basic_stacktrace<A>::current(alloc);
- return ret;
-}
-
-template <class A>
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::basic_stacktrace<A> test2(A& alloc) {
- test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
- auto ret = test1(alloc);
- return ret;
+void test_construct_with_alloc() {
+ std::stacktrace st;
+ assert(st.empty());
}
-template <typename T>
-struct test_alloc {
- using size_type = size_t;
- using value_type = T;
- using pointer = T*;
- using const_pointer = T const*;
-
- template <typename U>
- struct rebind {
- using other = test_alloc<U>;
- };
-
- std::allocator<T> wrapped_{};
+void test_construct_with_alloc_noexcept() {
+ static_assert(noexcept(std::stacktrace()));
- test_alloc() = default;
-
- template <typename U>
- test_alloc(test_alloc<U> const& rhs) : wrapped_(rhs.wrapped_) {}
-
- bool operator==(auto const& rhs) const { return &rhs == this; }
- bool operator==(test_alloc const&) const { return true; }
-
- T* allocate(size_t n) { return wrapped_.allocate(n); }
- auto allocate_at_least(size_t n) { return wrapped_.allocate_at_least(n); }
- void deallocate(T* ptr, size_t n) { return wrapped_.deallocate(ptr, n); }
-};
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_construct_with_allocator() {
- test_alloc<std::stacktrace_entry> alloc;
- std::basic_stacktrace<decltype(alloc)> st(alloc);
- assert(st.empty());
+ using A1 = std::allocator<std::stacktrace_entry>;
+ static_assert(std::is_nothrow_constructible_v<A1>);
+ static_assert(noexcept(std::basic_stacktrace<A1>(A1())));
- st = std::basic_stacktrace<decltype(alloc)>::current(alloc);
- assert(!st.empty());
+ 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())));
}
-_LIBCPP_NO_TAIL_CALLS
int main(int, char**) {
- test_construct_with_allocator();
+ 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
index c1c9b3ea70496..401bc97bcb7c1 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
@@ -62,6 +62,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
_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
index 55877f0a49785..55dc16c1cdc59 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
@@ -40,6 +40,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
_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
index a999845e92e01..ccd7c57acb425 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
@@ -37,6 +37,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
_LIBCPP_NO_TAIL_CALLS
int main(int, char**) {
+ static_assert(noexcept(std::stacktrace::current(0, 0)));
test_current_with_skip_depth();
return 0;
}
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..658140c616676
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ basic_stacktrace(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 "../test_allocs.h"
+
+void test_move_construct() {
+ auto a = std::stacktrace::current();
+ std::stacktrace b{a};
+ assert(a == b);
+ std::stacktrace c{std::move(b)};
+ assert(a == c);
+}
+
+void test_move_assign() {}
+
+_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.mod/swap.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
index 147ce1f65810d..1de9d83acfea6 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod/swap.pass.cpp
@@ -22,20 +22,43 @@
#include <cassert>
#include <stacktrace>
+#include "../test_allocs.h"
+
int main(int, char**) {
std::stacktrace empty;
- auto current = std::stacktrace::current();
-
- std::stacktrace a(empty);
- std::stacktrace b(current);
- assert(a == empty);
- assert(b == current);
+ auto a = std::stacktrace::current();
+ std::stacktrace b(empty);
+ assert(!a.empty());
+ assert(b.empty());
a.swap(b);
- assert(a == current);
- assert(b == empty);
+ 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)));
- // TODO(stacktrace23): should we also test swap w/ `select_on_container_swap` case
+ // `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/swap.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
index 281af1c245245..3d49094a525fc 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem/swap.pass.cpp
@@ -19,18 +19,39 @@
#include <cassert>
#include <stacktrace>
+#include "../test_allocs.h"
+
int main(int, char**) {
std::stacktrace empty;
- auto current = std::stacktrace::current();
-
- std::stacktrace a(empty);
- std::stacktrace b(current);
- assert(a == empty);
- assert(b == current);
+ auto a = std::stacktrace::current();
+ std::stacktrace b(empty);
+ assert(!a.empty());
+ assert(b.empty());
std::swap(a, b);
- assert(a == current);
- assert(b == empty);
+ 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/entry.cmp/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
index 4ddd27ed388c6..a3af41a07720a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
@@ -38,5 +38,7 @@ int main(int, char**) {
assert(a == b);
assert(a != c);
+ static_assert(noexcept(a == b));
+
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
index edefe70af1fc9..0bec29fcc9b68 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
@@ -48,5 +48,7 @@ int main(int, char**) {
assert(std::strong_ordering::equivalent == (a <=> b));
assert(std::strong_ordering::greater == (c <=> a));
+ static_assert(noexcept(a <=> b));
+
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
index c993a6df64508..9a5968d9cc6f4 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
@@ -24,11 +24,12 @@ namespace std {
#include <type_traits>
_LIBCPP_NO_TAIL_CALLS
-int main(int, char**) noexcept {
+int main(int, char**) {
static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
- std::stacktrace_entry entry_t2;
- std::stacktrace_entry entry_t4;
- entry_t4 = entry_t2;
+
+ std::stacktrace_entry e1;
+ std::stacktrace_entry e2;
+ 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
index 85dd6fb7853cc..bfc16478dfdb5 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
@@ -25,9 +25,11 @@ namespace std {
_LIBCPP_NO_TAIL_CALLS
int main(int, char**) {
- std::stacktrace_entry entry_t2;
static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
- std::stacktrace_entry entry_t3(entry_t2);
+
+ 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
index 03bd82cfd9c73..2f3376a34e4ce 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons/default.pass.cpp
@@ -23,13 +23,12 @@ namespace std {
#include <stacktrace>
#include <type_traits>
-_LIBCPP_NO_TAIL_CALLS
-int main(int, char**) noexcept {
- // "Postconditions: *this is empty."
- static_assert(std::is_default_constructible_v<std::stacktrace_entry>);
+int main(int, char**) {
static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
- std::stacktrace_entry entry_t2;
- assert(!entry_t2);
+
+ 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
index bf1a23080f307..2fdcdb49f2fd2 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
@@ -21,8 +21,10 @@ namespace std {
#include <stacktrace>
int main(int, char**) noexcept {
- std::stacktrace_entry entry_t6;
- assert(entry_t6.native_handle() == 0);
+ std::stacktrace_entry e;
+ assert(e.native_handle() == 0);
+
+ static_assert(noexcept(e.native_handle()));
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
index f1a685324b22d..5bc64a6a907bc 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/operator_bool.pass.cpp
@@ -22,13 +22,15 @@ namespace std {
#include <stacktrace>
int main(int, char**) {
- std::stacktrace_entry entry_t6;
+ std::stacktrace_entry e;
// "Returns: false if and only if *this is empty."
- assert(!entry_t6);
+ assert(!e);
// Now set addr to something nonzero
- *(uintptr_t*)(&entry_t6) = uintptr_t(&main);
- assert(entry_t6.native_handle() == uintptr_t(&main));
- assert(entry_t6);
+ *(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/test_allocs.h b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
new file mode 100644
index 0000000000000..9a2582edcffe2
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache 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_TEST_ALLOCS_H
+#define _LIBCPP_STACKTRACE_TEST_ALLOCS_H
+
+#include <iostream>
+#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 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>;
+
+ /** Same as this type but with a different value_type (T) */
+ template <typename U>
+ using Other = TestAlloc<U, _KNoExCtors, _KNoExAlloc, _KPropagate, _KAlwaysEqual>;
+
+ template <typename U>
+ struct rebind {
+ using other = Other<U>;
+ };
+
+ static std::shared_ptr<std::allocator<std::byte>> new_arena() {
+ auto ret = std::make_shared<std::allocator<std::byte>>();
+ std::cout << "@@@ new arena " << ret.get() << '\n';
+ return ret;
+ }
+
+ static std::shared_ptr<std::allocator<std::byte>> global_arena() {
+ auto ret = new_arena();
+ std::cout << "@@@ global arena " << ret.get() << '\n';
+ return ret;
+ }
+
+ /** Type-erased allocator used for servicing allocate and deallocate.
+ Two `TestAlloc`'s are equal IFF they contain the same arena pointer.
+ Always-equal `TestAlloc`'s get a pointer to a shared `global_arena`. */
+ std::shared_ptr<std::allocator<std::byte>> arena_;
+
+ /** Instances are equal IFF they have the same arena pointer (even if this is "always_equals",
+ since such instances point to the global arena). */
+ bool operator==(auto const& rhs) const noexcept {
+ std::cout << this << ": cmp " << &rhs << " : " << arena_.get() << " vs " << rhs.arena_.get() << '\n';
+ return arena_.get() == rhs.arena_.get();
+ }
+
+ /** Construct with a new arena, or, if always-equal, the global arena. */
+ TestAlloc() noexcept(_KNoExCtors) : arena_(_KAlwaysEqual ? global_arena() : new_arena()) {
+ std::cout << this << ": init " << arena_.get() << '\n';
+ }
+
+ template <typename U>
+ TestAlloc(Other<U> const& rhs) : arena_(rhs.arena_) {
+ std::cout << this << ": init (copyctor) " << arena_.get() << '\n';
+ }
+
+ template <typename U>
+ TestAlloc& operator=(Other<U> const& rhs) {
+ if (_KPropagate && !_KAlwaysEqual) {
+ arena_ = rhs.arena_;
+ std::cout << this << ": init (copy assign) prop " << arena_.get() << '\n';
+ } else {
+ std::cout << this << ": init (copy assign) noprop " << arena_.get() << '\n';
+ }
+ }
+
+ std::allocator<T>& arena() { return *(std::allocator<T>*)arena_.get(); }
+
+ T* allocate(size_t n) noexcept(_KNoExAlloc) { return arena().allocate(n); }
+ auto allocate_at_least(size_t n) noexcept(_KNoExAlloc) { return arena().allocate_at_least(n); }
+ void deallocate(T* ptr, size_t n) noexcept(_KNoExAlloc) { return arena().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
>From 8b065782d600c74c058affa015335df581c2bad2 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sat, 12 Jul 2025 12:03:50 -0400
Subject: [PATCH 10/14] new stacktrace "base" class from builder + alloc
classes [WIP]
---
libcxx/include/__stacktrace/base.h | 70 ++++++----
libcxx/include/__stacktrace/basic.h | 13 +-
...bcxxabi.v1.stable.exceptions.nonew.abilist | 4 +-
libcxx/src/CMakeLists.txt | 2 +-
.../src/stacktrace/{builder.cpp => base.cpp} | 4 +-
libcxx/src/stacktrace/linux/impl.cpp | 20 +--
libcxx/src/stacktrace/linux/impl.h | 2 +-
libcxx/src/stacktrace/macos/impl.cpp | 20 +--
libcxx/src/stacktrace/macos/impl.h | 2 +-
libcxx/src/stacktrace/tools/tools.cpp | 123 +++++++++---------
libcxx/src/stacktrace/tools/tools.h | 54 ++++----
libcxx/src/stacktrace/unwind/impl.cpp | 6 +-
libcxx/src/stacktrace/unwind/impl.h | 2 +-
libcxx/src/stacktrace/windows/dll.cpp | 10 +-
libcxx/src/stacktrace/windows/impl.cpp | 6 +-
libcxx/src/stacktrace/windows/impl.h | 6 +-
.../stacktrace/basic.cons/copy.pass.cpp | 2 +-
.../stacktrace/basic.cons/move.pass.cpp | 51 +++++++-
.../stacktrace/basic.obs/at.pass.cpp | 1 -
.../basic.obs/operator_index.pass.cpp | 1 -
.../std/diagnostics/stacktrace/test_allocs.h | 37 ++----
21 files changed, 240 insertions(+), 196 deletions(-)
rename libcxx/src/stacktrace/{builder.cpp => base.cpp} (93%)
diff --git a/libcxx/include/__stacktrace/base.h b/libcxx/include/__stacktrace/base.h
index 4583b2760f246..8c933ddea6068 100644
--- a/libcxx/include/__stacktrace/base.h
+++ b/libcxx/include/__stacktrace/base.h
@@ -42,19 +42,9 @@ class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry;
namespace __stacktrace {
-struct _LIBCPP_HIDE_FROM_ABI alloc final {
- function<byte*(size_t)> __alloc_bytes_;
- function<void(byte*, size_t)> __dealloc_bytes_;
-
- template <class _Allocator>
- _LIBCPP_HIDE_FROM_ABI alloc(_Allocator __alloc) {
- using _AT = allocator_traits<_Allocator>;
- using _BA = typename _AT::template rebind_alloc<byte>;
- auto __ba = _BA(__alloc);
- __alloc_bytes_ = [__ba](size_t __sz) mutable { return __ba.allocate(__sz); };
- __dealloc_bytes_ = [__ba](void* __ptr, size_t __sz) mutable { __ba.deallocate((byte*)__ptr, __sz); };
- }
+struct _LIBCPP_HIDE_FROM_ABI entry_base;
+struct _LIBCPP_EXPORTED_FROM_ABI base {
template <typename _Tp>
struct _LIBCPP_HIDE_FROM_ABI Alloc {
function<byte*(size_t)> __alloc_bytes_;
@@ -66,6 +56,7 @@ struct _LIBCPP_HIDE_FROM_ABI alloc final {
template <typename _T2 = _Tp>
Alloc(Alloc<_T2> const& __rhs) : Alloc(__rhs.__alloc_bytes_, __rhs.__dealloc_bytes_) {}
+ // XXX Alignment?
using value_type = _Tp;
[[nodiscard]] _Tp* allocate(size_t __sz) { return (_Tp*)__alloc_bytes_(__sz * sizeof(_Tp)); }
void deallocate(_Tp* __ptr, size_t __sz) { __dealloc_bytes_((byte*)__ptr, __sz * sizeof(_Tp)); }
@@ -103,30 +94,53 @@ struct _LIBCPP_HIDE_FROM_ABI alloc final {
_LIBCPP_HIDE_FROM_ABI list<_Tp> make_list(_Args... __args) {
return list(std::forward<_Args>(__args)..., make_alloc<_Tp>());
}
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+ build_stacktrace(size_t __skip, size_t __max_depth);
+
+ base();
+
+ template <class _Allocator>
+ explicit _LIBCPP_EXPORTED_FROM_ABI base(_Allocator __alloc);
+
+ function<byte*(size_t)> __alloc_bytes_;
+ function<void(byte*, size_t)> __dealloc_bytes_;
+ vec<entry_base> __entries_;
+ str __main_prog_path_;
};
struct _LIBCPP_HIDE_FROM_ABI entry_base {
- uintptr_t __addr_actual_{}; // this address, as observed in this current process
- uintptr_t __addr_unslid_{}; // address adjusted for ASLR
- optional<__stacktrace::alloc::str> __desc_{}; // uses wrapped _Allocator from caller
- optional<__stacktrace::alloc::str> __file_{}; // uses wrapped _Allocator from caller
+ uintptr_t __addr_actual_{}; // this address, as observed in this current process
+ uintptr_t __addr_unslid_{}; // address adjusted for ASLR
+ optional<__stacktrace::base::str> __desc_{}; // uses wrapped _Allocator from caller
+ optional<__stacktrace::base::str> __file_{}; // uses wrapped _Allocator from caller
uint_least32_t __line_{};
_LIBCPP_HIDE_FROM_ABI stacktrace_entry to_stacktrace_entry() const;
};
-struct _LIBCPP_EXPORTED_FROM_ABI builder final {
- alloc __alloc_; // wraps the caller-provided allocator
- alloc::vec<entry_base> __entries_;
- alloc::str __main_prog_path_;
-
- template <class _Allocator>
- explicit _LIBCPP_EXPORTED_FROM_ABI builder(_Allocator __alloc)
- : __alloc_(__alloc), __entries_(__alloc_.make_vec<entry_base>()), __main_prog_path_(__alloc_.make_str()) {}
-
- _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
- build_stacktrace(size_t __skip, size_t __max_depth);
-};
+template <class _Allocator>
+auto __alloc_wrap(_Allocator const& __alloc) {
+ using _AT = allocator_traits<_Allocator>;
+ using _BA = typename _AT::template rebind_alloc<byte>;
+ auto __ba = _BA(__alloc);
+ return [__ba = std::move(__ba)](size_t __sz) mutable { return __ba.allocate(__sz); };
+}
+
+template <class _Allocator>
+auto __dealloc_wrap(_Allocator const& __alloc) {
+ using _AT = allocator_traits<_Allocator>;
+ using _BA = typename _AT::template rebind_alloc<byte>;
+ auto __ba = _BA(__alloc);
+ return [__ba = std::move(__ba)](void* __ptr, size_t __sz) mutable { __ba.deallocate((byte*)__ptr, __sz); };
+}
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI base::base(_Allocator __alloc)
+ : __alloc_bytes_(__alloc_wrap(__alloc)),
+ __dealloc_bytes_(__dealloc_wrap(__alloc)),
+ __entries_(make_vec<entry_base>()),
+ __main_prog_path_(make_str()) {}
} // namespace __stacktrace
diff --git a/libcxx/include/__stacktrace/basic.h b/libcxx/include/__stacktrace/basic.h
index 524bacd305a8f..dd34ad1a123bf 100644
--- a/libcxx/include/__stacktrace/basic.h
+++ b/libcxx/include/__stacktrace/basic.h
@@ -43,7 +43,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
class stacktrace_entry;
template <class _Allocator>
-class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
+class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace : private __stacktrace::base {
friend struct hash<basic_stacktrace<_Allocator>>;
friend struct __stacktrace::__to_string;
@@ -56,7 +56,8 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
constexpr static bool __kNoThrowAlloc =
noexcept(noexcept(_Allocator().allocate(1)) && noexcept(_Allocator().allocate_at_least(1)));
- [[no_unique_address]] _Allocator __alloc_;
+ [[no_unique_address]]
+ _Allocator __alloc_;
using __entry_vec _LIBCPP_NODEBUG = vector<stacktrace_entry, _Allocator>;
__entry_vec __entries_;
@@ -94,7 +95,7 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
current(size_type __skip,
size_type __max_depth,
const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
- __stacktrace::builder __builder(__caller_alloc);
+ __stacktrace::base __builder(__caller_alloc);
__builder.build_stacktrace(__skip + 1, __max_depth);
basic_stacktrace<_Allocator> __ret{__caller_alloc};
__ret.__entries_.reserve(__builder.__entries_.size());
@@ -109,7 +110,7 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace() noexcept(__kNoThrowDflConstruct) : basic_stacktrace(allocator_type()) {}
_LIBCPP_EXPORTED_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc) noexcept
- : __alloc_(__alloc), __entries_(__alloc_) {}
+ : base(__alloc), __entries_(__alloc_) {}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other)
: __alloc_(_ATraits::select_on_container_copy_construction(__other.__alloc_)),
@@ -119,10 +120,10 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
: __alloc_(std::move(__other.__alloc_)), __entries_(std::move(__other.__entries_)) {}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
- : __alloc_(__alloc), __entries_(__other.__entries_, __alloc) {}
+ : base(__alloc), __entries_(__other.__entries_, __alloc) {}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
- : __alloc_(__alloc) {
+ : base(__alloc) {
__entries_ = {std::move(__other.__entries_), __alloc_};
}
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 114f3beb42a71..83a2d41f8680e 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
@@ -983,7 +983,9 @@
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclERKNS_16stacktrace_entryE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '__ZNSt3__112__stacktrace7builder16build_stacktraceEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4base16build_stacktraceEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4baseC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__112__stacktrace4baseC2Ev', '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/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 5e12aec47467d..de43a57f72c95 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,7 +40,7 @@ set(LIBCXX_SOURCES
ryu/d2fixed.cpp
ryu/d2s.cpp
ryu/f2s.cpp
- stacktrace/builder.cpp
+ stacktrace/base.cpp
stacktrace/linux/elf.cpp
stacktrace/linux/images.cpp
stacktrace/linux/impl.cpp
diff --git a/libcxx/src/stacktrace/builder.cpp b/libcxx/src/stacktrace/base.cpp
similarity index 93%
rename from libcxx/src/stacktrace/builder.cpp
rename to libcxx/src/stacktrace/base.cpp
index 000135b4ccc38..7ddb865968be5 100644
--- a/libcxx/src/stacktrace/builder.cpp
+++ b/libcxx/src/stacktrace/base.cpp
@@ -21,8 +21,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
+base::base() : base(std::allocator<std::stacktrace_entry>()) {}
+
_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
-builder::build_stacktrace(size_t skip, size_t max_depth) {
+base::build_stacktrace(size_t skip, size_t max_depth) {
#if defined(_LIBCPP_WIN32API)
// Windows implementation
win_impl dbghelp{*this};
diff --git a/libcxx/src/stacktrace/linux/impl.cpp b/libcxx/src/stacktrace/linux/impl.cpp
index 3f18d015aa93f..c01fcb02f30ef 100644
--- a/libcxx/src/stacktrace/linux/impl.cpp
+++ b/libcxx/src/stacktrace/linux/impl.cpp
@@ -35,11 +35,11 @@ void linux::ident_modules() {
auto mainProg = images.mainProg();
if (mainProg) {
- builder_.__main_prog_path_ = mainProg->name_;
+ base_.__main_prog_path_ = mainProg->name_;
}
unsigned index = 1; // Starts at one, and is moved around in this loop
- for (auto& entry : builder_.__entries_) {
+ for (auto& entry : base_.__entries_) {
while (images[index].loaded_at_ > entry.__addr_actual_) {
--index;
}
@@ -47,7 +47,7 @@ void linux::ident_modules() {
++index;
}
entry.__addr_unslid_ = entry.__addr_actual_ - images[index].slide_;
- entry.__file_ = builder_.__alloc_.make_str(images[index].name_);
+ entry.__file_ = base_.make_str(images[index].name_);
}
}
@@ -65,28 +65,28 @@ void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
if (_mm) {
static elf::ELF _this_elf(_mm.addr_);
if (_this_elf) {
- for (auto& entry : builder_.__entries_) {
+ for (auto& entry : base_.__entries_) {
if (entry.__desc_->empty() && entry.__file_ == main_elf_name) {
auto name = _this_elf.getSym(entry.__addr_unslid_).name();
- entry.__desc_ = builder_.__alloc_.make_str(name);
+ entry.__desc_ = base_.make_str(name);
}
}
}
}
}
-bool symbolize_entry(alloc& alloc, entry_base& entry) {
+bool symbolize_entry(base& base, entry_base& entry) {
bool ret = false;
Dl_info info;
if (dladdr((void*)entry.__addr_actual_, &info)) {
ret = true; // at least partially successful
if (info.dli_fname && entry.__file_->empty()) {
// provide at least the binary filename in case we cannot lookup source location
- entry.__file_ = alloc.make_str(info.dli_fname);
+ entry.__file_ = base.make_str(info.dli_fname);
}
if (info.dli_sname && entry.__desc_->empty()) {
// provide at least the mangled name; try to unmangle in a later step
- entry.__desc_ = alloc.make_str(info.dli_sname);
+ entry.__desc_ = base.make_str(info.dli_sname);
}
}
return ret;
@@ -96,8 +96,8 @@ bool symbolize_entry(alloc& alloc, entry_base& entry) {
// except for symbols in the main program. If addr2line-style tools are enabled, that step
// might also be able to get symbols directly from the binary's debug info.
void linux::symbolize() {
- for (auto& entry : builder_.__entries_) {
- symbolize_entry(builder_.__alloc_, entry);
+ for (auto& entry : base_.__entries_) {
+ symbolize_entry(base_, entry);
}
// Symbols might be missing, because both (1) Linux's `dladdr` won't try to resolve non-exported symbols,
// which can be the case for the main program executable; and (2) debug info was not preserved.
diff --git a/libcxx/src/stacktrace/linux/impl.h b/libcxx/src/stacktrace/linux/impl.h
index 0a91bf88fcf9b..5ace2db1a3fa8 100644
--- a/libcxx/src/stacktrace/linux/impl.h
+++ b/libcxx/src/stacktrace/linux/impl.h
@@ -15,7 +15,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
struct linux {
- builder& builder_;
+ base& base_;
#if defined(__linux__)
// defined in linux.cpp
diff --git a/libcxx/src/stacktrace/macos/impl.cpp b/libcxx/src/stacktrace/macos/impl.cpp
index 575a2927a78ca..d47381c795f53 100644
--- a/libcxx/src/stacktrace/macos/impl.cpp
+++ b/libcxx/src/stacktrace/macos/impl.cpp
@@ -22,7 +22,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-void ident_module(alloc& alloc, entry_base& entry, unsigned& index, image* images) {
+void ident_module(base& base, entry_base& entry, unsigned& index, image* images) {
if (entry.__addr_actual_) {
while (images[index].loaded_at_ > entry.__addr_actual_) {
--index;
@@ -33,7 +33,7 @@ void ident_module(alloc& alloc, entry_base& entry, unsigned& index, image* image
auto& image = images[index];
if (image) {
entry.__addr_unslid_ = entry.__addr_actual_ - images[index].slide_;
- entry.__file_ = alloc.make_str(images[index].name_);
+ entry.__file_ = base.make_str(images[index].name_);
}
}
}
@@ -65,31 +65,31 @@ void macos::ident_modules() {
}
// First image (the main program) is at index 1
- builder_.__main_prog_path_ = builder_.__alloc_.make_str(images.at(1).name_);
+ base_.__main_prog_path_ = base_.make_str(images.at(1).name_);
unsigned index = 1; // Starts at one, and is moved by 'ident_module'
- for (auto& entry : builder_.__entries_) {
- ident_module(builder_.__alloc_, (entry_base&)entry, index, images.data());
+ for (auto& entry : base_.__entries_) {
+ ident_module(base_, (entry_base&)entry, index, images.data());
}
}
-void symbolize_entry(alloc& alloc, entry_base& entry) {
+void symbolize_entry(base& base, entry_base& entry) {
Dl_info info;
if (dladdr((void*)entry.__addr_actual_, &info)) {
if (info.dli_fname && entry.__file_->empty()) {
// provide at least the binary filename in case we cannot lookup source location
- entry.__file_ = alloc.make_str(info.dli_fname);
+ entry.__file_ = base.make_str(info.dli_fname);
}
if (info.dli_sname && entry.__desc_->empty()) {
// provide at least the mangled name; try to unmangle in a later step
- entry.__desc_ = alloc.make_str(info.dli_sname);
+ entry.__desc_ = base.make_str(info.dli_sname);
}
}
}
void macos::symbolize() {
- for (auto& entry : builder_.__entries_) {
- symbolize_entry(builder_.__alloc_, (entry_base&)entry);
+ for (auto& entry : base_.__entries_) {
+ symbolize_entry(base_, entry);
}
}
diff --git a/libcxx/src/stacktrace/macos/impl.h b/libcxx/src/stacktrace/macos/impl.h
index e0a24228a6ade..71c3c04a6471e 100644
--- a/libcxx/src/stacktrace/macos/impl.h
+++ b/libcxx/src/stacktrace/macos/impl.h
@@ -22,7 +22,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
struct macos {
- builder& builder_;
+ base& base_;
#if defined(__APPLE__)
// defined in macos.cpp
diff --git a/libcxx/src/stacktrace/tools/tools.cpp b/libcxx/src/stacktrace/tools/tools.cpp
index 8f04a1806bbc7..f1d6e966e56fe 100644
--- a/libcxx/src/stacktrace/tools/tools.cpp
+++ b/libcxx/src/stacktrace/tools/tools.cpp
@@ -33,67 +33,67 @@ namespace __stacktrace {
namespace {
-_LIBCPP_HIDE_FROM_ABI alloc::str hex_string(alloc& alloc, uintptr_t __addr) {
+_LIBCPP_HIDE_FROM_ABI base::str hex_string(base& base, uintptr_t __addr) {
char __ret[19]; // "0x" + 16 digits + NUL
auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
- return alloc.make_str(__ret, size_t(__size));
+ return base.make_str(__ret, size_t(__size));
}
-_LIBCPP_HIDE_FROM_ABI alloc::str u64_string(alloc& alloc, uintptr_t __val) {
+_LIBCPP_HIDE_FROM_ABI base::str u64_string(base& base, uintptr_t __val) {
char __ret[21]; // 20 digits max + NUL
auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
- return alloc.make_str(__ret, size_t(__size));
+ return base.make_str(__ret, size_t(__size));
}
# define STRINGIFY0(x) #x
# define STRINGIFY(x) STRINGIFY0(x)
-void try_tools(alloc& alloc, function<bool(tool const&)> cb) {
+void try_tools(base& base, function<bool(tool const&)> cb) {
char const* prog_name;
if ((prog_name = getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH"))) {
- if (cb(llvm_symbolizer{alloc, prog_name})) {
+ if (cb(llvm_symbolizer{base, prog_name})) {
return;
}
} else {
# if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
- if (cb(llvm_symbolizer{alloc, STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)})) {
+ if (cb(llvm_symbolizer{base, STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)})) {
return;
}
# else
- if (cb(llvm_symbolizer{alloc})) {
+ if (cb(llvm_symbolizer{base})) {
return;
}
# endif
}
if ((prog_name = getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH"))) {
- if (cb(addr2line{alloc, prog_name})) {
+ if (cb(addr2line{base, prog_name})) {
return;
}
} else {
# if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
- if (cb(addr2line{alloc, STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)})) {
+ if (cb(addr2line{base, STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)})) {
return;
}
# else
- if (cb(addr2line{alloc})) {
+ if (cb(addr2line{base})) {
return;
}
# endif
}
if ((prog_name = getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH"))) {
- if (cb(atos{alloc, prog_name})) {
+ if (cb(atos{base, prog_name})) {
return;
}
} else {
# if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
- if (cb(atos{alloc, STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)})) {
+ if (cb(atos{base, STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)})) {
return;
}
# else
- if (cb(atos{alloc})) {
+ if (cb(atos{base})) {
return;
}
# endif
@@ -103,9 +103,9 @@ void try_tools(alloc& alloc, function<bool(tool const&)> cb) {
} // namespace
void spawner::resolve_lines() {
- try_tools(builder_.__alloc_, [&](tool const& prog) {
+ try_tools(base_, [&](tool const& prog) {
char buf[512];
- pspawn_tool proc(prog, builder_, buf, sizeof(buf));
+ pspawn_tool proc(prog, base_, buf, sizeof(buf));
try {
proc.run();
return true;
@@ -120,19 +120,19 @@ void spawner::resolve_lines() {
});
}
-alloc::list<alloc::str> llvm_symbolizer::buildArgs(builder& builder) const {
- auto ret = alloc_.make_list<alloc::str>();
- ret.push_back(alloc_.make_str(progName_));
- ret.push_back(alloc_.make_str("--demangle"));
- ret.push_back(alloc_.make_str("--no-inlines"));
- ret.push_back(alloc_.make_str("--verbose"));
- ret.push_back(alloc_.make_str("--relativenames"));
- ret.push_back(alloc_.make_str("--functions=short"));
- for (auto& st_entry : builder.__entries_) {
+base::list<base::str> llvm_symbolizer::buildArgs(base& base) const {
+ auto ret = base_.make_list<base::str>();
+ ret.push_back(base_.make_str(progName_));
+ ret.push_back(base_.make_str("--demangle"));
+ ret.push_back(base_.make_str("--no-inlines"));
+ ret.push_back(base_.make_str("--verbose"));
+ ret.push_back(base_.make_str("--relativenames"));
+ ret.push_back(base_.make_str("--functions=short"));
+ for (auto& st_entry : base.__entries_) {
auto& entry = (entry_base&)st_entry;
- auto addr_string = hex_string(alloc_, entry.__addr_unslid_);
+ auto addr_string = hex_string(base_, entry.__addr_unslid_);
if (entry.__file_) {
- auto arg = alloc_.make_str();
+ auto arg = base_.make_str();
arg.reserve(entry.__file_->size() + 40);
arg += "FILE:";
arg += *entry.__file_;
@@ -146,7 +146,7 @@ alloc::list<alloc::str> llvm_symbolizer::buildArgs(builder& builder) const {
return ret;
}
-void llvm_symbolizer::parseOutput(builder& builder, __stacktrace::entry_base& entry, std::istream& output) const {
+void llvm_symbolizer::parseOutput(base& base, __stacktrace::entry_base& entry, std::istream& output) const {
// clang-format off
/*
With "--verbose", parsing is a little easier, or at least, more reliable;
@@ -166,8 +166,7 @@ Note that this includes an extra empty line as a terminator.
*/
// clang-format on
- auto& alloc = builder.__alloc_;
- auto line = alloc.make_str();
+ auto line = base.make_str();
line.reserve(512);
std::string_view tmp;
while (true) {
@@ -189,7 +188,7 @@ Note that this includes an extra empty line as a terminator.
tmp = line;
tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
if (tmp != "??") {
- entry.__file_ = alloc.make_str(tmp);
+ entry.__file_ = base.make_str(tmp);
}
} else if (line.starts_with(" Line:")) {
tmp = line;
@@ -206,29 +205,28 @@ Note that this includes an extra empty line as a terminator.
}
}
-alloc::list<alloc::str> addr2line::buildArgs(builder& builder) const {
- auto& alloc = builder.__alloc_;
- auto ret = alloc.make_list<alloc::str>();
- if (builder.__main_prog_path_.empty()) {
+base::list<base::str> addr2line::buildArgs(base& base) const {
+ auto ret = base.make_list<base::str>();
+ if (base.__main_prog_path_.empty()) {
// Should not have reached here but be graceful anyway
- ret.push_back(alloc.make_str("/bin/false"));
+ ret.push_back(base.make_str("/bin/false"));
return ret;
}
- ret.push_back(alloc.make_str(progName_));
- ret.push_back(alloc.make_str("--functions"));
- ret.push_back(alloc.make_str("--demangle"));
- ret.push_back(alloc.make_str("--basenames"));
- ret.push_back(alloc.make_str("--pretty-print")); // This "human-readable form" is easier to parse
- ret.push_back(alloc.make_str("-e"));
- ret.push_back(builder.__main_prog_path_);
- for (auto& entry : builder.__entries_) {
- ret.push_back(hex_string(alloc, ((entry_base&)entry).__addr_unslid_));
+ ret.push_back(base.make_str(progName_));
+ ret.push_back(base.make_str("--functions"));
+ ret.push_back(base.make_str("--demangle"));
+ ret.push_back(base.make_str("--basenames"));
+ ret.push_back(base.make_str("--pretty-print")); // This "human-readable form" is easier to parse
+ ret.push_back(base.make_str("-e"));
+ ret.push_back(base.__main_prog_path_);
+ for (auto& entry : base.__entries_) {
+ ret.push_back(hex_string(base, ((entry_base&)entry).__addr_unslid_));
}
return ret;
}
-void addr2line::parseOutput(builder& builder, entry_base& entry, std::istream& output) const {
+void addr2line::parseOutput(base& base, entry_base& entry, std::istream& output) const {
// clang-format off
/*
Example:
@@ -244,8 +242,7 @@ test::Foo::Foo(int) at foo.cc:11
*/
// clang-format on
- auto& alloc = builder.__alloc_;
- auto line = alloc.make_str();
+ auto line = base.make_str();
line.reserve(512);
std::getline(output, line);
while (!line.empty() && isspace(line.back())) {
@@ -261,16 +258,16 @@ test::Foo::Foo(int) at foo.cc:11
return;
}
if (sepIndex > 0) {
- entry.__desc_ = alloc.make_str(string_view(line).substr(0, sepIndex));
+ entry.__desc_ = base.make_str(string_view(line).substr(0, sepIndex));
}
auto fileBegin = sepIndex + 4;
if (fileBegin >= line.size()) {
return;
}
- auto fileline = alloc.make_str(string_view(line).substr(fileBegin));
+ auto fileline = base.make_str(string_view(line).substr(fileBegin));
auto colon = fileline.find_last_of(":");
if (colon > 0 && !fileline.starts_with("?")) {
- entry.__file_ = alloc.make_str(string_view(fileline).substr(0, colon));
+ entry.__file_ = base.make_str(string_view(fileline).substr(0, colon));
}
if (colon == std::string::npos) {
@@ -284,21 +281,20 @@ test::Foo::Foo(int) at foo.cc:11
entry.__line_ = lineno;
}
-alloc::list<alloc::str> atos::buildArgs(builder& builder) const {
- auto& alloc = builder.__alloc_;
- auto ret = alloc.make_list<alloc::str>();
- ret.push_back(alloc.make_str(progName_));
- ret.push_back(alloc.make_str("-p"));
- ret.push_back(u64_string(alloc, getpid()));
+base::list<base::str> atos::buildArgs(base& base) const {
+ auto ret = base.make_list<base::str>();
+ ret.push_back(base.make_str(progName_));
+ ret.push_back(base.make_str("-p"));
+ ret.push_back(u64_string(base, getpid()));
// TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
// ret.push_back("--fullPath");
- for (auto& entry : builder.__entries_) {
- ret.push_back(hex_string(alloc, ((entry_base&)entry).__addr_actual_));
+ for (auto& entry : base.__entries_) {
+ ret.push_back(hex_string(base, ((entry_base&)entry).__addr_actual_));
}
return ret;
}
-void atos::parseOutput(builder& builder, entry_base& entry, std::istream& output) const {
+void atos::parseOutput(base& base, entry_base& entry, std::istream& output) const {
// Simple example:
//
// main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
@@ -322,8 +318,7 @@ void atos::parseOutput(builder& builder, entry_base& entry, std::istream& output
// If this more or less fits our expected format we'll take these data,
// even if the line number is 0.
- auto& alloc = builder.__alloc_;
- auto line = alloc.make_str();
+ auto line = base.make_str();
line.reserve(512);
std::getline(output, line);
while (!line.empty() && isspace(line.back())) {
@@ -360,12 +355,12 @@ void atos::parseOutput(builder& builder, entry_base& entry, std::istream& output
// we have the name provided by atos; only use that if we have no symbol
// (no need to copy more strings otherwise).
if (entry.__desc_->empty() && !sym.empty()) {
- entry.__desc_ = alloc.make_str(sym);
+ entry.__desc_ = base.make_str(sym);
}
std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
if (file != "?" && file != "??" && !file.empty()) {
- entry.__file_ = alloc.make_str(file);
+ entry.__file_ = base.make_str(file);
}
unsigned lineno = 0;
diff --git a/libcxx/src/stacktrace/tools/tools.h b/libcxx/src/stacktrace/tools/tools.h
index 620a1e2f4f9a9..6f1a7935a183a 100644
--- a/libcxx/src/stacktrace/tools/tools.h
+++ b/libcxx/src/stacktrace/tools/tools.h
@@ -35,42 +35,42 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
struct tool {
- alloc& alloc_;
+ base& base_;
char const* progName_;
- tool(alloc& alloc, char const* progName) : alloc_(alloc), progName_(progName) {}
+ tool(base& base, char const* progName) : base_(base), progName_(progName) {}
virtual ~tool() = default;
/** Construct complete `argv` for the spawned process.
Includes the program name at argv[0], followed by flags */
- virtual alloc::list<alloc::str> buildArgs(builder& trace) const = 0;
+ virtual base::list<base::str> buildArgs(base& trace) const = 0;
/** Parse line(s) output by the tool, and modify `entry`. */
- virtual void parseOutput(builder& trace, entry_base& entry, std::istream& output) const = 0;
+ virtual void parseOutput(base& trace, entry_base& entry, std::istream& output) const = 0;
};
struct llvm_symbolizer : tool {
virtual ~llvm_symbolizer() = default;
- explicit llvm_symbolizer(alloc& alloc) : llvm_symbolizer(alloc, "llvm_symbolizer") {}
- llvm_symbolizer(alloc& alloc, char const* progName) : tool{alloc, progName} {}
- alloc::list<alloc::str> buildArgs(builder& trace) const override;
- void parseOutput(builder& trace, entry_base& entry, std::istream& output) const override;
+ explicit llvm_symbolizer(base& base) : llvm_symbolizer(base, "llvm_symbolizer") {}
+ llvm_symbolizer(base& base, char const* progName) : tool{base, progName} {}
+ base::list<base::str> buildArgs(base& trace) const override;
+ void parseOutput(base& trace, entry_base& entry, std::istream& output) const override;
};
struct addr2line : tool {
virtual ~addr2line() = default;
- explicit addr2line(alloc& alloc) : addr2line(alloc, "addr2line") {}
- addr2line(alloc& alloc, char const* progName) : tool{alloc, progName} {}
- alloc::list<alloc::str> buildArgs(builder& trace) const override;
- void parseOutput(builder& trace, entry_base& entry, std::istream& stream) const override;
+ explicit addr2line(base& base) : addr2line(base, "addr2line") {}
+ addr2line(base& base, char const* progName) : tool{base, progName} {}
+ base::list<base::str> buildArgs(base& trace) const override;
+ void parseOutput(base& trace, entry_base& entry, std::istream& stream) const override;
};
struct atos : tool {
virtual ~atos() = default;
- explicit atos(alloc& alloc) : atos(alloc, "atos") {}
- atos(alloc& alloc, char const* progName) : tool{alloc, progName} {}
- alloc::list<alloc::str> buildArgs(builder& trace) const override;
- void parseOutput(builder& trace, entry_base& entry, std::istream& output) const override;
+ explicit atos(base& base) : atos(base, "atos") {}
+ atos(base& base, char const* progName) : tool{base, progName} {}
+ base::list<base::str> buildArgs(base& trace) const override;
+ void parseOutput(base& trace, entry_base& entry, std::istream& output) const override;
};
struct file_actions {
@@ -124,8 +124,8 @@ struct pspawn {
}
}
- void spawn(alloc::list<alloc::str> const& argStrings) {
- alloc::vec<char const*> argv = tool_.alloc_.make_vec<char const*>();
+ void spawn(base::list<base::str> const& argStrings) {
+ base::vec<char const*> argv = tool_.base_.make_vec<char const*>();
argv.reserve(argStrings.size() + 1);
for (auto const& str : argStrings) {
argv.push_back(str.data());
@@ -145,13 +145,13 @@ struct pspawn {
};
struct pspawn_tool : pspawn {
- builder& builder_;
+ base& base_;
fd fd_;
fd_streambuf buf_;
fd_istream stream_;
- pspawn_tool(tool const& a2l, builder& trace, char* buf, size_t size)
- : pspawn{a2l}, builder_(trace), fd_(fa_.redirectOutFD()), buf_(fd_, buf, size), stream_(buf_) {
+ pspawn_tool(tool const& a2l, base& trace, char* buf, size_t size)
+ : pspawn{a2l}, base_(trace), fd_(fa_.redirectOutFD()), buf_(fd_, buf, size), stream_(buf_) {
if (!debug::enabled()) {
fa_.redirectErrNull();
}
@@ -162,11 +162,11 @@ struct pspawn_tool : pspawn {
// Cannot run "addr2line" or similar without addresses, since we
// provide them in argv, and if there are none passed in argv, the
// tool will try to read from stdin and hang.
- if (builder_.__entries_.empty()) {
+ if (base_.__entries_.empty()) {
return;
}
- auto argStrings = tool_.buildArgs(builder_);
+ auto argStrings = tool_.buildArgs(base_);
if (debug::enabled()) {
debug() << "Trying to get stacktrace using:";
for (auto& str : argStrings) {
@@ -177,17 +177,17 @@ struct pspawn_tool : pspawn {
spawn(argStrings);
- auto end = builder_.__entries_.end();
- auto it = builder_.__entries_.begin();
+ auto end = base_.__entries_.end();
+ auto it = base_.__entries_.begin();
while (it != end) {
auto& entry = (entry_base&)(*it++);
- tool_.parseOutput(builder_, entry, stream_);
+ tool_.parseOutput(base_, entry, stream_);
}
}
};
struct spawner {
- builder& builder_;
+ base& base_;
void resolve_lines();
};
diff --git a/libcxx/src/stacktrace/unwind/impl.cpp b/libcxx/src/stacktrace/unwind/impl.cpp
index 4742988dac64c..332712348e4b3 100644
--- a/libcxx/src/stacktrace/unwind/impl.cpp
+++ b/libcxx/src/stacktrace/unwind/impl.cpp
@@ -21,7 +21,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
struct unwind_backtrace {
- builder& builder_;
+ base& base_;
size_t skip_;
size_t maxDepth_;
@@ -39,7 +39,7 @@ struct unwind_backtrace {
if (!ip) {
return _Unwind_Reason_Code::_URC_NORMAL_STOP;
}
- auto& entry = builder_.__entries_.emplace_back();
+ auto& entry = base_.__entries_.emplace_back();
auto& eb = (entry_base&)entry;
eb.__addr_actual_ = (ipBefore ? ip : ip - 1);
eb.__addr_unslid_ = eb.__addr_actual_; // in case we can't un-slide
@@ -52,7 +52,7 @@ struct unwind_backtrace {
};
_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void unwind::collect(size_t skip, size_t max_depth) {
- unwind_backtrace bt{builder_, skip + 1, max_depth}; // skip this call as well
+ unwind_backtrace bt{base_, skip + 1, max_depth}; // skip this call as well
_Unwind_Backtrace(unwind_backtrace::callback, &bt);
}
diff --git a/libcxx/src/stacktrace/unwind/impl.h b/libcxx/src/stacktrace/unwind/impl.h
index 4b552349d75bc..1eab787dff745 100644
--- a/libcxx/src/stacktrace/unwind/impl.h
+++ b/libcxx/src/stacktrace/unwind/impl.h
@@ -22,7 +22,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
struct unwind {
- builder& builder_;
+ base& base_;
void collect(size_t skip, size_t max_depth);
};
diff --git a/libcxx/src/stacktrace/windows/dll.cpp b/libcxx/src/stacktrace/windows/dll.cpp
index d48bead956041..fad8b591ec4e5 100644
--- a/libcxx/src/stacktrace/windows/dll.cpp
+++ b/libcxx/src/stacktrace/windows/dll.cpp
@@ -12,7 +12,7 @@
# include <__stacktrace/base.h>
-# include "stacktrace/alloc.h"
+# include "stacktrace/base.h"
# include "stacktrace/config.h"
# include "stacktrace/utils.h"
# include "stacktrace/windows/dll.h"
@@ -49,7 +49,7 @@ size_t moduleCount; // 0 IFF module enumeration failed
} // namespace
-win_impl::WinDebugAPIs(builder& trace) : builder_(trace), guard_(gWindowsAPILock) {
+win_impl::WinDebugAPIs(base& trace) : base_(trace), guard_(gWindowsAPILock) {
if (!globalInitialized) {
// Cannot proceed without these DLLs:
if (!dbg) {
@@ -108,7 +108,7 @@ void win_impl::symbolize() {
return;
}
- for (auto& entry : builder_.__entries_) {
+ for (auto& entry : base_.__entries_) {
char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName];
auto* sym = (IMAGEHLP_SYMBOL64*)space;
sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
@@ -128,7 +128,7 @@ void win_impl::resolve_lines() {
return;
}
- for (auto& entry : builder_.__entries_) {
+ for (auto& entry : base_.__entries_) {
DWORD disp{0};
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
@@ -185,7 +185,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_
continue;
}
--max_depth;
- auto& entry = builder_.__entries_.emplace_back();
+ auto& entry = base_.__entries_.emplace_back();
// We don't need to compute the un-slid addr; windbg only needs the actual addresses.
// Assume address is of the instruction after a call instruction, since we can't
// differentiate between a signal, SEH exception handler, or a normal function call.
diff --git a/libcxx/src/stacktrace/windows/impl.cpp b/libcxx/src/stacktrace/windows/impl.cpp
index e8901e469e768..476690d7f0760 100644
--- a/libcxx/src/stacktrace/windows/impl.cpp
+++ b/libcxx/src/stacktrace/windows/impl.cpp
@@ -109,7 +109,7 @@ void win_impl::symbolize() {
return;
}
- for (auto& entry : builder_.__entries_) {
+ for (auto& entry : base_.__entries_) {
char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName];
auto* sym = (IMAGEHLP_SYMBOL64*)space;
sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
@@ -129,7 +129,7 @@ void win_impl::resolve_lines() {
return;
}
- for (auto& entry : builder_.__entries_) {
+ for (auto& entry : base_.__entries_) {
DWORD disp{0};
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
@@ -186,7 +186,7 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_
continue;
}
--max_depth;
- auto& entry = builder_.__entries_.emplace_back();
+ auto& entry = base_.__entries_.emplace_back();
// We don't need to compute the un-slid addr; windbg only needs the actual addresses.
// Assume address is of the instruction after a call instruction, since we can't
// differentiate between a signal, SEH exception handler, or a normal function call.
diff --git a/libcxx/src/stacktrace/windows/impl.h b/libcxx/src/stacktrace/windows/impl.h
index b88a3f9d8a3a1..95df844c28ca3 100644
--- a/libcxx/src/stacktrace/windows/impl.h
+++ b/libcxx/src/stacktrace/windows/impl.h
@@ -18,16 +18,16 @@
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-struct builder;
+struct base;
struct win_impl {
- builder& builder_;
+ base& base_;
#if defined(_LIBCPP_WIN32API)
static std::mutex mutex_;
std::lock_guard<std::mutex> guard_;
- explicit win_impl(builder& builder) : builder_(builder), guard_(mutex_) { global_init(); }
+ explicit win_impl(base& base) : base_(base), guard_(mutex_) { global_init(); }
~win_impl();
void global_init();
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 db5d8960f05f3..642626475eb5f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/copy.pass.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
// REQUIRES: std-at-least-c++23
-// ADDITIONAL_COMPILE_FLAGS: -g
+// ADDITIONAL_COMPILE_FLAGS: -g -Og
/*
(19.6.4.2)
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 658140c616676..f859fff61e4dc 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/move.pass.cpp
@@ -29,11 +29,56 @@ void test_move_construct() {
auto a = std::stacktrace::current();
std::stacktrace b{a};
assert(a == b);
- std::stacktrace c{std::move(b)};
- assert(a == c);
}
-void test_move_assign() {}
+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**) {
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 4562f33af2687..f9604c761b17c 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/at.pass.cpp
@@ -15,7 +15,6 @@
*/
#include <cassert>
-#include <iterator>
#include <stacktrace>
_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
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 aab086b47dd32..d837434f8d643 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
@@ -15,7 +15,6 @@
*/
#include <cassert>
-#include <iterator>
#include <stacktrace>
_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
diff --git a/libcxx/test/std/diagnostics/stacktrace/test_allocs.h b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
index 9a2582edcffe2..3219a3e7daf68 100644
--- a/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
+++ b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
@@ -9,7 +9,6 @@
#ifndef _LIBCPP_STACKTRACE_TEST_ALLOCS_H
#define _LIBCPP_STACKTRACE_TEST_ALLOCS_H
-#include <iostream>
#include <memory>
#include <type_traits>
@@ -20,14 +19,17 @@ struct TestAlloc {
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>;
- /** Same as this type but with a different value_type (T) */
- template <typename U>
- using Other = TestAlloc<U, _KNoExCtors, _KNoExAlloc, _KPropagate, _KAlwaysEqual>;
+ auto select_on_container_copy_construction(this auto& self) { return _KPropagate ? self : Self(); }
template <typename U>
struct rebind {
@@ -35,14 +37,11 @@ struct TestAlloc {
};
static std::shared_ptr<std::allocator<std::byte>> new_arena() {
- auto ret = std::make_shared<std::allocator<std::byte>>();
- std::cout << "@@@ new arena " << ret.get() << '\n';
- return ret;
+ return std::make_shared<std::allocator<std::byte>>();
}
static std::shared_ptr<std::allocator<std::byte>> global_arena() {
- auto ret = new_arena();
- std::cout << "@@@ global arena " << ret.get() << '\n';
+ static auto ret = new_arena();
return ret;
}
@@ -53,29 +52,17 @@ struct TestAlloc {
/** Instances are equal IFF they have the same arena pointer (even if this is "always_equals",
since such instances point to the global arena). */
- bool operator==(auto const& rhs) const noexcept {
- std::cout << this << ": cmp " << &rhs << " : " << arena_.get() << " vs " << rhs.arena_.get() << '\n';
- return arena_.get() == rhs.arena_.get();
- }
+ bool operator==(auto const& rhs) const noexcept { return arena_.get() == rhs.arena_.get(); }
/** Construct with a new arena, or, if always-equal, the global arena. */
- TestAlloc() noexcept(_KNoExCtors) : arena_(_KAlwaysEqual ? global_arena() : new_arena()) {
- std::cout << this << ": init " << arena_.get() << '\n';
- }
+ TestAlloc() noexcept(_KNoExCtors) : arena_(_KAlwaysEqual ? global_arena() : new_arena()) {}
template <typename U>
- TestAlloc(Other<U> const& rhs) : arena_(rhs.arena_) {
- std::cout << this << ": init (copyctor) " << arena_.get() << '\n';
- }
+ TestAlloc(Other<U> const& rhs) : arena_(rhs.arena_) {}
template <typename U>
TestAlloc& operator=(Other<U> const& rhs) {
- if (_KPropagate && !_KAlwaysEqual) {
- arena_ = rhs.arena_;
- std::cout << this << ": init (copy assign) prop " << arena_.get() << '\n';
- } else {
- std::cout << this << ": init (copy assign) noprop " << arena_.get() << '\n';
- }
+ arena_ = rhs.arena_;
}
std::allocator<T>& arena() { return *(std::allocator<T>*)arena_.get(); }
>From 6b7b8d250b7c7aa1ee1682fc5739d7004876c33b Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 14 Jul 2025 15:22:18 -0400
Subject: [PATCH 11/14] more noexcept tests
---
...bcxxabi.v1.stable.exceptions.nonew.abilist | 18 +++-
.../stacktrace/basic.cmp/equality.pass.cpp | 5 +-
.../basic.cmp/strong_ordering.pass.cpp | 3 +
.../basic.cons/only_uses_allocator.pass.cpp | 102 +++++++-----------
.../stacktrace/basic.obs/begin_end.pass.cpp | 2 +
.../stacktrace/basic.obs/cbegin_cend.pass.cpp | 2 +
.../basic.obs/crbegin_crend.pass.cpp | 2 +
.../stacktrace/basic.obs/empty.pass.cpp | 1 +
.../basic.obs/get_allocator.pass.cpp | 1 +
.../stacktrace/basic.obs/max_size.pass.cpp | 1 +
.../basic.obs/operator_index.pass.cpp | 1 -
.../stacktrace/basic.obs/rbegin_rend.pass.cpp | 2 +
.../stacktrace/basic.obs/size.pass.cpp | 1 +
.../stacktrace/entry.cmp/equality.pass.cpp | 6 +-
.../entry.cmp/strong_ordering.pass.cpp | 5 +-
.../entry.cons/copy_assign.pass.cpp | 4 +-
.../entry.cons/copy_construct.pass.cpp | 3 +-
.../entry.obs/native_handle.pass.cpp | 4 +-
.../std/diagnostics/stacktrace/test_allocs.h | 4 +
19 files changed, 94 insertions(+), 73 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 8e8ae5bdd27da..6cb8566da45de 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__stacktrace11__to_stringclERKNS_16stacktrace_entryE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEEPKNS_16stacktrace_entryEm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace11__to_stringclERNS_13basic_ostreamIcNS_11char_traitsIcEEEERKNS_16stacktrace_entryE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__112__stacktrace7builder16build_stacktraceEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base16build_stacktraceEmm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4baseC1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4baseC2Ev', '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'}
@@ -1624,9 +1626,13 @@
{'is_defined': True, 'name': '_ZTCNSt3__19strstreamE16_NS_13basic_ostreamIcNS_11char_traitsIcEEEE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt12experimental15fundamentals_v112bad_any_castE', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt12experimental19bad_optional_accessE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFPSt4bytemEEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace3elf6SymbolEEEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace4toolEEEE', 'size': 16, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFvPSt4bytemEEE', 'size': 16, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__110__function6__funcIZNS_12__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_FPSt4bytemEEE', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTINSt3__110__function6__funcIZNS_12__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_FvPSt4bytemEEE', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__time_getE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__time_putE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110ctype_baseE', 'size': 16, 'type': 'OBJECT'}
@@ -1764,11 +1770,17 @@
{'is_defined': True, 'name': '_ZTISt16nested_exception', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTISt18bad_variant_access', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTISt19bad_optional_access', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTIZNSt3__112__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_', 'size': 16, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTIZNSt3__112__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt12experimental15fundamentals_v112bad_any_castE', 'size': 50, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt12experimental19bad_optional_accessE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFPSt4bytemEEE', 'size': 41, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf6SymbolEEEE', 'size': 64, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 65, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace4toolEEEE', 'size': 58, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFvPSt4bytemEEE', 'size': 42, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__110__function6__funcIZNS_12__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_FPSt4bytemEEE', 'size': 126, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSNSt3__110__function6__funcIZNS_12__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_FvPSt4bytemEEE', 'size': 131, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__time_getE', 'size': 21, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__time_putE', 'size': 21, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110ctype_baseE', 'size': 21, 'type': 'OBJECT'}
@@ -1906,6 +1918,8 @@
{'is_defined': True, 'name': '_ZTSSt16nested_exception', 'size': 21, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSSt18bad_variant_access', 'size': 23, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSSt19bad_optional_access', 'size': 24, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSZNSt3__112__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_', 'size': 90, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTSZNSt3__112__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_', 'size': 94, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__110istrstreamE', 'size': 32, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__110ostrstreamE', 'size': 32, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__112__stacktrace10fd_istreamE', 'size': 32, 'type': 'OBJECT'}
@@ -1922,6 +1936,8 @@
{'is_defined': True, 'name': '_ZTTNSt3__19strstreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt12experimental15fundamentals_v112bad_any_castE', 'size': 40, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt12experimental19bad_optional_accessE', 'size': 40, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__funcIZNS_12__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_FPSt4bytemEEE', 'size': 88, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__funcIZNS_12__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_FvPSt4bytemEEE', 'size': 88, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110istrstreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIcLb0EEE', 'size': 112, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIcLb1EEE', 'size': 112, 'type': 'OBJECT'}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
index 19441ab2d3b48..bd1418220d275 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/equality.pass.cpp
@@ -28,6 +28,9 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2b() { return test1()
_LIBCPP_NO_TAIL_CALLS
int main(int, char**) {
auto st1a = test1(); // [test1, main, ...]
+
+ static_assert(noexcept(st1a == st1a));
+
assert(st1a == st1a);
auto st1b = st1a;
@@ -47,7 +50,7 @@ int main(int, char**) {
assert(st2a.size() == st2b.size());
assert(st2a != st2b);
- static_assert(noexcept(st2a.size() == st2b.size()));
+ static_assert(noexcept(st2a == st2b));
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
index 5664e9bc41db2..30a8e36dbd812 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp/strong_ordering.pass.cpp
@@ -34,6 +34,9 @@ int main(int, char**) {
auto st1a = test1(); // [test1, main, ...]
auto st1b = st1a;
+
+ static_assert(noexcept(st1a <=> st1b));
+
assert(st1a == st1b);
auto st2a = test2a(); // [test1, test2a, main, ...]
assert(st1a != st2a);
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.pass.cpp
index ff6a3404a676a..e0d9beef98ec8 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons/only_uses_allocator.pass.cpp
@@ -9,31 +9,6 @@
// REQUIRES: std-at-least-c++23
// ADDITIONAL_COMPILE_FLAGS: -g -O0
-/*
- (19.6.4.2)
-
- // [stacktrace.basic.cons], creation and assignment
- static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept; [1]
- static basic_stacktrace current(size_type skip,
- const allocator_type& alloc = allocator_type()) noexcept; [2]
- static basic_stacktrace current(size_type skip, size_type max_depth,
- const allocator_type& alloc = allocator_type()) noexcept; [3]
-
- basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>); [4]
- explicit basic_stacktrace(const allocator_type& alloc) noexcept; [5]
-
- basic_stacktrace(const basic_stacktrace& other); [6]
- basic_stacktrace(basic_stacktrace&& other) noexcept; [7]
- basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc); [8]
- basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc); [9]
- basic_stacktrace& operator=(const basic_stacktrace& other); [10]
- basic_stacktrace& operator=(basic_stacktrace&& other)
- noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
- allocator_traits<Allocator>::is_always_equal::value); [11]
-
- ~basic_stacktrace(); [12]
-*/
-
#include <cassert>
#include <cstdlib>
#include <stacktrace>
@@ -41,18 +16,38 @@
/*
* 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.
+ * counting the number of calls, through and not through the allocator.
*/
+unsigned new_count;
+unsigned del_count;
unsigned custom_alloc;
unsigned custom_dealloc;
-void* operator new(size_t size) { return malloc(size); }
-void* operator new[](size_t size) { return malloc(size); }
-void operator delete(void* ptr) noexcept { free(ptr); }
-void operator delete(void* ptr, size_t) noexcept { free(ptr); }
-void operator delete[](void* ptr) noexcept { free(ptr); }
-void operator delete[](void* ptr, size_t) noexcept { free(ptr); }
+void* operator new(size_t size) {
+ ++new_count;
+ return malloc(size);
+}
+void* operator new[](size_t size) {
+ ++new_count;
+ return malloc(size);
+}
+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 {
@@ -92,37 +87,22 @@ struct test_alloc {
}
};
-/*
- (19.6.4.2) [stacktrace.basic.cons], creation and assignment,
- only exercising usage of caller-provided allocator.
-
- static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept; [1]
- static basic_stacktrace current(size_type skip,
- const allocator_type& alloc = allocator_type()) noexcept; [2]
- static basic_stacktrace current(size_type skip, size_type max_depth,
- const allocator_type& alloc = allocator_type()) noexcept; [3]
-
- explicit basic_stacktrace(const allocator_type& alloc) noexcept; [5]
- basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc); [8]
- basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc); [9]
-
- basic_stacktrace& operator=(const basic_stacktrace& other); [10]
- basic_stacktrace& operator=(basic_stacktrace&& other)
- noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
- allocator_traits<Allocator>::is_always_equal::value); [11]
-*/
-
-void do_current_stacktrace() {
- using A = test_alloc<std::stacktrace_entry>;
- (void)std::basic_stacktrace<A>::current(A());
-}
-
_LIBCPP_NO_TAIL_CALLS
int main(int, char**) {
+ // Clear these counters in case anything was created/deleted prior to `main`,
+ // and in case taking a stacktrace involved initialization of something and is
+ // outside our control.
+ (void)std::stacktrace::current();
+ new_count = del_count = 0;
+
{
- do_current_stacktrace();
- assert(custom_alloc > 0);
- }
- assert(custom_dealloc == custom_alloc);
+ using A = test_alloc<std::stacktrace_entry>;
+ auto st = std::basic_stacktrace<A>::current();
+ assert(custom_alloc > 0); // Ensure allocator was called at some point
+ } // Exit this scope to destroy stacktrace (as well as allocator)
+
+ assert(custom_alloc == new_count); // All "new" calls should have been through allocator
+ assert(custom_alloc == custom_dealloc); // and all allocations should be deallocated
+
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
index db3189d7cb007..23c3e44a36c26 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
@@ -26,6 +26,8 @@ _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:
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 3d084d45ef7b2..237bf314fb233 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
@@ -26,6 +26,8 @@ _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:
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 5d4d6fc6737f9..fa4e646992439 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
@@ -26,6 +26,8 @@ _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:
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 dc3c6baa8b978..f60be2f13f6ad 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/empty.pass.cpp
@@ -24,6 +24,7 @@ _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());
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
index 2b1004f38051d..a199657963ee2 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/get_allocator.pass.cpp
@@ -20,6 +20,7 @@
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
index 81768ec965cff..fbdfaa854caa7 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
@@ -26,6 +26,7 @@ _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, std::allocator<std::stacktrace_entry>>().max_size()));
return 0;
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs/operator_index.pass.cpp
index d837434f8d643..69bd00372d109 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
@@ -24,7 +24,6 @@ _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]);
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 f8446d42b5f03..31c1b7449482c 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
@@ -26,6 +26,8 @@ _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:
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 cbfac43b4beaf..6fa04e5b7b55a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs/size.pass.cpp
@@ -24,6 +24,7 @@ _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);
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
index a3af41a07720a..37edce6f7f313 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/equality.pass.cpp
@@ -32,13 +32,15 @@ int main(int, char**) {
std::stacktrace_entry a;
std::stacktrace_entry b;
std::stacktrace_entry c;
+
*(uintptr_t*)(&a) = uintptr_t(&func1);
*(uintptr_t*)(&b) = uintptr_t(&func1);
*(uintptr_t*)(&c) = uintptr_t(&func2);
- assert(a == b);
- assert(a != c);
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
index 0bec29fcc9b68..fb89592a5177f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp/strong_ordering.pass.cpp
@@ -40,15 +40,16 @@ int main(int, char**) {
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));
- static_assert(noexcept(a <=> b));
-
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
index 9a5968d9cc6f4..4a025695bc1bf 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
@@ -27,8 +27,10 @@ _LIBCPP_NO_TAIL_CALLS
int main(int, char**) {
static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
- std::stacktrace_entry e1;
+ std::stacktrace_entry 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
index bfc16478dfdb5..9862a63a01479 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
@@ -27,7 +27,8 @@ _LIBCPP_NO_TAIL_CALLS
int main(int, char**) {
static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
- std::stacktrace_entry e1;
+ std::stacktrace_entry e1 = std::stacktrace::current()[0];
+ static_assert(noexcept(std::stacktrace_entry(e1)));
std::stacktrace_entry e2(e1);
assert(e2 == e1);
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
index 2fdcdb49f2fd2..fa11b419ff9ee 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs/native_handle.pass.cpp
@@ -22,9 +22,7 @@ namespace std {
int main(int, char**) noexcept {
std::stacktrace_entry e;
- assert(e.native_handle() == 0);
-
static_assert(noexcept(e.native_handle()));
-
+ assert(e.native_handle() == 0);
return 0;
}
diff --git a/libcxx/test/std/diagnostics/stacktrace/test_allocs.h b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
index 3219a3e7daf68..274b6362d4e36 100644
--- a/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
+++ b/libcxx/test/std/diagnostics/stacktrace/test_allocs.h
@@ -6,6 +6,10 @@
//
//===----------------------------------------------------------------------===//
+/*
+Allocator class useful for testing various propagation, always-equal scenarios.
+*/
+
#ifndef _LIBCPP_STACKTRACE_TEST_ALLOCS_H
#define _LIBCPP_STACKTRACE_TEST_ALLOCS_H
>From 09cd2020fb36731c6fbda86e7124f0370a6c8ba5 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 14 Jul 2025 16:07:47 -0400
Subject: [PATCH 12/14] avoid creating strings
---
libcxx/src/stacktrace/utils/failed.h | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/libcxx/src/stacktrace/utils/failed.h b/libcxx/src/stacktrace/utils/failed.h
index 3f8a24dd88eb6..dcd89507339c1 100644
--- a/libcxx/src/stacktrace/utils/failed.h
+++ b/libcxx/src/stacktrace/utils/failed.h
@@ -11,16 +11,18 @@
#include <__config>
#include <cerrno>
-#include <stdexcept>
+#include <exception>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-struct failed : std::runtime_error {
- virtual ~failed() = default;
+struct failed : std::exception {
+ char const* msg_{};
int errno_{0};
- failed() : std::runtime_error({}) {}
- failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
+ failed(char const* msg, int err) : std::exception(), msg_(msg), errno_(err) {}
+
+ virtual ~failed() noexcept = default;
+ const char* what() const noexcept override { return msg_; }
};
} // namespace __stacktrace
>From 46f85ab739a06172ec11746c8e1d214cef4ea7ce Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 14 Jul 2025 23:17:52 -0400
Subject: [PATCH 13/14] fix clang-tidy errors
---
libcxx/include/__stacktrace/base.h | 32 ++++++++++++++---------------
libcxx/include/__stacktrace/basic.h | 5 ++---
libcxx/src/stacktrace/macos/impl.h | 2 --
libcxx/src/stacktrace/utils/fd.h | 2 +-
4 files changed, 19 insertions(+), 22 deletions(-)
diff --git a/libcxx/include/__stacktrace/base.h b/libcxx/include/__stacktrace/base.h
index 8c933ddea6068..b76b336595e9b 100644
--- a/libcxx/include/__stacktrace/base.h
+++ b/libcxx/include/__stacktrace/base.h
@@ -95,6 +95,22 @@ struct _LIBCPP_EXPORTED_FROM_ABI base {
return list(std::forward<_Args>(__args)..., make_alloc<_Tp>());
}
+ template <class _Allocator>
+ auto _LIBCPP_HIDE_FROM_ABI __alloc_wrap(_Allocator const& __alloc) {
+ using _AT = allocator_traits<_Allocator>;
+ using _BA = typename _AT::template rebind_alloc<byte>;
+ auto __ba = _BA(__alloc);
+ return [__ba = std::move(__ba)](size_t __sz) mutable { return __ba.allocate(__sz); };
+ }
+
+ template <class _Allocator>
+ auto _LIBCPP_HIDE_FROM_ABI __dealloc_wrap(_Allocator const& __alloc) {
+ using _AT = allocator_traits<_Allocator>;
+ using _BA = typename _AT::template rebind_alloc<byte>;
+ auto __ba = _BA(__alloc);
+ return [__ba = std::move(__ba)](void* __ptr, size_t __sz) mutable { __ba.deallocate((byte*)__ptr, __sz); };
+ }
+
_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
build_stacktrace(size_t __skip, size_t __max_depth);
@@ -119,22 +135,6 @@ struct _LIBCPP_HIDE_FROM_ABI entry_base {
_LIBCPP_HIDE_FROM_ABI stacktrace_entry to_stacktrace_entry() const;
};
-template <class _Allocator>
-auto __alloc_wrap(_Allocator const& __alloc) {
- using _AT = allocator_traits<_Allocator>;
- using _BA = typename _AT::template rebind_alloc<byte>;
- auto __ba = _BA(__alloc);
- return [__ba = std::move(__ba)](size_t __sz) mutable { return __ba.allocate(__sz); };
-}
-
-template <class _Allocator>
-auto __dealloc_wrap(_Allocator const& __alloc) {
- using _AT = allocator_traits<_Allocator>;
- using _BA = typename _AT::template rebind_alloc<byte>;
- auto __ba = _BA(__alloc);
- return [__ba = std::move(__ba)](void* __ptr, size_t __sz) mutable { __ba.deallocate((byte*)__ptr, __sz); };
-}
-
template <class _Allocator>
_LIBCPP_EXPORTED_FROM_ABI base::base(_Allocator __alloc)
: __alloc_bytes_(__alloc_wrap(__alloc)),
diff --git a/libcxx/include/__stacktrace/basic.h b/libcxx/include/__stacktrace/basic.h
index dd34ad1a123bf..6ce1f10c51f64 100644
--- a/libcxx/include/__stacktrace/basic.h
+++ b/libcxx/include/__stacktrace/basic.h
@@ -113,14 +113,13 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace : private __stacktrace::base {
: base(__alloc), __entries_(__alloc_) {}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other)
- : __alloc_(_ATraits::select_on_container_copy_construction(__other.__alloc_)),
- __entries_(__other.__entries_, __alloc_) {}
+ : basic_stacktrace(__other, _ATraits::select_on_container_copy_construction(__other.__alloc_)) {}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept
: __alloc_(std::move(__other.__alloc_)), __entries_(std::move(__other.__entries_)) {}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
- : base(__alloc), __entries_(__other.__entries_, __alloc) {}
+ : base(__alloc), __alloc_(__alloc), __entries_(__other.__entries_, __alloc) {}
_LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
: base(__alloc) {
diff --git a/libcxx/src/stacktrace/macos/impl.h b/libcxx/src/stacktrace/macos/impl.h
index 71c3c04a6471e..fcc8a11501255 100644
--- a/libcxx/src/stacktrace/macos/impl.h
+++ b/libcxx/src/stacktrace/macos/impl.h
@@ -14,8 +14,6 @@
#include <cstddef>
#include <cstdlib>
-#include <__config>
-#include <__config_site>
#include <__stacktrace/base.h>
_LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/utils/fd.h b/libcxx/src/stacktrace/utils/fd.h
index f4be1876e5d37..75f169dc5fa14 100644
--- a/libcxx/src/stacktrace/utils/fd.h
+++ b/libcxx/src/stacktrace/utils/fd.h
@@ -110,7 +110,7 @@ struct fd_mmap final {
_LIBCPP_HIDE_FROM_ABI explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
if (fd_) {
- if ((size_ = ::lseek(fd, 0, SEEK_END))) {
+ if ((size_ = ::lseek(fd_, 0, SEEK_END))) {
addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
}
}
>From 7319e6d08b6186fb78df7416730b9c8aa4c7e792 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Tue, 15 Jul 2025 09:10:12 -0400
Subject: [PATCH 14/14] fix ABI list
---
...bcxxabi.v1.stable.exceptions.nonew.abilist | 24 +++++++++++--------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6cb8566da45de..d3ceb4a6792a6 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'}
@@ -225,6 +226,10 @@
{'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__116stacktrace_entry11descriptionEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__116stacktrace_entry11source_fileEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__116stacktrace_entry11source_lineEv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNKSt3__116stacktrace_entry13native_handleEv', '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'}
@@ -630,6 +635,7 @@
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4base16build_stacktraceEmm', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4baseC1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4baseC2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__112__stacktrace4baseC2INS_9allocatorINS_16stacktrace_entryEEEEET_', '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'}
@@ -1631,8 +1637,6 @@
{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFbRKNS_12__stacktrace4toolEEEE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__function6__baseIFvPSt4bytemEEE', 'size': 16, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTINSt3__110__function6__funcIZNS_12__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_FPSt4bytemEEE', 'size': 24, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTINSt3__110__function6__funcIZNS_12__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_FvPSt4bytemEEE', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__time_getE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110__time_putE', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTINSt3__110ctype_baseE', 'size': 16, 'type': 'OBJECT'}
@@ -1770,8 +1774,6 @@
{'is_defined': True, 'name': '_ZTISt16nested_exception', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTISt18bad_variant_access', 'size': 24, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTISt19bad_optional_access', 'size': 24, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTIZNSt3__112__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_', 'size': 16, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTIZNSt3__112__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_', 'size': 16, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt12experimental15fundamentals_v112bad_any_castE', 'size': 50, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt12experimental19bad_optional_accessE', 'size': 40, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFPSt4bytemEEE', 'size': 41, 'type': 'OBJECT'}
@@ -1779,8 +1781,6 @@
{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 65, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFbRKNS_12__stacktrace4toolEEEE', 'size': 58, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__function6__baseIFvPSt4bytemEEE', 'size': 42, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTSNSt3__110__function6__funcIZNS_12__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_FPSt4bytemEEE', 'size': 126, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTSNSt3__110__function6__funcIZNS_12__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_FvPSt4bytemEEE', 'size': 131, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__time_getE', 'size': 21, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110__time_putE', 'size': 21, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSNSt3__110ctype_baseE', 'size': 21, 'type': 'OBJECT'}
@@ -1918,8 +1918,6 @@
{'is_defined': True, 'name': '_ZTSSt16nested_exception', 'size': 21, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSSt18bad_variant_access', 'size': 23, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTSSt19bad_optional_access', 'size': 24, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTSZNSt3__112__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_', 'size': 90, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTSZNSt3__112__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_', 'size': 94, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__110istrstreamE', 'size': 32, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__110ostrstreamE', 'size': 32, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTTNSt3__112__stacktrace10fd_istreamE', 'size': 32, 'type': 'OBJECT'}
@@ -1936,8 +1934,11 @@
{'is_defined': True, 'name': '_ZTTNSt3__19strstreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt12experimental15fundamentals_v112bad_any_castE', 'size': 40, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt12experimental19bad_optional_accessE', 'size': 40, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__110__function6__funcIZNS_12__stacktrace12__alloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlmE_FPSt4bytemEEE', 'size': 88, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZTVNSt3__110__function6__funcIZNS_12__stacktrace14__dealloc_wrapINS_9allocatorINS_16stacktrace_entryEEEEEDaRKT_EUlPvmE_FvPSt4bytemEEE', 'size': 88, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFPSt4bytemEEE', 'size': 88, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf6SymbolEEEE', 'size': 88, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFbRKNS_12__stacktrace3elf7SectionEEEE', 'size': 88, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFbRKNS_12__stacktrace4toolEEEE', 'size': 88, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__110__function6__baseIFvPSt4bytemEEE', 'size': 88, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110istrstreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIcLb0EEE', 'size': 112, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__110moneypunctIcLb1EEE', 'size': 112, 'type': 'OBJECT'}
@@ -1948,6 +1949,7 @@
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace10fd_istreamE', 'size': 80, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace15llvm_symbolizerE', 'size': 48, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace4atosE', 'size': 48, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace4toolE', 'size': 48, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace6failedE', 'size': 40, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112__stacktrace9addr2lineE', 'size': 48, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZTVNSt3__112bad_weak_ptrE', 'size': 40, 'type': 'OBJECT'}
@@ -2012,6 +2014,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'}
More information about the libcxx-commits
mailing list