[libcxx-commits] [libcxx] [llvm] Add C++23 stacktrace (P0881R7) (PR #136528)
Steve O'Brien via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Apr 23 12:23:39 PDT 2025
https://github.com/elsteveogrande updated https://github.com/llvm/llvm-project/pull/136528
>From bbd5ce7ab02d89a5dd1c0da2e0d1ff4900c91a5a 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 1/6] Add C++23 stacktrace
---
libcxx/CMakeLists.txt | 6 +
libcxx/docs/UserDocumentation.rst | 1 +
libcxx/docs/VendorDocumentation.rst | 8 +
libcxx/include/CMakeLists.txt | 7 +
libcxx/include/__config | 27 ++
libcxx/include/__config_site.in | 1 +
libcxx/include/__ostream/basic_ostream.h | 2 +-
.../__stacktrace/basic_stacktrace.h | 306 +++++++++++++++++
.../experimental/__stacktrace/detail/alloc.h | 103 ++++++
.../__stacktrace/detail/context.h | 53 +++
.../experimental/__stacktrace/detail/entry.h | 44 +++
.../__stacktrace/detail/to_string.h | 46 +++
.../__stacktrace/stacktrace_entry.h | 111 ++++++
libcxx/include/experimental/stacktrace | 191 +++++++++++
libcxx/include/module.modulemap.in | 10 +
libcxx/modules/std/stacktrace.inc | 7 +-
libcxx/src/CMakeLists.txt | 19 ++
libcxx/src/experimental/stacktrace/alloc.cpp | 20 ++
.../experimental/stacktrace/common/config.h | 41 +++
.../experimental/stacktrace/common/debug.cpp | 19 ++
.../experimental/stacktrace/common/debug.h | 55 +++
.../experimental/stacktrace/common/failed.h | 29 ++
.../src/experimental/stacktrace/common/fd.cpp | 31 ++
.../src/experimental/stacktrace/common/fd.h | 122 +++++++
.../experimental/stacktrace/common/images.h | 33 ++
.../src/experimental/stacktrace/context.cpp | 104 ++++++
.../src/experimental/stacktrace/linux/elf.h | 315 ++++++++++++++++++
.../stacktrace/linux/linux-dl.cpp | 58 ++++
.../stacktrace/linux/linux-elf.cpp | 53 +++
.../stacktrace/linux/linux-sym.cpp | 61 ++++
.../src/experimental/stacktrace/linux/linux.h | 98 ++++++
.../src/experimental/stacktrace/osx/osx.cpp | 111 ++++++
libcxx/src/experimental/stacktrace/osx/osx.h | 31 ++
.../experimental/stacktrace/stacktrace.cpp | 94 ++++++
.../stacktrace/tools/addr2line.cpp | 100 ++++++
.../experimental/stacktrace/tools/atos.cpp | 115 +++++++
.../stacktrace/tools/llvm_symbolizer.cpp | 112 +++++++
.../experimental/stacktrace/tools/pspawn.h | 163 +++++++++
.../src/experimental/stacktrace/tools/tools.h | 65 ++++
.../stacktrace/tools/toolspawner.cpp | 185 ++++++++++
.../experimental/stacktrace/unwind/unwind.cpp | 60 ++++
.../experimental/stacktrace/unwind/unwind.h | 30 ++
.../stacktrace/windows/dbghelp_dll.cpp | 56 ++++
.../stacktrace/windows/dbghelp_dll.h | 71 ++++
.../experimental/stacktrace/windows/dll.cpp | 40 +++
.../src/experimental/stacktrace/windows/dll.h | 68 ++++
.../stacktrace/windows/psapi_dll.cpp | 48 +++
.../stacktrace/windows/psapi_dll.h | 55 +++
.../stacktrace/windows/win_impl.cpp | 204 ++++++++++++
.../stacktrace/windows/win_impl.h | 38 +++
.../stacktrace/simple.o0.nodebug.pass.cpp | 32 ++
.../stacktrace/simple.o0.nosplit.pass.cpp | 29 ++
.../stacktrace/simple.o0.split.pass.cpp | 29 ++
.../stacktrace/simple.o3.nodebug.pass.cpp | 32 ++
.../stacktrace/simple.o3.nosplit.pass.cpp | 29 ++
.../stacktrace/simple.o3.split.pass.cpp | 29 ++
.../test/libcxx/transitive_includes/cxx03.csv | 5 +
.../test/libcxx/transitive_includes/cxx11.csv | 5 +
.../test/libcxx/transitive_includes/cxx14.csv | 5 +
.../test/libcxx/transitive_includes/cxx17.csv | 5 +
.../test/libcxx/transitive_includes/cxx23.csv | 29 ++
.../test/libcxx/transitive_includes/cxx26.csv | 29 ++
.../diagnostics/stacktrace/basic.cmp.pass.cpp | 106 ++++++
.../stacktrace/basic.cons.pass.cpp | 308 +++++++++++++++++
.../stacktrace/basic.hash.pass.cpp | 44 +++
.../diagnostics/stacktrace/basic.mod.pass.cpp | 40 +++
.../stacktrace/basic.nonmem.pass.cpp | 91 +++++
.../diagnostics/stacktrace/basic.obs.pass.cpp | 156 +++++++++
.../std/diagnostics/stacktrace/basic.pass.cpp | 154 +++++++++
.../diagnostics/stacktrace/entry.cmp.pass.cpp | 65 ++++
.../stacktrace/entry.cons.pass.cpp | 63 ++++
.../diagnostics/stacktrace/entry.obs.pass.cpp | 51 +++
.../std/diagnostics/stacktrace/entry.pass.cpp | 66 ++++
.../stacktrace/entry.query.pass.cpp | 85 +++++
.../diagnostics/stacktrace/format.pass.cpp | 53 +++
.../std/diagnostics/stacktrace/syn.pass.cpp | 147 ++++++++
libcxx/utils/libcxx/header_information.py | 4 +-
llvm/utils/gn/secondary/libcxx/src/BUILD.gn | 34 ++
78 files changed, 5348 insertions(+), 4 deletions(-)
create mode 100644 libcxx/include/experimental/__stacktrace/basic_stacktrace.h
create mode 100644 libcxx/include/experimental/__stacktrace/detail/alloc.h
create mode 100644 libcxx/include/experimental/__stacktrace/detail/context.h
create mode 100644 libcxx/include/experimental/__stacktrace/detail/entry.h
create mode 100644 libcxx/include/experimental/__stacktrace/detail/to_string.h
create mode 100644 libcxx/include/experimental/__stacktrace/stacktrace_entry.h
create mode 100644 libcxx/include/experimental/stacktrace
create mode 100644 libcxx/src/experimental/stacktrace/alloc.cpp
create mode 100644 libcxx/src/experimental/stacktrace/common/config.h
create mode 100644 libcxx/src/experimental/stacktrace/common/debug.cpp
create mode 100644 libcxx/src/experimental/stacktrace/common/debug.h
create mode 100644 libcxx/src/experimental/stacktrace/common/failed.h
create mode 100644 libcxx/src/experimental/stacktrace/common/fd.cpp
create mode 100644 libcxx/src/experimental/stacktrace/common/fd.h
create mode 100644 libcxx/src/experimental/stacktrace/common/images.h
create mode 100644 libcxx/src/experimental/stacktrace/context.cpp
create mode 100644 libcxx/src/experimental/stacktrace/linux/elf.h
create mode 100644 libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
create mode 100644 libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
create mode 100644 libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
create mode 100644 libcxx/src/experimental/stacktrace/linux/linux.h
create mode 100644 libcxx/src/experimental/stacktrace/osx/osx.cpp
create mode 100644 libcxx/src/experimental/stacktrace/osx/osx.h
create mode 100644 libcxx/src/experimental/stacktrace/stacktrace.cpp
create mode 100644 libcxx/src/experimental/stacktrace/tools/addr2line.cpp
create mode 100644 libcxx/src/experimental/stacktrace/tools/atos.cpp
create mode 100644 libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
create mode 100644 libcxx/src/experimental/stacktrace/tools/pspawn.h
create mode 100644 libcxx/src/experimental/stacktrace/tools/tools.h
create mode 100644 libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
create mode 100644 libcxx/src/experimental/stacktrace/unwind/unwind.cpp
create mode 100644 libcxx/src/experimental/stacktrace/unwind/unwind.h
create mode 100644 libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
create mode 100644 libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
create mode 100644 libcxx/src/experimental/stacktrace/windows/dll.cpp
create mode 100644 libcxx/src/experimental/stacktrace/windows/dll.h
create mode 100644 libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
create mode 100644 libcxx/src/experimental/stacktrace/windows/psapi_dll.h
create mode 100644 libcxx/src/experimental/stacktrace/windows/win_impl.cpp
create mode 100644 libcxx/src/experimental/stacktrace/windows/win_impl.h
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
create mode 100644 libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
create mode 100644 libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index ebaa6e9fd0e97..b12bf2ead76e5 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -131,6 +131,11 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
the shared library they shipped should turn this on and see `include/__configuration/availability.h`
for more details." OFF)
+option(LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
+ "For C++23 <stacktrace>: whether to allow invocation of `addr2line`, `llvm-addr2line` or `atos`
+ at runtime (if it's available in PATH) to resolve call-chain addresses in the stacktrace
+ into source locations, if other methods are not available." ON)
+
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
elseif(MINGW)
@@ -757,6 +762,7 @@ config_define(${LIBCXX_ENABLE_UNICODE} _LIBCPP_HAS_UNICODE)
config_define(${LIBCXX_ENABLE_WIDE_CHARACTERS} _LIBCPP_HAS_WIDE_CHARACTERS)
config_define(${LIBCXX_ENABLE_TIME_ZONE_DATABASE} _LIBCPP_HAS_TIME_ZONE_DATABASE)
config_define(${LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS} _LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS)
+config_define(${LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME} _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME)
# TODO: Remove in LLVM 21. We're leaving an error to make this fail explicitly.
if (LIBCXX_ENABLE_ASSERTIONS)
diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 4a11a10224ae9..649b7551eb746 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -70,6 +70,7 @@ when ``-fexperimental-library`` is passed:
* The parallel algorithms library (``<execution>`` and the associated algorithms)
* ``std::chrono::tzdb`` and related time zone functionality
+* ``<stacktrace>``
* ``<syncstream>``
.. note::
diff --git a/libcxx/docs/VendorDocumentation.rst b/libcxx/docs/VendorDocumentation.rst
index 959a28607d75d..b05d494db4f9b 100644
--- a/libcxx/docs/VendorDocumentation.rst
+++ b/libcxx/docs/VendorDocumentation.rst
@@ -185,6 +185,14 @@ General purpose options
ship the IANA time zone database. When time zones are not supported,
time zone support in <chrono> will be disabled.
+.. option:: LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME:BOOL
+
+ **Default**: ``OFF``
+
+ For C++23 <stacktrace>: whether to allow invocation of ``addr2line`` or ``llvm-addr2line``
+ at runtime (if it's available in PATH) to resolve call-chain addresses in the stacktrace
+ into source locations, if other methods are not available.
+
.. option:: LIBCXX_INSTALL_LIBRARY_DIR:PATH
**Default**: ``lib${LIBCXX_LIBDIR_SUFFIX}``
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index f1bdf684a8549..ef091e40b9ec8 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -991,10 +991,17 @@ set(files
experimental/__simd/traits.h
experimental/__simd/utility.h
experimental/__simd/vec_ext.h
+ experimental/__stacktrace/basic_stacktrace.h
+ experimental/__stacktrace/detail/alloc.h
+ experimental/__stacktrace/detail/context.h
+ experimental/__stacktrace/detail/entry.h
+ experimental/__stacktrace/detail/to_string.h
+ experimental/__stacktrace/stacktrace_entry.h
experimental/iterator
experimental/memory
experimental/propagate_const
experimental/simd
+ experimental/stacktrace
experimental/type_traits
experimental/utility
ext/__hash
diff --git a/libcxx/include/__config b/libcxx/include/__config
index e14632f65b877..071961cf73438 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -977,6 +977,33 @@ typedef __char32_t char32_t;
# define _LIBCPP_NOINLINE
# endif
+// Some functions, e.g. std::stacktrace::current, need to avoid being
+// tail-called by (and tail-calling other) functions, for proper enumeration of
+// call-stack frames.
+// clang-format off
+
+// Disables tail-call optimization for "outbound" calls
+// performed in the function annotated with this attribute.
+# if __has_cpp_attribute(_Clang::__disable_tail_calls__)
+# define _LIBCPP_NO_TAIL_CALLS_OUT [[_Clang::__disable_tail_calls__]]
+# elif __has_cpp_attribute(__gnu__::__optimize__)
+# define _LIBCPP_NO_TAIL_CALLS_OUT [[__gnu__::__optimize__("no-optimize-sibling-calls")]]
+# else
+# define _LIBCPP_NO_TAIL_CALLS_OUT
+# endif
+
+// Disables tail-call optimization for "inbound" calls -- that is,
+// calls from some other function calling the one having this attribute.
+# if __has_cpp_attribute(_Clang::__not_tail_called__)
+# define _LIBCPP_NO_TAIL_CALLS_IN [[_Clang::__not_tail_called__]]
+# else
+# define _LIBCPP_NO_TAIL_CALLS_IN
+# endif
+
+// Disable TCO for calls into, and out from, the annotated function.
+# define _LIBCPP_NO_TAIL_CALLS _LIBCPP_NO_TAIL_CALLS_IN _LIBCPP_NO_TAIL_CALLS_OUT
+// clang-format on
+
// We often repeat things just for handling wide characters in the library.
// When wide characters are disabled, it can be useful to have a quick way of
// disabling it without having to resort to #if-#endif, which has a larger
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index fc01aaf2d8746..4e25ed040c9d0 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -33,6 +33,7 @@
#cmakedefine _LIBCPP_HAS_NO_STD_MODULES
#cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
#cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
+#cmakedefine01 _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
// PSTL backends
#cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h
index f7473a36d8ccc..891dddbf79c42 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -570,7 +570,7 @@ _LIBCPP_HIDE_FROM_ABI _Stream&& operator<<(_Stream&& __os, const _Tp& __x) {
}
template <class _CharT, class _Traits, class _Allocator>
-basic_ostream<_CharT, _Traits>&
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const basic_string<_CharT, _Traits, _Allocator>& __str) {
return std::__put_character_sequence(__os, __str.data(), __str.size());
}
diff --git a/libcxx/include/experimental/__stacktrace/basic_stacktrace.h b/libcxx/include/experimental/__stacktrace/basic_stacktrace.h
new file mode 100644
index 0000000000000..8ac1de82b1c63
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/basic_stacktrace.h
@@ -0,0 +1,306 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
+#define _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
+
+#include <experimental/__stacktrace/detail/entry.h>
+#include <experimental/__stacktrace/stacktrace_entry.h>
+
+#include <__config>
+#include <__format/formatter.h>
+#include <__functional/function.h>
+#include <__functional/hash.h>
+#include <__fwd/format.h>
+#include <__fwd/sstream.h>
+#include <__iterator/iterator.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/reverse_access.h>
+#include <__iterator/reverse_iterator.h>
+#include <__memory/allocator.h>
+#include <__memory/allocator_traits.h>
+#include <__memory_resource/memory_resource.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__ostream/basic_ostream.h>
+#include <__utility/move.h>
+#include <__vector/pmr.h>
+#include <__vector/swap.h>
+#include <__vector/vector.h>
+#include <cstddef>
+#include <list>
+#include <memory>
+#include <string>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/to_string.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// (19.6.4)
+// Class template basic_stacktrace [stacktrace.basic]
+
+class stacktrace_entry;
+
+template <class _Allocator>
+class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
+ friend struct hash<basic_stacktrace<_Allocator>>;
+ friend struct __stacktrace::__to_string;
+
+ using _ATraits _LIBCPP_NODEBUG = allocator_traits<_Allocator>;
+ constexpr static bool __kPropOnCopy = _ATraits::propagate_on_container_copy_assignment::value;
+ constexpr static bool __kPropOnMove = _ATraits::propagate_on_container_move_assignment::value;
+ constexpr static bool __kPropOnSwap = _ATraits::propagate_on_container_swap::value;
+ constexpr static bool __kAlwaysEqual = _ATraits::is_always_equal::value;
+ constexpr static bool __kNoThrowDflConstruct = is_nothrow_default_constructible_v<_Allocator>;
+ constexpr static bool __kNoThrowAlloc =
+ noexcept(noexcept(_Allocator().allocate(1)) && noexcept(_Allocator().allocate_at_least(1)));
+
+ using __entry_vec _LIBCPP_NODEBUG = vector<stacktrace_entry, _Allocator>;
+
+ [[no_unique_address]] _Allocator __alloc_;
+ __entry_vec __entries_;
+
+ _LIBCPP_HIDE_FROM_ABI basic_stacktrace(const _Allocator& __alloc, std::pmr::list<__stacktrace::entry>&& __vec)
+ : __alloc_(__alloc), __entries_(__alloc) {
+ __entries_.reserve(__vec.size());
+ for (auto& __entry : __vec) {
+ __entries_.emplace_back(std::move(__entry));
+ }
+ }
+
+public:
+ // (19.6.4.1)
+ // Overview [stacktrace.basic.overview]
+
+ using value_type = stacktrace_entry;
+ using const_reference = const value_type&;
+ using reference = value_type&;
+ using difference_type = ptrdiff_t;
+ using size_type = size_t;
+ using allocator_type = _Allocator;
+ using const_iterator = decltype(__entries_)::const_iterator;
+ using iterator = const_iterator;
+
+ using reverse_iterator = std::reverse_iterator<basic_stacktrace::iterator>;
+ using const_reverse_iterator = std::reverse_iterator<basic_stacktrace::const_iterator>;
+
+ // (19.6.4.2)
+ // Creation and assignment [stacktrace.basic.cons]
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
+ current(const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ __stacktrace::alloc __alloc(__caller_alloc);
+ __stacktrace::context __tr{__alloc};
+ __tr.do_stacktrace(1, /* infinite max_depth */ ~0);
+ return {__caller_alloc, std::move(__tr.__entries_)};
+ }
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
+ current(size_type __skip, const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ __stacktrace::alloc __alloc(__caller_alloc);
+ __stacktrace::context __tr{__alloc};
+ __tr.do_stacktrace(__skip + 1, /* infinite max_depth */ ~0);
+ return {__caller_alloc, std::move(__tr.__entries_)};
+ }
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
+ current(size_type __skip,
+ size_type __max_depth,
+ const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ __stacktrace::alloc __alloc(__caller_alloc);
+ __stacktrace::context __tr{__alloc};
+ __tr.do_stacktrace(__skip + 1, __max_depth);
+ return {__caller_alloc, std::move(__tr.__entries_)};
+ }
+
+ _LIBCPP_EXPORTED_FROM_ABI constexpr ~basic_stacktrace() = default;
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace() noexcept(__kNoThrowDflConstruct) : basic_stacktrace(allocator_type()) {}
+
+ _LIBCPP_EXPORTED_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc) noexcept
+ : __alloc_(__alloc), __entries_(__alloc_) {}
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other) = default;
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other) noexcept = default;
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace const& __other, allocator_type const& __alloc)
+ : __alloc_(__alloc), __entries_(__other.__entries_, __alloc) {}
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, allocator_type const& __alloc)
+ : __alloc_(__alloc) {
+ if (__kAlwaysEqual || __alloc_ == __other.__alloc_) {
+ __entries_ = std::move(__other.__entries_);
+ } else {
+ // "moving" from a container with a different allocator; we're forced to copy items instead
+ for (auto const& __entry : __other.__entries_) {
+ __entries_.push_back(__entry);
+ }
+ }
+ }
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& __other) {
+ if (this == std::addressof(__other)) {
+ return *this;
+ }
+ if (__kPropOnCopy) {
+ __alloc_ = __other.__alloc_;
+ }
+ __entries_ = {__other.__entries_, __alloc_};
+ return *this;
+ }
+
+ _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace&
+ operator=(basic_stacktrace&& __other) noexcept(__kPropOnMove || __kAlwaysEqual) {
+ if (this == std::addressof(__other)) {
+ return *this;
+ }
+ if (__kPropOnMove) {
+ __alloc_ = __other.__alloc_;
+ __entries_ = std::move(__other.__entries_);
+ } else {
+ auto __allocs_eq = __kAlwaysEqual || __alloc_ == __other.__alloc_;
+ if (__allocs_eq) {
+ __entries_ = std::move(__other.__entries_);
+ } else {
+ // "moving" from a container with a different allocator;
+ // we're forced to copy items instead
+ for (auto const& __entry : __other.__entries_) {
+ __entries_.push_back(__entry);
+ }
+ }
+ }
+ return *this;
+ }
+
+ // clang-format on
+
+ // (19.6.4.3)
+ // [stacktrace.basic.obs], observers
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI allocator_type get_allocator() const noexcept { return __alloc_; }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator begin() const noexcept { return __entries_.begin(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator end() const noexcept { return __entries_.end(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator rbegin() const noexcept { return __entries_.rbegin(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator rend() const noexcept { return __entries_.rend(); }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator cbegin() const noexcept { return __entries_.cbegin(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator cend() const noexcept { return __entries_.cend(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator crbegin() const noexcept {
+ return __entries_.crbegin();
+ }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reverse_iterator crend() const noexcept { return __entries_.crend(); }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI bool empty() const noexcept { return __entries_.empty(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI size_type size() const noexcept { return __entries_.size(); }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI size_type max_size() const noexcept { return __entries_.max_size(); }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reference operator[](size_type __i) const { return __entries_[__i]; }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_reference at(size_type __i) const { return __entries_.at(__i); }
+
+ // (19.6.4.4)
+ // [stacktrace.basic.cmp], comparisons
+
+ template <class _Allocator2>
+ _LIBCPP_EXPORTED_FROM_ABI friend bool
+ operator==(const basic_stacktrace& __x, const basic_stacktrace<_Allocator2>& __y) noexcept {
+ if (__x.size() != __y.size()) {
+ return false;
+ }
+ auto __xi = __x.begin();
+ auto __yi = __y.begin();
+ auto __xe = __x.end();
+ while (__xi != __xe) {
+ if (*__xi++ != *__yi++) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ template <class _Allocator2>
+ _LIBCPP_EXPORTED_FROM_ABI friend strong_ordering
+ operator<=>(const basic_stacktrace& __x, const basic_stacktrace<_Allocator2>& __y) noexcept {
+ auto __ret = __x.size() <=> __y.size();
+ if (__ret != std::strong_ordering::equal) {
+ return __ret;
+ }
+ auto __xi = __x.begin();
+ auto __yi = __y.begin();
+ auto __xe = __x.end();
+ while ((__ret == std::strong_ordering::equal) && __xi != __xe) {
+ __ret = *__xi++ <=> *__yi++;
+ }
+ return __ret;
+ }
+
+ // (19.6.4.5)
+ // [stacktrace.basic.mod], modifiers
+
+ _LIBCPP_EXPORTED_FROM_ABI void swap(basic_stacktrace<_Allocator>& __other) noexcept {
+ std::swap(__entries_, __other.__entries_);
+ if (__kPropOnSwap) {
+ std::swap(__alloc_, __other.__alloc_);
+ }
+ }
+};
+
+using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+
+namespace pmr {
+using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
+} // namespace pmr
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI inline void
+swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) noexcept(noexcept(__a.swap(__b))) {
+ __a.swap(__b);
+}
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI inline string to_string(const basic_stacktrace<_Allocator>& __stacktrace) {
+ return __stacktrace::__to_string()(__stacktrace);
+}
+
+template <class _Allocator>
+_LIBCPP_EXPORTED_FROM_ABI inline ostream& operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __stacktrace) {
+ auto __str = __stacktrace::__to_string()(__stacktrace);
+ return __os << __str;
+}
+
+// (19.6.5)
+// Formatting support [stacktrace.format]
+
+// TODO(stacktrace23): needs `formatter`
+template <class _Allocator>
+struct _LIBCPP_EXPORTED_FROM_ABI formatter<basic_stacktrace<_Allocator>>;
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <class _Allocator>
+struct _LIBCPP_EXPORTED_FROM_ABI hash<basic_stacktrace<_Allocator>> {
+ [[nodiscard]] size_t operator()(basic_stacktrace<_Allocator> const& __context) const noexcept {
+ size_t __ret = 1;
+ for (auto const& __entry : __context.__entries_) {
+ __ret += hash<uintptr_t>()(__entry.native_handle());
+ __ret *= 3001;
+ }
+ return __ret;
+ }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
diff --git a/libcxx/include/experimental/__stacktrace/detail/alloc.h b/libcxx/include/experimental/__stacktrace/detail/alloc.h
new file mode 100644
index 0000000000000..a0949b5751899
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/alloc.h
@@ -0,0 +1,103 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+
+#include <__config>
+#include <__functional/function.h>
+#include <__memory/allocator_traits.h>
+#include <__memory_resource/memory_resource.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <cstddef>
+#include <list>
+#include <memory>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class stacktrace_entry;
+
+namespace __stacktrace {
+
+/** Per-stacktrace-invocation allocator which wraps a caller-provided allocator of any type.
+This is intended to be used with `std::pmr::` containers and strings throughout the stacktrace
+creation process. */
+struct alloc final : std::pmr::memory_resource {
+ template <class _Allocator>
+ _LIBCPP_HIDE_FROM_ABI explicit alloc(_Allocator const& __a) {
+ // Take the given allocator type, and rebind with a new type having <byte> as the template arg
+ using _AT = std::allocator_traits<_Allocator>;
+ using _BA = typename _AT::template rebind_alloc<std::byte>;
+ auto __ba = _BA(__a);
+ __alloc_func_ = [__ba](size_t __sz) mutable { return __ba.allocate(__sz); };
+ __dealloc_func_ = [__ba](void* __ptr, size_t __sz) mutable { return __ba.deallocate((std::byte*)__ptr, __sz); };
+ __alloc_opaque_ = std::addressof(__a);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL ~alloc() override = default;
+
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual void _anchor_vfunc();
+
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t __align) override {
+ // Avoiding "assert" in a system header, but we expect this to hold:
+ // assert(__align <= alignof(std::max_align_t));
+ (void)__align;
+ return __alloc_func_(__size);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t __align) override {
+ (void)__align;
+ __dealloc_func_((std::byte*)__ptr, __size);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
+ auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
+ return __rhs_ba && (__rhs_ba->__alloc_opaque_ == __alloc_opaque_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
+ std::pmr::string __ret{this};
+ if (__size) {
+ __ret.reserve(__size);
+ __ret[0] = 0;
+ }
+ return __ret;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI std::pmr::string hex_string(uintptr_t __addr) {
+ char __ret[19]; // "0x" + 16 digits + NUL
+ auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
+ return {__ret, size_t(__size), this};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI std::pmr::string u64_string(uintptr_t __val) {
+ char __ret[21]; // 20 digits max + NUL
+ auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
+ return {__ret, size_t(__size), this};
+ }
+
+ template <typename _Tp>
+ _LIBCPP_HIDE_FROM_ABI std::pmr::list<_Tp> new_list() {
+ return std::pmr::list<_Tp>{this};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI std::pmr::list<std::pmr::string> new_string_list() { return new_list<std::pmr::string>(); }
+
+private:
+ std::function<std::byte*(size_t)> __alloc_func_;
+ std::function<void(std::byte*, size_t)> __dealloc_func_;
+ /** Only used for equality */
+ void const* __alloc_opaque_;
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
diff --git a/libcxx/include/experimental/__stacktrace/detail/context.h b/libcxx/include/experimental/__stacktrace/detail/context.h
new file mode 100644
index 0000000000000..df791b37b386d
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/context.h
@@ -0,0 +1,53 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+
+#include <__config>
+#include <__memory_resource/memory_resource.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__vector/pmr.h>
+#include <__vector/vector.h>
+#include <cstddef>
+#include <string>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct _LIBCPP_HIDE_FROM_ABI alloc;
+struct _LIBCPP_HIDE_FROM_ABI entry;
+
+namespace __stacktrace {
+
+/** Represents the state of the current in-progress stacktrace operation. This includes
+the working list of `entry` objects, as well as the caller-provided allocator (wrapped
+in `polymorphic_allocator`) and a handful of utility functions involving that allocator. */
+struct _LIBCPP_HIDE_FROM_ABI context {
+ /** Encapsulates and type-removes the caller's allocator. */
+ alloc& __alloc_;
+
+ /** The working set of entry data; these will later be copied to their final `stacktrace_entry` objects. */
+ std::pmr::list<entry> __entries_;
+
+ /** Path to this process's main executable. */
+ std::pmr::string __main_prog_path_;
+
+ _LIBCPP_HIDE_FROM_ABI explicit context(alloc& __byte_alloc)
+ : __alloc_(__byte_alloc), __entries_(&__alloc_), __main_prog_path_(__alloc_.new_string()) {}
+
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void do_stacktrace(size_t __skip, size_t __max_depth);
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
diff --git a/libcxx/include/experimental/__stacktrace/detail/entry.h b/libcxx/include/experimental/__stacktrace/detail/entry.h
new file mode 100644
index 0000000000000..2bab14228b508
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/entry.h
@@ -0,0 +1,44 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+
+#include <__config>
+#include <cstdint>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __stacktrace {
+
+/** Contains fields which will be used to generate the final `std::stacktrace_entry`.
+This is an intermediate object which owns strings allocated via the caller-provided allocator,
+which are later freed back to that allocator and converted to plain `std::string`s. */
+struct _LIBCPP_HIDE_FROM_ABI entry {
+ /** Caller's / faulting insn's address, including ASLR/slide */
+ uintptr_t __addr_{};
+
+ /** the address minus its image's slide offset */
+ uintptr_t __addr_unslid_{};
+
+ /** entry's description (symbol name) */
+ std::pmr::string __desc_{};
+
+ /** source file name */
+ std::pmr::string __file_{};
+
+ /** line number in source file */
+ uint32_t __line_{};
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
diff --git a/libcxx/include/experimental/__stacktrace/detail/to_string.h b/libcxx/include/experimental/__stacktrace/detail/to_string.h
new file mode 100644
index 0000000000000..47feddf07faf9
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/detail/to_string.h
@@ -0,0 +1,46 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+
+#include <__config>
+#include <__fwd/sstream.h>
+#include <__ostream/basic_ostream.h>
+#include <cstddef>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Allocator>
+class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace;
+
+class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry;
+
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI __to_string {
+ _LIBCPP_HIDE_FROM_ABI string operator()(stacktrace_entry const& __entry);
+
+ _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
+
+ _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
+
+ string operator()(std::stacktrace_entry const* __entries, size_t __count);
+
+ template <class _Allocator>
+ _LIBCPP_HIDE_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
+ return (*this)(__st.__entries_.data(), __st.__entries_.size());
+ }
+};
+
+} // namespace __stacktrace
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
diff --git a/libcxx/include/experimental/__stacktrace/stacktrace_entry.h b/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
new file mode 100644
index 0000000000000..be8abb3ea91b2
--- /dev/null
+++ b/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
@@ -0,0 +1,111 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
+
+#include <__cstddef/byte.h>
+#include <__cstddef/ptrdiff_t.h>
+#include <__cstddef/size_t.h>
+#include <__format/formatter.h>
+#include <__functional/function.h>
+#include <__functional/hash.h>
+#include <__fwd/format.h>
+#include <__fwd/ostream.h>
+#include <__fwd/sstream.h>
+#include <__fwd/vector.h>
+#include <__iterator/iterator.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/reverse_access.h>
+#include <__iterator/reverse_iterator.h>
+#include <__memory/allocator.h>
+#include <__memory/allocator_traits.h>
+#include <__utility/move.h>
+#include <__vector/pmr.h>
+#include <__vector/swap.h>
+#include <__vector/vector.h>
+#include <cstdint>
+#include <string>
+
+#include "experimental/__stacktrace/detail/entry.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
+public:
+ // (19.6.3.1) Overview [stacktrace.entry.overview]
+ using native_handle_type = uintptr_t;
+
+ // (19.6.3.2) [stacktrace.entry.cons], constructors
+ _LIBCPP_EXPORTED_FROM_ABI ~stacktrace_entry() noexcept = default;
+ _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry() noexcept = default;
+ _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry(const stacktrace_entry&) noexcept = default;
+ _LIBCPP_EXPORTED_FROM_ABI constexpr stacktrace_entry& operator=(const stacktrace_entry&) noexcept = default;
+
+ // (19.6.3.3) [stacktrace.entry.obs], observers
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr native_handle_type native_handle() const noexcept {
+ return __addr_;
+ }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI constexpr explicit operator bool() const noexcept {
+ return native_handle() != 0;
+ }
+
+ // (19.6.3.4) [stacktrace.entry.query], query
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string description() const { return __desc_; }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string source_file() const { return __file_; }
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI uint_least32_t source_line() const { return __line_; }
+
+ // (19.6.3.5) [stacktrace.entry.cmp], comparison
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr bool
+ operator==(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+ return __x.native_handle() == __y.native_handle();
+ }
+
+ [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI friend constexpr strong_ordering
+ operator<=>(const stacktrace_entry& __x, const stacktrace_entry& __y) noexcept {
+ return __x.native_handle() <=> __y.native_handle();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI explicit stacktrace_entry(__stacktrace::entry&& __e)
+ : __addr_(__e.__addr_), __desc_(std::move(__e.__desc_)), __file_(std::move(__e.__file_)), __line_(__e.__line_) {}
+
+private:
+ uintptr_t __addr_{};
+ std::string __desc_{};
+ std::string __file_{};
+ uint_least32_t __line_{};
+};
+
+// (19.6.4.6)
+// Non-member functions [stacktrace.basic.nonmem]
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry);
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry);
+
+// (19.6.5)
+// Formatting support [stacktrace.format]
+
+// TODO(stacktrace23): needs `formatter`
+template <>
+struct _LIBCPP_EXPORTED_FROM_ABI formatter<stacktrace_entry>;
+
+// (19.6.6)
+// Hash support [stacktrace.basic.hash]
+
+template <>
+struct _LIBCPP_EXPORTED_FROM_ABI hash<stacktrace_entry> {
+ [[nodiscard]] size_t operator()(stacktrace_entry const& __entry) const noexcept {
+ auto __addr = __entry.native_handle();
+ return hash<uintptr_t>()(__addr);
+ }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
diff --git a/libcxx/include/experimental/stacktrace b/libcxx/include/experimental/stacktrace
new file mode 100644
index 0000000000000..9e1f7259ca3da
--- /dev/null
+++ b/libcxx/include/experimental/stacktrace
@@ -0,0 +1,191 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE
+#define _LIBCPP_EXPERIMENTAL_STACKTRACE
+
+/*
+ Header <stacktrace> synopsis
+ (19.6.2)
+
+#include <compare> // see [compare.syn]
+
+namespace std {
+ // [stacktrace.entry], class stacktrace_entry
+ class stacktrace_entry;
+
+ // [stacktrace.basic], class template basic_stacktrace
+ template<class Allocator>
+ class basic_stacktrace;
+
+ // basic_stacktrace typedef-names
+ using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+
+ // [stacktrace.basic.nonmem], non-member functions
+ template<class Allocator>
+ void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+ noexcept(noexcept(a.swap(b)));
+
+ string to_string(const stacktrace_entry& f);
+
+ template<class Allocator>
+ string to_string(const basic_stacktrace<Allocator>& st);
+
+ ostream& operator<<(ostream& os, const stacktrace_entry& f);
+ template<class Allocator>
+ ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+
+ // [stacktrace.format], formatting support
+ template<> struct formatter<stacktrace_entry>;
+ template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+
+ namespace pmr {
+ using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
+ }
+
+ // [stacktrace.basic.hash], hash support
+ template<class T> struct hash;
+ template<> struct hash<stacktrace_entry>;
+ template<class Allocator> struct hash<basic_stacktrace<Allocator>>;
+}
+
+// [stacktrace.entry]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ using native_handle_type = implementation-defined;
+
+ // [stacktrace.entry.cons], constructors
+ constexpr stacktrace_entry() noexcept;
+ constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+ constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+
+ ~stacktrace_entry();
+
+ // [stacktrace.entry.obs], observers
+ constexpr native_handle_type native_handle() const noexcept;
+ constexpr explicit operator bool() const noexcept;
+
+ // [stacktrace.entry.query], query
+ string description() const;
+ string source_file() const;
+ uint_least32_t source_line() const;
+
+ // [stacktrace.entry.cmp], comparison
+ friend constexpr bool operator==(const stacktrace_entry& x,
+ const stacktrace_entry& y) noexcept;
+ friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+ const stacktrace_entry& y) noexcept;
+ };
+}
+
+// [stacktrace.basic]
+
+namespace std {
+ template<class Allocator>
+ class basic_stacktrace {
+ public:
+ using value_type = stacktrace_entry;
+ using const_reference = const value_type&;
+ using reference = value_type&;
+ using const_iterator = implementation-defined; // see [stacktrace.basic.obs]
+ using iterator = const_iterator;
+ using reverse_iterator = reverse_iterator<iterator>;
+ using const_reverse_iterator = reverse_iterator<const_iterator>;
+ using difference_type = implementation-defined;
+ using size_type = implementation-defined;
+ using allocator_type = Allocator;
+
+ // [stacktrace.basic.cons], creation and assignment
+ static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+ static basic_stacktrace current(size_type skip,
+ const allocator_type& alloc = allocator_type()) noexcept;
+ static basic_stacktrace current(size_type skip, size_type max_depth,
+ const allocator_type& alloc = allocator_type()) noexcept;
+
+ basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+ explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+
+ basic_stacktrace(const basic_stacktrace& other);
+ basic_stacktrace(basic_stacktrace&& other) noexcept;
+ basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+ basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+ basic_stacktrace& operator=(const basic_stacktrace& other);
+ basic_stacktrace& operator=(basic_stacktrace&& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+ allocator_traits<Allocator>::is_always_equal::value);
+
+ ~basic_stacktrace();
+
+ // [stacktrace.basic.obs], observers
+ allocator_type get_allocator() const noexcept;
+
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_reverse_iterator rbegin() const noexcept;
+ const_reverse_iterator rend() const noexcept;
+
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+ const_reverse_iterator crbegin() const noexcept;
+ const_reverse_iterator crend() const noexcept;
+
+ bool empty() const noexcept;
+ size_type size() const noexcept;
+ size_type max_size() const noexcept;
+
+ const_reference operator[](size_type) const;
+ const_reference at(size_type) const;
+
+ // [stacktrace.basic.cmp], comparisons
+ template<class Allocator2>
+ friend bool operator==(const basic_stacktrace& x,
+ const basic_stacktrace<Allocator2>& y) noexcept;
+ template<class Allocator2>
+ friend strong_ordering operator<=>(const basic_stacktrace& x,
+ const basic_stacktrace<Allocator2>& y) noexcept;
+
+ // [stacktrace.basic.mod], modifiers
+ void swap(basic_stacktrace& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+ allocator_traits<Allocator>::is_always_equal::value);
+
+ private:
+ vector<value_type, allocator_type> frames_; // exposition only
+ };
+}
+
+*/
+
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+# include <__cxx03/__config>
+#else
+# include <__config>
+
+# if _LIBCPP_STD_VER >= 23
+
+# include <compare> // [stacktrace.syn]
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
+
+_LIBCPP_PUSH_MACROS
+# include <__undef_macros>
+
+# include "experimental/__stacktrace/basic_stacktrace.h"
+# include "experimental/__stacktrace/stacktrace_entry.h"
+
+_LIBCPP_POP_MACROS
+
+# endif // _LIBCPP_STD_VER >= 23
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
+#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index af928a63f2315..8867681832bcf 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2225,6 +2225,16 @@ module std [system] {
header "experimental/simd"
export *
}
+ module stacktrace {
+ private header "experimental/__stacktrace/basic_stacktrace.h"
+ private header "experimental/__stacktrace/detail/alloc.h"
+ private header "experimental/__stacktrace/detail/context.h"
+ private header "experimental/__stacktrace/detail/entry.h"
+ private header "experimental/__stacktrace/detail/to_string.h"
+ private header "experimental/__stacktrace/stacktrace_entry.h"
+ header "experimental/stacktrace"
+ export *
+ }
}
// Implementation detail headers that are private to libc++. These modules
diff --git a/libcxx/modules/std/stacktrace.inc b/libcxx/modules/std/stacktrace.inc
index c1184087c0df4..e92f730cec62c 100644
--- a/libcxx/modules/std/stacktrace.inc
+++ b/libcxx/modules/std/stacktrace.inc
@@ -8,7 +8,9 @@
//===----------------------------------------------------------------------===//
export namespace std {
-#if 0
+// TODO(stacktrace23): update this when stacktrace is taken out of experimental
+#if 0 //_LIBCPP_STD_VER >= 23
+
// [stacktrace.entry], class stacktrace_entry
using std::stacktrace_entry;
@@ -31,5 +33,6 @@ export namespace std {
// [stacktrace.basic.hash], hash support
using std::hash;
-#endif
+
+#endif // _LIBCPP_STD_VER >= 23
} // namespace std
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 4e9bf900af4c5..b24b98da2bf46 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -309,6 +309,24 @@ add_custom_target(cxx DEPENDS ${LIBCXX_BUILD_TARGETS})
# Build the experimental static library
set(LIBCXX_EXPERIMENTAL_SOURCES
experimental/keep.cpp
+ experimental/stacktrace/alloc.cpp
+ experimental/stacktrace/common/debug.cpp
+ experimental/stacktrace/common/fd.cpp
+ experimental/stacktrace/context.cpp
+ experimental/stacktrace/linux/linux-dl.cpp
+ experimental/stacktrace/linux/linux-elf.cpp
+ experimental/stacktrace/linux/linux-sym.cpp
+ experimental/stacktrace/osx/osx.cpp
+ experimental/stacktrace/stacktrace.cpp
+ experimental/stacktrace/tools/addr2line.cpp
+ experimental/stacktrace/tools/atos.cpp
+ experimental/stacktrace/tools/llvm_symbolizer.cpp
+ experimental/stacktrace/tools/toolspawner.cpp
+ experimental/stacktrace/unwind/unwind.cpp
+ experimental/stacktrace/windows/dbghelp_dll.cpp
+ experimental/stacktrace/windows/dll.cpp
+ experimental/stacktrace/windows/psapi_dll.cpp
+ experimental/stacktrace/windows/win_impl.cpp
)
if (LIBCXX_PSTL_BACKEND STREQUAL "libdispatch")
@@ -334,6 +352,7 @@ endif()
add_library(cxx_experimental STATIC ${LIBCXX_EXPERIMENTAL_SOURCES})
target_link_libraries(cxx_experimental PUBLIC cxx-headers)
+
if (LIBCXX_ENABLE_SHARED)
target_link_libraries(cxx_experimental PRIVATE cxx_shared)
else()
diff --git a/libcxx/src/experimental/stacktrace/alloc.cpp b/libcxx/src/experimental/stacktrace/alloc.cpp
new file mode 100644
index 0000000000000..956e01b312d3b
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/alloc.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+void alloc::_anchor_vfunc() {}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/config.h b/libcxx/src/experimental/stacktrace/common/config.h
new file mode 100644
index 0000000000000..0915b57463deb
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/config.h
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_CONFIG_H
+#define _LIBCPP_STACKTRACE_CONFIG_H
+
+#include <__config>
+#include <__config_site>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+#if __has_include(<unwind.h>)
+# define _LIBCPP_STACKTRACE_COLLECT_UNWIND
+#endif
+
+#if defined(__APPLE__)
+# define _LIBCPP_STACKTRACE_APPLE
+#endif
+
+#if defined(__linux__)
+# define _LIBCPP_STACKTRACE_LINUX
+#endif
+
+#if __has_include(<spawn.h>) && _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
+# define _LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS
+#endif
+
+#if __has_include(<windows.h>) && __has_include(<dbghelp.h>) && __has_include(<psapi.h>)
+# define _LIBCPP_STACKTRACE_WINDOWS
+#endif
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_CONFIG_H
diff --git a/libcxx/src/experimental/stacktrace/common/debug.cpp b/libcxx/src/experimental/stacktrace/common/debug.cpp
new file mode 100644
index 0000000000000..4f113e10c0c23
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/debug.cpp
@@ -0,0 +1,19 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "debug.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+debug::~debug() = default;
+
+debug::dummy_ostream::~dummy_ostream() = default;
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/debug.h b/libcxx/src/experimental/stacktrace/common/debug.h
new file mode 100644
index 0000000000000..0cee301d42d8b
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/debug.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_DEBUG_H
+#define _LIBCPP_STACKTRACE_DEBUG_H
+
+#include <__config>
+#include <iostream>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/** Debug-message output stream. If `LIBCXX_STACKTRACE_DEBUG` is defined in the environment
+or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
+otherwise its does nothing by returning a dummy stream. */
+struct debug : std::ostream {
+ virtual ~debug();
+
+ static bool enabled() {
+#if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
+ return true;
+#else
+ static bool ret = [] {
+ auto const* val = getenv("LIBCXX_STACKTRACE_DEBUG");
+ return val && !strncmp(val, "1", 1);
+ }();
+ return ret;
+#endif
+ }
+
+ /** No-op output stream. */
+ struct dummy_ostream final : std::ostream {
+ virtual ~dummy_ostream();
+ friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
+ };
+
+ friend std::ostream& operator<<(debug& dp, auto const& val) {
+ static dummy_ostream kdummy;
+ if (!enabled()) {
+ return kdummy;
+ }
+ std::cerr << val;
+ return std::cerr;
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_DEBUG_H
diff --git a/libcxx/src/experimental/stacktrace/common/failed.h b/libcxx/src/experimental/stacktrace/common/failed.h
new file mode 100644
index 0000000000000..e53eb1123aabc
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/failed.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_FAILED_H
+#define _LIBCPP_STACKTRACE_FAILED_H
+
+#include <stdexcept>
+
+// TODO(stacktrace23) Use std::expected instead?
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct failed : std::runtime_error {
+ virtual ~failed() = default;
+ int errno_{0};
+ failed() : std::runtime_error({}) {}
+ failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_FAILED_H
diff --git a/libcxx/src/experimental/stacktrace/common/fd.cpp b/libcxx/src/experimental/stacktrace/common/fd.cpp
new file mode 100644
index 0000000000000..b7728ea7826dd
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/fd.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "fd.h"
+#include "failed.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+fd_streambuf::~fd_streambuf() = default;
+fd_istream::~fd_istream() = default;
+
+int fd_streambuf::underflow() {
+ int bytesRead = ::read(fd_, buf_, size_);
+ if (bytesRead < 0) {
+ throw failed("I/O error reading from child process", errno);
+ }
+ if (bytesRead == 0) {
+ return traits_type::eof();
+ }
+ setg(buf_, buf_, buf_ + bytesRead);
+ return int(*buf_);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/fd.h b/libcxx/src/experimental/stacktrace/common/fd.h
new file mode 100644
index 0000000000000..ff003e16a6bd8
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/fd.h
@@ -0,0 +1,122 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_FD_H
+#define _LIBCPP_STACKTRACE_FD_H
+
+#include <__config>
+#include <cassert>
+#include <cerrno>
+#include <cstdio>
+#include <iostream>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <utility>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/** Encapsulates a plain old file descriptor `int`. Avoids copies in order to
+force some component to "own" this, although it's freely convertible back to
+integer form. Default-constructed, closed, and moved-out-of instances will have
+the invalid fd `-1`. */
+class fd {
+ int fd_{-1};
+
+public:
+ fd() : fd(-1) {}
+ fd(int fdint) : fd_(fdint) {}
+
+ fd(fd const&) = delete;
+ fd& operator=(fd const&) = delete;
+
+ fd(fd&& rhs) {
+ if (&rhs != this) {
+ std::exchange(fd_, rhs.fd_);
+ }
+ }
+
+ fd& operator=(fd&& rhs) { return *new (this) fd(std::move(rhs)); }
+
+ ~fd() { close(); }
+
+ /** Returns true IFF fd is above zero */
+ bool valid() const { return fd_ > 0; }
+
+ /* implicit */ operator int() const {
+ auto ret = fd_;
+ assert(ret > 0);
+ return ret;
+ }
+
+ void close() {
+ int fd_old = -1;
+ std::exchange(fd_old, fd_);
+ if (fd_old != -1) {
+ ::close(fd_old);
+ }
+ }
+
+ static fd& null_fd() {
+ static fd ret = {::open("/dev/null", O_RDWR)};
+ return ret;
+ }
+
+ static fd open(std::string_view path) {
+ fd ret = {::open(path.data(), O_RDONLY)};
+ return ret;
+ }
+};
+
+/** Wraps a readable fd using the `streambuf` interface. I/O errors arising
+from reading the provided fd will result in a `Failed` being thrown. */
+struct fd_streambuf final : std::streambuf {
+ fd& fd_;
+ char* buf_;
+ size_t size_;
+ fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
+ virtual ~fd_streambuf();
+ int underflow() override;
+};
+
+/** Wraps an `FDInStreamBuffer` in an `istream` */
+struct fd_istream final : std::istream {
+ fd_streambuf& buf_;
+ virtual ~fd_istream();
+ explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
+};
+
+struct fd_mmap final {
+ fd fd_{};
+ size_t size_{0};
+ std::byte const* addr_{nullptr};
+
+ explicit fd_mmap(std::string_view path) : fd_mmap(fd::open(path)) {}
+
+ explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
+ if (fd_) {
+ if ((size_ = ::lseek(fd, 0, SEEK_END))) {
+ addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
+ }
+ }
+ }
+
+ operator bool() const { return addr_; }
+
+ ~fd_mmap() {
+ if (addr_) {
+ ::munmap(const_cast<void*>((void const*)addr_), size_);
+ }
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_FD_H
diff --git a/libcxx/src/experimental/stacktrace/common/images.h b/libcxx/src/experimental/stacktrace/common/images.h
new file mode 100644
index 0000000000000..cb9eac47cf9dd
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/common/images.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_IMAGES_H
+#define _LIBCPP_STACKTRACE_IMAGES_H
+
+#include <cstdint>
+#include <string_view>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+constexpr unsigned k_max_images = 256;
+
+struct image {
+ uintptr_t loaded_at_{};
+ intptr_t slide_{};
+ std::string_view name_{};
+ bool is_main_prog_{};
+
+ bool operator<(image const& rhs) const { return loaded_at_ < rhs.loaded_at_; }
+ operator bool() const { return !name_.empty(); }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/experimental/stacktrace/context.cpp b/libcxx/src/experimental/stacktrace/context.cpp
new file mode 100644
index 0000000000000..3d311af4d7a89
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/context.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <list>
+
+#include <experimental/__stacktrace/basic_stacktrace.h>
+#include <experimental/__stacktrace/stacktrace_entry.h>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+#include <experimental/__stacktrace/detail/to_string.h>
+
+#include "common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+# include "linux/linux.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+# include "osx/osx.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+# include "tools/pspawn.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+# include "unwind/unwind.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+# include "windows/win_impl.h"
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip, size_t max_depth) {
+ /*
+ Here we declare stacktrace components or "backends" which will handle the different tasks:
+
+ (1) get the addresses from the call stack
+ (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
+ (3) resolve using debug info, and possibly with an external tool on the $PATH
+ (4+) extra passes to get symbols, in case 3 couldn't
+
+ Based on the macros produced by `stacktrace.h`, throw all backends we have available at the task. Ideally the #ifdef
+ gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
+ symbolizer. If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
+
+ All these classes do their best to provide any of the requested fields they can: (symbol, filename, source line),
+ substituting if needed with something reasonable. For example, if the source filename and line are not available
+ then we will at least report that the address and symbol are in the module `foo.exe`.
+
+ These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
+ already-populated data. We take care not to crash / abort / throw in any of these, and we'll silently fail. See
+ `common/debug.h` for a debugging logger you can enable at runtime.
+ */
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+ win_impl dbghelp{*this};
+ auto& collector = dbghelp;
+ auto& mod_ident = dbghelp;
+ auto& resolver = dbghelp;
+ auto& symbolizer = dbghelp;
+#endif
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+ unwind unwind{*this};
+ auto& collector = unwind;
+#endif
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+ osx osx{*this};
+ auto& mod_ident = osx;
+ auto& symbolizer = osx;
+#endif
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+ linux linux{*this};
+ auto& mod_ident = linux;
+ auto& symbolizer = linux;
+#endif
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+ spawner pspawn{*this};
+ auto& resolver = pspawn;
+#endif
+
+ collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
+ if (__entries_.size()) { // (Can't proceed if empty)
+ mod_ident.ident_modules(); // Associate addrs with binaries (ELF/MachO/etc.)
+ resolver.resolve_lines(); // Resolve addresses to symbols, filename, linenumber
+ symbolizer.symbolize(); // Populate missing symbols, if any.
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/linux/elf.h b/libcxx/src/experimental/stacktrace/linux/elf.h
new file mode 100644
index 0000000000000..214e8cc5a7819
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/elf.h
@@ -0,0 +1,315 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_ELF_H
+#define _LIBCPP_STACKTRACE_ELF_H
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <functional>
+#include <string_view>
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// Includes ELF constants and structs copied from <elf.h>, with a few changes.
+
+namespace elf {
+
+struct Header32 final {
+ uint8_t ident[16]; /* Magic number and other info */
+ uint16_t type; /* Object file type */
+ uint16_t machine; /* Architecture */
+ uint32_t version; /* Object file version */
+ uint32_t entry; /* Entry point virtual address */
+ uint32_t phoff; /* Program header table file offset */
+ uint32_t shoff; /* Section header table file offset */
+ uint32_t flags; /* Processor-specific flags */
+ uint16_t ehsize; /* ELF header size in bytes */
+ uint16_t phentsize; /* Program header table entry size */
+ uint16_t phnum; /* Program header table entry count */
+ uint16_t shentsize; /* Section header table entry size */
+ uint16_t shnum; /* Section header table entry count */
+ uint16_t shstrndx; /* Section header string table index */
+};
+
+struct Section32 final {
+ uint32_t name; /* Section name (string tbl index) */
+ uint32_t type; /* Section type */
+ uint32_t flags; /* Section flags */
+ uint32_t addr; /* Section virtual addr at execution */
+ uint32_t offset; /* Section file offset */
+ uint32_t size; /* Section size in bytes */
+ uint32_t link; /* Link to another section */
+ uint32_t info; /* Additional section information */
+ uint32_t addralign; /* Section alignment */
+ uint32_t entsize; /* Entry size if section holds table */
+};
+
+struct Symbol32 final {
+ uint32_t name; /* Symbol name (string tbl index) */
+ uint32_t value; /* Symbol value */
+ uint32_t size; /* Symbol size */
+ uint8_t info; /* Symbol type and binding */
+ uint8_t other; /* Symbol visibility */
+ uint16_t shndx; /* Section index */
+};
+
+struct Header64 final {
+ uint8_t ident[16]; /* Magic number and other info */
+ uint16_t type; /* Object file type */
+ uint16_t machine; /* Architecture */
+ uint32_t version; /* Object file version */
+ uint64_t entry; /* Entry point virtual address */
+ uint64_t phoff; /* Program header table file offset */
+ uint64_t shoff; /* Section header table file offset */
+ uint32_t flags; /* Processor-specific flags */
+ uint16_t ehsize; /* ELF header size in bytes */
+ uint16_t phentsize; /* Program header table entry size */
+ uint16_t phnum; /* Program header table entry count */
+ uint16_t shentsize; /* Section header table entry size */
+ uint16_t shnum; /* Section header table entry count */
+ uint16_t shstrndx; /* Section header string table index */
+};
+
+struct Section64 final {
+ uint32_t name; /* Section name (string tbl index) */
+ uint32_t type; /* Section type */
+ uint64_t flags; /* Section flags */
+ uint64_t addr; /* Section virtual addr at execution */
+ uint64_t offset; /* Section file offset */
+ uint64_t size; /* Section size in bytes */
+ uint32_t link; /* Link to another section */
+ uint32_t info; /* Additional section information */
+ uint64_t addralign; /* Section alignment */
+ uint64_t entsize; /* Entry size if section holds table */
+};
+
+struct Symbol64 final {
+ uint32_t name; /* Symbol name (string tbl index) */
+ uint8_t info; /* Symbol type and binding */
+ uint8_t other; /* Symbol visibility */
+ uint16_t shndx; /* Section index */
+ uint64_t value; /* Symbol value */
+ uint64_t size; /* Symbol size */
+};
+
+/** Represents an ELF header. Supports the minimum needed to navigate an ELF file's sections and get at the symbol and
+ * string tables. */
+struct Header final {
+ std::byte const* ptr_{};
+ uintptr_t shoff_{};
+ size_t shnum_{};
+ size_t shstrndx_{};
+
+ Header() = default;
+ Header(Header const&) = default;
+ Header& operator=(Header const& rhs) { return *new (this) Header(rhs); }
+
+ operator bool() { return ptr_; }
+
+ template <class H>
+ explicit Header(H* h)
+ : ptr_((std::byte const*)h),
+ shoff_(uintptr_t(h->shoff)),
+ shnum_(size_t(h->shnum)),
+ shstrndx_(size_t(h->shstrndx)) {}
+};
+
+struct ELF;
+struct StringTable;
+
+struct Section final {
+ constexpr static uint32_t kSymTab = 2; // symbol table
+ constexpr static uint32_t kStrTab = 3; // name table for symbols or sections
+
+ ELF* elf_{};
+ std::byte const* ptr_{};
+ uintptr_t nameIndex_{};
+ uint32_t type_{};
+ uintptr_t offset_{};
+ size_t size_{};
+
+ Section() = default;
+
+ template <class S>
+ Section(ELF* elf, S* sec)
+ : elf_(elf),
+ ptr_((std::byte const*)sec),
+ nameIndex_(sec->name),
+ type_(sec->type),
+ offset_(sec->offset),
+ size_(sec->size) {}
+
+ operator bool() const { return ptr_; }
+
+ template <class T = std::byte>
+ T const* data() const {
+ return (T const*)(elfBase() + offset_);
+ }
+
+ std::byte const* elfBase() const;
+ std::string_view name() const;
+};
+
+struct Symbol final {
+ constexpr static uint8_t kFunc = 0x02; // STT_FUNC (code object)
+
+ ELF* elf_{};
+ std::byte const* ptr_{};
+ uintptr_t nameIndex_{};
+ uint32_t type_{};
+ uintptr_t value_{};
+
+ Symbol() = default;
+ Symbol(Symbol const&) = default;
+ Symbol& operator=(Symbol const& rhs) { return *new (this) Symbol(rhs); }
+
+ operator bool() { return ptr_; }
+
+ bool isCode() const { return type_ == kFunc; }
+
+ template <class S>
+ Symbol(ELF* elf, S* sym)
+ : elf_(elf), ptr_((std::byte const*)sym), nameIndex_(sym->name), type_(0x0f & sym->info), value_(sym->value) {}
+
+ std::byte const* elfBase() const;
+ std::string_view name() const;
+};
+
+/** Represents one of the ELF's `strtab`s. This is a block of string data, with strings appended one after another, and
+ * NUL-terminated. Strings are indexed according to their starting offset. At offset 0 is typically an empty string.
+ */
+struct StringTable {
+ std::string_view data_{};
+
+ StringTable() = default;
+
+ /* implicit */ StringTable(Section const& sec) : data_(sec.data<char>(), sec.size_) {}
+
+ operator bool() { return data_.size(); }
+
+ std::string_view at(size_t index) {
+ auto* ret = data_.data() + index;
+ return {ret, strlen(ret)};
+ }
+};
+
+/** Encapsulates an ELF image specified by byte-address (e.g. from an mmapped file or a program image or shared object
+ * in memory). If given a supported ELF image, this will test true with `operator bool` to indicate it is supported and
+ * was able to parse some basic information from the header. */
+struct ELF {
+ Header header_{};
+ Section (*makeSection_)(ELF*, std::byte const*){};
+ Symbol (*makeSymbol_)(ELF*, std::byte const*){};
+ size_t secSize_{};
+ size_t symSize_{};
+ StringTable nametab_{};
+ Section symtab_{};
+ StringTable strtab_{};
+ size_t symCount_{};
+
+ static Section makeSection32(ELF* elf, std::byte const* ptr) { return Section(elf, (Section32 const*)ptr); }
+ static Section makeSection64(ELF* elf, std::byte const* ptr) { return Section(elf, (Section64 const*)ptr); }
+ static Symbol makeSymbol32(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol32 const*)ptr); }
+ static Symbol makeSymbol64(ELF* elf, std::byte const* ptr) { return Symbol(elf, (Symbol64 const*)ptr); }
+
+ operator bool() { return header_; }
+
+ explicit ELF(std::byte const* image) {
+ auto* p = (uint8_t const*)image;
+ // Bytes 0..3: magic bytes: 0x7F, 'E', 'L', 'F'
+ if (*p++ == 0x7f && *p++ == 0x45 && *p++ == 0x4c && *p++ == 0x46) {
+ auto klass = *p++; // Byte 4 (EI_CLASS): ELF class, 32- or 64-bit (0x01 or 0x02)
+ auto dataFormat = *p++; // Byte 5 (EI_DATA): (0x01) little- or (0x02) big-endian
+ auto fileVersion = *p++; // Byte 6 (EI_VERSION): ELF version: expect 1 (latest ELF version)
+ constexpr static uint16_t kEndianTestWord{0x0201};
+ auto hostEndianness = *(uint8_t const*)&kEndianTestWord;
+ if (dataFormat == hostEndianness && fileVersion == 1) {
+ if (klass == 0x01) {
+ header_ = Header((Header32 const*)image);
+ makeSection_ = makeSection32;
+ makeSymbol_ = makeSymbol32;
+ secSize_ = sizeof(Section32);
+ symSize_ = sizeof(Symbol32);
+ } else if (klass == 0x02) {
+ header_ = Header((Header64 const*)image);
+ makeSection_ = makeSection64;
+ makeSymbol_ = makeSymbol64;
+ secSize_ = sizeof(Section64);
+ symSize_ = sizeof(Symbol64);
+ }
+ }
+ }
+ if (*this) {
+ nametab_ = section(header_.shstrndx_);
+ eachSection([&](auto& sec) mutable -> bool {
+ if (sec.type_ == Section::kSymTab && sec.name() == ".symtab") {
+ symtab_ = sec;
+ } else if (sec.type_ == Section::kStrTab && sec.name() == ".strtab") {
+ strtab_ = sec;
+ }
+ return !symtab_ || !strtab_;
+ });
+ }
+ if (symtab_) {
+ symCount_ = symtab_.size_ / symSize_;
+ }
+ }
+
+ Section section(size_t index) {
+ auto* addr = header_.ptr_ + header_.shoff_ + (index * secSize_);
+ return makeSection_(this, addr);
+ }
+
+ Symbol symbol(size_t index) {
+ auto* addr = symtab_.data() + (index * symSize_);
+ return makeSymbol_(this, addr);
+ }
+
+ template <class T>
+ using CB = std::function<bool(T const&)>;
+
+ void eachSection(CB<Section> cb) {
+ for (size_t i = 0; i < header_.shnum_ && cb(section(i)); i++)
+ ;
+ }
+
+ void eachSymbol(CB<Symbol> cb) {
+ for (size_t i = 0; i < symCount_ && cb(symbol(i)); i++)
+ ;
+ }
+
+ Symbol getSym(uintptr_t addr) {
+ Symbol ret{};
+ eachSymbol([&](auto& sym) -> bool {
+ if (sym.value_ <= addr && sym.value_ > ret.value_) {
+ ret = sym;
+ }
+ return true;
+ });
+ return ret;
+ }
+};
+
+inline std::byte const* Section::elfBase() const { return elf_->header_.ptr_; }
+inline std::byte const* Symbol::elfBase() const { return elf_->header_.ptr_; }
+
+inline std::string_view Section::name() const { return elf_->nametab_.at(nameIndex_); }
+inline std::string_view Symbol::name() const { return elf_->strtab_.at(nameIndex_); }
+
+} // namespace elf
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp b/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
new file mode 100644
index 0000000000000..62cad72b9f124
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+# include "linux.h"
+
+# include <cassert>
+# include <dlfcn.h>
+# include <link.h>
+# include <unistd.h>
+
+# include "../common/images.h"
+
+# include <experimental/__stacktrace/detail/context.h>
+# include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+void linux::ident_modules() {
+ auto& images = images::get();
+
+ // Aside from the left/right sentinels in the array (hence the 2),
+ // are there any other real images?
+ if (images.count_ <= 2) {
+ return;
+ }
+
+ auto mainProg = images.mainProg();
+ if (mainProg) {
+ cx_.__main_prog_path_ = mainProg->name_;
+ }
+
+ unsigned index = 1; // Starts at one, and is moved around in this loop
+ for (auto& entry : cx_.__entries_) {
+ while (images[index].loaded_at_ > entry.__addr_) {
+ --index;
+ }
+ while (images[index + 1].loaded_at_ <= entry.__addr_) {
+ ++index;
+ }
+ entry.__addr_unslid_ = entry.__addr_ - images[index].slide_;
+ entry.__file_ = images[index].name_;
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp b/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
new file mode 100644
index 0000000000000..7cf82789ea2ed
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+# include "linux.h"
+
+# include <cassert>
+# include <unistd.h>
+
+# include <experimental/__stacktrace/detail/context.h>
+# include <experimental/__stacktrace/detail/entry.h>
+
+# include "../common/fd.h"
+# include "elf.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/**
+When trying to collect a stacktrace under Linux, there are narrow (but still quite common) cases where we will fail
+to resolve symbols. Linux's `dl` doesn't want to read symbols from the non-exported symbol table at runtime,
+and older versions of `addr2line` or `llvm-symbolizer` will also not resolve these.
+
+This implementation the minimum necessary to resolve symbols. It can identify this as an ELF (32 or 64 bits), locate
+the symbol and symbol-string table, and fill in any remaining missing symbols.
+*/
+void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
+ // We can statically initialize these, because main_elf_name should be the same every time.
+ static fd_mmap _mm(main_elf_name);
+ if (_mm) {
+ static elf::ELF _this_elf(_mm.addr_);
+ if (_this_elf) {
+ for (auto& entry : cx_.__entries_) {
+ if (entry.__desc_.empty() && entry.__file_ == main_elf_name) {
+ entry.__desc_ = _this_elf.getSym(entry.__addr_unslid_).name();
+ }
+ }
+ }
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp b/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
new file mode 100644
index 0000000000000..fa7489c64c923
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+# include "linux.h"
+
+# include <cassert>
+# include <dlfcn.h>
+# include <unistd.h>
+
+# include <experimental/__stacktrace/detail/context.h>
+# include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+bool symbolize_entry(entry& entry) {
+ bool ret = false;
+ Dl_info info;
+ if (dladdr((void*)entry.__addr_, &info)) {
+ ret = true; // at least partially successful
+ if (info.dli_fname && entry.__file_.empty()) {
+ // provide at least the binary filename in case we cannot lookup source location
+ entry.__file_ = info.dli_fname;
+ }
+ if (info.dli_sname && entry.__desc_.empty()) {
+ // provide at least the mangled name; try to unmangle in a later step
+ entry.__desc_ = info.dli_sname;
+ }
+ }
+ return ret;
+}
+
+// NOTE: We can use `dlfcn` to resolve addresses to symbols, which works great --
+// except for symbols in the main program. If addr2line-style tools are enabled, that step
+// might also be able to get symbols directly from the binary's debug info.
+void linux::symbolize() {
+ for (auto& entry : cx_.__entries_) {
+ symbolize_entry(entry);
+ }
+ // Symbols might be missing, because both (1) Linux's `dladdr` won't try to resolve non-exported symbols,
+ // which can be the case for the main program executable; and (2) debug info was not preserved.
+ // As a last resort, this function (see `linux-elf.cpp`) can still access symbol table directly.
+ image* mainELF = images::get().mainProg();
+ if (mainELF && !mainELF->name_.empty()) {
+ resolve_main_elf_syms(mainELF->name_);
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/experimental/stacktrace/linux/linux.h b/libcxx/src/experimental/stacktrace/linux/linux.h
new file mode 100644
index 0000000000000..a8e8c2279f6fc
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/linux/linux.h
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_LINUX_H
+#define _LIBCPP_STACKTRACE_LINUX_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+# include <algorithm>
+# include <array>
+# include <cassert>
+# include <cstddef>
+# include <cstdlib>
+# include <link.h>
+# include <string_view>
+# include <unistd.h>
+
+# include "../common/images.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct linux {
+ context& cx_;
+ void ident_modules();
+ void symbolize();
+
+private:
+ void resolve_main_elf_syms(std::string_view elf_name);
+};
+
+struct images {
+ // How many images this contains, including the left/right sentinels.
+ unsigned count_{0};
+ std::array<image, k_max_images + 2> images_{};
+
+ int add(dl_phdr_info& info) {
+ assert(count_ < k_max_images);
+ auto isFirst = (count_ == 0);
+ auto& image = images_.at(count_++);
+ image.loaded_at_ = info.dlpi_addr;
+ image.slide_ = info.dlpi_addr;
+ image.name_ = info.dlpi_name;
+ image.is_main_prog_ = isFirst; // first one is the main ELF
+ if (image.name_.empty() && isFirst) {
+ static char buffer[PATH_MAX + 1];
+ uint32_t length = sizeof(buffer);
+ if (readlink("/proc/self/exe", buffer, length) > 0) {
+ image.name_ = buffer;
+ }
+ }
+ return count_ == k_max_images; // return nonzero if we're at the limit
+ }
+
+ static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
+
+ images() {
+ dl_iterate_phdr(images::callback, this);
+ images_[count_++] = {0uz, 0}; // sentinel at low end
+ images_[count_++] = {~0uz, 0}; // sentinel at high end
+ std::sort(images_.begin(), images_.begin() + count_ - 1);
+ }
+
+ image& operator[](size_t index) {
+ assert(index < count_);
+ return images_.at(index);
+ }
+
+ image* mainProg() {
+ for (auto& image : images_) {
+ if (image.is_main_prog_) {
+ return ℑ
+ }
+ }
+ return nullptr;
+ }
+
+ static images& get() {
+ static images images;
+ return images;
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_LINUX
+
+#endif // _LIBCPP_STACKTRACE_LINUX_H
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.cpp b/libcxx/src/experimental/stacktrace/osx/osx.cpp
new file mode 100644
index 0000000000000..c00a7ea94e780
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/osx/osx.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+
+# include <algorithm>
+# include <array>
+# include <dlfcn.h>
+# include <mach-o/dyld.h>
+# include <mach-o/loader.h>
+
+# include <experimental/__stacktrace/detail/context.h>
+# include <experimental/__stacktrace/detail/entry.h>
+
+# include "osx.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+constexpr unsigned kMaxImages = 256;
+
+struct Image {
+ uintptr_t loadedAt{};
+ intptr_t slide{};
+ std::string_view name{};
+ bool operator<(Image const& rhs) const { return loadedAt < rhs.loadedAt; }
+ operator bool() const { return !name.empty(); }
+};
+
+void ident_module(entry& entry, unsigned& index, Image* images) {
+ if (entry.__addr_) {
+ while (images[index].loadedAt > entry.__addr_) {
+ --index;
+ }
+ while (images[index + 1].loadedAt <= entry.__addr_) {
+ ++index;
+ }
+ auto& image = images[index];
+ if (image) {
+ entry.__addr_unslid_ = entry.__addr_ - images[index].slide;
+ entry.__file_ = images[index].name;
+ }
+ }
+}
+
+bool enum_modules(unsigned& count, auto& images) {
+ count = std::min(kMaxImages, _dyld_image_count());
+ for (size_t i = 0; i < count; i++) {
+ auto& image = images[i];
+ image.slide = _dyld_get_image_vmaddr_slide(i);
+ image.loadedAt = uintptr_t(_dyld_get_image_header(i));
+ image.name = _dyld_get_image_name(i);
+ }
+ images[count++] = {0uz, 0}; // sentinel at low end
+ images[count++] = {~0uz, 0}; // sentinel at high end
+ std::sort(images.begin(), images.begin() + count - 1);
+ return true;
+}
+
+void osx::ident_modules() {
+ static unsigned imageCount;
+ static std::array<Image, kMaxImages + 2> images;
+ static bool atomicInitialized = enum_modules(imageCount, images);
+ (void)atomicInitialized;
+
+ // Aside from the left/right sentinels in the array (hence the 2),
+ // are there any other real images?
+ if (imageCount <= 2) {
+ return;
+ }
+
+ // First image (the main program) is at index 1
+ cx_.__main_prog_path_ = images.at(1).name;
+
+ unsigned index = 1; // Starts at one, and is moved by 'ident_module'
+ for (auto& entry : cx_.__entries_) {
+ ident_module(entry, index, images.data());
+ }
+}
+
+void symbolize_entry(entry& entry) {
+ Dl_info info;
+ if (dladdr((void*)entry.__addr_, &info)) {
+ if (info.dli_fname && entry.__file_.empty()) {
+ // provide at least the binary filename in case we cannot lookup source location
+ entry.__file_ = info.dli_fname;
+ }
+ if (info.dli_sname && entry.__desc_.empty()) {
+ // provide at least the mangled name; try to unmangle in a later step
+ entry.__desc_ = info.dli_sname;
+ }
+ }
+}
+
+void osx::symbolize() {
+ for (auto& entry : cx_.__entries_) {
+ symbolize_entry(entry);
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_APPLE
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.h b/libcxx/src/experimental/stacktrace/osx/osx.h
new file mode 100644
index 0000000000000..ca0890929453e
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/osx/osx.h
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_OSX_H
+#define _LIBCPP_STACKTRACE_OSX_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct osx {
+ context& cx_;
+ void ident_modules();
+ void symbolize();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_OSX_H
diff --git a/libcxx/src/experimental/stacktrace/stacktrace.cpp b/libcxx/src/experimental/stacktrace/stacktrace.cpp
new file mode 100644
index 0000000000000..e33b94205a8ea
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/stacktrace.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <experimental/__stacktrace/basic_stacktrace.h>
+#include <experimental/__stacktrace/stacktrace_entry.h>
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+#include <experimental/__stacktrace/detail/to_string.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+/*
+ * `to_string` Helpers
+ */
+
+void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
+ // Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
+ constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
+
+ __os << "0x" << std::hex << std::setfill('0') << std::setw(__k_addr_width) << entry.native_handle();
+ if (!entry.description().empty()) {
+ __os << ": " << entry.description();
+ }
+ if (!entry.source_file().empty()) {
+ __os << ": " << entry.source_file();
+ }
+ if (entry.source_line()) {
+ __os << ":" << std::dec << entry.source_line();
+ }
+}
+
+void __to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
+ /*
+ * Print each entry as a line, as per `operator()`, with additional whitespace
+ * at the start of the line, and only a newline added at the end:
+ *
+ * frame 1: 0xbeefbeefbeef: _symbol_name: /path/to/file.cc:123
+ */
+ if (!__count) {
+ __os << "(empty stacktrace)";
+ } else {
+ for (size_t __i = 0; __i < __count; __i++) {
+ if (__i) {
+ // Insert newlines between entries (but not before the first or after the last)
+ __os << std::endl;
+ }
+ __os << " frame " << std::setw(3) << std::setfill(' ') << std::dec << (__i + 1) << ": ";
+ (*this)(__os, __entries[__i]);
+ }
+ }
+}
+
+string __to_string::operator()(std::stacktrace_entry const& entry) {
+ stringstream __ss;
+ (*this)(__ss, entry);
+ return __ss.str();
+}
+
+string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
+ stringstream __ss;
+ (*this)(__ss, __entries, __count);
+ return __ss.str();
+}
+
+} // namespace __stacktrace
+
+// `to_string`-related non-member functions
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
+ return __stacktrace::__to_string()(__entry);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
+ __stacktrace::__to_string()(__os, __entry);
+ return __os;
+}
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/addr2line.cpp b/libcxx/src/experimental/stacktrace/tools/addr2line.cpp
new file mode 100644
index 0000000000000..310e99699569b
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/addr2line.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <iostream>
+
+#include "tools.h"
+
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+std::pmr::list<std::pmr::string> addr2line::buildArgs(context& cx) const {
+ auto& alloc = cx.__alloc_;
+ auto ret = alloc.new_string_list();
+ if (cx.__main_prog_path_.empty()) {
+ // Should not have reached here but be graceful anyway
+ ret.push_back("/bin/false");
+ return ret;
+ }
+
+ ret.push_back(progName_);
+ ret.push_back("--functions");
+ ret.push_back("--demangle");
+ ret.push_back("--basenames");
+ ret.push_back("--pretty-print"); // This "human-readable form" is easier to parse
+ ret.push_back("-e");
+ ret.push_back(cx.__main_prog_path_);
+ for (auto& entry : cx.__entries_) {
+ ret.push_back(alloc.hex_string(entry.__addr_unslid_));
+ }
+ return ret;
+}
+
+void addr2line::parseOutput(context& trace, entry& entry, std::istream& output) const {
+ // clang-format off
+/*
+Example:
+--
+llvm-addr2line -e foo --functions --demangle --basenames --pretty-print --no-inlines 0x11a0 0x1120 0x3d58 0x1284
+
+Output: (1 line per input address)
+--
+main at foo.cc:15
+register_tm_clones at crtstuff.c:0
+GCC_except_table2 at foo.cc:0
+test::Foo::Foo(int) at foo.cc:11
+*/
+ // clang-format on
+
+ std::pmr::string line{&trace.__alloc_};
+ std::getline(output, line);
+ while (isspace(line.back())) {
+ line.pop_back();
+ }
+ if (line.empty()) {
+ return;
+ }
+ // Split at the sequence " at ". Barring weird symbols
+ // having " at " in them, this should work.
+ auto sepIndex = line.find(" at ");
+ if (sepIndex == std::string::npos) {
+ return;
+ }
+ if (sepIndex > 0) {
+ entry.__desc_ = line.substr(0, sepIndex);
+ }
+ auto fileBegin = sepIndex + 4;
+ if (fileBegin >= line.size()) {
+ return;
+ }
+ auto fileline = line.substr(fileBegin);
+ auto colon = fileline.find_last_of(":");
+ if (colon > 0 && !fileline.starts_with("?")) {
+ entry.__file_ = fileline.substr(0, colon);
+ }
+
+ if (colon == std::string::npos) {
+ return;
+ }
+ uint32_t lineno = 0;
+ auto pos = colon;
+ while (isdigit(fileline[++pos])) {
+ lineno = lineno * 10 + (fileline[pos] - '0');
+ }
+ entry.__line_ = lineno;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/atos.cpp b/libcxx/src/experimental/stacktrace/tools/atos.cpp
new file mode 100644
index 0000000000000..569883a4b12bc
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/atos.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <istream>
+#include <string>
+#include <unistd.h>
+
+#include "tools.h"
+
+#include <experimental/__stacktrace/detail/alloc.h>
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+std::pmr::list<std::pmr::string> atos::buildArgs(context& cx) const {
+ auto& alloc = cx.__alloc_;
+ auto ret = alloc.new_string_list();
+ ret.push_back(progName_);
+ ret.push_back("-p");
+ ret.push_back(alloc.u64_string(getpid()));
+ // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
+ // ret.push_back("--fullPath");
+ for (auto& entry : cx.__entries_) {
+ ret.push_back(alloc.hex_string(entry.__addr_));
+ }
+ return ret;
+}
+
+void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
+ // Simple example:
+ //
+ // main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+ //
+ // Assuming this is always atos's format (except when it returns empty lines)
+ // we can split the string like so:
+ //
+ // main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+ // ^^^^-----^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^-
+ // sym module filename line
+ //
+ // Note that very strange filenames or module names can confuse this.
+ // We'll do the best we can for a decent result, while definitely ensuring safety
+ // (i.e. careful with our bound-checking).
+ //
+ // Another more interesting example (with an added newline for legibility):
+ //
+ // std::__1::basic_ios<char, std::__1::char_traits<char>>::fill[abi:ne190107]() const (in testprog)
+ // (/opt/homebrew/Cellar/llvm/19.1.7_1/bin/../include/c++/v1/ios:0
+ //
+ // If this more or less fits our expected format we'll take these data,
+ // even if the line number is 0.
+
+ auto line = cx.__alloc_.new_string(256);
+ std::getline(output, line);
+ while (isspace(line.back())) {
+ line.pop_back();
+ }
+ if (line.empty()) {
+ return;
+ }
+ auto buf = line.data();
+ auto size = line.size();
+
+ auto* end = buf + size;
+ auto* symEnd = strstr(buf, " (in ");
+ if (!symEnd) {
+ return;
+ }
+ auto* modBegin = symEnd + 5;
+ auto* modEnd = strstr(modBegin, ") (");
+ if (!modEnd) {
+ return;
+ }
+ auto* fileBegin = modEnd + 3; // filename starts just after that
+ if (fileBegin >= end) {
+ return;
+ }
+ auto const* lastColon = fileBegin; // we'll search for last colon after filename
+ char const* nextColon;
+ while ((nextColon = strstr(lastColon + 1, ":"))) { // skip colons in filename (e.g. in "C:\foo.cpp")
+ lastColon = nextColon;
+ }
+
+ std::string_view sym{buf, size_t(symEnd - buf)};
+ // In case a previous step could not obtain the symbol name,
+ // we have the name provided by atos; only use that if we have no symbol
+ // (no need to copy more strings otherwise).
+ if (entry.__desc_.empty() && !sym.empty()) {
+ entry.__desc_ = sym;
+ }
+
+ std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
+ if (file != "?" && file != "??" && !file.empty()) {
+ entry.__file_ = file;
+ }
+
+ unsigned lineno = 0;
+ for (auto* digit = lastColon + 1; digit < end && isdigit(*digit); ++digit) {
+ lineno = (lineno * 10) + unsigned(*digit - '0');
+ }
+ entry.__line_ = lineno;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp b/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
new file mode 100644
index 0000000000000..0a9907978dcf2
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <iostream>
+#include <string_view>
+
+#include "../common/debug.h"
+#include "tools.h"
+
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// TODO(stacktrace23): possible to link against `libLLVMSymbolize.a`, or some shared obj at runtime (does that exist?)
+
+std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
+ auto& alloc = cx.__alloc_;
+ auto ret = alloc.new_string_list();
+ ret.push_back(progName_);
+ ret.push_back("--demangle");
+ ret.push_back("--no-inlines");
+ ret.push_back("--verbose");
+ ret.push_back("--relativenames");
+ ret.push_back("--functions=short");
+ for (auto& entry : cx.__entries_) {
+ auto addr_string = alloc.hex_string(entry.__addr_unslid_);
+ debug() << "@@@ " << addr_string << " " << entry.__file_ << " " << entry.__file_.empty() << '\n';
+ if (!entry.__file_.empty()) {
+ auto arg = alloc.new_string(entry.__file_.size() + 40);
+ arg += "FILE:";
+ arg += entry.__file_;
+ arg += " ";
+ arg += addr_string;
+ ret.push_back(arg);
+ } else {
+ ret.push_back(addr_string);
+ }
+ }
+ return ret;
+}
+
+void llvm_symbolizer::parseOutput(context& cx, entry& entry, std::istream& output) const {
+ // clang-format off
+/*
+With "--verbose", parsing is a little easier, or at least, more reliable;
+probably the best solution (until we have a JSON parser).
+Example output, verbatim, between the '---' lines:
+---
+test1<test_alloc<std::__1::stacktrace_entry> >
+ Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+ Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+ Function start line: 114
+ Function start address: 0x8dd0
+ Line: 116
+ Column: 14
+
+---
+Note that this includes an extra empty line as a terminator.
+*/
+ // clang-format on
+
+ auto& alloc = cx.__alloc_;
+ auto line = alloc.new_string(256);
+ std::string_view tmp;
+ while (true) {
+ std::getline(output, line);
+ while (isspace(line.back())) {
+ line.pop_back();
+ }
+ if (line.empty()) {
+ return;
+ }
+ if (!line.starts_with(" ")) {
+ // The symbol has no leading whitespace, while the other
+ // lines with "fields" like line, column, filename, etc.
+ // start with two spaces.
+ if (line != "??") {
+ entry.__desc_ = line;
+ }
+ } else if (line.starts_with(" Filename:")) {
+ tmp = line;
+ tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+ if (tmp != "??") {
+ entry.__file_ = tmp;
+ }
+ } else if (line.starts_with(" Line:")) {
+ tmp = line;
+ tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+ if (tmp != "??" && tmp != "0") {
+ uint32_t lineno = 0;
+ auto pos = 0;
+ while (isdigit(tmp[pos])) {
+ lineno = lineno * 10 + (tmp[pos++] - '0');
+ }
+ entry.__line_ = lineno;
+ }
+ }
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/pspawn.h b/libcxx/src/experimental/stacktrace/tools/pspawn.h
new file mode 100644
index 0000000000000..906a504441368
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/pspawn.h
@@ -0,0 +1,163 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_PSPAWN_H
+#define _LIBCPP_STACKTRACE_PSPAWN_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+
+# include <__config>
+# include <__config_site>
+# include <cassert>
+# include <cerrno>
+# include <csignal>
+# include <cstddef>
+# include <cstdlib>
+# include <list>
+# include <spawn.h>
+# include <string>
+# include <sys/fcntl.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+# include <vector>
+
+# include <experimental/__stacktrace/detail/context.h>
+# include <experimental/__stacktrace/detail/entry.h>
+
+# include "../common/debug.h"
+# include "../common/failed.h"
+# include "../common/fd.h"
+# include "tools.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct file_actions {
+ posix_spawn_file_actions_t fa_;
+
+ file_actions() {
+ if (posix_spawn_file_actions_init(&fa_)) {
+ throw failed("posix_spawn_file_actions_init", errno);
+ }
+ }
+
+ ~file_actions() { posix_spawn_file_actions_destroy(&fa_); }
+
+ void addClose(int fd) {
+ if (posix_spawn_file_actions_addclose(&fa_, fd)) {
+ throw failed("posix_spawn_file_actions_addclose", errno);
+ }
+ }
+ void addDup2(int fd, int stdfd) {
+ if (posix_spawn_file_actions_adddup2(&fa_, fd, stdfd)) {
+ throw failed("posix_spawn_file_actions_adddup2", errno);
+ }
+ }
+
+ fd redirectOutFD() {
+ int fds[2];
+ if (::pipe(fds)) {
+ throw failed("pipe", errno);
+ }
+ addClose(fds[0]);
+ addDup2(fds[1], 1);
+ return {fds[0]};
+ }
+
+ void redirectInNull() { addDup2(fd::null_fd(), 0); }
+ void redirectOutNull() { addDup2(fd::null_fd(), 1); }
+ void redirectErrNull() { addDup2(fd::null_fd(), 2); }
+};
+
+struct pspawn {
+ tool const& tool_;
+ pid_t pid_{0};
+ file_actions fa_{};
+
+ // TODO(stacktrace23): ignore SIGCHLD for spawned subprocess
+
+ ~pspawn() {
+ if (pid_) {
+ kill(pid_, SIGTERM);
+ wait();
+ }
+ }
+
+ void spawn(std::pmr::list<std::pmr::string> const& argStrings) {
+ std::pmr::vector<char const*> argv{argStrings.get_allocator()};
+ argv.reserve(argStrings.size() + 1);
+ for (auto const& str : argStrings) {
+ argv.push_back(str.data());
+ }
+ argv.push_back(nullptr);
+ int err;
+ if ((err = posix_spawnp(&pid_, argv[0], &fa_.fa_, nullptr, const_cast<char**>(argv.data()), nullptr))) {
+ throw failed("posix_spawnp", err);
+ }
+ }
+
+ int wait() {
+ int status;
+ waitpid(pid_, &status, 0);
+ return status;
+ }
+};
+
+struct pspawn_tool : pspawn {
+ context& cx_;
+ fd fd_;
+ fd_streambuf buf_;
+ fd_istream stream_;
+
+ pspawn_tool(tool const& a2l, context& cx, char* buf, size_t size)
+ : pspawn{a2l}, cx_(cx), fd_(fa_.redirectOutFD()), buf_(fd_, buf, size), stream_(buf_) {
+ if (!debug::enabled()) {
+ fa_.redirectErrNull();
+ }
+ fa_.redirectInNull();
+ }
+
+ void run() {
+ // Cannot run "addr2line" or similar without addresses, since we
+ // provide them in argv, and if there are none passed in argv, the
+ // tool will try to read from stdin and hang.
+ if (cx_.__entries_.empty()) {
+ return;
+ }
+
+ auto argStrings = tool_.buildArgs(cx_);
+ if (debug::enabled()) {
+ debug() << "Trying to get stacktrace using:";
+ for (auto& str : argStrings) {
+ debug() << " \"" << str << '"';
+ }
+ debug() << '\n';
+ }
+
+ spawn(argStrings);
+
+ for (auto& entry : cx_.__entries_) {
+ tool_.parseOutput(cx_, entry, stream_);
+ }
+ }
+};
+
+struct spawner {
+ context& cx_;
+ void resolve_lines();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#endif // _LIBCPP_STACKTRACE_PSPAWN_H
diff --git a/libcxx/src/experimental/stacktrace/tools/tools.h b/libcxx/src/experimental/stacktrace/tools/tools.h
new file mode 100644
index 0000000000000..d832974e69cbb
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/tools.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_TOOLS_H
+#define _LIBCPP_STACKTRACE_TOOLS_H
+
+#include <__config>
+#include <__config_site>
+#include <cassert>
+#include <cstddef>
+#include <list>
+#include <string>
+
+#include <experimental/__stacktrace/detail/context.h>
+#include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct tool {
+ char const* progName_;
+ explicit tool(char const* progName) : progName_(progName) {}
+ constexpr virtual ~tool() = default;
+
+ /** Construct complete `argv` for the spawned process.
+ Includes the program name at argv[0], followed by flags */
+ virtual std::pmr::list<std::pmr::string> buildArgs(context& trace) const = 0;
+
+ /** Parse line(s) output by the tool, and modify `entry`. */
+ virtual void parseOutput(context& trace, entry& e, std::istream& output) const = 0;
+};
+
+struct llvm_symbolizer : tool {
+ constexpr virtual ~llvm_symbolizer() = default;
+ llvm_symbolizer() : llvm_symbolizer("llvm-symbolizer") {}
+ explicit llvm_symbolizer(char const* progName) : tool{progName} {}
+ std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+ void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
+struct addr2line : tool {
+ constexpr virtual ~addr2line() = default;
+ addr2line() : addr2line("addr2line") {}
+ explicit addr2line(char const* progName) : tool{progName} {}
+ std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+ void parseOutput(context& trace, entry& e, std::istream& stream) const override;
+};
+
+struct atos : tool {
+ constexpr virtual ~atos() = default;
+ atos() : atos("atos") {}
+ explicit atos(char const* progName) : tool{progName} {}
+ std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+ void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_TOOLS_H
diff --git a/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp b/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
new file mode 100644
index 0000000000000..05a2f8e6c2eb4
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
@@ -0,0 +1,185 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+
+# include <__config>
+# include <__config_site>
+# include <cassert>
+# include <cerrno>
+# include <csignal>
+# include <cstddef>
+# include <cstdlib>
+# include <spawn.h>
+# include <string>
+# include <sys/fcntl.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+
+# include "../common/failed.h"
+# include "pspawn.h"
+
+# include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/**
+Returns a list of `tool` objects that can be tried during the first stacktrace operation.
+The result is an array of `tool*` pointers with a final `nullptr` terminator.
+
+The returned tool array pointer is saved during `resolve_lines`'s static init,
+with the first working tool being used from that point forward.
+
+This will first add any tools specified by these defines (in order):
+- LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH
+- LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH
+- LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH
+
+Then any tools specified by the environment variables of the same names (and added in the same order).
+
+Finally, this adds the tools `llvm-symbolizer`, `addr2line`, and `atos`, in that order.
+These tools won't have absolute paths, so $PATH will be searched for these.
+
+Called by `findWorkingTool` during its static initialization, therefore this is used in a threadsafe way.
+*/
+tool const** toolList() {
+ constexpr static size_t kMax = 20;
+ static tool const* array_[kMax];
+ size_t count = 0;
+
+ auto add = [&](tool* t) {
+ assert(count < kMax - 1);
+ array_[count++] = t;
+ };
+
+# define STRINGIFY0(x) #x
+# define STRINGIFY(x) STRINGIFY0(x)
+
+# if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
+ {
+ static llvm_symbolizer t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)};
+ add(&t);
+ }
+# endif
+# if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
+ {
+ static addr2line t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)};
+ add(&t);
+ }
+# endif
+# if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
+ {
+ static atos t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)};
+ add(&t);
+ }
+# endif
+
+ if (getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")) {
+ static llvm_symbolizer t{getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")};
+ add(&t);
+ }
+ if (getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")) {
+ static addr2line t{getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")};
+ add(&t);
+ }
+ if (getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")) {
+ static atos t{getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")};
+ add(&t);
+ }
+
+ {
+ static llvm_symbolizer t;
+ add(&t);
+ }
+ {
+ static addr2line t;
+ add(&t);
+ }
+ {
+ static atos t;
+ add(&t);
+ }
+
+ array_[count] = nullptr; // terminator
+ return array_;
+}
+
+// Try a handful of different addr2line-esque tools, returning the first discovered, or else nullptr
+tool const* findWorkingTool(auto& trace) {
+ // Try each of these programs and stop at the first one that works.
+ // There's no synchronization so this is subject to races on setting `prog`,
+ // but as long as one thread ends up setting it to something that works, we're good.
+ // Programs to try ($PATH is searched):
+ auto* it = toolList();
+ tool const* prog;
+ while ((prog = *it++)) {
+ pspawn test{*prog}; // Just try to run with "--help"
+ try {
+ std::pmr::list<std::pmr::string> testArgs{&trace.__alloc_};
+ testArgs.push_back(prog->progName_);
+ testArgs.push_back("--help");
+ test.fa_.redirectInNull();
+ test.fa_.redirectOutNull();
+ test.fa_.redirectErrNull();
+ test.spawn(testArgs);
+ if (test.wait() == 0) {
+ // Success
+ return prog;
+ }
+ } catch (failed const&) {
+ /* ignore during probe attempt */
+ }
+ }
+ return nullptr;
+}
+
+} // namespace
+
+void spawner::resolve_lines() {
+ // The address-to-line tool that worked (after lazy-setting, below)
+ static tool const* prog{nullptr};
+ // Whether we should not attempt because tool detection previously failed.
+ static bool fail{false};
+
+ // If this previously failed, don't try again.
+ if (fail) {
+ return;
+ }
+
+ if (!prog) {
+ prog = findWorkingTool(cx_);
+ if (!prog) {
+ fail = true;
+ return;
+ }
+ }
+ assert(prog);
+
+ char buf[256];
+ pspawn_tool proc(*prog, cx_, buf, sizeof(buf));
+ try {
+ proc.run();
+ } catch (failed const& failed) {
+ debug() << failed.what();
+ if (failed.errno_) {
+ debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
+ }
+ debug() << '\n';
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.cpp b/libcxx/src/experimental/stacktrace/unwind/unwind.cpp
new file mode 100644
index 0000000000000..a316306bc43eb
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/unwind/unwind.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+
+# include "./unwind.h"
+
+# include <experimental/__stacktrace/detail/context.h>
+# include <experimental/__stacktrace/detail/entry.h>
+# include <unwind.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct unwind_backtrace {
+ context& cx_;
+ size_t skip_;
+ size_t maxDepth_;
+
+ _Unwind_Reason_Code callback(_Unwind_Context* ucx) {
+ if (skip_) {
+ --skip_;
+ return _Unwind_Reason_Code::_URC_NO_REASON;
+ }
+ if (!maxDepth_) {
+ return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+ }
+ --maxDepth_;
+ int ipBefore;
+ auto ip = _Unwind_GetIPInfo(ucx, &ipBefore);
+ if (!ip) {
+ return _Unwind_Reason_Code::_URC_NORMAL_STOP;
+ }
+ auto& entry = cx_.__entries_.emplace_back();
+ entry.__addr_ = (ipBefore ? ip : ip - 1);
+ entry.__addr_unslid_ = entry.__addr_; // in case we can't un-slide
+ return _Unwind_Reason_Code::_URC_NO_REASON;
+ }
+
+ static _Unwind_Reason_Code callback(_Unwind_Context* cx, void* self) {
+ return ((unwind_backtrace*)self)->callback(cx);
+ }
+};
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void unwind::collect(size_t skip, size_t max_depth) {
+ unwind_backtrace bt{cx_, skip + 1, max_depth}; // skip this call as well
+ _Unwind_Backtrace(unwind_backtrace::callback, &bt);
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.h b/libcxx/src/experimental/stacktrace/unwind/unwind.h
new file mode 100644
index 0000000000000..1b099e3a0b922
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/unwind/unwind.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_UNWIND_H
+#define _LIBCPP_STACKTRACE_UNWIND_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct unwind {
+ context& cx_;
+ void collect(size_t skip, size_t max_depth);
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_UNWIND_H
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
new file mode 100644
index 0000000000000..274c9ecb2b314
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+# include <windows.h>
+// other windows-specific headers
+# include <dbghelp.h>
+# define PSAPI_VERSION 1
+# include <psapi.h>
+// standard headers
+# include <cstdlib>
+# include <mutex>
+
+# include "dll.h"
+# include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+dbghelp_dll::~dbghelp_dll() = default;
+
+dbghelp_dll& dbghelp_dll::get() {
+ dbghelp_dll ret;
+ return ret;
+}
+
+dbghelp_dll::dbghelp_dll() : dll("dbghelp.dll") {
+ // clang-format off
+if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+ if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+ if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+ if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+ if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+ if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+ if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+ if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+ if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+ if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+ if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+ valid_ = true;
+ // clang-format on
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
new file mode 100644
index 0000000000000..920391fb9ba52
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
+#define _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+// windows.h must be first
+# include <windows.h>
+// other windows-specific headers
+# include <dbghelp.h>
+# define PSAPI_VERSION 1
+# include <psapi.h>
+// standard headers
+# include <cstdlib>
+# include <mutex>
+
+# include <experimental/__stacktrace/detail/entry.h>
+
+# include "../common/debug.h"
+# include "dll.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// clang-format off
+
+struct dbghelp_dll final : dll {
+ virtual ~dbghelp_dll() = default;
+ static dbghelp_dll& get() { static dbghelp_dll ret; return ret; }
+
+ IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
+ bool (*StackWalk64) (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
+ bool (*SymCleanup) (HANDLE);
+ void* (*SymFunctionTableAccess64)(HANDLE, DWORD64);
+ bool (*SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
+ DWORD64 (*SymGetModuleBase64) (HANDLE, DWORD64);
+ DWORD (*SymGetOptions) ();
+ bool (*SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
+ bool (*SymInitialize) (HANDLE, char const*, bool);
+ DWORD64 (*SymLoadModule64) (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
+ DWORD (*SymSetOptions) (DWORD);
+
+ dbghelp_dll() : dll("dbghelp.dll") {
+ if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+ if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+ if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+ if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+ if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+ if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+ if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+ if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+ if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+ if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+ if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+ valid_ = true;
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WINDOWS
+#endif // _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.cpp b/libcxx/src/experimental/stacktrace/windows/dll.cpp
new file mode 100644
index 0000000000000..55fd729a9caf4
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dll.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+# include <windows.h>
+// other windows-specific headers
+# include <dbghelp.h>
+# define PSAPI_VERSION 1
+# include <psapi.h>
+// standard headers
+# include <cstdlib>
+# include <mutex>
+
+# include "dll.h"
+# include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+dll::~dll() { FreeLibrary(module_); }
+
+dll::dll(char const* name) : name_(name), module_(LoadLibrary(name)) {
+ if (!module_) {
+ debug() << "LoadLibrary failed: " << name_ << ": " << GetLastError() << '\n';
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.h b/libcxx/src/experimental/stacktrace/windows/dll.h
new file mode 100644
index 0000000000000..ab363ce2c2561
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/dll.h
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_WINDOWS_DLL_H
+#define _LIBCPP_STACKTRACE_WINDOWS_DLL_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+// windows.h must be first
+# include <windows.h>
+// other windows-specific headers
+# include <dbghelp.h>
+# define PSAPI_VERSION 1
+# include <psapi.h>
+// standard headers
+# include <cstdlib>
+# include <mutex>
+
+# include <experimental/__stacktrace/detail/entry.h>
+
+# include "../common/debug.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// clang-format off
+
+struct dll {
+ char const* name_;
+ HMODULE module_;
+ /** Set to true in subclass's ctor if initialized successfully. */
+ bool valid_{false};
+
+ operator bool() const { return valid_; }
+
+ explicit dll(char const* name)
+ : name_(name), module_(LoadLibrary(name)) {
+ if (!module_) {
+ debug() << "LoadLibrary failed: "
+ << name_ << ": " << GetLastError() << '\n';
+ }
+ }
+
+ virtual ~dll() { FreeLibrary(module_); }
+
+ template <typename F>
+ bool get_func(F* func, char const* name) {
+ if (!(*func = (F)GetProcAddress(module_, name))) {
+ debug() << "GetProcAddress failed: "
+ << name << "' (" << name_ << "): "
+ << GetLastError() << "\n";
+ return false;
+ }
+ return true;
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WINDOWS
+#endif // _LIBCPP_STACKTRACE_WINDOWS_DLL_H
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp b/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
new file mode 100644
index 0000000000000..ab28df8d3fbd7
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+# include <windows.h>
+// other windows-specific headers
+# include <dbghelp.h>
+# define PSAPI_VERSION 1
+# include <psapi.h>
+// standard headers
+# include <cstdlib>
+# include <mutex>
+
+# include "dll.h"
+# include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+psapi_dll::~psapi_dll() = default;
+
+psapi_dll& psapi_dll::get() {
+ psapi_dll ret;
+ return ret;
+}
+
+psapi_dll() : dll("psapi.dll") {
+ // clang-format off
+if (!getFunc(&EnumProcessModules, "EnumProcessModules")) { return; }
+ if (!getFunc(&GetModuleInformation, "GetModuleInformation")) { return; }
+ if (!getFunc(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+ valid_ = true;
+ // clang-format on
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.h b/libcxx/src/experimental/stacktrace/windows/psapi_dll.h
new file mode 100644
index 0000000000000..4e6a192f3d790
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/psapi_dll.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
+#define _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
+
+#include "../common/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+// windows.h must be first
+# include <windows.h>
+// other windows-specific headers
+# include <dbghelp.h>
+# define PSAPI_VERSION 1
+# include <psapi.h>
+// standard headers
+# include <cstdlib>
+# include <mutex>
+
+# include <experimental/__stacktrace/detail/entry.h>
+
+# include "../common/debug.h"
+# include "dll.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+// clang-format off
+
+struct psapi_dll final : dll {
+ virtual ~psapi_dll() = default;
+ static psapi_dll& get() { static psapi_dll ret; return ret; }
+
+ bool (*EnumProcessModules) (HANDLE, HMODULE*, DWORD, DWORD*);
+ bool (*GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
+ DWORD (*GetModuleBaseName) (HANDLE, HMODULE, char**, DWORD);
+
+ psapi_dll() : dll("psapi.dll") {
+ if (!get_func(&EnumProcessModules, "EnumProcessModules")) { return; }
+ if (!get_func(&GetModuleInformation, "GetModuleInformation")) { return; }
+ if (!get_func(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+ valid_ = true;
+ }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WINDOWS
+#endif // _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.cpp b/libcxx/src/experimental/stacktrace/windows/win_impl.cpp
new file mode 100644
index 0000000000000..7773d0d872816
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/win_impl.cpp
@@ -0,0 +1,204 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "win_impl.h"
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
+// windows.h must be first
+# include <windows.h>
+// other windows-specific headers
+# include <dbghelp.h>
+# define PSAPI_VERSION 1
+# include <psapi.h>
+// standard headers
+# include <cstdlib>
+# include <mutex>
+
+# include "dll.h"
+# include <experimental/__stacktrace/detail/entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/*
+Global objects, shared among all threads and among all stacktrace operations.
+The `dbghelp` APIs are not safe to call concurrently (according to their docs)
+so we claim a lock in the `WinDebugAPIs` constructor.
+*/
+
+// Statically-initialized
+std::mutex gWindowsAPILock;
+DbgHelpDLL dbg;
+PSAPIDLL ps;
+
+// Initialized once, in first WinDebugAPIs construction;
+// protected by the above mutex.
+HANDLE proc;
+HMODULE exe;
+IMAGE_NT_HEADERS* ntHeaders;
+bool globalInitialized{false};
+
+// Globals used across invocations of the functions below.
+// Also guarded by the above mutex.
+bool symsInitialized{false};
+HMODULE moduleHandles[1024];
+size_t moduleCount; // 0 IFF module enumeration failed
+
+} // namespace
+
+win_impl::WinDebugAPIs(context& trace) : trace_(trace), guard_(gWindowsAPILock) {
+ if (!globalInitialized) {
+ // Cannot proceed without these DLLs:
+ if (!dbg) {
+ return;
+ }
+ if (!ps) {
+ return;
+ }
+ proc = GetCurrentProcess();
+ if (!(exe = GetModuleHandle(nullptr))) {
+ return;
+ }
+ if (!(ntHeaders = (*dbg.ImageNtHeader)(exe))) {
+ return;
+ }
+
+ globalInitialized = true;
+ }
+
+ // Initialize symbol machinery.
+ // Presumably the symbols in this process's space can change between
+ // stacktraces, so we'll do this each time we take a trace.
+ // The final `true` means we want the runtime to enumerate all this
+ // process's modules' symbol tables.
+ symsInitialized = (*dbg.SymInitialize)(proc, nullptr, true);
+ DWORD symOptions = (*dbg.SymGetOptions)();
+ symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
+ (*dbg.SymSetOptions)(symOptions);
+}
+
+win_impl::~WinDebugAPIs() {
+ if (symsInitialized) {
+ (*dbg.SymCleanup)(proc);
+ symsInitialized = false;
+ }
+}
+
+void win_impl::ident_modules() {
+ if (!globalInitialized) {
+ return;
+ }
+ DWORD needBytes;
+ auto enumMods = (*ps.EnumProcessModules)(proc, moduleHandles, sizeof(moduleHandles), LPDWORD(&needBytes));
+ if (enumMods) {
+ moduleCount = needBytes / sizeof(HMODULE);
+ } else {
+ moduleCount = 0;
+ Debug() << "EnumProcessModules failed: " << GetLastError() << '\n';
+ }
+}
+
+void win_impl::symbolize() {
+ // Very long symbols longer than this amount will be truncated.
+ static constexpr size_t kMaxSymName = 256;
+ if (!globalInitialized) {
+ return;
+ }
+
+ for (auto& entry : cx_.__entries_) {
+ char space[sizeof(IMAGEHLP_SYMBOL64) + kMaxSymName];
+ auto* sym = (IMAGEHLP_SYMBOL64*)space;
+ sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ sym->MaxNameLength = kMaxSymName;
+ uint64_t disp{0};
+ if ((*dbg.SymGetSymFromAddr64)(proc, entry.__addr_, &disp, sym)) {
+ // Copy chars into the destination string which uses the caller-provided allocator.
+ entry.__desc_ = {sym->Name};
+ } else {
+ Debug() << "SymGetSymFromAddr64 failed: " << GetLastError() << '\n';
+ }
+ }
+}
+
+void win_impl::resolve_lines() {
+ if (!globalInitialized) {
+ return;
+ }
+
+ for (auto& entry : cx_.__entries_) {
+ DWORD disp{0};
+ IMAGEHLP_LINE64 line;
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ if ((*dbg.SymGetLineFromAddr64)(proc, entry.__addr_, &disp, &line)) {
+ // Copy chars into the destination string which uses the caller-provided allocator.
+ entry.__file_ = line.FileName;
+ entry.__line_ = line.LineNumber;
+ } else {
+ Debug() << "SymGetLineFromAddr64 failed: " << GetLastError() << '\n';
+ }
+ }
+}
+
+/*
+Inlining is disabled from here on;
+this is to ensure `collect` below doesn't get merged into its caller
+and mess around with the top of the stack (making `skip` inaccurate).
+*/
+# pragma auto_inline(off)
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_t max_depth) {
+ if (!globalInitialized) {
+ return;
+ }
+
+ auto thread = GetCurrentThread();
+ auto machine = ntHeaders->FileHeader.Machine;
+
+ CONTEXT ccx;
+ RtlCaptureContext(&ccx);
+
+ STACKFRAME64 frame;
+ memset(&frame, 0, sizeof(frame));
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrStack.Mode = AddrModeFlat;
+ frame.AddrFrame.Mode = AddrModeFlat;
+ frame.AddrPC.Offset = ccx.Rip;
+ frame.AddrStack.Offset = ccx.Rsp;
+ frame.AddrFrame.Offset = ccx.Rbp;
+
+ while (max_depth &&
+ (*dbg.StackWalk64)(
+ machine,
+ proc,
+ thread,
+ &frame,
+ &ccx,
+ nullptr,
+ dbg.SymFunctionTableAccess64,
+ dbg.SymGetModuleBase64,
+ nullptr)) {
+ if (skip) {
+ --skip;
+ continue;
+ }
+ --max_depth;
+ auto& entry = cx_.__entries_.emplace_back();
+ // We don't need to compute the un-slid addr; windbg only needs the actual addresses.
+ // Assume address is of the instruction after a call instruction, since we can't
+ // differentiate between a signal, SEH exception handler, or a normal function call.
+ entry.__addr_ = frame.AddrPC.Offset - 1; // Back up 1 byte to get into prev insn range
+ }
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.h b/libcxx/src/experimental/stacktrace/windows/win_impl.h
new file mode 100644
index 0000000000000..459347ad9a00f
--- /dev/null
+++ b/libcxx/src/experimental/stacktrace/windows/win_impl.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_WIN_IMPL_H
+#define _LIBCPP_STACKTRACE_WIN_IMPL_H
+
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+#include <mutex>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct _LIBCPP_HIDE_FROM_ABI context;
+
+struct win_impl {
+ context& cx_;
+ std::lock_guard<std::mutex> guard_;
+ win_impl(context& trace);
+ ~win_impl();
+
+ void collect(size_t skip, size_t max_depth);
+ void ident_modules();
+ void symbolize();
+ void resolve_lines();
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_WIN_IMPL_H
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
new file mode 100644
index 0000000000000..6c4f3ee6008a2
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g0
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // This is 'nodebug', so we cannot get the source file and line:
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ // But this should at least produce the executable filename
+ assert(entry.source_file().contains("simple.o0.nodebug.pass.cpp"));
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
new file mode 100644
index 0000000000000..58d3c8fed5c5e
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
new file mode 100644
index 0000000000000..a00866181a0e6
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
+
+#include <cassert>
+#include <iostream>
+#include <experimental/stacktrace>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cerr << trace << '\n';
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
new file mode 100644
index 0000000000000..e8387bfebb14f
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O3 -g0
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // This is 'nodebug', so we cannot get the source file and line:
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ // But this should at least produce the executable filename
+ assert(entry.source_file().contains("simple.o3.nodebug.pass.cpp"));
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
new file mode 100644
index 0000000000000..e3796c2bc8add
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O3 -g
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ return 0;
+}
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
new file mode 100644
index 0000000000000..7b58f9accf7c9
--- /dev/null
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O3 -g -gsplit-dwarf
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <iostream>
+
+int main(int, char**) {
+ // Get the current trace.
+ // uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+ std::cout << trace << std::endl;
+ // First entry of this should be `main`.
+ auto entry = trace.at(0);
+ assert(entry);
+ assert(entry.native_handle());
+ assert(entry.description() == "main" || entry.description() == "_main");
+ // assert(entry.source_file().ends_with(".pass.cpp"));
+ // assert(entry.source_line() == line_number);
+ return 0;
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier cctype
barrier climits
barrier cmath
barrier compare
barrier concepts
barrier cstddef
barrier cstdint
+barrier cstdio
barrier cstdlib
barrier cstring
barrier ctime
+barrier cwchar
+barrier cwctype
barrier exception
barrier initializer_list
barrier iosfwd
@@ -2032,6 +2036,7 @@ stdexcept new
stdexcept type_traits
stdexcept typeinfo
stdexcept version
+stop_token cstddef
stop_token iosfwd
stop_token version
streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..920a6145a02b8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier cctype
barrier climits
barrier cmath
barrier compare
barrier concepts
barrier cstddef
barrier cstdint
+barrier cstdio
barrier cstdlib
barrier cstring
barrier ctime
+barrier cwchar
+barrier cwctype
barrier exception
barrier initializer_list
barrier iosfwd
@@ -2032,6 +2036,7 @@ stdexcept new
stdexcept type_traits
stdexcept typeinfo
stdexcept version
+stop_token cstddef
stop_token iosfwd
stop_token version
streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..cfcf7bc7c4c19 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier cctype
barrier climits
barrier cmath
barrier compare
barrier concepts
barrier cstddef
barrier cstdint
+barrier cstdio
barrier cstdlib
barrier cstring
barrier ctime
+barrier cwchar
+barrier cwctype
barrier exception
barrier initializer_list
barrier iosfwd
@@ -2072,6 +2076,7 @@ stdexcept new
stdexcept type_traits
stdexcept typeinfo
stdexcept version
+stop_token cstddef
stop_token iosfwd
stop_token version
streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..534776e6f0526 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier cctype
barrier climits
barrier cmath
barrier compare
barrier concepts
barrier cstddef
barrier cstdint
+barrier cstdio
barrier cstdlib
barrier cstring
barrier ctime
+barrier cwchar
+barrier cwctype
barrier exception
barrier initializer_list
barrier iosfwd
@@ -2085,6 +2089,7 @@ stdexcept new
stdexcept type_traits
stdexcept typeinfo
stdexcept version
+stop_token cstddef
stop_token iosfwd
stop_token version
streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index cb23c7a98de1b..fbfa8152b8070 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -295,6 +295,35 @@ experimental/propagate_const version
experimental/simd cstdint
experimental/simd limits
experimental/simd version
+experimental/stacktrace bitset
+experimental/stacktrace cctype
+experimental/stacktrace cerrno
+experimental/stacktrace climits
+experimental/stacktrace clocale
+experimental/stacktrace compare
+experimental/stacktrace cstddef
+experimental/stacktrace cstdint
+experimental/stacktrace cstdio
+experimental/stacktrace cstdlib
+experimental/stacktrace cstring
+experimental/stacktrace ctime
+experimental/stacktrace cwchar
+experimental/stacktrace cwctype
+experimental/stacktrace initializer_list
+experimental/stacktrace ios
+experimental/stacktrace iosfwd
+experimental/stacktrace limits
+experimental/stacktrace list
+experimental/stacktrace locale
+experimental/stacktrace memory
+experimental/stacktrace ratio
+experimental/stacktrace stdexcept
+experimental/stacktrace streambuf
+experimental/stacktrace string
+experimental/stacktrace string_view
+experimental/stacktrace tuple
+experimental/stacktrace typeinfo
+experimental/stacktrace version
experimental/type_traits cstdint
experimental/type_traits initializer_list
experimental/type_traits type_traits
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index ce8f0261f2b27..06dd30b07537e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -295,6 +295,35 @@ experimental/propagate_const version
experimental/simd cstdint
experimental/simd limits
experimental/simd version
+experimental/stacktrace bitset
+experimental/stacktrace cctype
+experimental/stacktrace cerrno
+experimental/stacktrace climits
+experimental/stacktrace clocale
+experimental/stacktrace compare
+experimental/stacktrace cstddef
+experimental/stacktrace cstdint
+experimental/stacktrace cstdio
+experimental/stacktrace cstdlib
+experimental/stacktrace cstring
+experimental/stacktrace ctime
+experimental/stacktrace cwchar
+experimental/stacktrace cwctype
+experimental/stacktrace initializer_list
+experimental/stacktrace ios
+experimental/stacktrace iosfwd
+experimental/stacktrace limits
+experimental/stacktrace list
+experimental/stacktrace locale
+experimental/stacktrace memory
+experimental/stacktrace ratio
+experimental/stacktrace stdexcept
+experimental/stacktrace streambuf
+experimental/stacktrace string
+experimental/stacktrace string_view
+experimental/stacktrace tuple
+experimental/stacktrace typeinfo
+experimental/stacktrace version
experimental/type_traits cstdint
experimental/type_traits initializer_list
experimental/type_traits type_traits
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
new file mode 100644
index 0000000000000..495abe79da20f
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+ (19.6.4.4) Comparisons [stacktrace.basic.cmp]
+
+ template<class Allocator2>
+ friend bool operator==(const basic_stacktrace& x,
+ const basic_stacktrace<Allocator2>& y) noexcept;
+
+ template<class Allocator2>
+ friend strong_ordering operator<=>(const basic_stacktrace& x,
+ const basic_stacktrace<Allocator2>& y) noexcept;
+*/
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2a() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2b() { return test1(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ /*
+ template<class Allocator2>
+ friend bool operator==(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
+ Returns: equal(x.begin(), x.end(), y.begin(), y.end()).
+ */
+ auto st1a = test1(); // [test1, main, ...]
+ assert(st1a == st1a);
+
+ auto st1b = st1a;
+ assert(st1a == st1b);
+
+ auto st2a = test2a(); // [test1, test2a, main, ...]
+ assert(st1a != st2a);
+
+ std::stacktrace empty; // []
+ assert(st1a != empty);
+ assert(st2a != empty);
+
+ assert(st2a.size() > st1a.size());
+ assert(st1a.size() > empty.size());
+
+ auto st2b = test2b(); // [test1, test2b, main, ...]
+ assert(st2a.size() == st2b.size());
+ assert(st2a != st2b);
+
+ /*
+ template<class Allocator2>
+ friend strong_ordering
+ operator<=>(const basic_stacktrace& x, const basic_stacktrace<Allocator2>& y) noexcept;
+
+ Returns: x.size() <=> y.size() if x.size() != y.size();
+ lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end()) otherwise.
+ */
+
+ // empty: []
+ // st1a: [test1, main, ...]
+ // st1b: [test1, main, ...] (copy of st1a)
+ // st2a: [test1, test2a, main:X, ...]
+ // st2b: [test1, test2b, main:Y, ...], Y > X
+
+ assert(std::strong_ordering::equal == empty <=> empty);
+ assert(std::strong_ordering::less == empty <=> st1a);
+ assert(std::strong_ordering::less == empty <=> st1b);
+ assert(std::strong_ordering::less == empty <=> st2a);
+ assert(std::strong_ordering::less == empty <=> st2b);
+
+ assert(std::strong_ordering::greater == st1a <=> empty);
+ assert(std::strong_ordering::equal == st1a <=> st1a);
+ assert(std::strong_ordering::equal == st1a <=> st1b);
+ assert(std::strong_ordering::less == st1a <=> st2a);
+ assert(std::strong_ordering::less == st1a <=> st2b);
+
+ assert(std::strong_ordering::greater == st1b <=> empty);
+ assert(std::strong_ordering::equal == st1b <=> st1a);
+ assert(std::strong_ordering::equal == st1b <=> st1b);
+ assert(std::strong_ordering::less == st1b <=> st2a);
+ assert(std::strong_ordering::less == st1b <=> st2b);
+
+ assert(std::strong_ordering::greater == st2a <=> empty);
+ assert(std::strong_ordering::greater == st2a <=> st1a);
+ assert(std::strong_ordering::greater == st2a <=> st1b);
+ assert(std::strong_ordering::equal == st2a <=> st2a);
+ assert(std::strong_ordering::less == st2a <=> st2b);
+
+ assert(std::strong_ordering::greater == st2b <=> empty);
+ assert(std::strong_ordering::greater == st2b <=> st1a);
+ assert(std::strong_ordering::greater == st2b <=> st1b);
+ assert(std::strong_ordering::greater == st2b <=> st2a);
+ assert(std::strong_ordering::equal == st2b <=> st2b);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
new file mode 100644
index 0000000000000..64e54fdf5793d
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
@@ -0,0 +1,308 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O1 -g
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <iostream>
+
+/**
+ * This file includes tests which ensure any allocations performed by `basic_stacktrace`
+ * are done via the user-provided allocator, and not via bare `malloc` / `operator new`s.
+ * Our overridden `malloc` below will fail if `malloc_disabled` is `true`.
+ * The program's startup code may need to malloc, so we'll allow malloc initially.
+ * This is only activated during the "test_no_malloc_or_new_ex_allocator" test,
+ * during which it should be using a `test_alloc` we'll provide (which knows how to
+ * unblock `malloc` temporarily).
+ */
+bool malloc_disabled{false};
+
+void* malloc(size_t size) {
+ // If flag has not been temporarily disabled by our allocator, abort
+ if (malloc_disabled) {
+ abort();
+ }
+ // Since we overrode malloc with this function, and there's no way to call up
+ // to the "real malloc", allocate a different way. Assumes nothing actually uses `calloc`.
+ return calloc(1, size);
+}
+
+// All the various ways to monkey around with heap memory without an allocator:
+// we'll simply redirect these through our gate-keeping `malloc` above.
+void* operator new(size_t size) { return malloc(size); }
+void* operator new[](size_t size) { return malloc(size); }
+void operator delete(void* ptr) noexcept { free(ptr); }
+void operator delete(void* ptr, size_t) noexcept { free(ptr); }
+void operator delete[](void* ptr) noexcept { free(ptr); }
+void operator delete[](void* ptr, size_t) noexcept { free(ptr); }
+
+/** RAII-style scope object to temporarily permit heap allocations, used by `test_alloc`.*/
+struct scope_enable_malloc {
+ bool prev_malloc_disabled_;
+ scope_enable_malloc() : prev_malloc_disabled_(malloc_disabled) { malloc_disabled = false; }
+ ~scope_enable_malloc() { malloc_disabled = true; }
+};
+
+template <typename T>
+struct test_alloc {
+ using size_type = size_t;
+ using value_type = T;
+ using pointer = T*;
+ using const_pointer = T const*;
+
+ template <typename U>
+ struct rebind {
+ using other = test_alloc<U>;
+ };
+
+ std::allocator<T> wrapped_{};
+
+ test_alloc() = default;
+
+ template <typename U>
+ test_alloc(test_alloc<U> const& rhs) : wrapped_(rhs.wrapped_) {}
+
+ bool operator==(auto const& rhs) const { return &rhs == this; }
+ bool operator==(test_alloc const&) const { return true; }
+
+ T* allocate(size_t n) { return wrapped_.allocate(n); }
+
+ auto allocate_at_least(size_t n) { return wrapped_.allocate_at_least(n); }
+
+ void deallocate(T* ptr, size_t n) { return wrapped_.deallocate(ptr, n); }
+};
+
+/*
+ (19.6.4.2)
+
+ // [stacktrace.basic.cons], creation and assignment
+ static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept; [1]
+ static basic_stacktrace current(size_type skip,
+ const allocator_type& alloc = allocator_type()) noexcept; [2]
+ static basic_stacktrace current(size_type skip, size_type max_depth,
+ const allocator_type& alloc = allocator_type()) noexcept; [3]
+
+ basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>); [4]
+ explicit basic_stacktrace(const allocator_type& alloc) noexcept; [5]
+
+ basic_stacktrace(const basic_stacktrace& other); [6]
+ basic_stacktrace(basic_stacktrace&& other) noexcept; [7]
+ basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc); [8]
+ basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc); [9]
+ basic_stacktrace& operator=(const basic_stacktrace& other); [10]
+ basic_stacktrace& operator=(basic_stacktrace&& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+ allocator_traits<Allocator>::is_always_equal::value); [11]
+
+ ~basic_stacktrace(); [12]
+*/
+
+// clang-format off
+uint32_t test1_line;
+uint32_t test2_line;
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test1(A& alloc) {
+ test1_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = std::basic_stacktrace<A>::current(alloc);
+ return ret;
+}
+
+template <class A>
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE
+std::basic_stacktrace<A> test2(A& alloc) {
+ test2_line = __LINE__ + 1; // add 1 to get the next line (where the call to `current` occurs)
+ auto ret = test1(alloc);
+ return ret;
+}
+// clang-format on
+
+/*
+ [1]
+ static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+
+ Returns: A basic_stacktrace object with frames_ storing the stacktrace of the current evaluation
+ in the current thread of execution, or an empty basic_stacktrace object if the initialization of
+ frames_ failed. alloc is passed to the constructor of the frames_ object.
+ [Note 1: If the stacktrace was successfully obtained, then frames_.front() is the stacktrace_entry
+ representing approximately the current evaluation, and frames_.back() is the stacktrace_entry
+ representing approximately the initial function of the current thread of execution. — end note]
+ */
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current() {
+ test_alloc<std::stacktrace_entry> alloc;
+ uint32_t main_line = __LINE__ + 1;
+ auto st = test2(alloc);
+
+ std::cerr << "*** Stacktrace obtained at line " << main_line << '\n' << st << '\n';
+
+ assert(st.size() >= 3);
+ assert(st[0]);
+ assert(st[0].native_handle());
+ assert(st[0].description().contains("test1"));
+ assert(st[0].source_file().contains("basic.cons.pass.cpp"));
+ assert(st[1]);
+ assert(st[1].native_handle());
+ assert(st[1].description().contains("test2"));
+ assert(st[1].source_file().contains("basic.cons.pass.cpp"));
+ assert(st[2]);
+ assert(st[2].native_handle());
+ assert(st[2].description().contains("test_current"));
+ assert(st[2].source_file().contains("basic.cons.pass.cpp"));
+
+ // We unfortunately cannot guarantee the following; in CI, and possibly on users' build machines,
+ // there may not be an up-to-date version of e.g. `addr2line`.
+ // assert(st[0].source_file().ends_with("basic.cons.pass.cpp"));
+ // assert(st[0].source_line() == test1_line);
+ // assert(st[1].source_file().ends_with("basic.cons.pass.cpp"));
+ // assert(st[1].source_line() == test2_line);
+ // assert(st[2].source_file().ends_with("basic.cons.pass.cpp"));
+ // assert(st[2].source_line() == main_line);
+}
+
+/*
+ [2]
+ static basic_stacktrace current(size_type skip,
+ const allocator_type& alloc = allocator_type()) noexcept;
+ Let t be a stacktrace as-if obtained via basic_stacktrace::current(alloc). Let n be t.size().
+ Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
+ t.begin() + min(n, skip), t.end(), and alloc, or an empty basic_stacktrace object if the
+ initialization of frames_ failed.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip() {
+ // Use default allocator for simplicity; alloc is covered above
+ auto st_skip0 = std::stacktrace::current();
+ std::cerr << "*** st_skip0:\n" << st_skip0 << '\n';
+ assert(st_skip0.size() >= 2);
+ auto st_skip1 = std::stacktrace::current(1);
+ std::cerr << "*** st_skip1:\n" << st_skip1 << '\n';
+ assert(st_skip1.size() >= 1);
+ assert(st_skip0.size() == st_skip1.size() + 1);
+ assert(st_skip0[1] == st_skip1[0]);
+ auto st_skip_many = std::stacktrace::current(1 << 20);
+ assert(st_skip_many.empty());
+}
+
+/*
+ [3]
+ static basic_stacktrace current(size_type skip, size_type max_depth,
+ const allocator_type& alloc = allocator_type()) noexcept;
+ Let t be a stacktrace as-if obtained via basic_stacktrace::current(alloc). Let n be t.size().
+ Preconditions: skip <= skip + max_depth is true.
+ Returns: A basic_stacktrace object where frames_ is direct-non-list-initialized from arguments
+ t.begin() + min(n, skip), t.begin() + min(n, skip + max_depth), and alloc, or an empty
+ basic_stacktrace object if the initialization of frames_ failed.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_current_with_skip_depth() {
+ // current stack is: [this function, main, (possibly something else, e.g. `_start` from libc)]
+ // so it's probably 3 functions deep -- but certainly at least 2 deep.
+ auto st = std::stacktrace::current();
+ assert(st.size() >= 2);
+ auto it = st.begin();
+ auto entry1 = *(it++); // represents this function
+ auto entry2 = *(it++); // represents our caller, `main`
+
+ // get current trace again, but skip the 1st
+ st = std::stacktrace::current(1, 1);
+ assert(st.size() >= 1);
+ assert(*st.begin() == entry2);
+}
+
+/*
+ [4]
+ basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+ Postconditions: empty() is true.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_default_construct() {
+ std::stacktrace st;
+ assert(st.empty());
+}
+
+/*
+ [5]
+ explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+ Effects: alloc is passed to the frames_ constructor.
+ Postconditions: empty() is true.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_construct_with_allocator() {
+ test_alloc<std::stacktrace_entry> alloc;
+ std::basic_stacktrace<decltype(alloc)> st(alloc);
+ assert(st.empty());
+
+ st = std::basic_stacktrace<decltype(alloc)>::current(alloc);
+ assert(!st.empty());
+}
+
+/*
+ [6] basic_stacktrace(const basic_stacktrace& other);
+ [7] basic_stacktrace(basic_stacktrace&& other) noexcept;
+ [8] basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+ [9] basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+ [10] basic_stacktrace& operator=(const basic_stacktrace& other);
+ [11] basic_stacktrace& operator=(basic_stacktrace&& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+ allocator_traits<Allocator>::is_always_equal::value);
+
+ Remarks: Implementations may strengthen the exception specification for these functions
+ ([res.on.exception.handling]) by ensuring that empty() is true on failed allocation.
+*/
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void test_copy_move_ctors() {
+ using A = std::allocator<std::stacktrace_entry>;
+ A alloc;
+ auto st = std::basic_stacktrace<A>::current(alloc);
+
+ auto copy_constr = std::basic_stacktrace<A>(st);
+ assert(st == copy_constr);
+
+ std::basic_stacktrace<A> copy_assign;
+ copy_assign = std::basic_stacktrace<A>(st);
+ assert(st == copy_assign);
+
+ auto st2 = test2(alloc);
+ assert(st2.size());
+ std::basic_stacktrace<A> move_constr(std::move(st2));
+ assert(move_constr.size());
+ assert(!st2.size());
+
+ auto st3 = test2(alloc);
+ assert(st3.size());
+ std::basic_stacktrace<A> move_assign;
+ move_assign = std::move(st3);
+ assert(move_assign.size());
+ assert(!st3.size());
+
+ // TODO(stacktrace23): should we add test cases with `select_on_container_copy_construction`?
+}
+
+/** Ensure all allocations take place through a given allocator, and that none
+ * are sneaking around it by accidentally using malloc or operator new. */
+void test_no_malloc_or_new_ex_allocator() {
+ // A generic allocator we'll use for everything
+ using A = test_alloc<std::stacktrace_entry>;
+ A alloc;
+ // After this point all stacktrace operations must properly use `alloc`
+ malloc_disabled = true;
+ // Try all the stack trace operations
+ auto st = std::basic_stacktrace<A>(alloc);
+}
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ test_current();
+ test_current_with_skip();
+ test_current_with_skip_depth();
+ test_default_construct();
+ test_construct_with_allocator();
+ test_copy_move_ctors();
+ test_no_malloc_or_new_ex_allocator();
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
new file mode 100644
index 0000000000000..db33a8fabb41a
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+ (19.6.6) Hash Support
+
+ template<> struct hash<stacktrace_entry>; [1]
+ template<class Allocator> struct hash<basic_stacktrace<Allocator>>; [2]
+
+ The specializations are enabled ([unord.hash]).
+*/
+
+int main(int, char**) {
+ /*
+ [1]
+ template<> struct hash<stacktrace_entry>;
+ */
+ std::stacktrace_entry empty_entry;
+ assert(std::hash<std::stacktrace_entry>()(empty_entry) == 0);
+
+ /*
+ [2]
+ template<class Allocator> struct hash<basic_stacktrace<Allocator>>;
+ */
+ std::stacktrace trace; // initially empty
+ auto hash_val_empty = std::hash<std::stacktrace>()(trace);
+ trace = /* reassign */ std::stacktrace::current();
+ auto hash_val_nonempty = std::hash<std::stacktrace>()(trace);
+
+ assert(hash_val_empty != hash_val_nonempty);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
new file mode 100644
index 0000000000000..5929cd8f8d0f9
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+ (19.6.4.5) Modifiers [stacktrace.basic.mod]
+
+ void swap(basic_stacktrace& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+ allocator_traits<Allocator>::is_always_equal::value);
+
+ Effects: Exchanges the contents of *this and other.
+*/
+int main(int, char**) {
+ std::stacktrace empty;
+ auto current = std::stacktrace::current();
+
+ std::stacktrace a(empty);
+ std::stacktrace b(current);
+ assert(a == empty);
+ assert(b == current);
+
+ a.swap(b);
+ assert(a == current);
+ assert(b == empty);
+
+ // TODO(stacktrace23): should we also test swap w/ `select_on_container_swap` case
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
new file mode 100644
index 0000000000000..665fb475f56e9
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <sstream>
+
+/*
+ (19.6.4.6) Non-member functions
+
+ template<class Allocator>
+ void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+ noexcept(noexcept(a.swap(b)));
+
+ string to_string(const stacktrace_entry& f);
+
+ template<class Allocator>
+ string to_string(const basic_stacktrace<Allocator>& st);
+
+ ostream& operator<<(ostream& os, const stacktrace_entry& f);
+ template<class Allocator>
+ ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+*/
+int main(int, char**) {
+ /*
+ template<class Allocator>
+ void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+ noexcept(noexcept(a.swap(b)));
+ Effects: Equivalent to a.swap(b).
+ */
+ std::stacktrace empty;
+ auto current = std::stacktrace::current();
+
+ std::stacktrace a(empty);
+ std::stacktrace b(current);
+ assert(a == empty);
+ assert(b == current);
+
+ std::swap(a, b);
+ assert(a == current);
+ assert(b == empty);
+
+ /*
+ string to_string(const stacktrace_entry& f);
+ Returns: A string with a description of f.
+ Recommended practice: The description should provide information about the contained evaluation,
+ including information from f.source_file() and f.source_line().
+ */
+
+ assert(std::to_string(a[0]).contains("main"));
+ assert(std::to_string(a[0]).contains("basic.nonmem.pass"));
+
+ /*
+ template<class Allocator>
+ string to_string(const basic_stacktrace<Allocator>& st);
+ Returns: A string with a description of st.
+ [Note 1: The number of lines is not guaranteed to be equal to st.size(). — end note]
+ */
+
+ assert(std::to_string(a).contains("main"));
+ assert(std::to_string(a).contains("basic.nonmem.pass"));
+
+ /*
+ ostream& operator<<(ostream& os, const stacktrace_entry& f);
+ Effects: Equivalent to: return os << to_string(f);
+ */
+
+ std::stringstream entry_os;
+ entry_os << a[0];
+ assert(entry_os.str() == std::to_string(a[0]));
+
+ /*
+ template<class Allocator>
+ ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+ Effects: Equivalent to: return os << to_string(st);
+ */
+
+ std::stringstream trace_os;
+ trace_os << a;
+ assert(trace_os.str() == std::to_string(a));
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
new file mode 100644
index 0000000000000..b5c6cf77dde71
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+#include <iostream>
+#include <iterator>
+#include <memory>
+
+#include <cassert>
+#include <stdexcept>
+
+/*
+ (19.6.4.3) Observers [stacktrace.basic.obs]
+
+ // [stacktrace.basic.obs], observers
+ allocator_type get_allocator() const noexcept; [1]
+
+ const_iterator begin() const noexcept; [2]
+ const_iterator end() const noexcept; [3]
+ const_reverse_iterator rbegin() const noexcept; [4]
+ const_reverse_iterator rend() const noexcept; [5]
+
+ const_iterator cbegin() const noexcept; [6]
+ const_iterator cend() const noexcept; [7]
+ const_reverse_iterator crbegin() const noexcept; [8]
+ const_reverse_iterator crend() const noexcept; [9]
+
+ bool empty() const noexcept; [10]
+ size_type size() const noexcept; [11]
+ size_type max_size() const noexcept; [12]
+
+ const_reference operator[](size_type) const; [13]
+ const_reference at(size_type) const; [14]
+*/
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test1() { return std::stacktrace::current(0, 4); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test2() { return test1(); }
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE std::stacktrace test3() { return test2(); }
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ std::stacktrace st;
+
+ /*
+ using const_iterator = implementation-defined;
+ The type models random_access_iterator ([iterator.concept.random.access]) and meets the
+ Cpp17RandomAccessIterator requirements ([random.access.iterators]).
+ */
+ static_assert(std::random_access_iterator<decltype(st.begin())>);
+ static_assert(std::random_access_iterator<decltype(st.rbegin())>);
+ static_assert(std::random_access_iterator<decltype(st.cbegin())>);
+ static_assert(std::random_access_iterator<decltype(st.crbegin())>);
+
+ /*
+ allocator_type get_allocator() const noexcept;
+ Returns: frames_.get_allocator().
+ */
+ static_assert(std::same_as<decltype(st.get_allocator()), std::stacktrace::allocator_type>);
+
+ /*
+ const_iterator begin() const noexcept;
+ const_iterator cbegin() const noexcept;
+ Returns: An iterator referring to the first element in frames_. If empty() is true,
+ then it returns the same value as end().
+
+ const_iterator end() const noexcept;
+ const_iterator cend() const noexcept;
+ Returns: The end iterator.
+
+ const_reverse_iterator rbegin() const noexcept;
+ const_reverse_iterator crbegin() const noexcept;
+ Returns: reverse_iterator(cend()).
+
+ const_reverse_iterator rend() const noexcept;
+ const_reverse_iterator crend() const noexcept;
+ Returns: reverse_iterator(cbegin()).
+ */
+
+ // st is initially empty:
+ assert(st.begin() == st.end());
+ assert(st.cbegin() == st.cend());
+ assert(st.rbegin() == st.rend());
+ assert(st.crbegin() == st.crend());
+
+ /*
+ bool empty() const noexcept;
+ Returns: frames_.empty().
+ */
+ assert(st.empty());
+ assert(st.size() == 0);
+
+ // no longer empty:
+ st = test3();
+ assert(!st.empty());
+ assert(st.begin() != st.end());
+ assert(st.cbegin() != st.cend());
+
+ /*
+ size_type size() const noexcept;
+ Returns: frames_.size().
+
+ size_type max_size() const noexcept;
+ Returns: frames_.max_size().
+ */
+ std::cout << st << std::endl;
+ assert(st.size() >= 4);
+ assert(st.max_size() == (std::vector<std::stacktrace_entry, std::allocator<std::stacktrace_entry>>().max_size()));
+
+ /*
+ const_reference operator[](size_type frame_no) const;
+ Preconditions: frame_no < size() is true.
+ Returns: frames_[frame_no].
+ Throws: Nothing.
+ */
+ assert(st[0]);
+ assert(st[1]);
+ assert(st[2]);
+ assert(st[3]);
+
+ /*
+ const_reference at(size_type frame_no) const;
+ Returns: frames_[frame_no].
+ Throws: out_of_range if frame_no >= size().
+ */
+ assert(st.at(0));
+ assert(st.at(1));
+ assert(st.at(2));
+ assert(st.at(3));
+ try {
+ (void)st.at(42);
+ assert(false && "'at' should have thrown range_error");
+ } catch (std::out_of_range const&) {
+ // ok
+ }
+
+ auto f0 = st[0];
+ auto f1 = st[1];
+ auto f2 = st[2];
+ auto f3 = st[3];
+
+ auto fit = st.begin();
+ assert(*fit++ == f0);
+ assert(fit != st.end());
+
+ auto rit = st.rbegin();
+ assert(rit != st.rend());
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
new file mode 100644
index 0000000000000..ce502626a45ea
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+#include <iterator>
+
+#include <cassert>
+#include <type_traits>
+
+/*
+ (19.6.4) Class template basic_stacktrace [stacktrace.basic]
+ (19.6.4.1) Overview [stacktrace.basic.overview]
+
+namespace std {
+ template<class Allocator>
+ class basic_stacktrace {
+ public:
+ using value_type = stacktrace_entry;
+ using const_reference = const value_type&;
+ using reference = value_type&;
+ using const_iterator = implementation-defined; // see [stacktrace.basic.obs]
+ using iterator = const_iterator;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using difference_type = implementation-defined;
+ using size_type = implementation-defined;
+ using allocator_type = Allocator;
+
+ // (19.6.4.2)
+ // [stacktrace.basic.cons], creation and assignment
+ static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept;
+ static basic_stacktrace current(size_type skip,
+ const allocator_type& alloc = allocator_type()) noexcept;
+ static basic_stacktrace current(size_type skip, size_type max_depth,
+ const allocator_type& alloc = allocator_type()) noexcept;
+
+ basic_stacktrace() noexcept(is_nothrow_default_constructible_v<allocator_type>);
+ explicit basic_stacktrace(const allocator_type& alloc) noexcept;
+
+ basic_stacktrace(const basic_stacktrace& other);
+ basic_stacktrace(basic_stacktrace&& other) noexcept;
+ basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc);
+ basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc);
+ basic_stacktrace& operator=(const basic_stacktrace& other);
+ basic_stacktrace& operator=(basic_stacktrace&& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
+ allocator_traits<Allocator>::is_always_equal::value);
+
+ ~basic_stacktrace();
+
+ // (19.6.4.3)
+ // [stacktrace.basic.obs], observers
+ allocator_type get_allocator() const noexcept;
+
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+ const_reverse_iterator rbegin() const noexcept;
+ const_reverse_iterator rend() const noexcept;
+
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+ const_reverse_iterator crbegin() const noexcept;
+ const_reverse_iterator crend() const noexcept;
+
+ bool empty() const noexcept;
+ size_type size() const noexcept;
+ size_type max_size() const noexcept;
+
+ const_reference operator[](size_type) const;
+ const_reference at(size_type) const;
+
+ // (19.6.4.4)
+ // [stacktrace.basic.cmp], comparisons
+ template<class Allocator2>
+ friend bool operator==(const basic_stacktrace& x,
+ const basic_stacktrace<Allocator2>& y) noexcept;
+ template<class Allocator2>
+ friend strong_ordering operator<=>(const basic_stacktrace& x,
+ const basic_stacktrace<Allocator2>& y) noexcept;
+
+ // (19.6.4.5)
+ // [stacktrace.basic.mod], modifiers
+ void swap(basic_stacktrace& other)
+ noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
+ allocator_traits<Allocator>::is_always_equal::value);
+
+ private:
+ vector<value_type, allocator_type> frames_; // exposition only
+ };
+
+ // (19.6.4.6)
+ // [stacktrace.basic.nonmem], non-member functions
+
+ template<class Allocator>
+ void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+ noexcept(noexcept(a.swap(b)));
+
+ string to_string(const stacktrace_entry& f);
+
+ template<class Allocator>
+ string to_string(const basic_stacktrace<Allocator>& st);
+
+ ostream& operator<<(ostream& os, const stacktrace_entry& f);
+ template<class Allocator>
+ ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+}
+
+*/
+
+int main(int, char**) {
+ // using value_type = stacktrace_entry;
+ // using const_reference = const value_type&;
+ // using reference = value_type&;
+ // using const_iterator = implementation-defined; // see [stacktrace.basic.obs]
+ // using iterator = const_iterator;
+ // using reverse_iterator = std::reverse_iterator<iterator>;
+ // using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ // using difference_type = implementation-defined;
+ // using size_type = implementation-defined;
+ // using allocator_type = Allocator;
+
+ // This test will only verify these member types exist and are
+ // defined as expected; their actual behavior is tested in another .cpp.
+
+ using A = std::allocator<std::stacktrace_entry>;
+ using S = std::basic_stacktrace<A>;
+
+ static_assert(std::is_same_v<S::value_type, std::stacktrace_entry>);
+ static_assert(std::is_same_v<S::const_reference, std::stacktrace_entry const&>);
+ static_assert(std::is_same_v<S::reference, std::stacktrace_entry&>);
+
+ static_assert(std::forward_iterator<S::const_iterator>);
+ static_assert(std::forward_iterator<S::iterator>);
+ using CRI = S::const_reverse_iterator;
+ static_assert(std::is_same_v<CRI, decltype(S(A()).crbegin())>);
+ using RI = S::reverse_iterator;
+ static_assert(std::is_same_v<RI, decltype(S(A()).rbegin())>);
+
+ using IterT = S::iterator;
+ using DiffT = S::difference_type;
+ static_assert(std::is_same_v<IterT, decltype(IterT() + DiffT())>);
+
+ static_assert(std::is_integral_v<S::size_type>);
+ static_assert(std::is_same_v<S::allocator_type, A>);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
new file mode 100644
index 0000000000000..4c6531319fc09
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+ (19.6.3.5) Comparison [stacktrace.entry.cmp]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ // [stacktrace.entry.cmp], comparison
+ friend constexpr bool operator==(const stacktrace_entry& x,
+ const stacktrace_entry& y) noexcept; // [T11]
+ friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+ const stacktrace_entry& y) noexcept; // [T12]
+
+ [. . .]
+ };
+}
+*/
+
+int main(int, char**) {
+ // Two empty entries
+
+ std::stacktrace_entry a;
+ assert(a.native_handle() == 0);
+ assert(!a);
+
+ std::stacktrace_entry b;
+ assert(b.native_handle() == 0);
+ assert(!b);
+
+ // A non-empty entry.
+ std::stacktrace_entry c;
+ ((std::__stacktrace::entry*)(&c))->__addr_ = (uintptr_t)&main;
+ assert(c);
+
+ // [T11]
+ // friend constexpr bool operator==(const stacktrace_entry& x,
+ // const stacktrace_entry& y) noexcept;
+ // "Returns: true if and only if x and y represent the same
+ // stacktrace entry or both x and y are empty."
+ // (We represent "empty" with a native_handle of zero.)
+ assert(a == b);
+ assert(a != c);
+
+ // [T12]
+ // friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+ // const stacktrace_entry& y) noexcept;
+ assert(std::strong_ordering::equal == (a <=> b));
+ assert(std::strong_ordering::equivalent == (a <=> b));
+ assert(std::strong_ordering::greater == (c <=> a));
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
new file mode 100644
index 0000000000000..944b4c519f0fd
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -g
+
+#include <cassert>
+#include <experimental/stacktrace>
+#include <type_traits>
+
+/*
+ (19.6.3.2) Constructors [stacktrace.entry.cons]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ // [stacktrace.entry.cons], constructors
+ constexpr stacktrace_entry() noexcept; // [T2]
+ constexpr stacktrace_entry(const stacktrace_entry& other) noexcept; // [T3]
+ constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept; // [T4]
+ ~stacktrace_entry(); // [T5]
+
+ [. . .]
+ };
+}
+*/
+
+_LIBCPP_NO_TAIL_CALLS
+int main(int, char**) {
+ // [T2]
+ // constexpr stacktrace_entry() noexcept;
+ // "Postconditions: *this is empty."
+ static_assert(std::is_default_constructible_v<std::stacktrace_entry>);
+ static_assert(std::is_nothrow_default_constructible_v<std::stacktrace_entry>);
+ std::stacktrace_entry entry_t2;
+ assert(!entry_t2);
+
+ // [T3]
+ // constexpr stacktrace_entry(const stacktrace_entry& other) noexcept;
+ static_assert(std::is_nothrow_copy_constructible_v<std::stacktrace_entry>);
+ std::stacktrace_entry entry_t3(entry_t2);
+
+ // [T4]
+ // constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept;
+ static_assert(std::is_nothrow_copy_assignable_v<std::stacktrace_entry>);
+ std::stacktrace_entry entry_t4;
+ entry_t4 = entry_t2;
+
+ // [T5]
+ // ~stacktrace_entry();
+ std::stacktrace_entry* entry_ptr{nullptr};
+ delete entry_ptr;
+ {
+ auto entry_t5(entry_t4); /* construct and immediately let it go out of scope */
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
new file mode 100644
index 0000000000000..da6e3ccec92b6
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+
+/*
+ (19.6.3.3) Observers [stacktrace.entry.obs]
+
+namespace std {
+ class stacktrace_entry {
+ // [stacktrace.entry.obs], observers
+ constexpr native_handle_type native_handle() const noexcept; // [T6]
+ constexpr explicit operator bool() const noexcept; // [T7]
+
+ [. . .]
+ };
+}
+*/
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {}
+_LIBCPP_END_NAMESPACE_STD
+
+int main(int, char**) {
+ // [T6]
+ std::stacktrace_entry entry_t6;
+
+ // constexpr native_handle_type native_handle() const noexcept;
+ assert(entry_t6.native_handle() == 0);
+
+ // [T7]
+ // constexpr explicit operator bool() const noexcept;
+ // "Returns: false if and only if *this is empty."
+ assert(!entry_t6);
+
+ // Now set addr to something nonzero
+ ((std::__stacktrace::entry*)(&entry_t6))->__addr_ = (uintptr_t)&main;
+ assert(entry_t6.native_handle() == (uintptr_t)&main);
+ assert(entry_t6);
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
new file mode 100644
index 0000000000000..b55bdcde966d3
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <concepts>
+#include <cstddef>
+
+/*
+ (19.6.3) Class stacktrace_entry [stacktrace.entry]
+ (19.6.3.1) Overview [stacktrace.entry.overview]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ using native_handle_type = implementation-defined; // [T1] [entry.pass.cpp]
+
+ // [stacktrace.entry.cons], constructors
+ constexpr stacktrace_entry() noexcept; // [T2] [entry.cons.pass.cpp]
+ constexpr stacktrace_entry(const stacktrace_entry& other) noexcept; // [T3]
+ constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept; // [T4]
+ ~stacktrace_entry(); // [T5]
+
+ // [stacktrace.entry.obs], observers
+ constexpr native_handle_type native_handle() const noexcept; // [T6] [entry.obs.pass.cpp]
+ constexpr explicit operator bool() const noexcept; // [T7]
+
+ // [stacktrace.entry.query], query
+ string description() const; // [T8] [entry.query.pass.cpp]
+ string source_file() const; // [T9]
+ uint_least32_t source_line() const; // [T10]
+
+ // [stacktrace.entry.cmp], comparison
+ friend constexpr bool operator==(const stacktrace_entry& x,
+ const stacktrace_entry& y) noexcept; // [T11] [entry.cmp.pass.cpp]
+ friend constexpr strong_ordering operator<=>(const stacktrace_entry& x,
+ const stacktrace_entry& y) noexcept; // [T12]
+ };
+}
+*/
+
+int main(int, char**) {
+ // [stacktrace.entry.overview]
+ // "The class stacktrace_entry models regular ([concepts.object])
+ // and three_way_comparable<strong_ordering> ([cmp.concept])."
+ static_assert(std::regular<std::stacktrace_entry>);
+ static_assert(std::three_way_comparable<std::stacktrace_entry, std::strong_ordering>);
+
+ // [T1]
+ // using native_handle_type = implementation-defined;
+ //
+ // [Implementation note: For our purposes anything that unique identifies this stacktrace_entry
+ // would be good enough. We'll use a pointer-sized numeric type (to represent location of the
+ // calling instruction). Internally this is defined as `uintptr_t`.]
+ static_assert(sizeof(std::stacktrace_entry::native_handle_type) >= sizeof(void*));
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
new file mode 100644
index 0000000000000..bfdb54e4df668
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
+
+#include <experimental/stacktrace>
+
+#include <cassert>
+#include <iostream>
+#include <string>
+
+/*
+ (19.6.3.4) Query [stacktrace.entry.query]
+
+namespace std {
+ class stacktrace_entry {
+ public:
+ // [stacktrace.entry.query], query
+ string description() const; // [T8]
+ string source_file() const; // [T9]
+ uint_least32_t source_line() const; // [T10]
+
+ [. . .]
+ };
+}
+*/
+
+int main(int, char**) {
+ // empty trace entry
+ std::stacktrace_entry e;
+
+ // [T8]
+ // string description() const;
+ auto desc = e.description();
+ assert(desc.c_str()[0] == 0);
+ assert(desc.size() == 0);
+
+ // [T9]
+ // string source_file() const;
+ auto src = e.source_file();
+ assert(src.c_str()[0] == 0);
+ assert(src.size() == 0);
+
+ // [T10]
+ // uint_least32_t source_line() const;
+ assert(e.source_line() == 0);
+
+ // Get the current trace.
+ uint32_t line_number = __LINE__ + 1; // record where `current` is being called:
+ auto trace = std::stacktrace::current();
+
+ // First entry of this should be `main`.
+ e = trace.at(0);
+ assert(e);
+
+ std::cout << "main is at: " << (void*)&main << std::endl;
+ std::cout << "e.native_handle: " << (void*)e.native_handle() << std::endl;
+ std::cout << "e.description: " << e.description() << std::endl;
+ std::cout << "e.source_file: " << e.source_file() << std::endl;
+ std::cout << "e.source_line: " << e.source_line() << std::endl;
+
+ std::cout << trace << std::endl;
+
+ assert(e.native_handle());
+ assert(e.native_handle() >= (uintptr_t)&main);
+ assert(e.description() == "main" || e.description() == "_main");
+ // Even if we cannot get the debug info or call out to llvm-addr2line,
+ // we should at least get the executable filename, e.g. `entry.query.pass.cpp.dir/t.exe`
+ assert(!e.source_file().empty());
+ assert(e.source_file().contains("entry.query.pass.cpp"));
+
+ // These might not work at testing time, so disable.
+ // Complete tests checking the filename:line part are in `std/diagnostics/stacktrace`
+ // assert(e.source_file().ends_with("entry.query.pass.cpp"));
+ // assert(e.source_line() == line_number);
+ (void)line_number;
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
new file mode 100644
index 0000000000000..b37681659d43c
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <experimental/stacktrace>
+
+/*
+ (19.6.5) Formatting support [stacktrace.format]
+
+ template<> struct formatter<stacktrace_entry>; [1]
+ template<class Allocator> struct formatter<basic_stacktrace<Allocator>>; [2]
+*/
+
+int main(int, char**) {
+ /*
+ [1]
+ template<> struct formatter<stacktrace_entry>;
+
+ formatter<stacktrace_entry> interprets format-spec as a stacktrace-entry-format-spec.
+ The syntax of format specifications is as follows:
+
+ stacktrace-entry-format-spec :
+ fill-and-align_[opt] width_[opt]
+
+ [Note 1: The productions fill-and-align and width are described in [format.string.std]. — end note]
+
+ A stacktrace_entry object se is formatted as if by copying to_string(se) through the output iterator
+ of the context with additional padding and adjustments as specified by the format specifiers.
+ */
+
+ // TODO(stacktrace23): needs `formatter`
+
+ /*
+ [2]
+ template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+
+ For formatter<basic_stacktrace<Allocator>>, format-spec is empty.
+
+ A basic_stacktrace<Allocator> object s is formatted as if by copying to_string(s) through the
+ output iterator of the context.
+ */
+
+ // TODO(stacktrace23): needs `formatter`
+
+ return 0;
+}
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
new file mode 100644
index 0000000000000..3c92016b2c900
--- /dev/null
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <experimental/stacktrace>
+
+#include <memory>
+#include <type_traits>
+
+/*
+
+// (19.6.2) Header <stacktrace> synopsis [stacktrace.syn]
+
+namespace std {
+
+ // [stacktrace.entry], class stacktrace_entry
+ class stacktrace_entry; [1]
+
+ // [stacktrace.basic], class template basic_stacktrace
+ template<class Allocator>
+ class basic_stacktrace; [2]
+
+ // basic_stacktrace typedef-names
+ using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>; [3]
+
+ // [stacktrace.basic.nonmem], non-member functions
+ template<class Allocator>
+ void swap(basic_stacktrace<Allocator>& a,
+ basic_stacktrace<Allocator>& b)
+ noexcept(noexcept(a.swap(b))); [4]
+
+ string to_string(const stacktrace_entry& f); [5]
+
+ template<class Allocator>
+ string to_string(const basic_stacktrace<Allocator>& st); [6]
+
+ ostream& operator<<(ostream& os, const stacktrace_entry& f); [7]
+ template<class Allocator>
+ ostream& operator<<(ostream& os,
+ const basic_stacktrace<Allocator>& st); [8]
+
+ // [stacktrace.format], formatting support
+ template<> struct formatter<stacktrace_entry>; [9]
+ template<class Allocator>
+ struct formatter<basic_stacktrace<Allocator>>; [10]
+
+ namespace pmr {
+ using stacktrace =
+ basic_stacktrace<polymorphic_allocator<stacktrace_entry>>; [11]
+ }
+
+ // [stacktrace.basic.hash], hash support
+ template<> struct hash<stacktrace_entry>; [12]
+ template<class Allocator> struct hash<basic_stacktrace<Allocator>>; [13]
+}
+*/
+
+// static_assert(__cpp_lib_stacktrace == 202011L);
+
+int main(int, char**) {
+ // Very basic tests to ensure the required things are declared.
+ // Only checking for types' and functions' existence, parameter and return types,
+ // and type-completeness. Functional tests exist in the other .cpp's in this directory.
+
+ using Alloc = std::allocator<std::stacktrace_entry>;
+
+ // [1]
+ // [stacktrace.entry], class stacktrace_entry
+ // class stacktrace_entry;
+ using T1 = std::stacktrace_entry;
+ assert(std::is_constructible_v<T1>);
+
+ // [2]
+ // [stacktrace.basic], class template basic_stacktrace
+ // template<class Allocator>
+ // class basic_stacktrace;
+ using T2 = std::basic_stacktrace<Alloc>;
+ assert(std::is_constructible_v<T2>);
+
+ // [3]
+ // basic_stacktrace typedef-names
+ // using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
+ using T3 = std::stacktrace;
+ static_assert(std::is_same_v<T3, std::basic_stacktrace<std::allocator<std::stacktrace_entry>>>);
+
+ // [4]
+ // [stacktrace.basic.nonmem], non-member functions
+ // template<class Allocator>
+ // void swap(basic_stacktrace<Allocator>& a, basic_stacktrace<Allocator>& b)
+ // noexcept(noexcept(a.swap(b)));
+ std::basic_stacktrace<Alloc> a;
+ std::basic_stacktrace<Alloc> b;
+ std::swap(a, b);
+
+ // [5]
+ // string to_string(const stacktrace_entry& f);
+ using T5 = decltype(std::to_string(std::stacktrace_entry()));
+ static_assert(std::is_same_v<std::string, T5>);
+
+ // [6]
+ // template<class Allocator>
+ // string to_string(const basic_stacktrace<Allocator>& st);
+ using T6 = decltype(std::to_string(std::basic_stacktrace<Alloc>()));
+ static_assert(std::is_same_v<std::string, T6>);
+
+ // [7]
+ // ostream& operator<<(ostream& os, const stacktrace_entry& f);
+ std::ostream* os;
+ using T7 = decltype(operator<<(*os, std::stacktrace_entry()));
+ static_assert(std::is_same_v<std::ostream&, T7>);
+
+ // [8]
+ // template<class Allocator>
+ // ostream& operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+ using T8 = decltype(operator<<(*os, std::basic_stacktrace<Alloc>()));
+ static_assert(std::is_same_v<std::ostream&, T8>);
+
+ // [9]
+ // template<> struct formatter<stacktrace_entry>;
+ // using T9 = std::formatter<std::stacktrace_entry>;
+ // static_assert(std::is_constructible_v<T9>);
+
+ // TODO(stacktrace23): needs `formatter`
+
+ // [10]
+ // template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+ // using T10 = std::formatter<std::basic_stacktrace<Alloc>>;
+ // static_assert(std::is_constructible_v<T10>);
+
+ // TODO(stacktrace23): needs `formatter`
+
+ // [11]
+ // namespace pmr { using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>; }
+ using AllocPMR = std::pmr::polymorphic_allocator<std::stacktrace_entry>;
+ using BasicPMR = std::basic_stacktrace<AllocPMR>;
+ using T11 = std::pmr::stacktrace;
+ static_assert(std::is_same_v<T11, BasicPMR>);
+
+ return 0;
+}
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index a505d37b65b81..6d69cf29af308 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -170,7 +170,7 @@ def __hash__(self) -> int:
"linalg",
"rcu",
"spanstream",
- "stacktrace",
+ "stacktrace", # TODO(stacktrace23): remove this when stacktrace is taken out of experimental
"stdfloat",
"text_encoding",
]))
@@ -208,6 +208,7 @@ def __hash__(self) -> int:
"experimental/iterator": "// UNSUPPORTED: c++03",
"experimental/propagate_const": "// UNSUPPORTED: c++03",
"experimental/simd": "// UNSUPPORTED: c++03",
+ "experimental/stacktrace": "// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20",
"experimental/type_traits": "// UNSUPPORTED: c++03",
"experimental/utility": "// UNSUPPORTED: c++03",
"filesystem": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14",
@@ -275,6 +276,7 @@ def __hash__(self) -> int:
"regex": ["compare", "initializer_list"],
"set": ["compare", "initializer_list"],
"stack": ["compare", "initializer_list"],
+ "stacktrace": ["compare"],
"string_view": ["compare"],
"string": ["compare", "initializer_list"],
"syncstream": ["ostream"],
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index 2bc851a99afb1..9e045d2971179 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -317,6 +317,40 @@ if (libcxx_enable_experimental) {
output_dir = runtimes_dir
output_name = "c++experimental"
sources = [ "experimental/keep.cpp" ]
+ sources += [
+ "experimental/stacktrace/alloc.cpp",
+ "experimental/stacktrace/common/config.h",
+ "experimental/stacktrace/common/debug.cpp",
+ "experimental/stacktrace/common/debug.h",
+ "experimental/stacktrace/common/failed.h",
+ "experimental/stacktrace/common/fd.cpp",
+ "experimental/stacktrace/common/fd.h",
+ "experimental/stacktrace/common/images.h",
+ "experimental/stacktrace/context.cpp",
+ "experimental/stacktrace/linux/linux-dl.cpp",
+ "experimental/stacktrace/linux/linux-elf.cpp",
+ "experimental/stacktrace/linux/linux-sym.cpp",
+ "experimental/stacktrace/linux/linux.h",
+ "experimental/stacktrace/osx/osx.cpp",
+ "experimental/stacktrace/osx/osx.h",
+ "experimental/stacktrace/stacktrace.cpp",
+ "experimental/stacktrace/tools/addr2line.cpp",
+ "experimental/stacktrace/tools/atos.cpp",
+ "experimental/stacktrace/tools/llvm_symbolizer.cpp",
+ "experimental/stacktrace/tools/pspawn.h",
+ "experimental/stacktrace/tools/tools.h",
+ "experimental/stacktrace/tools/toolspawner.cpp",
+ "experimental/stacktrace/unwind/unwind.cpp",
+ "experimental/stacktrace/unwind/unwind.h",
+ "experimental/stacktrace/windows/dbghelp_dll.cpp",
+ "experimental/stacktrace/windows/dbghelp_dll.h",
+ "experimental/stacktrace/windows/dll.cpp",
+ "experimental/stacktrace/windows/dll.h",
+ "experimental/stacktrace/windows/psapi_dll.cpp",
+ "experimental/stacktrace/windows/psapi_dll.h",
+ "experimental/stacktrace/windows/win_impl.cpp",
+ "experimental/stacktrace/windows/win_impl.h",
+ ]
if (libcxx_enable_filesystem && libcxx_enable_time_zone_database) {
sources += [
# TODO TZDB The exception could be moved in chrono once the TZDB library
>From b5f77a0a61757315dbd85c2e9d3968f2080e9e9f Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Sun, 20 Apr 2025 23:48:58 -0400
Subject: [PATCH 2/6] Remove unrelated change
---
libcxx/include/__ostream/basic_ostream.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h
index 891dddbf79c42..f7473a36d8ccc 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -570,7 +570,7 @@ _LIBCPP_HIDE_FROM_ABI _Stream&& operator<<(_Stream&& __os, const _Tp& __x) {
}
template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
+basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const basic_string<_CharT, _Traits, _Allocator>& __str) {
return std::__put_character_sequence(__os, __str.data(), __str.size());
}
>From 6d4431ad2d73c3b3a349e4db932006f41e4a1fa9 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 09:10:49 -0400
Subject: [PATCH 3/6] fix cflag option
---
libcxx/CMakeLists.txt | 8 ++++----
libcxx/docs/VendorDocumentation.rst | 6 +++---
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index b12bf2ead76e5..f67e7d997da6b 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -131,9 +131,9 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
the shared library they shipped should turn this on and see `include/__configuration/availability.h`
for more details." OFF)
-option(LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
- "For C++23 <stacktrace>: whether to allow invocation of `addr2line`, `llvm-addr2line` or `atos`
- at runtime (if it's available in PATH) to resolve call-chain addresses in the stacktrace
+option(LIBCXX_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME
+ "For C++23 <stacktrace>: whether to allow invocation of `addr2line`, `llvm-addr2line`, or `atos`
+ at runtime (if it's available in `PATH`) to resolve call-chain addresses in the stacktrace
into source locations, if other methods are not available." ON)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@@ -762,7 +762,7 @@ config_define(${LIBCXX_ENABLE_UNICODE} _LIBCPP_HAS_UNICODE)
config_define(${LIBCXX_ENABLE_WIDE_CHARACTERS} _LIBCPP_HAS_WIDE_CHARACTERS)
config_define(${LIBCXX_ENABLE_TIME_ZONE_DATABASE} _LIBCPP_HAS_TIME_ZONE_DATABASE)
config_define(${LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS} _LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS)
-config_define(${LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME} _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME)
+config_define(${LIBCXX_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME} _LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME)
# TODO: Remove in LLVM 21. We're leaving an error to make this fail explicitly.
if (LIBCXX_ENABLE_ASSERTIONS)
diff --git a/libcxx/docs/VendorDocumentation.rst b/libcxx/docs/VendorDocumentation.rst
index b05d494db4f9b..11d67458b4df9 100644
--- a/libcxx/docs/VendorDocumentation.rst
+++ b/libcxx/docs/VendorDocumentation.rst
@@ -185,12 +185,12 @@ General purpose options
ship the IANA time zone database. When time zones are not supported,
time zone support in <chrono> will be disabled.
-.. option:: LIBCPP_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME:BOOL
+.. option:: LIBCXX_STACKTRACE_ALLOW_TOOLS_AT_RUNTIME:BOOL
**Default**: ``OFF``
- For C++23 <stacktrace>: whether to allow invocation of ``addr2line`` or ``llvm-addr2line``
- at runtime (if it's available in PATH) to resolve call-chain addresses in the stacktrace
+ For C++23 <stacktrace>: whether to allow invocation of ``addr2line``, ``llvm-addr2line``, or ``atos``
+ at runtime (if it's available in ``PATH``) to resolve call-chain addresses in the stacktrace
into source locations, if other methods are not available.
.. option:: LIBCXX_INSTALL_LIBRARY_DIR:PATH
>From 316e17cad2cd6c00c6564d469bbf0c01797599a5 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 21 Apr 2025 19:26:15 -0400
Subject: [PATCH 4/6] Move stacktrace headers out of experimental
---
libcxx/include/CMakeLists.txt | 14 ++--
.../__stacktrace/basic_stacktrace.h | 6 +-
.../__stacktrace/detail/alloc.h | 0
.../__stacktrace/detail/context.h | 0
.../__stacktrace/detail/entry.h | 0
.../__stacktrace/detail/to_string.h | 0
.../__stacktrace/stacktrace_entry.h | 2 +-
libcxx/include/module.modulemap.in | 21 +++---
libcxx/include/{experimental => }/stacktrace | 4 +-
libcxx/modules/std.compat.cppm.in | 5 +-
libcxx/modules/std.cppm.in | 3 -
llvm/utils/gn/secondary/libcxx/src/BUILD.gn | 66 +++++++++----------
12 files changed, 57 insertions(+), 64 deletions(-)
rename libcxx/include/{experimental => }/__stacktrace/basic_stacktrace.h (98%)
rename libcxx/include/{experimental => }/__stacktrace/detail/alloc.h (100%)
rename libcxx/include/{experimental => }/__stacktrace/detail/context.h (100%)
rename libcxx/include/{experimental => }/__stacktrace/detail/entry.h (100%)
rename libcxx/include/{experimental => }/__stacktrace/detail/to_string.h (100%)
rename libcxx/include/{experimental => }/__stacktrace/stacktrace_entry.h (98%)
rename libcxx/include/{experimental => }/stacktrace (98%)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index ef091e40b9ec8..65b69674433f4 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -727,6 +727,12 @@ set(files
__ranges/views.h
__ranges/zip_view.h
__split_buffer
+ __stacktrace/basic_stacktrace.h
+ __stacktrace/detail/alloc.h
+ __stacktrace/detail/context.h
+ __stacktrace/detail/entry.h
+ __stacktrace/detail/to_string.h
+ __stacktrace/stacktrace_entry.h
__std_mbstate_t.h
__stop_token/atomic_unique_lock.h
__stop_token/intrusive_list_view.h
@@ -991,17 +997,10 @@ set(files
experimental/__simd/traits.h
experimental/__simd/utility.h
experimental/__simd/vec_ext.h
- experimental/__stacktrace/basic_stacktrace.h
- experimental/__stacktrace/detail/alloc.h
- experimental/__stacktrace/detail/context.h
- experimental/__stacktrace/detail/entry.h
- experimental/__stacktrace/detail/to_string.h
- experimental/__stacktrace/stacktrace_entry.h
experimental/iterator
experimental/memory
experimental/propagate_const
experimental/simd
- experimental/stacktrace
experimental/type_traits
experimental/utility
ext/__hash
@@ -1054,6 +1053,7 @@ set(files
span
sstream
stack
+ stacktrace
stdatomic.h
stdbool.h
stddef.h
diff --git a/libcxx/include/experimental/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
similarity index 98%
rename from libcxx/include/experimental/__stacktrace/basic_stacktrace.h
rename to libcxx/include/__stacktrace/basic_stacktrace.h
index 8ac1de82b1c63..cad3571d559bc 100644
--- a/libcxx/include/experimental/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -37,9 +37,9 @@
#include <memory>
#include <string>
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/to_string.h>
+#include <__stacktrace/detail/alloc.h>
+#include <__stacktrace/detail/context.h>
+#include <__stacktrace/detail/to_string.h>
_LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/include/experimental/__stacktrace/detail/alloc.h b/libcxx/include/__stacktrace/detail/alloc.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/alloc.h
rename to libcxx/include/__stacktrace/detail/alloc.h
diff --git a/libcxx/include/experimental/__stacktrace/detail/context.h b/libcxx/include/__stacktrace/detail/context.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/context.h
rename to libcxx/include/__stacktrace/detail/context.h
diff --git a/libcxx/include/experimental/__stacktrace/detail/entry.h b/libcxx/include/__stacktrace/detail/entry.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/entry.h
rename to libcxx/include/__stacktrace/detail/entry.h
diff --git a/libcxx/include/experimental/__stacktrace/detail/to_string.h b/libcxx/include/__stacktrace/detail/to_string.h
similarity index 100%
rename from libcxx/include/experimental/__stacktrace/detail/to_string.h
rename to libcxx/include/__stacktrace/detail/to_string.h
diff --git a/libcxx/include/experimental/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
similarity index 98%
rename from libcxx/include/experimental/__stacktrace/stacktrace_entry.h
rename to libcxx/include/__stacktrace/stacktrace_entry.h
index be8abb3ea91b2..f06fb0b6d4cb1 100644
--- a/libcxx/include/experimental/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -33,7 +33,7 @@
#include <cstdint>
#include <string>
-#include "experimental/__stacktrace/detail/entry.h"
+#include <__stacktrace/detail/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8867681832bcf..4491768cfe935 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1985,6 +1985,17 @@ module std [system] {
export *
}
+ module stacktrace {
+ module basic_stacktrace { header "__stacktrace/basic_stacktrace.h" }
+ module alloc { header "__stacktrace/detail/alloc.h" }
+ module context { header "__stacktrace/detail/context.h" }
+ module entry { header "__stacktrace/detail/entry.h" }
+ module to_string { header "__stacktrace/detail/to_string.h" }
+ module stacktrace_entry { header "__stacktrace/stacktrace_entry.h" }
+ header "stacktrace"
+ export *
+ }
+
module stdexcept {
header "stdexcept"
export *
@@ -2225,16 +2236,6 @@ module std [system] {
header "experimental/simd"
export *
}
- module stacktrace {
- private header "experimental/__stacktrace/basic_stacktrace.h"
- private header "experimental/__stacktrace/detail/alloc.h"
- private header "experimental/__stacktrace/detail/context.h"
- private header "experimental/__stacktrace/detail/entry.h"
- private header "experimental/__stacktrace/detail/to_string.h"
- private header "experimental/__stacktrace/stacktrace_entry.h"
- header "experimental/stacktrace"
- export *
- }
}
// Implementation detail headers that are private to libc++. These modules
diff --git a/libcxx/include/experimental/stacktrace b/libcxx/include/stacktrace
similarity index 98%
rename from libcxx/include/experimental/stacktrace
rename to libcxx/include/stacktrace
index 9e1f7259ca3da..33415533c2eea 100644
--- a/libcxx/include/experimental/stacktrace
+++ b/libcxx/include/stacktrace
@@ -180,8 +180,8 @@ namespace std {
_LIBCPP_PUSH_MACROS
# include <__undef_macros>
-# include "experimental/__stacktrace/basic_stacktrace.h"
-# include "experimental/__stacktrace/stacktrace_entry.h"
+# include <__stacktrace/basic_stacktrace.h>
+# include <__stacktrace/stacktrace_entry.h>
_LIBCPP_POP_MACROS
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index 95931447ccdc6..2ee56c6b810c9 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -71,9 +71,6 @@ module;
# if __has_include(<spanstream>)
# error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<spanstream>)
-# if __has_include(<stacktrace>)
-# error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
-# endif // __has_include(<stacktrace>)
# if __has_include(<stdfloat>)
# error "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<stdfloat>)
@@ -86,4 +83,4 @@ export module std.compat;
export import std;
- at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
\ No newline at end of file
+ at LIBCXX_MODULE_STD_COMPAT_INCLUDE_SOURCES@
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index 5c523691bff4e..dc40af57e573a 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -181,9 +181,6 @@ module;
# if __has_include(<spanstream>)
# error "please update the header information for <spanstream> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<spanstream>)
-# if __has_include(<stacktrace>)
-# error "please update the header information for <stacktrace> in headers_not_available in utils/libcxx/header_information.py"
-# endif // __has_include(<stacktrace>)
# if __has_include(<stdfloat>)
# error "please update the header information for <stdfloat> in headers_not_available in utils/libcxx/header_information.py"
# endif // __has_include(<stdfloat>)
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index 9e045d2971179..d30d70ac40842 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -167,6 +167,38 @@ cxx_sources = [
"ryu/d2s.cpp",
"ryu/f2s.cpp",
"shared_mutex.cpp",
+ # "stacktrace/alloc.cpp",
+ # "stacktrace/common/config.h",
+ # "stacktrace/common/debug.cpp",
+ # "stacktrace/common/debug.h",
+ # "stacktrace/common/failed.h",
+ # "stacktrace/common/fd.cpp",
+ # "stacktrace/common/fd.h",
+ # "stacktrace/common/images.h",
+ # "stacktrace/context.cpp",
+ # "stacktrace/linux/linux-dl.cpp",
+ # "stacktrace/linux/linux-elf.cpp",
+ # "stacktrace/linux/linux-sym.cpp",
+ # "stacktrace/linux/linux.h",
+ # "stacktrace/osx/osx.cpp",
+ # "stacktrace/osx/osx.h",
+ # "stacktrace/stacktrace.cpp",
+ # "stacktrace/tools/addr2line.cpp",
+ # "stacktrace/tools/atos.cpp",
+ # "stacktrace/tools/llvm_symbolizer.cpp",
+ # "stacktrace/tools/pspawn.h",
+ # "stacktrace/tools/tools.h",
+ # "stacktrace/tools/toolspawner.cpp",
+ # "stacktrace/unwind/unwind.cpp",
+ # "stacktrace/unwind/unwind.h",
+ # "stacktrace/windows/dbghelp_dll.cpp",
+ # "stacktrace/windows/dbghelp_dll.h",
+ # "stacktrace/windows/dll.cpp",
+ # "stacktrace/windows/dll.h",
+ # "stacktrace/windows/psapi_dll.cpp",
+ # "stacktrace/windows/psapi_dll.h",
+ # "stacktrace/windows/win_impl.cpp",
+ # "stacktrace/windows/win_impl.h",
"stdexcept.cpp",
"string.cpp",
"strstream.cpp",
@@ -317,40 +349,6 @@ if (libcxx_enable_experimental) {
output_dir = runtimes_dir
output_name = "c++experimental"
sources = [ "experimental/keep.cpp" ]
- sources += [
- "experimental/stacktrace/alloc.cpp",
- "experimental/stacktrace/common/config.h",
- "experimental/stacktrace/common/debug.cpp",
- "experimental/stacktrace/common/debug.h",
- "experimental/stacktrace/common/failed.h",
- "experimental/stacktrace/common/fd.cpp",
- "experimental/stacktrace/common/fd.h",
- "experimental/stacktrace/common/images.h",
- "experimental/stacktrace/context.cpp",
- "experimental/stacktrace/linux/linux-dl.cpp",
- "experimental/stacktrace/linux/linux-elf.cpp",
- "experimental/stacktrace/linux/linux-sym.cpp",
- "experimental/stacktrace/linux/linux.h",
- "experimental/stacktrace/osx/osx.cpp",
- "experimental/stacktrace/osx/osx.h",
- "experimental/stacktrace/stacktrace.cpp",
- "experimental/stacktrace/tools/addr2line.cpp",
- "experimental/stacktrace/tools/atos.cpp",
- "experimental/stacktrace/tools/llvm_symbolizer.cpp",
- "experimental/stacktrace/tools/pspawn.h",
- "experimental/stacktrace/tools/tools.h",
- "experimental/stacktrace/tools/toolspawner.cpp",
- "experimental/stacktrace/unwind/unwind.cpp",
- "experimental/stacktrace/unwind/unwind.h",
- "experimental/stacktrace/windows/dbghelp_dll.cpp",
- "experimental/stacktrace/windows/dbghelp_dll.h",
- "experimental/stacktrace/windows/dll.cpp",
- "experimental/stacktrace/windows/dll.h",
- "experimental/stacktrace/windows/psapi_dll.cpp",
- "experimental/stacktrace/windows/psapi_dll.h",
- "experimental/stacktrace/windows/win_impl.cpp",
- "experimental/stacktrace/windows/win_impl.h",
- ]
if (libcxx_enable_filesystem && libcxx_enable_time_zone_database) {
sources += [
# TODO TZDB The exception could be moved in chrono once the TZDB library
>From 8ca8c154938221c30112e6d850c173ce620d91ef Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Mon, 21 Apr 2025 22:35:16 -0400
Subject: [PATCH 5/6] Move stacktrace srcs out of experimental
---
libcxx/docs/UserDocumentation.rst | 1 -
libcxx/include/CMakeLists.txt | 8 +--
.../include/__stacktrace/{detail => }/alloc.h | 28 ++++----
.../include/__stacktrace/basic_stacktrace.h | 16 ++---
.../__stacktrace/{detail => }/context.h | 20 +++---
.../include/__stacktrace/{detail => }/entry.h | 6 +-
.../include/__stacktrace/stacktrace_entry.h | 8 +--
.../__stacktrace/{detail => }/to_string.h | 6 +-
libcxx/include/module.modulemap.in | 8 +--
libcxx/include/stacktrace | 6 +-
libcxx/modules/std/stacktrace.inc | 3 +-
libcxx/src/CMakeLists.txt | 36 +++++------
libcxx/src/stacktrace/README.md | 5 ++
.../{experimental => }/stacktrace/alloc.cpp | 4 +-
.../stacktrace/common/config.h | 0
.../stacktrace/common/debug.cpp | 0
.../stacktrace/common/debug.h | 0
.../stacktrace/common/failed.h | 0
.../stacktrace/common/fd.cpp | 0
.../{experimental => }/stacktrace/common/fd.h | 0
.../stacktrace/common/images.h | 0
.../{experimental => }/stacktrace/context.cpp | 14 ++--
.../{experimental => }/stacktrace/linux/elf.h | 0
.../stacktrace/linux/linux-dl.cpp | 4 +-
.../stacktrace/linux/linux-elf.cpp | 4 +-
.../stacktrace/linux/linux-sym.cpp | 4 +-
.../stacktrace/linux/linux.h | 2 +-
.../{experimental => }/stacktrace/osx/osx.cpp | 4 +-
.../{experimental => }/stacktrace/osx/osx.h | 2 +-
.../stacktrace/stacktrace.cpp | 12 ++--
.../stacktrace/tools/addr2line.cpp | 4 +-
.../stacktrace/tools/atos.cpp | 6 +-
.../stacktrace/tools/llvm_symbolizer.cpp | 4 +-
.../stacktrace/tools/pspawn.h | 4 +-
.../stacktrace/tools/tools.h | 4 +-
.../stacktrace/tools/toolspawner.cpp | 2 +-
.../stacktrace/unwind/unwind.cpp | 4 +-
.../stacktrace/unwind/unwind.h | 2 +-
.../stacktrace/windows/dbghelp_dll.cpp | 2 +-
.../stacktrace/windows/dbghelp_dll.h | 2 +-
.../stacktrace/windows/dll.cpp | 2 +-
.../stacktrace/windows/dll.h | 2 +-
.../stacktrace/windows/psapi_dll.cpp | 2 +-
.../stacktrace/windows/psapi_dll.h | 2 +-
.../stacktrace/windows/win_impl.cpp | 2 +-
.../stacktrace/windows/win_impl.h | 2 +-
.../stacktrace/simple.o0.nodebug.pass.cpp | 2 +-
.../stacktrace/simple.o0.nosplit.pass.cpp | 2 +-
.../stacktrace/simple.o0.split.pass.cpp | 2 +-
.../stacktrace/simple.o3.nodebug.pass.cpp | 2 +-
.../stacktrace/simple.o3.nosplit.pass.cpp | 2 +-
.../stacktrace/simple.o3.split.pass.cpp | 2 +-
.../test/libcxx/transitive_includes/cxx23.csv | 58 ++++++++---------
.../test/libcxx/transitive_includes/cxx26.csv | 58 ++++++++---------
.../diagnostics/stacktrace/basic.cmp.pass.cpp | 2 +-
.../stacktrace/basic.cons.pass.cpp | 2 +-
.../stacktrace/basic.hash.pass.cpp | 2 +-
.../diagnostics/stacktrace/basic.mod.pass.cpp | 2 +-
.../stacktrace/basic.nonmem.pass.cpp | 2 +-
.../diagnostics/stacktrace/basic.obs.pass.cpp | 2 +-
.../std/diagnostics/stacktrace/basic.pass.cpp | 2 +-
.../diagnostics/stacktrace/entry.cmp.pass.cpp | 2 +-
.../stacktrace/entry.cons.pass.cpp | 2 +-
.../diagnostics/stacktrace/entry.obs.pass.cpp | 2 +-
.../std/diagnostics/stacktrace/entry.pass.cpp | 2 +-
.../stacktrace/entry.query.pass.cpp | 2 +-
.../diagnostics/stacktrace/format.pass.cpp | 2 +-
.../std/diagnostics/stacktrace/syn.pass.cpp | 2 +-
libcxx/utils/libcxx/header_information.py | 3 +-
llvm/utils/gn/secondary/libcxx/src/BUILD.gn | 64 +++++++++----------
70 files changed, 233 insertions(+), 237 deletions(-)
rename libcxx/include/__stacktrace/{detail => }/alloc.h (80%)
rename libcxx/include/__stacktrace/{detail => }/context.h (72%)
rename libcxx/include/__stacktrace/{detail => }/entry.h (88%)
rename libcxx/include/__stacktrace/{detail => }/to_string.h (89%)
create mode 100644 libcxx/src/stacktrace/README.md
rename libcxx/src/{experimental => }/stacktrace/alloc.cpp (86%)
rename libcxx/src/{experimental => }/stacktrace/common/config.h (100%)
rename libcxx/src/{experimental => }/stacktrace/common/debug.cpp (100%)
rename libcxx/src/{experimental => }/stacktrace/common/debug.h (100%)
rename libcxx/src/{experimental => }/stacktrace/common/failed.h (100%)
rename libcxx/src/{experimental => }/stacktrace/common/fd.cpp (100%)
rename libcxx/src/{experimental => }/stacktrace/common/fd.h (100%)
rename libcxx/src/{experimental => }/stacktrace/common/images.h (100%)
rename libcxx/src/{experimental => }/stacktrace/context.cpp (88%)
rename libcxx/src/{experimental => }/stacktrace/linux/elf.h (100%)
rename libcxx/src/{experimental => }/stacktrace/linux/linux-dl.cpp (92%)
rename libcxx/src/{experimental => }/stacktrace/linux/linux-elf.cpp (93%)
rename libcxx/src/{experimental => }/stacktrace/linux/linux-sym.cpp (94%)
rename libcxx/src/{experimental => }/stacktrace/linux/linux.h (98%)
rename libcxx/src/{experimental => }/stacktrace/osx/osx.cpp (96%)
rename libcxx/src/{experimental => }/stacktrace/osx/osx.h (95%)
rename libcxx/src/{experimental => }/stacktrace/stacktrace.cpp (88%)
rename libcxx/src/{experimental => }/stacktrace/tools/addr2line.cpp (96%)
rename libcxx/src/{experimental => }/stacktrace/tools/atos.cpp (95%)
rename libcxx/src/{experimental => }/stacktrace/tools/llvm_symbolizer.cpp (96%)
rename libcxx/src/{experimental => }/stacktrace/tools/pspawn.h (97%)
rename libcxx/src/{experimental => }/stacktrace/tools/tools.h (95%)
rename libcxx/src/{experimental => }/stacktrace/tools/toolspawner.cpp (98%)
rename libcxx/src/{experimental => }/stacktrace/unwind/unwind.cpp (93%)
rename libcxx/src/{experimental => }/stacktrace/unwind/unwind.h (95%)
rename libcxx/src/{experimental => }/stacktrace/windows/dbghelp_dll.cpp (97%)
rename libcxx/src/{experimental => }/stacktrace/windows/dbghelp_dll.h (98%)
rename libcxx/src/{experimental => }/stacktrace/windows/dll.cpp (95%)
rename libcxx/src/{experimental => }/stacktrace/windows/dll.h (96%)
rename libcxx/src/{experimental => }/stacktrace/windows/psapi_dll.cpp (95%)
rename libcxx/src/{experimental => }/stacktrace/windows/psapi_dll.h (96%)
rename libcxx/src/{experimental => }/stacktrace/windows/win_impl.cpp (99%)
rename libcxx/src/{experimental => }/stacktrace/windows/win_impl.h (96%)
diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 649b7551eb746..4a11a10224ae9 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -70,7 +70,6 @@ when ``-fexperimental-library`` is passed:
* The parallel algorithms library (``<execution>`` and the associated algorithms)
* ``std::chrono::tzdb`` and related time zone functionality
-* ``<stacktrace>``
* ``<syncstream>``
.. note::
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 65b69674433f4..f1b2aa10eb317 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -728,10 +728,10 @@ set(files
__ranges/zip_view.h
__split_buffer
__stacktrace/basic_stacktrace.h
- __stacktrace/detail/alloc.h
- __stacktrace/detail/context.h
- __stacktrace/detail/entry.h
- __stacktrace/detail/to_string.h
+ __stacktrace/alloc.h
+ __stacktrace/context.h
+ __stacktrace/entry.h
+ __stacktrace/to_string.h
__stacktrace/stacktrace_entry.h
__std_mbstate_t.h
__stop_token/atomic_unique_lock.h
diff --git a/libcxx/include/__stacktrace/detail/alloc.h b/libcxx/include/__stacktrace/alloc.h
similarity index 80%
rename from libcxx/include/__stacktrace/detail/alloc.h
rename to libcxx/include/__stacktrace/alloc.h
index a0949b5751899..402cf3c371161 100644
--- a/libcxx/include/__stacktrace/detail/alloc.h
+++ b/libcxx/include/__stacktrace/alloc.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+#ifndef _LIBCPP_STACKTRACE_ALLOC
+#define _LIBCPP_STACKTRACE_ALLOC
#include <__config>
#include <__functional/function.h>
@@ -29,7 +29,7 @@ namespace __stacktrace {
/** Per-stacktrace-invocation allocator which wraps a caller-provided allocator of any type.
This is intended to be used with `std::pmr::` containers and strings throughout the stacktrace
creation process. */
-struct alloc final : std::pmr::memory_resource {
+struct _LIBCPP_HIDDEN alloc final : std::pmr::memory_resource {
template <class _Allocator>
_LIBCPP_HIDE_FROM_ABI explicit alloc(_Allocator const& __a) {
// Take the given allocator type, and rebind with a new type having <byte> as the template arg
@@ -38,28 +38,22 @@ struct alloc final : std::pmr::memory_resource {
auto __ba = _BA(__a);
__alloc_func_ = [__ba](size_t __sz) mutable { return __ba.allocate(__sz); };
__dealloc_func_ = [__ba](void* __ptr, size_t __sz) mutable { return __ba.deallocate((std::byte*)__ptr, __sz); };
- __alloc_opaque_ = std::addressof(__a);
+ __alloc_ptr = std::addressof(__a);
}
- _LIBCPP_HIDE_FROM_ABI_VIRTUAL ~alloc() override = default;
+ _LIBCPP_HIDE_FROM_ABI ~alloc() override;
- _LIBCPP_HIDE_FROM_ABI_VIRTUAL virtual void _anchor_vfunc();
-
- _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t __align) override {
- // Avoiding "assert" in a system header, but we expect this to hold:
- // assert(__align <= alignof(std::max_align_t));
- (void)__align;
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t /*__align*/) override {
return __alloc_func_(__size);
}
- _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t __align) override {
- (void)__align;
+ _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t /*__align*/) override {
__dealloc_func_((std::byte*)__ptr, __size);
}
_LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
- return __rhs_ba && (__rhs_ba->__alloc_opaque_ == __alloc_opaque_);
+ return __rhs_ba && (__rhs_ba->__alloc_ptr == __alloc_ptr);
}
_LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
@@ -93,11 +87,11 @@ struct alloc final : std::pmr::memory_resource {
private:
std::function<std::byte*(size_t)> __alloc_func_;
std::function<void(std::byte*, size_t)> __dealloc_func_;
- /** Only used for equality */
- void const* __alloc_opaque_;
+ /** Only used for checking equality */
+ void const* __alloc_ptr;
};
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ALLOC
+#endif // _LIBCPP_STACKTRACE_ALLOC
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index cad3571d559bc..5e2ee1ae1248a 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -7,11 +7,11 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
-#define _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
+#ifndef _LIBCPP_BASIC_STACKTRACE
+#define _LIBCPP_BASIC_STACKTRACE
-#include <experimental/__stacktrace/detail/entry.h>
-#include <experimental/__stacktrace/stacktrace_entry.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/stacktrace_entry.h>
#include <__config>
#include <__format/formatter.h>
@@ -37,9 +37,9 @@
#include <memory>
#include <string>
-#include <__stacktrace/detail/alloc.h>
-#include <__stacktrace/detail/context.h>
-#include <__stacktrace/detail/to_string.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/to_string.h>
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -303,4 +303,4 @@ struct _LIBCPP_EXPORTED_FROM_ABI hash<basic_stacktrace<_Allocator>> {
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_EXPERIMENTAL_BASIC_STACKTRACE
+#endif // _LIBCPP_BASIC_STACKTRACE
diff --git a/libcxx/include/__stacktrace/detail/context.h b/libcxx/include/__stacktrace/context.h
similarity index 72%
rename from libcxx/include/__stacktrace/detail/context.h
rename to libcxx/include/__stacktrace/context.h
index df791b37b386d..2a23798751c60 100644
--- a/libcxx/include/__stacktrace/detail/context.h
+++ b/libcxx/include/__stacktrace/context.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+#ifndef _LIBCPP_STACKTRACE_CONTEXT
+#define _LIBCPP_STACKTRACE_CONTEXT
#include <__config>
#include <__memory_resource/memory_resource.h>
@@ -18,20 +18,20 @@
#include <cstddef>
#include <string>
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
-struct _LIBCPP_HIDE_FROM_ABI alloc;
-struct _LIBCPP_HIDE_FROM_ABI entry;
+struct _LIBCPP_HIDDEN alloc;
+struct _LIBCPP_HIDDEN entry;
namespace __stacktrace {
/** Represents the state of the current in-progress stacktrace operation. This includes
the working list of `entry` objects, as well as the caller-provided allocator (wrapped
in `polymorphic_allocator`) and a handful of utility functions involving that allocator. */
-struct _LIBCPP_HIDE_FROM_ABI context {
+struct _LIBCPP_HIDDEN context {
/** Encapsulates and type-removes the caller's allocator. */
alloc& __alloc_;
@@ -41,13 +41,13 @@ struct _LIBCPP_HIDE_FROM_ABI context {
/** Path to this process's main executable. */
std::pmr::string __main_prog_path_;
- _LIBCPP_HIDE_FROM_ABI explicit context(alloc& __byte_alloc)
+ _LIBCPP_HIDDEN explicit context(alloc& __byte_alloc)
: __alloc_(__byte_alloc), __entries_(&__alloc_), __main_prog_path_(__alloc_.new_string()) {}
- _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void do_stacktrace(size_t __skip, size_t __max_depth);
+ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void do_stacktrace(size_t __skip, size_t __max_depth);
};
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_CONTEXT
+#endif // _LIBCPP_STACKTRACE_CONTEXT
diff --git a/libcxx/include/__stacktrace/detail/entry.h b/libcxx/include/__stacktrace/entry.h
similarity index 88%
rename from libcxx/include/__stacktrace/detail/entry.h
rename to libcxx/include/__stacktrace/entry.h
index 2bab14228b508..50853b87cc693 100644
--- a/libcxx/include/__stacktrace/detail/entry.h
+++ b/libcxx/include/__stacktrace/entry.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+#ifndef _LIBCPP_STACKTRACE_DETAIL_ENTRY
+#define _LIBCPP_STACKTRACE_DETAIL_ENTRY
#include <__config>
#include <cstdint>
@@ -41,4 +41,4 @@ struct _LIBCPP_HIDE_FROM_ABI entry {
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_DETAIL_ENTRY
+#endif // _LIBCPP_STACKTRACE_DETAIL_ENTRY
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index f06fb0b6d4cb1..e5071655bd0d4 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
+#ifndef _LIBCPP_STACKTRACE_ENTRY
+#define _LIBCPP_STACKTRACE_ENTRY
#include <__cstddef/byte.h>
#include <__cstddef/ptrdiff_t.h>
@@ -33,7 +33,7 @@
#include <cstdint>
#include <string>
-#include <__stacktrace/detail/entry.h>
+#include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -108,4 +108,4 @@ struct _LIBCPP_EXPORTED_FROM_ABI hash<stacktrace_entry> {
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_ENTRY
+#endif // _LIBCPP_STACKTRACE_ENTRY
diff --git a/libcxx/include/__stacktrace/detail/to_string.h b/libcxx/include/__stacktrace/to_string.h
similarity index 89%
rename from libcxx/include/__stacktrace/detail/to_string.h
rename to libcxx/include/__stacktrace/to_string.h
index 47feddf07faf9..1d2c51a7a5c10 100644
--- a/libcxx/include/__stacktrace/detail/to_string.h
+++ b/libcxx/include/__stacktrace/to_string.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+#ifndef _LIBCPP_STACKTRACE_TO_STRING
+#define _LIBCPP_STACKTRACE_TO_STRING
#include <__config>
#include <__fwd/sstream.h>
@@ -43,4 +43,4 @@ struct _LIBCPP_HIDE_FROM_ABI __to_string {
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE_TO_STRING
+#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4491768cfe935..3313c960f4cd6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1987,10 +1987,10 @@ module std [system] {
module stacktrace {
module basic_stacktrace { header "__stacktrace/basic_stacktrace.h" }
- module alloc { header "__stacktrace/detail/alloc.h" }
- module context { header "__stacktrace/detail/context.h" }
- module entry { header "__stacktrace/detail/entry.h" }
- module to_string { header "__stacktrace/detail/to_string.h" }
+ module alloc { header "__stacktrace/alloc.h" }
+ module context { header "__stacktrace/context.h" }
+ module entry { header "__stacktrace/entry.h" }
+ module to_string { header "__stacktrace/to_string.h" }
module stacktrace_entry { header "__stacktrace/stacktrace_entry.h" }
header "stacktrace"
export *
diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace
index 33415533c2eea..8c7d8f82acbd2 100644
--- a/libcxx/include/stacktrace
+++ b/libcxx/include/stacktrace
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_EXPERIMENTAL_STACKTRACE
-#define _LIBCPP_EXPERIMENTAL_STACKTRACE
+#ifndef _LIBCPP_STACKTRACE
+#define _LIBCPP_STACKTRACE
/*
Header <stacktrace> synopsis
@@ -188,4 +188,4 @@ _LIBCPP_POP_MACROS
# endif // _LIBCPP_STD_VER >= 23
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
-#endif // _LIBCPP_EXPERIMENTAL_STACKTRACE
+#endif // _LIBCPP_STACKTRACE
diff --git a/libcxx/modules/std/stacktrace.inc b/libcxx/modules/std/stacktrace.inc
index e92f730cec62c..e7b31fd29b3a6 100644
--- a/libcxx/modules/std/stacktrace.inc
+++ b/libcxx/modules/std/stacktrace.inc
@@ -8,8 +8,7 @@
//===----------------------------------------------------------------------===//
export namespace std {
-// TODO(stacktrace23): update this when stacktrace is taken out of experimental
-#if 0 //_LIBCPP_STD_VER >= 23
+#if _LIBCPP_STD_VER >= 23
// [stacktrace.entry], class stacktrace_entry
using std::stacktrace_entry;
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index b24b98da2bf46..5076d1b13758f 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,6 +40,24 @@ set(LIBCXX_SOURCES
ryu/d2fixed.cpp
ryu/d2s.cpp
ryu/f2s.cpp
+ stacktrace/alloc.cpp
+ stacktrace/common/debug.cpp
+ stacktrace/common/fd.cpp
+ stacktrace/context.cpp
+ stacktrace/linux/linux-dl.cpp
+ stacktrace/linux/linux-elf.cpp
+ stacktrace/linux/linux-sym.cpp
+ stacktrace/osx/osx.cpp
+ stacktrace/stacktrace.cpp
+ stacktrace/tools/addr2line.cpp
+ stacktrace/tools/atos.cpp
+ stacktrace/tools/llvm_symbolizer.cpp
+ stacktrace/tools/toolspawner.cpp
+ stacktrace/unwind/unwind.cpp
+ stacktrace/windows/dbghelp_dll.cpp
+ stacktrace/windows/dll.cpp
+ stacktrace/windows/psapi_dll.cpp
+ stacktrace/windows/win_impl.cpp
stdexcept.cpp
string.cpp
support/runtime/exception_fallback.ipp
@@ -309,24 +327,6 @@ add_custom_target(cxx DEPENDS ${LIBCXX_BUILD_TARGETS})
# Build the experimental static library
set(LIBCXX_EXPERIMENTAL_SOURCES
experimental/keep.cpp
- experimental/stacktrace/alloc.cpp
- experimental/stacktrace/common/debug.cpp
- experimental/stacktrace/common/fd.cpp
- experimental/stacktrace/context.cpp
- experimental/stacktrace/linux/linux-dl.cpp
- experimental/stacktrace/linux/linux-elf.cpp
- experimental/stacktrace/linux/linux-sym.cpp
- experimental/stacktrace/osx/osx.cpp
- experimental/stacktrace/stacktrace.cpp
- experimental/stacktrace/tools/addr2line.cpp
- experimental/stacktrace/tools/atos.cpp
- experimental/stacktrace/tools/llvm_symbolizer.cpp
- experimental/stacktrace/tools/toolspawner.cpp
- experimental/stacktrace/unwind/unwind.cpp
- experimental/stacktrace/windows/dbghelp_dll.cpp
- experimental/stacktrace/windows/dll.cpp
- experimental/stacktrace/windows/psapi_dll.cpp
- experimental/stacktrace/windows/win_impl.cpp
)
if (LIBCXX_PSTL_BACKEND STREQUAL "libdispatch")
diff --git a/libcxx/src/stacktrace/README.md b/libcxx/src/stacktrace/README.md
new file mode 100644
index 0000000000000..afdfb9bf2e0f9
--- /dev/null
+++ b/libcxx/src/stacktrace/README.md
@@ -0,0 +1,5 @@
+# C++23 `<stacktrace>`
+
+# References
+
+1. https://eel.is/c++draft/stacktrace
diff --git a/libcxx/src/experimental/stacktrace/alloc.cpp b/libcxx/src/stacktrace/alloc.cpp
similarity index 86%
rename from libcxx/src/experimental/stacktrace/alloc.cpp
rename to libcxx/src/stacktrace/alloc.cpp
index 956e01b312d3b..43842aa1a5b47 100644
--- a/libcxx/src/experimental/stacktrace/alloc.cpp
+++ b/libcxx/src/stacktrace/alloc.cpp
@@ -9,12 +9,12 @@
#include <__config>
#include <__config_site>
-#include <experimental/__stacktrace/detail/alloc.h>
+#include <__stacktrace/alloc.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-void alloc::_anchor_vfunc() {}
+alloc::~alloc() {}
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/common/config.h b/libcxx/src/stacktrace/common/config.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/config.h
rename to libcxx/src/stacktrace/common/config.h
diff --git a/libcxx/src/experimental/stacktrace/common/debug.cpp b/libcxx/src/stacktrace/common/debug.cpp
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/debug.cpp
rename to libcxx/src/stacktrace/common/debug.cpp
diff --git a/libcxx/src/experimental/stacktrace/common/debug.h b/libcxx/src/stacktrace/common/debug.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/debug.h
rename to libcxx/src/stacktrace/common/debug.h
diff --git a/libcxx/src/experimental/stacktrace/common/failed.h b/libcxx/src/stacktrace/common/failed.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/failed.h
rename to libcxx/src/stacktrace/common/failed.h
diff --git a/libcxx/src/experimental/stacktrace/common/fd.cpp b/libcxx/src/stacktrace/common/fd.cpp
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/fd.cpp
rename to libcxx/src/stacktrace/common/fd.cpp
diff --git a/libcxx/src/experimental/stacktrace/common/fd.h b/libcxx/src/stacktrace/common/fd.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/fd.h
rename to libcxx/src/stacktrace/common/fd.h
diff --git a/libcxx/src/experimental/stacktrace/common/images.h b/libcxx/src/stacktrace/common/images.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/common/images.h
rename to libcxx/src/stacktrace/common/images.h
diff --git a/libcxx/src/experimental/stacktrace/context.cpp b/libcxx/src/stacktrace/context.cpp
similarity index 88%
rename from libcxx/src/experimental/stacktrace/context.cpp
rename to libcxx/src/stacktrace/context.cpp
index 3d311af4d7a89..e2d1445aa4995 100644
--- a/libcxx/src/experimental/stacktrace/context.cpp
+++ b/libcxx/src/stacktrace/context.cpp
@@ -11,13 +11,13 @@
#include <list>
-#include <experimental/__stacktrace/basic_stacktrace.h>
-#include <experimental/__stacktrace/stacktrace_entry.h>
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
-#include <experimental/__stacktrace/detail/to_string.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/to_string.h>
#include "common/config.h"
@@ -44,7 +44,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip, size_t max_depth) {
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void context::do_stacktrace(size_t skip, size_t max_depth) {
/*
Here we declare stacktrace components or "backends" which will handle the different tasks:
diff --git a/libcxx/src/experimental/stacktrace/linux/elf.h b/libcxx/src/stacktrace/linux/elf.h
similarity index 100%
rename from libcxx/src/experimental/stacktrace/linux/elf.h
rename to libcxx/src/stacktrace/linux/elf.h
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp b/libcxx/src/stacktrace/linux/linux-dl.cpp
similarity index 92%
rename from libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
rename to libcxx/src/stacktrace/linux/linux-dl.cpp
index 62cad72b9f124..e15403f7d3a50 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux-dl.cpp
+++ b/libcxx/src/stacktrace/linux/linux-dl.cpp
@@ -19,8 +19,8 @@
# include "../common/images.h"
-# include <experimental/__stacktrace/detail/context.h>
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/context.h>
+# include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp b/libcxx/src/stacktrace/linux/linux-elf.cpp
similarity index 93%
rename from libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
rename to libcxx/src/stacktrace/linux/linux-elf.cpp
index 7cf82789ea2ed..2cbeade56534b 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux-elf.cpp
+++ b/libcxx/src/stacktrace/linux/linux-elf.cpp
@@ -15,8 +15,8 @@
# include <cassert>
# include <unistd.h>
-# include <experimental/__stacktrace/detail/context.h>
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/context.h>
+# include <__stacktrace/entry.h>
# include "../common/fd.h"
# include "elf.h"
diff --git a/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp b/libcxx/src/stacktrace/linux/linux-sym.cpp
similarity index 94%
rename from libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
rename to libcxx/src/stacktrace/linux/linux-sym.cpp
index fa7489c64c923..b9a8c313e668b 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux-sym.cpp
+++ b/libcxx/src/stacktrace/linux/linux-sym.cpp
@@ -16,8 +16,8 @@
# include <dlfcn.h>
# include <unistd.h>
-# include <experimental/__stacktrace/detail/context.h>
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/context.h>
+# include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/linux/linux.h b/libcxx/src/stacktrace/linux/linux.h
similarity index 98%
rename from libcxx/src/experimental/stacktrace/linux/linux.h
rename to libcxx/src/stacktrace/linux/linux.h
index a8e8c2279f6fc..52b0377bcb144 100644
--- a/libcxx/src/experimental/stacktrace/linux/linux.h
+++ b/libcxx/src/stacktrace/linux/linux.h
@@ -27,7 +27,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
struct linux {
context& cx_;
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.cpp b/libcxx/src/stacktrace/osx/osx.cpp
similarity index 96%
rename from libcxx/src/experimental/stacktrace/osx/osx.cpp
rename to libcxx/src/stacktrace/osx/osx.cpp
index c00a7ea94e780..f377c0bded38a 100644
--- a/libcxx/src/experimental/stacktrace/osx/osx.cpp
+++ b/libcxx/src/stacktrace/osx/osx.cpp
@@ -16,8 +16,8 @@
# include <mach-o/dyld.h>
# include <mach-o/loader.h>
-# include <experimental/__stacktrace/detail/context.h>
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/context.h>
+# include <__stacktrace/entry.h>
# include "osx.h"
diff --git a/libcxx/src/experimental/stacktrace/osx/osx.h b/libcxx/src/stacktrace/osx/osx.h
similarity index 95%
rename from libcxx/src/experimental/stacktrace/osx/osx.h
rename to libcxx/src/stacktrace/osx/osx.h
index ca0890929453e..ce38aa0a378c6 100644
--- a/libcxx/src/experimental/stacktrace/osx/osx.h
+++ b/libcxx/src/stacktrace/osx/osx.h
@@ -17,7 +17,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
struct osx {
context& cx_;
diff --git a/libcxx/src/experimental/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
similarity index 88%
rename from libcxx/src/experimental/stacktrace/stacktrace.cpp
rename to libcxx/src/stacktrace/stacktrace.cpp
index e33b94205a8ea..98152c0c323f6 100644
--- a/libcxx/src/experimental/stacktrace/stacktrace.cpp
+++ b/libcxx/src/stacktrace/stacktrace.cpp
@@ -14,13 +14,13 @@
#include <sstream>
#include <string>
-#include <experimental/__stacktrace/basic_stacktrace.h>
-#include <experimental/__stacktrace/stacktrace_entry.h>
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
-#include <experimental/__stacktrace/detail/to_string.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
+#include <__stacktrace/to_string.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/addr2line.cpp b/libcxx/src/stacktrace/tools/addr2line.cpp
similarity index 96%
rename from libcxx/src/experimental/stacktrace/tools/addr2line.cpp
rename to libcxx/src/stacktrace/tools/addr2line.cpp
index 310e99699569b..c1d36e83f13a6 100644
--- a/libcxx/src/experimental/stacktrace/tools/addr2line.cpp
+++ b/libcxx/src/stacktrace/tools/addr2line.cpp
@@ -13,8 +13,8 @@
#include "tools.h"
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
#include <string>
_LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/tools/atos.cpp b/libcxx/src/stacktrace/tools/atos.cpp
similarity index 95%
rename from libcxx/src/experimental/stacktrace/tools/atos.cpp
rename to libcxx/src/stacktrace/tools/atos.cpp
index 569883a4b12bc..f6e6458aaae0d 100644
--- a/libcxx/src/experimental/stacktrace/tools/atos.cpp
+++ b/libcxx/src/stacktrace/tools/atos.cpp
@@ -15,9 +15,9 @@
#include "tools.h"
-#include <experimental/__stacktrace/detail/alloc.h>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/alloc.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp b/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
similarity index 96%
rename from libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
rename to libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
index 0a9907978dcf2..18b493c97a040 100644
--- a/libcxx/src/experimental/stacktrace/tools/llvm_symbolizer.cpp
+++ b/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
@@ -15,8 +15,8 @@
#include "../common/debug.h"
#include "tools.h"
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/pspawn.h b/libcxx/src/stacktrace/tools/pspawn.h
similarity index 97%
rename from libcxx/src/experimental/stacktrace/tools/pspawn.h
rename to libcxx/src/stacktrace/tools/pspawn.h
index 906a504441368..4e2fa381083ad 100644
--- a/libcxx/src/experimental/stacktrace/tools/pspawn.h
+++ b/libcxx/src/stacktrace/tools/pspawn.h
@@ -29,8 +29,8 @@
# include <unistd.h>
# include <vector>
-# include <experimental/__stacktrace/detail/context.h>
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/context.h>
+# include <__stacktrace/entry.h>
# include "../common/debug.h"
# include "../common/failed.h"
diff --git a/libcxx/src/experimental/stacktrace/tools/tools.h b/libcxx/src/stacktrace/tools/tools.h
similarity index 95%
rename from libcxx/src/experimental/stacktrace/tools/tools.h
rename to libcxx/src/stacktrace/tools/tools.h
index d832974e69cbb..70374cf075ebf 100644
--- a/libcxx/src/experimental/stacktrace/tools/tools.h
+++ b/libcxx/src/stacktrace/tools/tools.h
@@ -16,8 +16,8 @@
#include <list>
#include <string>
-#include <experimental/__stacktrace/detail/context.h>
-#include <experimental/__stacktrace/detail/entry.h>
+#include <__stacktrace/context.h>
+#include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp b/libcxx/src/stacktrace/tools/toolspawner.cpp
similarity index 98%
rename from libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
rename to libcxx/src/stacktrace/tools/toolspawner.cpp
index 05a2f8e6c2eb4..54ddaf156fc64 100644
--- a/libcxx/src/experimental/stacktrace/tools/toolspawner.cpp
+++ b/libcxx/src/stacktrace/tools/toolspawner.cpp
@@ -27,7 +27,7 @@
# include "../common/failed.h"
# include "pspawn.h"
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.cpp b/libcxx/src/stacktrace/unwind/unwind.cpp
similarity index 93%
rename from libcxx/src/experimental/stacktrace/unwind/unwind.cpp
rename to libcxx/src/stacktrace/unwind/unwind.cpp
index a316306bc43eb..51689c5f1c15d 100644
--- a/libcxx/src/experimental/stacktrace/unwind/unwind.cpp
+++ b/libcxx/src/stacktrace/unwind/unwind.cpp
@@ -12,8 +12,8 @@
# include "./unwind.h"
-# include <experimental/__stacktrace/detail/context.h>
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/context.h>
+# include <__stacktrace/entry.h>
# include <unwind.h>
_LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/src/experimental/stacktrace/unwind/unwind.h b/libcxx/src/stacktrace/unwind/unwind.h
similarity index 95%
rename from libcxx/src/experimental/stacktrace/unwind/unwind.h
rename to libcxx/src/stacktrace/unwind/unwind.h
index 1b099e3a0b922..a258a32f50c5d 100644
--- a/libcxx/src/experimental/stacktrace/unwind/unwind.h
+++ b/libcxx/src/stacktrace/unwind/unwind.h
@@ -17,7 +17,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
struct unwind {
context& cx_;
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp b/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
similarity index 97%
rename from libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
rename to libcxx/src/stacktrace/windows/dbghelp_dll.cpp
index 274c9ecb2b314..7753ad77d3d26 100644
--- a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.cpp
+++ b/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
@@ -21,7 +21,7 @@
# include <mutex>
# include "dll.h"
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h b/libcxx/src/stacktrace/windows/dbghelp_dll.h
similarity index 98%
rename from libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
rename to libcxx/src/stacktrace/windows/dbghelp_dll.h
index 920391fb9ba52..b1204a369a3f1 100644
--- a/libcxx/src/experimental/stacktrace/windows/dbghelp_dll.h
+++ b/libcxx/src/stacktrace/windows/dbghelp_dll.h
@@ -22,7 +22,7 @@
# include <cstdlib>
# include <mutex>
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/entry.h>
# include "../common/debug.h"
# include "dll.h"
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.cpp b/libcxx/src/stacktrace/windows/dll.cpp
similarity index 95%
rename from libcxx/src/experimental/stacktrace/windows/dll.cpp
rename to libcxx/src/stacktrace/windows/dll.cpp
index 55fd729a9caf4..b0ea2609b55cd 100644
--- a/libcxx/src/experimental/stacktrace/windows/dll.cpp
+++ b/libcxx/src/stacktrace/windows/dll.cpp
@@ -21,7 +21,7 @@
# include <mutex>
# include "dll.h"
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/dll.h b/libcxx/src/stacktrace/windows/dll.h
similarity index 96%
rename from libcxx/src/experimental/stacktrace/windows/dll.h
rename to libcxx/src/stacktrace/windows/dll.h
index ab363ce2c2561..4ef8e7c5df190 100644
--- a/libcxx/src/experimental/stacktrace/windows/dll.h
+++ b/libcxx/src/stacktrace/windows/dll.h
@@ -22,7 +22,7 @@
# include <cstdlib>
# include <mutex>
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/entry.h>
# include "../common/debug.h"
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp b/libcxx/src/stacktrace/windows/psapi_dll.cpp
similarity index 95%
rename from libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
rename to libcxx/src/stacktrace/windows/psapi_dll.cpp
index ab28df8d3fbd7..d4f7fdc006341 100644
--- a/libcxx/src/experimental/stacktrace/windows/psapi_dll.cpp
+++ b/libcxx/src/stacktrace/windows/psapi_dll.cpp
@@ -21,7 +21,7 @@
# include <mutex>
# include "dll.h"
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/psapi_dll.h b/libcxx/src/stacktrace/windows/psapi_dll.h
similarity index 96%
rename from libcxx/src/experimental/stacktrace/windows/psapi_dll.h
rename to libcxx/src/stacktrace/windows/psapi_dll.h
index 4e6a192f3d790..bcad1bfb72d0a 100644
--- a/libcxx/src/experimental/stacktrace/windows/psapi_dll.h
+++ b/libcxx/src/stacktrace/windows/psapi_dll.h
@@ -22,7 +22,7 @@
# include <cstdlib>
# include <mutex>
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/entry.h>
# include "../common/debug.h"
# include "dll.h"
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.cpp b/libcxx/src/stacktrace/windows/win_impl.cpp
similarity index 99%
rename from libcxx/src/experimental/stacktrace/windows/win_impl.cpp
rename to libcxx/src/stacktrace/windows/win_impl.cpp
index 7773d0d872816..44d6052706b85 100644
--- a/libcxx/src/experimental/stacktrace/windows/win_impl.cpp
+++ b/libcxx/src/stacktrace/windows/win_impl.cpp
@@ -21,7 +21,7 @@
# include <mutex>
# include "dll.h"
-# include <experimental/__stacktrace/detail/entry.h>
+# include <__stacktrace/entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/experimental/stacktrace/windows/win_impl.h b/libcxx/src/stacktrace/windows/win_impl.h
similarity index 96%
rename from libcxx/src/experimental/stacktrace/windows/win_impl.h
rename to libcxx/src/stacktrace/windows/win_impl.h
index 459347ad9a00f..c065987120425 100644
--- a/libcxx/src/experimental/stacktrace/windows/win_impl.h
+++ b/libcxx/src/stacktrace/windows/win_impl.h
@@ -18,7 +18,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
-struct _LIBCPP_HIDE_FROM_ABI context;
+struct context;
struct win_impl {
context& cx_;
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
index 6c4f3ee6008a2..460d61d58a915 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nodebug.pass.cpp
@@ -10,7 +10,7 @@
// ADDITIONAL_COMPILE_FLAGS: -O0 -g0
#include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <iostream>
int main(int, char**) {
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
index 58d3c8fed5c5e..bdf5ba22bc46b 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.nosplit.pass.cpp
@@ -10,7 +10,7 @@
// ADDITIONAL_COMPILE_FLAGS: -O0 -g
#include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <iostream>
int main(int, char**) {
diff --git a/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
index a00866181a0e6..022f27f5e569e 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o0.split.pass.cpp
@@ -11,7 +11,7 @@
#include <cassert>
#include <iostream>
-#include <experimental/stacktrace>
+#include <stacktrace>
int main(int, char**) {
// Get the current trace.
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
index e8387bfebb14f..35a30eb078212 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nodebug.pass.cpp
@@ -10,7 +10,7 @@
// ADDITIONAL_COMPILE_FLAGS: -O3 -g0
#include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <iostream>
int main(int, char**) {
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
index e3796c2bc8add..8d0fcf5806884 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.nosplit.pass.cpp
@@ -10,7 +10,7 @@
// ADDITIONAL_COMPILE_FLAGS: -O3 -g
#include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <iostream>
int main(int, char**) {
diff --git a/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
index 7b58f9accf7c9..cea9c79c87a77 100644
--- a/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
+++ b/libcxx/test/libcxx/stacktrace/simple.o3.split.pass.cpp
@@ -10,7 +10,7 @@
// ADDITIONAL_COMPILE_FLAGS: -O3 -g -gsplit-dwarf
#include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <iostream>
int main(int, char**) {
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index fbfa8152b8070..29b49f2ae7e84 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -295,35 +295,6 @@ experimental/propagate_const version
experimental/simd cstdint
experimental/simd limits
experimental/simd version
-experimental/stacktrace bitset
-experimental/stacktrace cctype
-experimental/stacktrace cerrno
-experimental/stacktrace climits
-experimental/stacktrace clocale
-experimental/stacktrace compare
-experimental/stacktrace cstddef
-experimental/stacktrace cstdint
-experimental/stacktrace cstdio
-experimental/stacktrace cstdlib
-experimental/stacktrace cstring
-experimental/stacktrace ctime
-experimental/stacktrace cwchar
-experimental/stacktrace cwctype
-experimental/stacktrace initializer_list
-experimental/stacktrace ios
-experimental/stacktrace iosfwd
-experimental/stacktrace limits
-experimental/stacktrace list
-experimental/stacktrace locale
-experimental/stacktrace memory
-experimental/stacktrace ratio
-experimental/stacktrace stdexcept
-experimental/stacktrace streambuf
-experimental/stacktrace string
-experimental/stacktrace string_view
-experimental/stacktrace tuple
-experimental/stacktrace typeinfo
-experimental/stacktrace version
experimental/type_traits cstdint
experimental/type_traits initializer_list
experimental/type_traits type_traits
@@ -955,6 +926,35 @@ stack limits
stack stdexcept
stack tuple
stack version
+stacktrace bitset
+stacktrace cctype
+stacktrace cerrno
+stacktrace climits
+stacktrace clocale
+stacktrace compare
+stacktrace cstddef
+stacktrace cstdint
+stacktrace cstdio
+stacktrace cstdlib
+stacktrace cstring
+stacktrace ctime
+stacktrace cwchar
+stacktrace cwctype
+stacktrace initializer_list
+stacktrace ios
+stacktrace iosfwd
+stacktrace limits
+stacktrace list
+stacktrace locale
+stacktrace memory
+stacktrace ratio
+stacktrace stdexcept
+stacktrace streambuf
+stacktrace string
+stacktrace string_view
+stacktrace tuple
+stacktrace typeinfo
+stacktrace version
stop_token atomic
stop_token climits
stop_token cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 06dd30b07537e..5f7b5401f081a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -295,35 +295,6 @@ experimental/propagate_const version
experimental/simd cstdint
experimental/simd limits
experimental/simd version
-experimental/stacktrace bitset
-experimental/stacktrace cctype
-experimental/stacktrace cerrno
-experimental/stacktrace climits
-experimental/stacktrace clocale
-experimental/stacktrace compare
-experimental/stacktrace cstddef
-experimental/stacktrace cstdint
-experimental/stacktrace cstdio
-experimental/stacktrace cstdlib
-experimental/stacktrace cstring
-experimental/stacktrace ctime
-experimental/stacktrace cwchar
-experimental/stacktrace cwctype
-experimental/stacktrace initializer_list
-experimental/stacktrace ios
-experimental/stacktrace iosfwd
-experimental/stacktrace limits
-experimental/stacktrace list
-experimental/stacktrace locale
-experimental/stacktrace memory
-experimental/stacktrace ratio
-experimental/stacktrace stdexcept
-experimental/stacktrace streambuf
-experimental/stacktrace string
-experimental/stacktrace string_view
-experimental/stacktrace tuple
-experimental/stacktrace typeinfo
-experimental/stacktrace version
experimental/type_traits cstdint
experimental/type_traits initializer_list
experimental/type_traits type_traits
@@ -955,6 +926,35 @@ stack limits
stack stdexcept
stack tuple
stack version
+stacktrace bitset
+stacktrace cctype
+stacktrace cerrno
+stacktrace climits
+stacktrace clocale
+stacktrace compare
+stacktrace cstddef
+stacktrace cstdint
+stacktrace cstdio
+stacktrace cstdlib
+stacktrace cstring
+stacktrace ctime
+stacktrace cwchar
+stacktrace cwctype
+stacktrace initializer_list
+stacktrace ios
+stacktrace iosfwd
+stacktrace limits
+stacktrace list
+stacktrace locale
+stacktrace memory
+stacktrace ratio
+stacktrace stdexcept
+stacktrace streambuf
+stacktrace string
+stacktrace string_view
+stacktrace tuple
+stacktrace typeinfo
+stacktrace version
stop_token atomic
stop_token climits
stop_token cstdint
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
index 495abe79da20f..e8b5c36d05ba1 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cmp.pass.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
index 64e54fdf5793d..edf07510687dc 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
@@ -9,7 +9,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// ADDITIONAL_COMPILE_FLAGS: -O1 -g
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <cassert>
#include <iostream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
index db33a8fabb41a..976b7ef05ccf2 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.hash.pass.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
index 5929cd8f8d0f9..9d473202d492f 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.mod.pass.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
index 665fb475f56e9..d0b327e738c6c 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.nonmem.pass.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <cassert>
#include <sstream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
index b5c6cf77dde71..1f4102bd1ca61 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.obs.pass.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <iostream>
#include <iterator>
#include <memory>
diff --git a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
index ce502626a45ea..8260b0a8028d5 100644
--- a/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/basic.pass.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <iterator>
#include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
index 4c6531319fc09..6da4d40c5e08a 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cmp.pass.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
index 944b4c519f0fd..84472ea91b2aa 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.cons.pass.cpp
@@ -10,7 +10,7 @@
// ADDITIONAL_COMPILE_FLAGS: -g
#include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <type_traits>
/*
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
index da6e3ccec92b6..f31d7d3e9f8b7 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.obs.pass.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <cassert>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
index b55bdcde966d3..a77687a5607a1 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.pass.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <cassert>
#include <concepts>
diff --git a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
index bfdb54e4df668..0d8fac58f30ee 100644
--- a/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/entry.query.pass.cpp
@@ -9,7 +9,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// ADDITIONAL_COMPILE_FLAGS: -O0 -g -gsplit-dwarf
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <cassert>
#include <iostream>
diff --git a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
index b37681659d43c..42accf6ce1daa 100644
--- a/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/format.pass.cpp
@@ -9,7 +9,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
#include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
/*
(19.6.5) Formatting support [stacktrace.format]
diff --git a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
index 3c92016b2c900..9682f6bfc146d 100644
--- a/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
+++ b/libcxx/test/std/diagnostics/stacktrace/syn.pass.cpp
@@ -9,7 +9,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
#include <cassert>
-#include <experimental/stacktrace>
+#include <stacktrace>
#include <memory>
#include <type_traits>
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index 6d69cf29af308..4f569ffba6325 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -170,7 +170,6 @@ def __hash__(self) -> int:
"linalg",
"rcu",
"spanstream",
- "stacktrace", # TODO(stacktrace23): remove this when stacktrace is taken out of experimental
"stdfloat",
"text_encoding",
]))
@@ -208,7 +207,6 @@ def __hash__(self) -> int:
"experimental/iterator": "// UNSUPPORTED: c++03",
"experimental/propagate_const": "// UNSUPPORTED: c++03",
"experimental/simd": "// UNSUPPORTED: c++03",
- "experimental/stacktrace": "// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20",
"experimental/type_traits": "// UNSUPPORTED: c++03",
"experimental/utility": "// UNSUPPORTED: c++03",
"filesystem": "// UNSUPPORTED: no-filesystem, c++03, c++11, c++14",
@@ -227,6 +225,7 @@ def __hash__(self) -> int:
"semaphore": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"shared_mutex": "// UNSUPPORTED: no-threads, c++03, c++11",
"sstream": "// UNSUPPORTED: no-localization",
+ "stacktrace": "// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20",
"stdatomic.h": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17, c++20",
"stop_token": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"streambuf": "// UNSUPPORTED: no-localization",
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index d30d70ac40842..d8b2d9e6553bb 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -167,38 +167,38 @@ cxx_sources = [
"ryu/d2s.cpp",
"ryu/f2s.cpp",
"shared_mutex.cpp",
- # "stacktrace/alloc.cpp",
- # "stacktrace/common/config.h",
- # "stacktrace/common/debug.cpp",
- # "stacktrace/common/debug.h",
- # "stacktrace/common/failed.h",
- # "stacktrace/common/fd.cpp",
- # "stacktrace/common/fd.h",
- # "stacktrace/common/images.h",
- # "stacktrace/context.cpp",
- # "stacktrace/linux/linux-dl.cpp",
- # "stacktrace/linux/linux-elf.cpp",
- # "stacktrace/linux/linux-sym.cpp",
- # "stacktrace/linux/linux.h",
- # "stacktrace/osx/osx.cpp",
- # "stacktrace/osx/osx.h",
- # "stacktrace/stacktrace.cpp",
- # "stacktrace/tools/addr2line.cpp",
- # "stacktrace/tools/atos.cpp",
- # "stacktrace/tools/llvm_symbolizer.cpp",
- # "stacktrace/tools/pspawn.h",
- # "stacktrace/tools/tools.h",
- # "stacktrace/tools/toolspawner.cpp",
- # "stacktrace/unwind/unwind.cpp",
- # "stacktrace/unwind/unwind.h",
- # "stacktrace/windows/dbghelp_dll.cpp",
- # "stacktrace/windows/dbghelp_dll.h",
- # "stacktrace/windows/dll.cpp",
- # "stacktrace/windows/dll.h",
- # "stacktrace/windows/psapi_dll.cpp",
- # "stacktrace/windows/psapi_dll.h",
- # "stacktrace/windows/win_impl.cpp",
- # "stacktrace/windows/win_impl.h",
+ "stacktrace/alloc.cpp",
+ "stacktrace/common/config.h",
+ "stacktrace/common/debug.cpp",
+ "stacktrace/common/debug.h",
+ "stacktrace/common/failed.h",
+ "stacktrace/common/fd.cpp",
+ "stacktrace/common/fd.h",
+ "stacktrace/common/images.h",
+ "stacktrace/context.cpp",
+ "stacktrace/linux/linux-dl.cpp",
+ "stacktrace/linux/linux-elf.cpp",
+ "stacktrace/linux/linux-sym.cpp",
+ "stacktrace/linux/linux.h",
+ "stacktrace/osx/osx.cpp",
+ "stacktrace/osx/osx.h",
+ "stacktrace/stacktrace.cpp",
+ "stacktrace/tools/addr2line.cpp",
+ "stacktrace/tools/atos.cpp",
+ "stacktrace/tools/llvm_symbolizer.cpp",
+ "stacktrace/tools/pspawn.h",
+ "stacktrace/tools/tools.h",
+ "stacktrace/tools/toolspawner.cpp",
+ "stacktrace/unwind/unwind.cpp",
+ "stacktrace/unwind/unwind.h",
+ "stacktrace/windows/dbghelp_dll.cpp",
+ "stacktrace/windows/dbghelp_dll.h",
+ "stacktrace/windows/dll.cpp",
+ "stacktrace/windows/dll.h",
+ "stacktrace/windows/psapi_dll.cpp",
+ "stacktrace/windows/psapi_dll.h",
+ "stacktrace/windows/win_impl.cpp",
+ "stacktrace/windows/win_impl.h",
"stdexcept.cpp",
"string.cpp",
"strstream.cpp",
>From 12636b4bed1eaf6802fc8640b465049ba00a3ba3 Mon Sep 17 00:00:00 2001
From: Steve O'Brien <steve at obrien.cc>
Date: Wed, 23 Apr 2025 09:50:44 -0400
Subject: [PATCH 6/6] simplify layout of stacktrace files: condense, collapse
---
libcxx/include/CMakeLists.txt | 5 +-
libcxx/include/__stacktrace/alloc.h | 97 ----
.../include/__stacktrace/basic_stacktrace.h | 36 +-
libcxx/include/__stacktrace/entry.h | 44 --
libcxx/include/__stacktrace/impl.h | 157 +++++++
.../include/__stacktrace/stacktrace_entry.h | 11 +-
libcxx/include/__stacktrace/to_string.h | 46 --
libcxx/include/module.modulemap.in | 14 +-
libcxx/include/stacktrace | 1 +
libcxx/modules/std.cppm.in | 1 +
libcxx/src/CMakeLists.txt | 22 +-
libcxx/src/stacktrace/alloc.cpp | 20 -
libcxx/src/stacktrace/common/debug.cpp | 19 -
libcxx/src/stacktrace/common/debug.h | 55 ---
libcxx/src/stacktrace/common/failed.h | 29 --
libcxx/src/stacktrace/common/fd.cpp | 31 --
libcxx/src/stacktrace/common/fd.h | 122 -----
libcxx/src/stacktrace/common/images.h | 33 --
libcxx/src/stacktrace/{common => }/config.h | 0
libcxx/src/stacktrace/context.cpp | 104 -----
.../__stacktrace => src/stacktrace}/context.h | 7 +-
.../{linux/linux-sym.cpp => linux.cpp} | 62 ++-
.../src/stacktrace/{linux/elf.h => linux.h} | 99 +++-
libcxx/src/stacktrace/linux/linux-dl.cpp | 58 ---
libcxx/src/stacktrace/linux/linux-elf.cpp | 53 ---
libcxx/src/stacktrace/linux/linux.h | 98 ----
libcxx/src/stacktrace/{osx => }/osx.cpp | 13 +-
libcxx/src/stacktrace/{osx => }/osx.h | 0
libcxx/src/stacktrace/stacktrace.cpp | 153 +++++-
libcxx/src/stacktrace/tools.cpp | 437 ++++++++++++++++++
.../stacktrace/{tools/pspawn.h => tools.h} | 94 ++--
libcxx/src/stacktrace/tools/addr2line.cpp | 100 ----
libcxx/src/stacktrace/tools/atos.cpp | 115 -----
.../src/stacktrace/tools/llvm_symbolizer.cpp | 112 -----
libcxx/src/stacktrace/tools/tools.h | 65 ---
libcxx/src/stacktrace/tools/toolspawner.cpp | 185 --------
libcxx/src/stacktrace/{unwind => }/unwind.cpp | 10 +-
libcxx/src/stacktrace/{unwind => }/unwind.h | 6 +
libcxx/src/stacktrace/utils.h | 176 +++++++
.../{windows/win_impl.cpp => windows.cpp} | 69 ++-
.../{windows/dbghelp_dll.h => windows.h} | 86 +++-
libcxx/src/stacktrace/windows/dbghelp_dll.cpp | 56 ---
libcxx/src/stacktrace/windows/dll.cpp | 40 --
libcxx/src/stacktrace/windows/dll.h | 68 ---
libcxx/src/stacktrace/windows/psapi_dll.cpp | 48 --
libcxx/src/stacktrace/windows/psapi_dll.h | 55 ---
libcxx/src/stacktrace/windows/win_impl.h | 38 --
llvm/utils/gn/secondary/libcxx/src/BUILD.gn | 42 +-
48 files changed, 1307 insertions(+), 1885 deletions(-)
delete mode 100644 libcxx/include/__stacktrace/alloc.h
delete mode 100644 libcxx/include/__stacktrace/entry.h
create mode 100644 libcxx/include/__stacktrace/impl.h
delete mode 100644 libcxx/include/__stacktrace/to_string.h
delete mode 100644 libcxx/src/stacktrace/alloc.cpp
delete mode 100644 libcxx/src/stacktrace/common/debug.cpp
delete mode 100644 libcxx/src/stacktrace/common/debug.h
delete mode 100644 libcxx/src/stacktrace/common/failed.h
delete mode 100644 libcxx/src/stacktrace/common/fd.cpp
delete mode 100644 libcxx/src/stacktrace/common/fd.h
delete mode 100644 libcxx/src/stacktrace/common/images.h
rename libcxx/src/stacktrace/{common => }/config.h (100%)
delete mode 100644 libcxx/src/stacktrace/context.cpp
rename libcxx/{include/__stacktrace => src/stacktrace}/context.h (93%)
rename libcxx/src/stacktrace/{linux/linux-sym.cpp => linux.cpp} (50%)
rename libcxx/src/stacktrace/{linux/elf.h => linux.h} (82%)
delete mode 100644 libcxx/src/stacktrace/linux/linux-dl.cpp
delete mode 100644 libcxx/src/stacktrace/linux/linux-elf.cpp
delete mode 100644 libcxx/src/stacktrace/linux/linux.h
rename libcxx/src/stacktrace/{osx => }/osx.cpp (92%)
rename libcxx/src/stacktrace/{osx => }/osx.h (100%)
create mode 100644 libcxx/src/stacktrace/tools.cpp
rename libcxx/src/stacktrace/{tools/pspawn.h => tools.h} (60%)
delete mode 100644 libcxx/src/stacktrace/tools/addr2line.cpp
delete mode 100644 libcxx/src/stacktrace/tools/atos.cpp
delete mode 100644 libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
delete mode 100644 libcxx/src/stacktrace/tools/tools.h
delete mode 100644 libcxx/src/stacktrace/tools/toolspawner.cpp
rename libcxx/src/stacktrace/{unwind => }/unwind.cpp (91%)
rename libcxx/src/stacktrace/{unwind => }/unwind.h (81%)
create mode 100644 libcxx/src/stacktrace/utils.h
rename libcxx/src/stacktrace/{windows/win_impl.cpp => windows.cpp} (74%)
rename libcxx/src/stacktrace/{windows/dbghelp_dll.h => windows.h} (55%)
delete mode 100644 libcxx/src/stacktrace/windows/dbghelp_dll.cpp
delete mode 100644 libcxx/src/stacktrace/windows/dll.cpp
delete mode 100644 libcxx/src/stacktrace/windows/dll.h
delete mode 100644 libcxx/src/stacktrace/windows/psapi_dll.cpp
delete mode 100644 libcxx/src/stacktrace/windows/psapi_dll.h
delete mode 100644 libcxx/src/stacktrace/windows/win_impl.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index f1b2aa10eb317..1500630cff9da 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -728,10 +728,7 @@ set(files
__ranges/zip_view.h
__split_buffer
__stacktrace/basic_stacktrace.h
- __stacktrace/alloc.h
- __stacktrace/context.h
- __stacktrace/entry.h
- __stacktrace/to_string.h
+ __stacktrace/impl.h
__stacktrace/stacktrace_entry.h
__std_mbstate_t.h
__stop_token/atomic_unique_lock.h
diff --git a/libcxx/include/__stacktrace/alloc.h b/libcxx/include/__stacktrace/alloc.h
deleted file mode 100644
index 402cf3c371161..0000000000000
--- a/libcxx/include/__stacktrace/alloc.h
+++ /dev/null
@@ -1,97 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_ALLOC
-#define _LIBCPP_STACKTRACE_ALLOC
-
-#include <__config>
-#include <__functional/function.h>
-#include <__memory/allocator_traits.h>
-#include <__memory_resource/memory_resource.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <cstddef>
-#include <list>
-#include <memory>
-#include <string>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-class stacktrace_entry;
-
-namespace __stacktrace {
-
-/** Per-stacktrace-invocation allocator which wraps a caller-provided allocator of any type.
-This is intended to be used with `std::pmr::` containers and strings throughout the stacktrace
-creation process. */
-struct _LIBCPP_HIDDEN alloc final : std::pmr::memory_resource {
- template <class _Allocator>
- _LIBCPP_HIDE_FROM_ABI explicit alloc(_Allocator const& __a) {
- // Take the given allocator type, and rebind with a new type having <byte> as the template arg
- using _AT = std::allocator_traits<_Allocator>;
- using _BA = typename _AT::template rebind_alloc<std::byte>;
- auto __ba = _BA(__a);
- __alloc_func_ = [__ba](size_t __sz) mutable { return __ba.allocate(__sz); };
- __dealloc_func_ = [__ba](void* __ptr, size_t __sz) mutable { return __ba.deallocate((std::byte*)__ptr, __sz); };
- __alloc_ptr = std::addressof(__a);
- }
-
- _LIBCPP_HIDE_FROM_ABI ~alloc() override;
-
- _LIBCPP_HIDE_FROM_ABI_VIRTUAL void* do_allocate(size_t __size, size_t /*__align*/) override {
- return __alloc_func_(__size);
- }
-
- _LIBCPP_HIDE_FROM_ABI_VIRTUAL void do_deallocate(void* __ptr, size_t __size, size_t /*__align*/) override {
- __dealloc_func_((std::byte*)__ptr, __size);
- }
-
- _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
- auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
- return __rhs_ba && (__rhs_ba->__alloc_ptr == __alloc_ptr);
- }
-
- _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
- std::pmr::string __ret{this};
- if (__size) {
- __ret.reserve(__size);
- __ret[0] = 0;
- }
- return __ret;
- }
-
- _LIBCPP_HIDE_FROM_ABI std::pmr::string hex_string(uintptr_t __addr) {
- char __ret[19]; // "0x" + 16 digits + NUL
- auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
- return {__ret, size_t(__size), this};
- }
-
- _LIBCPP_HIDE_FROM_ABI std::pmr::string u64_string(uintptr_t __val) {
- char __ret[21]; // 20 digits max + NUL
- auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
- return {__ret, size_t(__size), this};
- }
-
- template <typename _Tp>
- _LIBCPP_HIDE_FROM_ABI std::pmr::list<_Tp> new_list() {
- return std::pmr::list<_Tp>{this};
- }
-
- _LIBCPP_HIDE_FROM_ABI std::pmr::list<std::pmr::string> new_string_list() { return new_list<std::pmr::string>(); }
-
-private:
- std::function<std::byte*(size_t)> __alloc_func_;
- std::function<void(std::byte*, size_t)> __dealloc_func_;
- /** Only used for checking equality */
- void const* __alloc_ptr;
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_ALLOC
diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h
index 5e2ee1ae1248a..1fe5cfa1b0cf5 100644
--- a/libcxx/include/__stacktrace/basic_stacktrace.h
+++ b/libcxx/include/__stacktrace/basic_stacktrace.h
@@ -10,7 +10,7 @@
#ifndef _LIBCPP_BASIC_STACKTRACE
#define _LIBCPP_BASIC_STACKTRACE
-#include <__stacktrace/entry.h>
+#include <__stacktrace/impl.h>
#include <__stacktrace/stacktrace_entry.h>
#include <__config>
@@ -33,13 +33,7 @@
#include <__vector/swap.h>
#include <__vector/vector.h>
#include <cstddef>
-#include <list>
#include <memory>
-#include <string>
-
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/to_string.h>
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -67,13 +61,7 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
[[no_unique_address]] _Allocator __alloc_;
__entry_vec __entries_;
- _LIBCPP_HIDE_FROM_ABI basic_stacktrace(const _Allocator& __alloc, std::pmr::list<__stacktrace::entry>&& __vec)
- : __alloc_(__alloc), __entries_(__alloc) {
- __entries_.reserve(__vec.size());
- for (auto& __entry : __vec) {
- __entries_.emplace_back(std::move(__entry));
- }
- }
+ // _LIBCPP_HIDE_FROM_ABI basic_stacktrace(const _Allocator& __alloc, std::pmr::list<stacktrace_entry>&& __vec);
public:
// (19.6.4.1)
@@ -96,28 +84,26 @@ class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace {
_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
current(const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
- __stacktrace::alloc __alloc(__caller_alloc);
- __stacktrace::context __tr{__alloc};
- __tr.do_stacktrace(1, /* infinite max_depth */ ~0);
- return {__caller_alloc, std::move(__tr.__entries_)};
+ return current(1, /* no __max_depth */ ~0, __caller_alloc);
}
_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
current(size_type __skip, const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
- __stacktrace::alloc __alloc(__caller_alloc);
- __stacktrace::context __tr{__alloc};
- __tr.do_stacktrace(__skip + 1, /* infinite max_depth */ ~0);
- return {__caller_alloc, std::move(__tr.__entries_)};
+ return current(__skip + 1, /* no __max_depth */ ~0, __caller_alloc);
}
_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI static basic_stacktrace
current(size_type __skip,
size_type __max_depth,
const allocator_type& __caller_alloc = allocator_type()) noexcept(__kNoThrowAlloc) {
+ basic_stacktrace<_Allocator> __ret{__caller_alloc};
__stacktrace::alloc __alloc(__caller_alloc);
- __stacktrace::context __tr{__alloc};
- __tr.do_stacktrace(__skip + 1, __max_depth);
- return {__caller_alloc, std::move(__tr.__entries_)};
+ auto __resize = [&__ret](size_t __sz) { __ret.__entries_.resize(__sz); };
+ auto __assign = [&__ret](size_t __index, stacktrace_entry&& __entry) {
+ __ret.__entries_.at(__index) = std::move(__entry);
+ };
+ __stacktrace::__impl(__skip + 1, __max_depth, __alloc, __resize, __assign);
+ return __ret;
}
_LIBCPP_EXPORTED_FROM_ABI constexpr ~basic_stacktrace() = default;
diff --git a/libcxx/include/__stacktrace/entry.h b/libcxx/include/__stacktrace/entry.h
deleted file mode 100644
index 50853b87cc693..0000000000000
--- a/libcxx/include/__stacktrace/entry.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_DETAIL_ENTRY
-#define _LIBCPP_STACKTRACE_DETAIL_ENTRY
-
-#include <__config>
-#include <cstdint>
-#include <string>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-namespace __stacktrace {
-
-/** Contains fields which will be used to generate the final `std::stacktrace_entry`.
-This is an intermediate object which owns strings allocated via the caller-provided allocator,
-which are later freed back to that allocator and converted to plain `std::string`s. */
-struct _LIBCPP_HIDE_FROM_ABI entry {
- /** Caller's / faulting insn's address, including ASLR/slide */
- uintptr_t __addr_{};
-
- /** the address minus its image's slide offset */
- uintptr_t __addr_unslid_{};
-
- /** entry's description (symbol name) */
- std::pmr::string __desc_{};
-
- /** source file name */
- std::pmr::string __file_{};
-
- /** line number in source file */
- uint32_t __line_{};
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_DETAIL_ENTRY
diff --git a/libcxx/include/__stacktrace/impl.h b/libcxx/include/__stacktrace/impl.h
new file mode 100644
index 0000000000000..7aaec10672baa
--- /dev/null
+++ b/libcxx/include/__stacktrace/impl.h
@@ -0,0 +1,157 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_IMPL
+#define _LIBCPP_STACKTRACE_IMPL
+
+#include <__cstddef/byte.h>
+#include <__cstddef/ptrdiff_t.h>
+#include <__cstddef/size_t.h>
+#include <__format/formatter.h>
+#include <__functional/function.h>
+#include <__functional/hash.h>
+#include <__fwd/format.h>
+#include <__fwd/ostream.h>
+#include <__fwd/sstream.h>
+#include <__fwd/vector.h>
+#include <__iterator/iterator.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/reverse_access.h>
+#include <__iterator/reverse_iterator.h>
+#include <__memory/allocator.h>
+#include <__memory/allocator_traits.h>
+#include <__memory_resource/memory_resource.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__utility/move.h>
+#include <__vector/pmr.h>
+#include <__vector/swap.h>
+#include <__vector/vector.h>
+#include <list>
+#include <string>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Allocator>
+class basic_stacktrace;
+
+class stacktrace_entry;
+
+namespace __stacktrace {
+
+/** Per-stacktrace-invocation allocator which wraps a caller-provided allocator of any type.
+This is intended to be used with `std::pmr::` containers and strings throughout the stacktrace
+creation process. */
+struct _LIBCPP_HIDE_FROM_ABI alloc final : std::pmr::memory_resource {
+ template <class _Allocator>
+ explicit alloc(_Allocator const& __a) {
+ // Take the given allocator type, and rebind with a new type having <byte> as the template arg
+ using _AT = std::allocator_traits<_Allocator>;
+ using _BA = typename _AT::template rebind_alloc<std::byte>;
+ auto __ba = _BA(__a);
+ __alloc_func_ = [__ba](size_t __sz) mutable { return __ba.allocate(__sz); };
+ __dealloc_func_ = [__ba](void* __ptr, size_t __sz) mutable { return __ba.deallocate((std::byte*)__ptr, __sz); };
+ __alloc_ptr_ = std::addressof(__a);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI ~alloc() override = default;
+
+ _LIBCPP_HIDE_FROM_ABI void* do_allocate(size_t __size, size_t /*__align*/) override { return __alloc_func_(__size); }
+
+ _LIBCPP_HIDE_FROM_ABI void do_deallocate(void* __ptr, size_t __size, size_t /*__align*/) override {
+ __dealloc_func_((std::byte*)__ptr, __size);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool do_is_equal(std::pmr::memory_resource const& __rhs) const noexcept override {
+ auto* __rhs_ba = dynamic_cast<alloc const*>(&__rhs);
+ return __rhs_ba && (__rhs_ba->__alloc_ptr_ == __alloc_ptr_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI std::pmr::string new_string(size_t __size = 0) {
+ std::pmr::string __ret{this};
+ if (__size) {
+ __ret.reserve(__size);
+ __ret[0] = 0;
+ }
+ return __ret;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI std::pmr::string hex_string(uintptr_t __addr) {
+ char __ret[19]; // "0x" + 16 digits + NUL
+ auto __size = snprintf(__ret, sizeof(__ret), "0x%016llx", (unsigned long long)__addr);
+ return {__ret, size_t(__size), this};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI std::pmr::string u64_string(uintptr_t __val) {
+ char __ret[21]; // 20 digits max + NUL
+ auto __size = snprintf(__ret, sizeof(__ret), "%zu", __val);
+ return {__ret, size_t(__size), this};
+ }
+
+ template <typename _Tp>
+ _LIBCPP_HIDE_FROM_ABI std::pmr::list<_Tp> new_list() {
+ return std::pmr::list<_Tp>{this};
+ }
+
+ std::pmr::list<std::pmr::string> new_string_list() { return new_list<std::pmr::string>(); }
+
+private:
+ std::function<std::byte*(size_t)> __alloc_func_;
+ std::function<void(std::byte*, size_t)> __dealloc_func_;
+ /** Only used for checking equality */
+ void const* __alloc_ptr_;
+};
+
+/** Contains fields which will be used to generate the final `std::stacktrace_entry`.
+This is an intermediate object which owns strings allocated via the caller-provided allocator,
+which are later freed back to that allocator and converted to plain `std::string`s. */
+struct entry {
+ /** Caller's / faulting insn's address, including ASLR/slide */
+ uintptr_t __addr_{};
+
+ /** the address minus its image's slide offset */
+ uintptr_t __addr_unslid_{};
+
+ /** entry's description (symbol name) */
+ std::pmr::string __desc_{};
+
+ /** source file name */
+ std::pmr::string __file_{};
+
+ /** line number in source file */
+ uint32_t __line_{};
+
+ /* implicit */ operator std::stacktrace_entry();
+};
+
+struct __to_string {
+ _LIBCPP_EXPORTED_FROM_ABI string operator()(stacktrace_entry const& __entry);
+
+ _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
+
+ _LIBCPP_EXPORTED_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
+
+ _LIBCPP_EXPORTED_FROM_ABI string operator()(std::stacktrace_entry const* __entries, size_t __count);
+
+ template <class _Allocator>
+ _LIBCPP_EXPORTED_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
+ return (*this)(__st.__entries_.data(), __st.__entries_.size());
+ }
+};
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+__impl(size_t __skip,
+ size_t __max_depth,
+ alloc& __alloc,
+ std::function<void(size_t)> __resize_func,
+ std::function<void(size_t, std::stacktrace_entry&&)> __assign_func);
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_IMPL
diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h
index e5071655bd0d4..43b10d2a86f65 100644
--- a/libcxx/include/__stacktrace/stacktrace_entry.h
+++ b/libcxx/include/__stacktrace/stacktrace_entry.h
@@ -33,10 +33,12 @@
#include <cstdint>
#include <string>
-#include <__stacktrace/entry.h>
-
_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+struct entry;
+} // namespace __stacktrace
+
class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
public:
// (19.6.3.1) Overview [stacktrace.entry.overview]
@@ -72,10 +74,9 @@ class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry {
return __x.native_handle() <=> __y.native_handle();
}
- _LIBCPP_HIDE_FROM_ABI explicit stacktrace_entry(__stacktrace::entry&& __e)
- : __addr_(__e.__addr_), __desc_(std::move(__e.__desc_)), __file_(std::move(__e.__file_)), __line_(__e.__line_) {}
-
private:
+ friend struct __stacktrace::entry;
+
uintptr_t __addr_{};
std::string __desc_{};
std::string __file_{};
diff --git a/libcxx/include/__stacktrace/to_string.h b/libcxx/include/__stacktrace/to_string.h
deleted file mode 100644
index 1d2c51a7a5c10..0000000000000
--- a/libcxx/include/__stacktrace/to_string.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_TO_STRING
-#define _LIBCPP_STACKTRACE_TO_STRING
-
-#include <__config>
-#include <__fwd/sstream.h>
-#include <__ostream/basic_ostream.h>
-#include <cstddef>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <class _Allocator>
-class _LIBCPP_EXPORTED_FROM_ABI basic_stacktrace;
-
-class _LIBCPP_EXPORTED_FROM_ABI stacktrace_entry;
-
-namespace __stacktrace {
-
-struct _LIBCPP_HIDE_FROM_ABI __to_string {
- _LIBCPP_HIDE_FROM_ABI string operator()(stacktrace_entry const& __entry);
-
- _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, stacktrace_entry const& __entry);
-
- _LIBCPP_HIDE_FROM_ABI void operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count);
-
- string operator()(std::stacktrace_entry const* __entries, size_t __count);
-
- template <class _Allocator>
- _LIBCPP_HIDE_FROM_ABI string operator()(basic_stacktrace<_Allocator> const& __st) {
- return (*this)(__st.__entries_.data(), __st.__entries_.size());
- }
-};
-
-} // namespace __stacktrace
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_TO_STRING
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3313c960f4cd6..dbe50836583a2 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1986,12 +1986,14 @@ module std [system] {
}
module stacktrace {
- module basic_stacktrace { header "__stacktrace/basic_stacktrace.h" }
- module alloc { header "__stacktrace/alloc.h" }
- module context { header "__stacktrace/context.h" }
- module entry { header "__stacktrace/entry.h" }
- module to_string { header "__stacktrace/to_string.h" }
- module stacktrace_entry { header "__stacktrace/stacktrace_entry.h" }
+ module fwd {
+ header "__stacktrace/basic_stacktrace.h"
+ header "__stacktrace/stacktrace_entry.h"
+ }
+ module impl {
+ header "__stacktrace/impl.h"
+ }
+
header "stacktrace"
export *
}
diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace
index 8c7d8f82acbd2..f635208a64d33 100644
--- a/libcxx/include/stacktrace
+++ b/libcxx/include/stacktrace
@@ -181,6 +181,7 @@ _LIBCPP_PUSH_MACROS
# include <__undef_macros>
# include <__stacktrace/basic_stacktrace.h>
+# include <__stacktrace/impl.h>
# include <__stacktrace/stacktrace_entry.h>
_LIBCPP_POP_MACROS
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index dc40af57e573a..7691f7e860655 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -124,6 +124,7 @@ module;
# include <sstream>
#endif
#include <stack>
+#include <stacktrace>
#include <stdexcept>
#include <stop_token>
#if _LIBCPP_HAS_LOCALIZATION
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 5076d1b13758f..1d8d32975a15d 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -40,24 +40,12 @@ set(LIBCXX_SOURCES
ryu/d2fixed.cpp
ryu/d2s.cpp
ryu/f2s.cpp
- stacktrace/alloc.cpp
- stacktrace/common/debug.cpp
- stacktrace/common/fd.cpp
- stacktrace/context.cpp
- stacktrace/linux/linux-dl.cpp
- stacktrace/linux/linux-elf.cpp
- stacktrace/linux/linux-sym.cpp
- stacktrace/osx/osx.cpp
+ stacktrace/linux.cpp
+ stacktrace/osx.cpp
stacktrace/stacktrace.cpp
- stacktrace/tools/addr2line.cpp
- stacktrace/tools/atos.cpp
- stacktrace/tools/llvm_symbolizer.cpp
- stacktrace/tools/toolspawner.cpp
- stacktrace/unwind/unwind.cpp
- stacktrace/windows/dbghelp_dll.cpp
- stacktrace/windows/dll.cpp
- stacktrace/windows/psapi_dll.cpp
- stacktrace/windows/win_impl.cpp
+ stacktrace/tools.cpp
+ stacktrace/unwind.cpp
+ stacktrace/windows.cpp
stdexcept.cpp
string.cpp
support/runtime/exception_fallback.ipp
diff --git a/libcxx/src/stacktrace/alloc.cpp b/libcxx/src/stacktrace/alloc.cpp
deleted file mode 100644
index 43842aa1a5b47..0000000000000
--- a/libcxx/src/stacktrace/alloc.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-
-#include <__stacktrace/alloc.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-alloc::~alloc() {}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/common/debug.cpp b/libcxx/src/stacktrace/common/debug.cpp
deleted file mode 100644
index 4f113e10c0c23..0000000000000
--- a/libcxx/src/stacktrace/common/debug.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "debug.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-debug::~debug() = default;
-
-debug::dummy_ostream::~dummy_ostream() = default;
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/common/debug.h b/libcxx/src/stacktrace/common/debug.h
deleted file mode 100644
index 0cee301d42d8b..0000000000000
--- a/libcxx/src/stacktrace/common/debug.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_DEBUG_H
-#define _LIBCPP_STACKTRACE_DEBUG_H
-
-#include <__config>
-#include <iostream>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/** Debug-message output stream. If `LIBCXX_STACKTRACE_DEBUG` is defined in the environment
-or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
-otherwise its does nothing by returning a dummy stream. */
-struct debug : std::ostream {
- virtual ~debug();
-
- static bool enabled() {
-#if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
- return true;
-#else
- static bool ret = [] {
- auto const* val = getenv("LIBCXX_STACKTRACE_DEBUG");
- return val && !strncmp(val, "1", 1);
- }();
- return ret;
-#endif
- }
-
- /** No-op output stream. */
- struct dummy_ostream final : std::ostream {
- virtual ~dummy_ostream();
- friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
- };
-
- friend std::ostream& operator<<(debug& dp, auto const& val) {
- static dummy_ostream kdummy;
- if (!enabled()) {
- return kdummy;
- }
- std::cerr << val;
- return std::cerr;
- }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_DEBUG_H
diff --git a/libcxx/src/stacktrace/common/failed.h b/libcxx/src/stacktrace/common/failed.h
deleted file mode 100644
index e53eb1123aabc..0000000000000
--- a/libcxx/src/stacktrace/common/failed.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_FAILED_H
-#define _LIBCPP_STACKTRACE_FAILED_H
-
-#include <stdexcept>
-
-// TODO(stacktrace23) Use std::expected instead?
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct failed : std::runtime_error {
- virtual ~failed() = default;
- int errno_{0};
- failed() : std::runtime_error({}) {}
- failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_FAILED_H
diff --git a/libcxx/src/stacktrace/common/fd.cpp b/libcxx/src/stacktrace/common/fd.cpp
deleted file mode 100644
index b7728ea7826dd..0000000000000
--- a/libcxx/src/stacktrace/common/fd.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "fd.h"
-#include "failed.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-fd_streambuf::~fd_streambuf() = default;
-fd_istream::~fd_istream() = default;
-
-int fd_streambuf::underflow() {
- int bytesRead = ::read(fd_, buf_, size_);
- if (bytesRead < 0) {
- throw failed("I/O error reading from child process", errno);
- }
- if (bytesRead == 0) {
- return traits_type::eof();
- }
- setg(buf_, buf_, buf_ + bytesRead);
- return int(*buf_);
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/common/fd.h b/libcxx/src/stacktrace/common/fd.h
deleted file mode 100644
index ff003e16a6bd8..0000000000000
--- a/libcxx/src/stacktrace/common/fd.h
+++ /dev/null
@@ -1,122 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_FD_H
-#define _LIBCPP_STACKTRACE_FD_H
-
-#include <__config>
-#include <cassert>
-#include <cerrno>
-#include <cstdio>
-#include <iostream>
-#include <sys/fcntl.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include <utility>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/** Encapsulates a plain old file descriptor `int`. Avoids copies in order to
-force some component to "own" this, although it's freely convertible back to
-integer form. Default-constructed, closed, and moved-out-of instances will have
-the invalid fd `-1`. */
-class fd {
- int fd_{-1};
-
-public:
- fd() : fd(-1) {}
- fd(int fdint) : fd_(fdint) {}
-
- fd(fd const&) = delete;
- fd& operator=(fd const&) = delete;
-
- fd(fd&& rhs) {
- if (&rhs != this) {
- std::exchange(fd_, rhs.fd_);
- }
- }
-
- fd& operator=(fd&& rhs) { return *new (this) fd(std::move(rhs)); }
-
- ~fd() { close(); }
-
- /** Returns true IFF fd is above zero */
- bool valid() const { return fd_ > 0; }
-
- /* implicit */ operator int() const {
- auto ret = fd_;
- assert(ret > 0);
- return ret;
- }
-
- void close() {
- int fd_old = -1;
- std::exchange(fd_old, fd_);
- if (fd_old != -1) {
- ::close(fd_old);
- }
- }
-
- static fd& null_fd() {
- static fd ret = {::open("/dev/null", O_RDWR)};
- return ret;
- }
-
- static fd open(std::string_view path) {
- fd ret = {::open(path.data(), O_RDONLY)};
- return ret;
- }
-};
-
-/** Wraps a readable fd using the `streambuf` interface. I/O errors arising
-from reading the provided fd will result in a `Failed` being thrown. */
-struct fd_streambuf final : std::streambuf {
- fd& fd_;
- char* buf_;
- size_t size_;
- fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
- virtual ~fd_streambuf();
- int underflow() override;
-};
-
-/** Wraps an `FDInStreamBuffer` in an `istream` */
-struct fd_istream final : std::istream {
- fd_streambuf& buf_;
- virtual ~fd_istream();
- explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
-};
-
-struct fd_mmap final {
- fd fd_{};
- size_t size_{0};
- std::byte const* addr_{nullptr};
-
- explicit fd_mmap(std::string_view path) : fd_mmap(fd::open(path)) {}
-
- explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
- if (fd_) {
- if ((size_ = ::lseek(fd, 0, SEEK_END))) {
- addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
- }
- }
- }
-
- operator bool() const { return addr_; }
-
- ~fd_mmap() {
- if (addr_) {
- ::munmap(const_cast<void*>((void const*)addr_), size_);
- }
- }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_FD_H
diff --git a/libcxx/src/stacktrace/common/images.h b/libcxx/src/stacktrace/common/images.h
deleted file mode 100644
index cb9eac47cf9dd..0000000000000
--- a/libcxx/src/stacktrace/common/images.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_IMAGES_H
-#define _LIBCPP_STACKTRACE_IMAGES_H
-
-#include <cstdint>
-#include <string_view>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-constexpr unsigned k_max_images = 256;
-
-struct image {
- uintptr_t loaded_at_{};
- intptr_t slide_{};
- std::string_view name_{};
- bool is_main_prog_{};
-
- bool operator<(image const& rhs) const { return loaded_at_ < rhs.loaded_at_; }
- operator bool() const { return !name_.empty(); }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_IMAGES_H
diff --git a/libcxx/src/stacktrace/common/config.h b/libcxx/src/stacktrace/config.h
similarity index 100%
rename from libcxx/src/stacktrace/common/config.h
rename to libcxx/src/stacktrace/config.h
diff --git a/libcxx/src/stacktrace/context.cpp b/libcxx/src/stacktrace/context.cpp
deleted file mode 100644
index e2d1445aa4995..0000000000000
--- a/libcxx/src/stacktrace/context.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-
-#include <list>
-
-#include <__stacktrace/basic_stacktrace.h>
-#include <__stacktrace/stacktrace_entry.h>
-
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-#include <__stacktrace/to_string.h>
-
-#include "common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-# include "linux/linux.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_APPLE)
-# include "osx/osx.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-# include "tools/pspawn.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
-# include "unwind/unwind.h"
-#endif
-
-#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
-# include "windows/win_impl.h"
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_HIDDEN void context::do_stacktrace(size_t skip, size_t max_depth) {
- /*
- Here we declare stacktrace components or "backends" which will handle the different tasks:
-
- (1) get the addresses from the call stack
- (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
- (3) resolve using debug info, and possibly with an external tool on the $PATH
- (4+) extra passes to get symbols, in case 3 couldn't
-
- Based on the macros produced by `stacktrace.h`, throw all backends we have available at the task. Ideally the #ifdef
- gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
- symbolizer. If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
-
- All these classes do their best to provide any of the requested fields they can: (symbol, filename, source line),
- substituting if needed with something reasonable. For example, if the source filename and line are not available
- then we will at least report that the address and symbol are in the module `foo.exe`.
-
- These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
- already-populated data. We take care not to crash / abort / throw in any of these, and we'll silently fail. See
- `common/debug.h` for a debugging logger you can enable at runtime.
- */
-
-#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
- win_impl dbghelp{*this};
- auto& collector = dbghelp;
- auto& mod_ident = dbghelp;
- auto& resolver = dbghelp;
- auto& symbolizer = dbghelp;
-#endif
-#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
- unwind unwind{*this};
- auto& collector = unwind;
-#endif
-#if defined(_LIBCPP_STACKTRACE_APPLE)
- osx osx{*this};
- auto& mod_ident = osx;
- auto& symbolizer = osx;
-#endif
-#if defined(_LIBCPP_STACKTRACE_LINUX)
- linux linux{*this};
- auto& mod_ident = linux;
- auto& symbolizer = linux;
-#endif
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
- spawner pspawn{*this};
- auto& resolver = pspawn;
-#endif
-
- collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
- if (__entries_.size()) { // (Can't proceed if empty)
- mod_ident.ident_modules(); // Associate addrs with binaries (ELF/MachO/etc.)
- resolver.resolve_lines(); // Resolve addresses to symbols, filename, linenumber
- symbolizer.symbolize(); // Populate missing symbols, if any.
- }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__stacktrace/context.h b/libcxx/src/stacktrace/context.h
similarity index 93%
rename from libcxx/include/__stacktrace/context.h
rename to libcxx/src/stacktrace/context.h
index 2a23798751c60..585d5ff7ff4d1 100644
--- a/libcxx/include/__stacktrace/context.h
+++ b/libcxx/src/stacktrace/context.h
@@ -16,14 +16,15 @@
#include <__vector/pmr.h>
#include <__vector/vector.h>
#include <cstddef>
+#include <list>
#include <string>
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/entry.h>
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
-struct _LIBCPP_HIDDEN alloc;
+struct alloc;
struct _LIBCPP_HIDDEN entry;
namespace __stacktrace {
diff --git a/libcxx/src/stacktrace/linux/linux-sym.cpp b/libcxx/src/stacktrace/linux.cpp
similarity index 50%
rename from libcxx/src/stacktrace/linux/linux-sym.cpp
rename to libcxx/src/stacktrace/linux.cpp
index b9a8c313e668b..0ad3c276f5cb4 100644
--- a/libcxx/src/stacktrace/linux/linux-sym.cpp
+++ b/libcxx/src/stacktrace/linux.cpp
@@ -6,22 +6,74 @@
//
//===----------------------------------------------------------------------===//
-#include "../common/config.h"
+#include "stacktrace/config.h"
#if defined(_LIBCPP_STACKTRACE_LINUX)
-# include "linux.h"
-
# include <cassert>
# include <dlfcn.h>
+# include <link.h>
+# include <stacktrace>
# include <unistd.h>
-# include <__stacktrace/context.h>
-# include <__stacktrace/entry.h>
+# include "stacktrace/config.h"
+# include "stacktrace/context.h"
+# include "stacktrace/linux.h"
+# include "stacktrace/utils.h"
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
+void linux::ident_modules() {
+ auto& images = images::get();
+
+ // Aside from the left/right sentinels in the array (hence the 2),
+ // are there any other real images?
+ if (images.count_ <= 2) {
+ return;
+ }
+
+ auto mainProg = images.mainProg();
+ if (mainProg) {
+ cx_.__main_prog_path_ = mainProg->name_;
+ }
+
+ unsigned index = 1; // Starts at one, and is moved around in this loop
+ for (auto& entry : cx_.__entries_) {
+ while (images[index].loaded_at_ > entry.__addr_) {
+ --index;
+ }
+ while (images[index + 1].loaded_at_ <= entry.__addr_) {
+ ++index;
+ }
+ entry.__addr_unslid_ = entry.__addr_ - images[index].slide_;
+ entry.__file_ = images[index].name_;
+ }
+}
+
+/**
+When trying to collect a stacktrace under Linux, there are narrow (but still quite common) cases where we will fail
+to resolve symbols. Linux's `dl` doesn't want to read symbols from the non-exported symbol table at runtime,
+and older versions of `addr2line` or `llvm-symbolizer` will also not resolve these.
+
+This implementation the minimum necessary to resolve symbols. It can identify this as an ELF (32 or 64 bits), locate
+the symbol and symbol-string table, and fill in any remaining missing symbols.
+*/
+void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
+ // We can statically initialize these, because main_elf_name should be the same every time.
+ static fd_mmap _mm(main_elf_name);
+ if (_mm) {
+ static elf::ELF _this_elf(_mm.addr_);
+ if (_this_elf) {
+ for (auto& entry : cx_.__entries_) {
+ if (entry.__desc_.empty() && entry.__file_ == main_elf_name) {
+ entry.__desc_ = _this_elf.getSym(entry.__addr_unslid_).name();
+ }
+ }
+ }
+ }
+}
+
bool symbolize_entry(entry& entry) {
bool ret = false;
Dl_info info;
diff --git a/libcxx/src/stacktrace/linux/elf.h b/libcxx/src/stacktrace/linux.h
similarity index 82%
rename from libcxx/src/stacktrace/linux/elf.h
rename to libcxx/src/stacktrace/linux.h
index 214e8cc5a7819..fd337c0adc422 100644
--- a/libcxx/src/stacktrace/linux/elf.h
+++ b/libcxx/src/stacktrace/linux.h
@@ -6,21 +6,94 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_STACKTRACE_ELF_H
-#define _LIBCPP_STACKTRACE_ELF_H
-
-#include <cstddef>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <functional>
-#include <string_view>
-#include <sys/fcntl.h>
-#include <unistd.h>
+#ifndef _LIBCPP_STACKTRACE_LINUX_H
+#define _LIBCPP_STACKTRACE_LINUX_H
+
+#include "stacktrace/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+
+# include <algorithm>
+# include <array>
+# include <cassert>
+# include <cstddef>
+# include <cstdlib>
+# include <functional>
+# include <link.h>
+# include <stacktrace>
+# include <string_view>
+# include <unistd.h>
+
+# include "stacktrace/config.h"
+# include "stacktrace/context.h"
+# include "stacktrace/utils.h"
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
+struct context;
+
+struct linux {
+ context& cx_;
+ void ident_modules();
+ void symbolize();
+
+private:
+ void resolve_main_elf_syms(std::string_view elf_name);
+};
+
+struct images {
+ // How many images this contains, including the left/right sentinels.
+ unsigned count_{0};
+ std::array<image, k_max_images + 2> images_{};
+
+ int add(dl_phdr_info& info) {
+ assert(count_ < k_max_images);
+ auto isFirst = (count_ == 0);
+ auto& image = images_.at(count_++);
+ image.loaded_at_ = info.dlpi_addr;
+ image.slide_ = info.dlpi_addr;
+ image.name_ = info.dlpi_name;
+ image.is_main_prog_ = isFirst; // first one is the main ELF
+ if (image.name_.empty() && isFirst) {
+ static char buffer[PATH_MAX + 1];
+ uint32_t length = sizeof(buffer);
+ if (readlink("/proc/self/exe", buffer, length) > 0) {
+ image.name_ = buffer;
+ }
+ }
+ return count_ == k_max_images; // return nonzero if we're at the limit
+ }
+
+ static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
+
+ images() {
+ dl_iterate_phdr(images::callback, this);
+ images_[count_++] = {0uz, 0}; // sentinel at low end
+ images_[count_++] = {~0uz, 0}; // sentinel at high end
+ std::sort(images_.begin(), images_.begin() + count_ - 1);
+ }
+
+ image& operator[](size_t index) {
+ assert(index < count_);
+ return images_.at(index);
+ }
+
+ image* mainProg() {
+ for (auto& image : images_) {
+ if (image.is_main_prog_) {
+ return ℑ
+ }
+ }
+ return nullptr;
+ }
+
+ static images& get() {
+ static images images;
+ return images;
+ }
+};
+
// Includes ELF constants and structs copied from <elf.h>, with a few changes.
namespace elf {
@@ -312,4 +385,6 @@ inline std::string_view Symbol::name() const { return elf_->strtab_.at(nameIndex
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#endif
+#endif // _LIBCPP_STACKTRACE_LINUX
+
+#endif // _LIBCPP_STACKTRACE_LINUX_H
diff --git a/libcxx/src/stacktrace/linux/linux-dl.cpp b/libcxx/src/stacktrace/linux/linux-dl.cpp
deleted file mode 100644
index e15403f7d3a50..0000000000000
--- a/libcxx/src/stacktrace/linux/linux-dl.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-# include "linux.h"
-
-# include <cassert>
-# include <dlfcn.h>
-# include <link.h>
-# include <unistd.h>
-
-# include "../common/images.h"
-
-# include <__stacktrace/context.h>
-# include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-void linux::ident_modules() {
- auto& images = images::get();
-
- // Aside from the left/right sentinels in the array (hence the 2),
- // are there any other real images?
- if (images.count_ <= 2) {
- return;
- }
-
- auto mainProg = images.mainProg();
- if (mainProg) {
- cx_.__main_prog_path_ = mainProg->name_;
- }
-
- unsigned index = 1; // Starts at one, and is moved around in this loop
- for (auto& entry : cx_.__entries_) {
- while (images[index].loaded_at_ > entry.__addr_) {
- --index;
- }
- while (images[index + 1].loaded_at_ <= entry.__addr_) {
- ++index;
- }
- entry.__addr_unslid_ = entry.__addr_ - images[index].slide_;
- entry.__file_ = images[index].name_;
- }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/stacktrace/linux/linux-elf.cpp b/libcxx/src/stacktrace/linux/linux-elf.cpp
deleted file mode 100644
index 2cbeade56534b..0000000000000
--- a/libcxx/src/stacktrace/linux/linux-elf.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-# include "linux.h"
-
-# include <cassert>
-# include <unistd.h>
-
-# include <__stacktrace/context.h>
-# include <__stacktrace/entry.h>
-
-# include "../common/fd.h"
-# include "elf.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-/**
-When trying to collect a stacktrace under Linux, there are narrow (but still quite common) cases where we will fail
-to resolve symbols. Linux's `dl` doesn't want to read symbols from the non-exported symbol table at runtime,
-and older versions of `addr2line` or `llvm-symbolizer` will also not resolve these.
-
-This implementation the minimum necessary to resolve symbols. It can identify this as an ELF (32 or 64 bits), locate
-the symbol and symbol-string table, and fill in any remaining missing symbols.
-*/
-void linux::resolve_main_elf_syms(std::string_view main_elf_name) {
- // We can statically initialize these, because main_elf_name should be the same every time.
- static fd_mmap _mm(main_elf_name);
- if (_mm) {
- static elf::ELF _this_elf(_mm.addr_);
- if (_this_elf) {
- for (auto& entry : cx_.__entries_) {
- if (entry.__desc_.empty() && entry.__file_ == main_elf_name) {
- entry.__desc_ = _this_elf.getSym(entry.__addr_unslid_).name();
- }
- }
- }
- }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
diff --git a/libcxx/src/stacktrace/linux/linux.h b/libcxx/src/stacktrace/linux/linux.h
deleted file mode 100644
index 52b0377bcb144..0000000000000
--- a/libcxx/src/stacktrace/linux/linux.h
+++ /dev/null
@@ -1,98 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_LINUX_H
-#define _LIBCPP_STACKTRACE_LINUX_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_LINUX)
-
-# include <algorithm>
-# include <array>
-# include <cassert>
-# include <cstddef>
-# include <cstdlib>
-# include <link.h>
-# include <string_view>
-# include <unistd.h>
-
-# include "../common/images.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct context;
-
-struct linux {
- context& cx_;
- void ident_modules();
- void symbolize();
-
-private:
- void resolve_main_elf_syms(std::string_view elf_name);
-};
-
-struct images {
- // How many images this contains, including the left/right sentinels.
- unsigned count_{0};
- std::array<image, k_max_images + 2> images_{};
-
- int add(dl_phdr_info& info) {
- assert(count_ < k_max_images);
- auto isFirst = (count_ == 0);
- auto& image = images_.at(count_++);
- image.loaded_at_ = info.dlpi_addr;
- image.slide_ = info.dlpi_addr;
- image.name_ = info.dlpi_name;
- image.is_main_prog_ = isFirst; // first one is the main ELF
- if (image.name_.empty() && isFirst) {
- static char buffer[PATH_MAX + 1];
- uint32_t length = sizeof(buffer);
- if (readlink("/proc/self/exe", buffer, length) > 0) {
- image.name_ = buffer;
- }
- }
- return count_ == k_max_images; // return nonzero if we're at the limit
- }
-
- static int callback(dl_phdr_info* info, size_t, void* self) { return (*(images*)(self)).add(*info); }
-
- images() {
- dl_iterate_phdr(images::callback, this);
- images_[count_++] = {0uz, 0}; // sentinel at low end
- images_[count_++] = {~0uz, 0}; // sentinel at high end
- std::sort(images_.begin(), images_.begin() + count_ - 1);
- }
-
- image& operator[](size_t index) {
- assert(index < count_);
- return images_.at(index);
- }
-
- image* mainProg() {
- for (auto& image : images_) {
- if (image.is_main_prog_) {
- return ℑ
- }
- }
- return nullptr;
- }
-
- static images& get() {
- static images images;
- return images;
- }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_LINUX
-
-#endif // _LIBCPP_STACKTRACE_LINUX_H
diff --git a/libcxx/src/stacktrace/osx/osx.cpp b/libcxx/src/stacktrace/osx.cpp
similarity index 92%
rename from libcxx/src/stacktrace/osx/osx.cpp
rename to libcxx/src/stacktrace/osx.cpp
index f377c0bded38a..39e4d34b3e1c3 100644
--- a/libcxx/src/stacktrace/osx/osx.cpp
+++ b/libcxx/src/stacktrace/osx.cpp
@@ -6,20 +6,23 @@
//
//===----------------------------------------------------------------------===//
-#include "../common/config.h"
+#include "stacktrace/config.h"
#if defined(_LIBCPP_STACKTRACE_APPLE)
+# include "stacktrace/osx.h"
+
# include <algorithm>
# include <array>
# include <dlfcn.h>
# include <mach-o/dyld.h>
# include <mach-o/loader.h>
-# include <__stacktrace/context.h>
-# include <__stacktrace/entry.h>
-
-# include "osx.h"
+# include "stacktrace/config.h"
+# include "stacktrace/context.h"
+# include "stacktrace/utils.h"
+# include <__stacktrace/basic_stacktrace.h>
+# include <__stacktrace/stacktrace_entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/stacktrace/osx/osx.h b/libcxx/src/stacktrace/osx.h
similarity index 100%
rename from libcxx/src/stacktrace/osx/osx.h
rename to libcxx/src/stacktrace/osx.h
diff --git a/libcxx/src/stacktrace/stacktrace.cpp b/libcxx/src/stacktrace/stacktrace.cpp
index 98152c0c323f6..e3e2a0e1d73c2 100644
--- a/libcxx/src/stacktrace/stacktrace.cpp
+++ b/libcxx/src/stacktrace/stacktrace.cpp
@@ -10,26 +10,63 @@
#include <__config_site>
#include <iomanip>
+#include <ios>
#include <iostream>
#include <sstream>
+#include <stacktrace>
#include <string>
-#include <__stacktrace/basic_stacktrace.h>
-#include <__stacktrace/stacktrace_entry.h>
+#include "stacktrace/config.h"
+#include "stacktrace/context.h"
+#include "stacktrace/utils.h"
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-#include <__stacktrace/to_string.h>
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+# include "stacktrace/linux.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+# include "stacktrace/osx.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+# include "stacktrace/tools.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+# include "stacktrace/unwind.h"
+#endif
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+# include "stacktrace/windows.h"
+#endif
_LIBCPP_BEGIN_NAMESPACE_STD
+
+// `to_string`-related non-member functions
+
+_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
+ return __stacktrace::__to_string()(__entry);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
+ __stacktrace::__to_string()(__os, __entry);
+ return __os;
+}
+
namespace __stacktrace {
+/* Anchors for classes w/ vtables */
+// alloc::~alloc() = default;
+debug::~debug() = default;
+debug::dummy_ostream::~dummy_ostream() = default;
+fd_streambuf::~fd_streambuf() = default;
+fd_istream::~fd_istream() = default;
+
/*
* `to_string` Helpers
*/
-void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
+_LIBCPP_EXPORTED_FROM_ABI void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry) {
// Although 64-bit addresses are 16 nibbles long, they're often <= 0x7fff_ffff_ffff
constexpr static int __k_addr_width = (sizeof(void*) > 4) ? 12 : 8;
@@ -45,7 +82,8 @@ void __to_string::operator()(ostream& __os, std::stacktrace_entry const& entry)
}
}
-void __to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
+_LIBCPP_EXPORTED_FROM_ABI void
+__to_string::operator()(ostream& __os, std::stacktrace_entry const* __entries, size_t __count) {
/*
* Print each entry as a line, as per `operator()`, with additional whitespace
* at the start of the line, and only a newline added at the end:
@@ -66,29 +104,110 @@ void __to_string::operator()(ostream& __os, std::stacktrace_entry const* __entri
}
}
-string __to_string::operator()(std::stacktrace_entry const& entry) {
+_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const& entry) {
stringstream __ss;
(*this)(__ss, entry);
return __ss.str();
}
-string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
+_LIBCPP_EXPORTED_FROM_ABI string __to_string::operator()(std::stacktrace_entry const* __entries, size_t __count) {
stringstream __ss;
(*this)(__ss, __entries, __count);
return __ss.str();
}
-} // namespace __stacktrace
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void
+__impl(size_t skip,
+ size_t max_depth,
+ alloc& alloc,
+ std::function<void(size_t)> resize_func,
+ std::function<void(size_t, std::stacktrace_entry&&)> assign_func) {
+ context cx{alloc};
+ cx.do_stacktrace(1 + skip, max_depth);
+ resize_func(cx.__entries_.size());
+ size_t i = 0;
+ for (auto& entry : cx.__entries_) {
+ assign_func(i++, entry);
+ }
+}
-// `to_string`-related non-member functions
+entry::operator std::stacktrace_entry() {
+ std::stacktrace_entry __ret;
+ __ret.__addr_ = __addr_;
+ __ret.__desc_ = __desc_;
+ __ret.__file_ = __file_;
+ __ret.__line_ = __line_;
+ return __ret;
+}
-_LIBCPP_EXPORTED_FROM_ABI string to_string(const stacktrace_entry& __entry) {
- return __stacktrace::__to_string()(__entry);
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void context::do_stacktrace(size_t skip, size_t max_depth) {
+ /*
+ Here we declare stacktrace components or "backends" which will handle the different tasks:
+
+ (1) get the addresses from the call stack
+ (2) identify program images in process virtual space (program binary, plus modules, shared/dynamic libs)
+ (3) resolve using debug info, and possibly with an external tool on the $PATH
+ (4+) extra passes to get symbols, in case 3 couldn't
+
+ Based on the macros produced by `stacktrace.h`, throw all backends we have available at the task. Ideally the #ifdef
+ gauntlet below should result in one of each of the above functions: (1) collector, (2) mod_ident, (3) resolver, (4)
+ symbolizer. If any are missing or duplicated that is still fine; we work with zero or all the available utilities.
+
+ All these classes do their best to provide any of the requested fields they can: (symbol, filename, source line),
+ substituting if needed with something reasonable. For example, if the source filename and line are not available
+ then we will at least report that the address and symbol are in the module `foo.exe`.
+
+ These components should also tolerate: missing data, weirdly-formatted data (e.g. from the external tools), or even
+ already-populated data. We take care not to crash / abort / throw in any of these, and we'll silently fail. See
+ `common/debug.h` for a debugging logger you can enable at runtime.
+ */
+
+#if defined(_LIBCPP_STACKTRACE_USE_DBGHELP)
+ win_impl dbghelp{*this};
+ auto& collector = dbghelp;
+ auto& mod_ident = dbghelp;
+ auto& resolver = dbghelp;
+ auto& symbolizer = dbghelp;
+#endif
+#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
+ unwind unwind{*this};
+ auto& collector = unwind;
+#endif
+#if defined(_LIBCPP_STACKTRACE_APPLE)
+ osx osx{*this};
+ auto& mod_ident = osx;
+ auto& symbolizer = osx;
+#endif
+#if defined(_LIBCPP_STACKTRACE_LINUX)
+ linux linux{*this};
+ auto& mod_ident = linux;
+ auto& symbolizer = linux;
+#endif
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+ spawner pspawn{*this};
+ auto& resolver = pspawn;
+#endif
+
+ collector.collect(skip + 1, max_depth); // First get the instruction addresses, populate __entries_
+ if (__entries_.size()) { // (Can't proceed if empty)
+ mod_ident.ident_modules(); // Associate addrs with binaries (ELF/MachO/etc.)
+ resolver.resolve_lines(); // Resolve addresses to symbols, filename, linenumber
+ symbolizer.symbolize(); // Populate missing symbols, if any.
+ }
}
-_LIBCPP_EXPORTED_FROM_ABI ostream& operator<<(ostream& __os, const stacktrace_entry& __entry) {
- __stacktrace::__to_string()(__os, __entry);
- return __os;
+int fd_streambuf::underflow() {
+ int bytesRead = ::read(fd_, buf_, size_);
+ if (bytesRead < 0) {
+ throw failed("I/O error reading from child process", errno);
+ }
+ if (bytesRead == 0) {
+ return traits_type::eof();
+ }
+ setg(buf_, buf_, buf_ + bytesRead);
+ return int(*buf_);
}
+} // namespace __stacktrace
+
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools.cpp b/libcxx/src/stacktrace/tools.cpp
new file mode 100644
index 0000000000000..05f14d2f08575
--- /dev/null
+++ b/libcxx/src/stacktrace/tools.cpp
@@ -0,0 +1,437 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "stacktrace/config.h"
+
+#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
+
+# include <__config>
+# include <__config_site>
+# include <cassert>
+# include <cerrno>
+# include <csignal>
+# include <cstddef>
+# include <cstdlib>
+# include <spawn.h>
+# include <sys/fcntl.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+
+# include "stacktrace/context.h"
+# include "stacktrace/tools.h"
+# include "stacktrace/utils.h"
+# include <__stacktrace/basic_stacktrace.h>
+# include <__stacktrace/stacktrace_entry.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+/**
+Returns a list of `tool` objects that can be tried during the first stacktrace operation.
+The result is an array of `tool*` pointers with a final `nullptr` terminator.
+
+The returned tool array pointer is saved during `resolve_lines`'s static init,
+with the first working tool being used from that point forward.
+
+This will first add any tools specified by these defines (in order):
+- LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH
+- LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH
+- LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH
+
+Then any tools specified by the environment variables of the same names (and added in the same order).
+
+Finally, this adds the tools `llvm-symbolizer`, `addr2line`, and `atos`, in that order.
+These tools won't have absolute paths, so $PATH will be searched for these.
+
+Called by `findWorkingTool` during its static initialization, therefore this is used in a threadsafe way.
+*/
+tool const** toolList() {
+ constexpr static size_t kMax = 20;
+ static tool const* array_[kMax];
+ size_t count = 0;
+
+ auto add = [&](tool* t) {
+ assert(count < kMax - 1);
+ array_[count++] = t;
+ };
+
+# define STRINGIFY0(x) #x
+# define STRINGIFY(x) STRINGIFY0(x)
+
+# if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
+ {
+ static llvm_symbolizer t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)};
+ add(&t);
+ }
+# endif
+# if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
+ {
+ static addr2line t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)};
+ add(&t);
+ }
+# endif
+# if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
+ {
+ static atos t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)};
+ add(&t);
+ }
+# endif
+
+ if (getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")) {
+ static llvm_symbolizer t{getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")};
+ add(&t);
+ }
+ if (getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")) {
+ static addr2line t{getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")};
+ add(&t);
+ }
+ if (getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")) {
+ static atos t{getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")};
+ add(&t);
+ }
+
+ {
+ static llvm_symbolizer t;
+ add(&t);
+ }
+ {
+ static addr2line t;
+ add(&t);
+ }
+ {
+ static atos t;
+ add(&t);
+ }
+
+ array_[count] = nullptr; // terminator
+ return array_;
+}
+
+// Try a handful of different addr2line-esque tools, returning the first discovered, or else nullptr
+tool const* findWorkingTool(auto& trace) {
+ // Try each of these programs and stop at the first one that works.
+ // There's no synchronization so this is subject to races on setting `prog`,
+ // but as long as one thread ends up setting it to something that works, we're good.
+ // Programs to try ($PATH is searched):
+ auto* it = toolList();
+ tool const* prog;
+ while ((prog = *it++)) {
+ pspawn test{*prog}; // Just try to run with "--help"
+ try {
+ std::pmr::list<std::pmr::string> testArgs{&trace.__alloc_};
+ testArgs.push_back(prog->progName_);
+ testArgs.push_back("--help");
+ test.fa_.redirectInNull();
+ test.fa_.redirectOutNull();
+ test.fa_.redirectErrNull();
+ test.spawn(testArgs);
+ if (test.wait() == 0) {
+ // Success
+ return prog;
+ }
+ } catch (failed const&) {
+ /* ignore during probe attempt */
+ }
+ }
+ return nullptr;
+}
+
+} // namespace
+
+void spawner::resolve_lines() {
+ // The address-to-line tool that worked (after lazy-setting, below)
+ static tool const* prog{nullptr};
+ // Whether we should not attempt because tool detection previously failed.
+ static bool fail{false};
+
+ // If this previously failed, don't try again.
+ if (fail) {
+ return;
+ }
+
+ if (!prog) {
+ prog = findWorkingTool(cx_);
+ if (!prog) {
+ fail = true;
+ return;
+ }
+ }
+ assert(prog);
+
+ char buf[256];
+ pspawn_tool proc(*prog, cx_, buf, sizeof(buf));
+ try {
+ proc.run();
+ } catch (failed const& failed) {
+ debug() << failed.what();
+ if (failed.errno_) {
+ debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
+ }
+ debug() << '\n';
+ }
+}
+
+// TODO(stacktrace23): possible to link against `libLLVMSymbolize.a`, or some shared obj at runtime (does that exist?)
+
+std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
+ auto& alloc = cx.__alloc_;
+ auto ret = alloc.new_string_list();
+ ret.push_back(progName_);
+ ret.push_back("--demangle");
+ ret.push_back("--no-inlines");
+ ret.push_back("--verbose");
+ ret.push_back("--relativenames");
+ ret.push_back("--functions=short");
+ for (auto& entry : cx.__entries_) {
+ auto addr_string = alloc.hex_string(entry.__addr_unslid_);
+ debug() << "@@@ " << addr_string << " " << entry.__file_ << " " << entry.__file_.empty() << '\n';
+ if (!entry.__file_.empty()) {
+ auto arg = alloc.new_string(entry.__file_.size() + 40);
+ arg += "FILE:";
+ arg += entry.__file_;
+ arg += " ";
+ arg += addr_string;
+ ret.push_back(arg);
+ } else {
+ ret.push_back(addr_string);
+ }
+ }
+ return ret;
+}
+
+void llvm_symbolizer::parseOutput(context& cx, entry& entry, std::istream& output) const {
+ // clang-format off
+/*
+With "--verbose", parsing is a little easier, or at least, more reliable;
+probably the best solution (until we have a JSON parser).
+Example output, verbatim, between the '---' lines:
+---
+test1<test_alloc<std::__1::stacktrace_entry> >
+ Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+ Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
+ Function start line: 114
+ Function start address: 0x8dd0
+ Line: 116
+ Column: 14
+
+---
+Note that this includes an extra empty line as a terminator.
+*/
+ // clang-format on
+
+ auto& alloc = cx.__alloc_;
+ auto line = alloc.new_string(256);
+ std::string_view tmp;
+ while (true) {
+ std::getline(output, line);
+ while (isspace(line.back())) {
+ line.pop_back();
+ }
+ if (line.empty()) {
+ return;
+ }
+ if (!line.starts_with(" ")) {
+ // The symbol has no leading whitespace, while the other
+ // lines with "fields" like line, column, filename, etc.
+ // start with two spaces.
+ if (line != "??") {
+ entry.__desc_ = line;
+ }
+ } else if (line.starts_with(" Filename:")) {
+ tmp = line;
+ tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+ if (tmp != "??") {
+ entry.__file_ = tmp;
+ }
+ } else if (line.starts_with(" Line:")) {
+ tmp = line;
+ tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
+ if (tmp != "??" && tmp != "0") {
+ uint32_t lineno = 0;
+ auto pos = 0;
+ while (isdigit(tmp[pos])) {
+ lineno = lineno * 10 + (tmp[pos++] - '0');
+ }
+ entry.__line_ = lineno;
+ }
+ }
+ }
+}
+
+std::pmr::list<std::pmr::string> addr2line::buildArgs(context& cx) const {
+ auto& alloc = cx.__alloc_;
+ auto ret = alloc.new_string_list();
+ if (cx.__main_prog_path_.empty()) {
+ // Should not have reached here but be graceful anyway
+ ret.push_back("/bin/false");
+ return ret;
+ }
+
+ ret.push_back(progName_);
+ ret.push_back("--functions");
+ ret.push_back("--demangle");
+ ret.push_back("--basenames");
+ ret.push_back("--pretty-print"); // This "human-readable form" is easier to parse
+ ret.push_back("-e");
+ ret.push_back(cx.__main_prog_path_);
+ for (auto& entry : cx.__entries_) {
+ ret.push_back(alloc.hex_string(entry.__addr_unslid_));
+ }
+ return ret;
+}
+
+void addr2line::parseOutput(context& trace, entry& entry, std::istream& output) const {
+ // clang-format off
+/*
+Example:
+--
+llvm-addr2line -e foo --functions --demangle --basenames --pretty-print --no-inlines 0x11a0 0x1120 0x3d58 0x1284
+
+Output: (1 line per input address)
+--
+main at foo.cc:15
+register_tm_clones at crtstuff.c:0
+GCC_except_table2 at foo.cc:0
+test::Foo::Foo(int) at foo.cc:11
+*/
+ // clang-format on
+
+ std::pmr::string line{&trace.__alloc_};
+ std::getline(output, line);
+ while (isspace(line.back())) {
+ line.pop_back();
+ }
+ if (line.empty()) {
+ return;
+ }
+ // Split at the sequence " at ". Barring weird symbols
+ // having " at " in them, this should work.
+ auto sepIndex = line.find(" at ");
+ if (sepIndex == std::string::npos) {
+ return;
+ }
+ if (sepIndex > 0) {
+ entry.__desc_ = line.substr(0, sepIndex);
+ }
+ auto fileBegin = sepIndex + 4;
+ if (fileBegin >= line.size()) {
+ return;
+ }
+ auto fileline = line.substr(fileBegin);
+ auto colon = fileline.find_last_of(":");
+ if (colon > 0 && !fileline.starts_with("?")) {
+ entry.__file_ = fileline.substr(0, colon);
+ }
+
+ if (colon == std::string::npos) {
+ return;
+ }
+ uint32_t lineno = 0;
+ auto pos = colon;
+ while (isdigit(fileline[++pos])) {
+ lineno = lineno * 10 + (fileline[pos] - '0');
+ }
+ entry.__line_ = lineno;
+}
+
+std::pmr::list<std::pmr::string> atos::buildArgs(context& cx) const {
+ auto& alloc = cx.__alloc_;
+ auto ret = alloc.new_string_list();
+ ret.push_back(progName_);
+ ret.push_back("-p");
+ ret.push_back(alloc.u64_string(getpid()));
+ // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
+ // ret.push_back("--fullPath");
+ for (auto& entry : cx.__entries_) {
+ ret.push_back(alloc.hex_string(entry.__addr_));
+ }
+ return ret;
+}
+
+void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
+ // Simple example:
+ //
+ // main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+ //
+ // Assuming this is always atos's format (except when it returns empty lines)
+ // we can split the string like so:
+ //
+ // main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
+ // ^^^^-----^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^-
+ // sym module filename line
+ //
+ // Note that very strange filenames or module names can confuse this.
+ // We'll do the best we can for a decent result, while definitely ensuring safety
+ // (i.e. careful with our bound-checking).
+ //
+ // Another more interesting example (with an added newline for legibility):
+ //
+ // std::__1::basic_ios<char, std::__1::char_traits<char>>::fill[abi:ne190107]() const (in testprog)
+ // (/opt/homebrew/Cellar/llvm/19.1.7_1/bin/../include/c++/v1/ios:0
+ //
+ // If this more or less fits our expected format we'll take these data,
+ // even if the line number is 0.
+
+ auto line = cx.__alloc_.new_string(256);
+ std::getline(output, line);
+ while (isspace(line.back())) {
+ line.pop_back();
+ }
+ if (line.empty()) {
+ return;
+ }
+ auto buf = line.data();
+ auto size = line.size();
+
+ auto* end = buf + size;
+ auto* symEnd = strstr(buf, " (in ");
+ if (!symEnd) {
+ return;
+ }
+ auto* modBegin = symEnd + 5;
+ auto* modEnd = strstr(modBegin, ") (");
+ if (!modEnd) {
+ return;
+ }
+ auto* fileBegin = modEnd + 3; // filename starts just after that
+ if (fileBegin >= end) {
+ return;
+ }
+ auto const* lastColon = fileBegin; // we'll search for last colon after filename
+ char const* nextColon;
+ while ((nextColon = strstr(lastColon + 1, ":"))) { // skip colons in filename (e.g. in "C:\foo.cpp")
+ lastColon = nextColon;
+ }
+
+ std::string_view sym{buf, size_t(symEnd - buf)};
+ // In case a previous step could not obtain the symbol name,
+ // we have the name provided by atos; only use that if we have no symbol
+ // (no need to copy more strings otherwise).
+ if (entry.__desc_.empty() && !sym.empty()) {
+ entry.__desc_ = sym;
+ }
+
+ std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
+ if (file != "?" && file != "??" && !file.empty()) {
+ entry.__file_ = file;
+ }
+
+ unsigned lineno = 0;
+ for (auto* digit = lastColon + 1; digit < end && isdigit(*digit); ++digit) {
+ lineno = (lineno * 10) + unsigned(*digit - '0');
+ }
+ entry.__line_ = lineno;
+}
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
diff --git a/libcxx/src/stacktrace/tools/pspawn.h b/libcxx/src/stacktrace/tools.h
similarity index 60%
rename from libcxx/src/stacktrace/tools/pspawn.h
rename to libcxx/src/stacktrace/tools.h
index 4e2fa381083ad..b3e6e7ec22c30 100644
--- a/libcxx/src/stacktrace/tools/pspawn.h
+++ b/libcxx/src/stacktrace/tools.h
@@ -6,40 +6,70 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_STACKTRACE_PSPAWN_H
-#define _LIBCPP_STACKTRACE_PSPAWN_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-
-# include <__config>
-# include <__config_site>
-# include <cassert>
-# include <cerrno>
-# include <csignal>
-# include <cstddef>
-# include <cstdlib>
-# include <list>
-# include <spawn.h>
-# include <string>
-# include <sys/fcntl.h>
-# include <sys/types.h>
-# include <sys/wait.h>
-# include <unistd.h>
-# include <vector>
-
-# include <__stacktrace/context.h>
-# include <__stacktrace/entry.h>
-
-# include "../common/debug.h"
-# include "../common/failed.h"
-# include "../common/fd.h"
-# include "tools.h"
+#ifndef _LIBCPP_STACKTRACE_TOOLS_H
+#define _LIBCPP_STACKTRACE_TOOLS_H
+
+#include <__config>
+#include <__config_site>
+#include <cassert>
+#include <cerrno>
+#include <csignal>
+#include <cstddef>
+#include <cstdlib>
+#include <list>
+#include <spawn.h>
+#include <string>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <vector>
+
+#include "stacktrace/context.h"
+#include "stacktrace/utils.h"
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
+struct tool {
+ char const* progName_;
+ explicit tool(char const* progName) : progName_(progName) {}
+ constexpr virtual ~tool() = default;
+
+ /** Construct complete `argv` for the spawned process.
+ Includes the program name at argv[0], followed by flags */
+ virtual std::pmr::list<std::pmr::string> buildArgs(context& trace) const = 0;
+
+ /** Parse line(s) output by the tool, and modify `entry`. */
+ virtual void parseOutput(context& trace, entry& e, std::istream& output) const = 0;
+};
+
+struct llvm_symbolizer : tool {
+ constexpr virtual ~llvm_symbolizer() = default;
+ llvm_symbolizer() : llvm_symbolizer("llvm-symbolizer") {}
+ explicit llvm_symbolizer(char const* progName) : tool{progName} {}
+ std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+ void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
+struct addr2line : tool {
+ constexpr virtual ~addr2line() = default;
+ addr2line() : addr2line("addr2line") {}
+ explicit addr2line(char const* progName) : tool{progName} {}
+ std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+ void parseOutput(context& trace, entry& e, std::istream& stream) const override;
+};
+
+struct atos : tool {
+ constexpr virtual ~atos() = default;
+ atos() : atos("atos") {}
+ explicit atos(char const* progName) : tool{progName} {}
+ std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
+ void parseOutput(context& trace, entry& entry, std::istream& output) const override;
+};
+
struct file_actions {
posix_spawn_file_actions_t fa_;
@@ -158,6 +188,4 @@ struct spawner {
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#endif
-
-#endif // _LIBCPP_STACKTRACE_PSPAWN_H
+#endif // _LIBCPP_STACKTRACE_TOOLS_H
diff --git a/libcxx/src/stacktrace/tools/addr2line.cpp b/libcxx/src/stacktrace/tools/addr2line.cpp
deleted file mode 100644
index c1d36e83f13a6..0000000000000
--- a/libcxx/src/stacktrace/tools/addr2line.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <iostream>
-
-#include "tools.h"
-
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-#include <string>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-std::pmr::list<std::pmr::string> addr2line::buildArgs(context& cx) const {
- auto& alloc = cx.__alloc_;
- auto ret = alloc.new_string_list();
- if (cx.__main_prog_path_.empty()) {
- // Should not have reached here but be graceful anyway
- ret.push_back("/bin/false");
- return ret;
- }
-
- ret.push_back(progName_);
- ret.push_back("--functions");
- ret.push_back("--demangle");
- ret.push_back("--basenames");
- ret.push_back("--pretty-print"); // This "human-readable form" is easier to parse
- ret.push_back("-e");
- ret.push_back(cx.__main_prog_path_);
- for (auto& entry : cx.__entries_) {
- ret.push_back(alloc.hex_string(entry.__addr_unslid_));
- }
- return ret;
-}
-
-void addr2line::parseOutput(context& trace, entry& entry, std::istream& output) const {
- // clang-format off
-/*
-Example:
---
-llvm-addr2line -e foo --functions --demangle --basenames --pretty-print --no-inlines 0x11a0 0x1120 0x3d58 0x1284
-
-Output: (1 line per input address)
---
-main at foo.cc:15
-register_tm_clones at crtstuff.c:0
-GCC_except_table2 at foo.cc:0
-test::Foo::Foo(int) at foo.cc:11
-*/
- // clang-format on
-
- std::pmr::string line{&trace.__alloc_};
- std::getline(output, line);
- while (isspace(line.back())) {
- line.pop_back();
- }
- if (line.empty()) {
- return;
- }
- // Split at the sequence " at ". Barring weird symbols
- // having " at " in them, this should work.
- auto sepIndex = line.find(" at ");
- if (sepIndex == std::string::npos) {
- return;
- }
- if (sepIndex > 0) {
- entry.__desc_ = line.substr(0, sepIndex);
- }
- auto fileBegin = sepIndex + 4;
- if (fileBegin >= line.size()) {
- return;
- }
- auto fileline = line.substr(fileBegin);
- auto colon = fileline.find_last_of(":");
- if (colon > 0 && !fileline.starts_with("?")) {
- entry.__file_ = fileline.substr(0, colon);
- }
-
- if (colon == std::string::npos) {
- return;
- }
- uint32_t lineno = 0;
- auto pos = colon;
- while (isdigit(fileline[++pos])) {
- lineno = lineno * 10 + (fileline[pos] - '0');
- }
- entry.__line_ = lineno;
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/atos.cpp b/libcxx/src/stacktrace/tools/atos.cpp
deleted file mode 100644
index f6e6458aaae0d..0000000000000
--- a/libcxx/src/stacktrace/tools/atos.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <istream>
-#include <string>
-#include <unistd.h>
-
-#include "tools.h"
-
-#include <__stacktrace/alloc.h>
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-std::pmr::list<std::pmr::string> atos::buildArgs(context& cx) const {
- auto& alloc = cx.__alloc_;
- auto ret = alloc.new_string_list();
- ret.push_back(progName_);
- ret.push_back("-p");
- ret.push_back(alloc.u64_string(getpid()));
- // TODO(stackcx23): Allow options in env, e.g. LIBCPP_STACKTRACE_OPTIONS=FullPath
- // ret.push_back("--fullPath");
- for (auto& entry : cx.__entries_) {
- ret.push_back(alloc.hex_string(entry.__addr_));
- }
- return ret;
-}
-
-void atos::parseOutput(context& cx, entry& entry, std::istream& output) const {
- // Simple example:
- //
- // main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
- //
- // Assuming this is always atos's format (except when it returns empty lines)
- // we can split the string like so:
- //
- // main (in testprog) (/Users/steve/code/notes/testprog.cc:208)
- // ^^^^-----^^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^-
- // sym module filename line
- //
- // Note that very strange filenames or module names can confuse this.
- // We'll do the best we can for a decent result, while definitely ensuring safety
- // (i.e. careful with our bound-checking).
- //
- // Another more interesting example (with an added newline for legibility):
- //
- // std::__1::basic_ios<char, std::__1::char_traits<char>>::fill[abi:ne190107]() const (in testprog)
- // (/opt/homebrew/Cellar/llvm/19.1.7_1/bin/../include/c++/v1/ios:0
- //
- // If this more or less fits our expected format we'll take these data,
- // even if the line number is 0.
-
- auto line = cx.__alloc_.new_string(256);
- std::getline(output, line);
- while (isspace(line.back())) {
- line.pop_back();
- }
- if (line.empty()) {
- return;
- }
- auto buf = line.data();
- auto size = line.size();
-
- auto* end = buf + size;
- auto* symEnd = strstr(buf, " (in ");
- if (!symEnd) {
- return;
- }
- auto* modBegin = symEnd + 5;
- auto* modEnd = strstr(modBegin, ") (");
- if (!modEnd) {
- return;
- }
- auto* fileBegin = modEnd + 3; // filename starts just after that
- if (fileBegin >= end) {
- return;
- }
- auto const* lastColon = fileBegin; // we'll search for last colon after filename
- char const* nextColon;
- while ((nextColon = strstr(lastColon + 1, ":"))) { // skip colons in filename (e.g. in "C:\foo.cpp")
- lastColon = nextColon;
- }
-
- std::string_view sym{buf, size_t(symEnd - buf)};
- // In case a previous step could not obtain the symbol name,
- // we have the name provided by atos; only use that if we have no symbol
- // (no need to copy more strings otherwise).
- if (entry.__desc_.empty() && !sym.empty()) {
- entry.__desc_ = sym;
- }
-
- std::string_view file{fileBegin, size_t(lastColon - fileBegin)};
- if (file != "?" && file != "??" && !file.empty()) {
- entry.__file_ = file;
- }
-
- unsigned lineno = 0;
- for (auto* digit = lastColon + 1; digit < end && isdigit(*digit); ++digit) {
- lineno = (lineno * 10) + unsigned(*digit - '0');
- }
- entry.__line_ = lineno;
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp b/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
deleted file mode 100644
index 18b493c97a040..0000000000000
--- a/libcxx/src/stacktrace/tools/llvm_symbolizer.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <iostream>
-#include <string_view>
-
-#include "../common/debug.h"
-#include "tools.h"
-
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// TODO(stacktrace23): possible to link against `libLLVMSymbolize.a`, or some shared obj at runtime (does that exist?)
-
-std::pmr::list<std::pmr::string> llvm_symbolizer::buildArgs(context& cx) const {
- auto& alloc = cx.__alloc_;
- auto ret = alloc.new_string_list();
- ret.push_back(progName_);
- ret.push_back("--demangle");
- ret.push_back("--no-inlines");
- ret.push_back("--verbose");
- ret.push_back("--relativenames");
- ret.push_back("--functions=short");
- for (auto& entry : cx.__entries_) {
- auto addr_string = alloc.hex_string(entry.__addr_unslid_);
- debug() << "@@@ " << addr_string << " " << entry.__file_ << " " << entry.__file_.empty() << '\n';
- if (!entry.__file_.empty()) {
- auto arg = alloc.new_string(entry.__file_.size() + 40);
- arg += "FILE:";
- arg += entry.__file_;
- arg += " ";
- arg += addr_string;
- ret.push_back(arg);
- } else {
- ret.push_back(addr_string);
- }
- }
- return ret;
-}
-
-void llvm_symbolizer::parseOutput(context& cx, entry& entry, std::istream& output) const {
- // clang-format off
-/*
-With "--verbose", parsing is a little easier, or at least, more reliable;
-probably the best solution (until we have a JSON parser).
-Example output, verbatim, between the '---' lines:
----
-test1<test_alloc<std::__1::stacktrace_entry> >
- Filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
- Function start filename: /data/code/llvm-project/libcxx/test/std/diagnostics/stacktrace/basic.cons.pass.cpp
- Function start line: 114
- Function start address: 0x8dd0
- Line: 116
- Column: 14
-
----
-Note that this includes an extra empty line as a terminator.
-*/
- // clang-format on
-
- auto& alloc = cx.__alloc_;
- auto line = alloc.new_string(256);
- std::string_view tmp;
- while (true) {
- std::getline(output, line);
- while (isspace(line.back())) {
- line.pop_back();
- }
- if (line.empty()) {
- return;
- }
- if (!line.starts_with(" ")) {
- // The symbol has no leading whitespace, while the other
- // lines with "fields" like line, column, filename, etc.
- // start with two spaces.
- if (line != "??") {
- entry.__desc_ = line;
- }
- } else if (line.starts_with(" Filename:")) {
- tmp = line;
- tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
- if (tmp != "??") {
- entry.__file_ = tmp;
- }
- } else if (line.starts_with(" Line:")) {
- tmp = line;
- tmp = tmp.substr(tmp.find_first_of(":") + 2); // skip ": "
- if (tmp != "??" && tmp != "0") {
- uint32_t lineno = 0;
- auto pos = 0;
- while (isdigit(tmp[pos])) {
- lineno = lineno * 10 + (tmp[pos++] - '0');
- }
- entry.__line_ = lineno;
- }
- }
- }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/tools/tools.h b/libcxx/src/stacktrace/tools/tools.h
deleted file mode 100644
index 70374cf075ebf..0000000000000
--- a/libcxx/src/stacktrace/tools/tools.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_TOOLS_H
-#define _LIBCPP_STACKTRACE_TOOLS_H
-
-#include <__config>
-#include <__config_site>
-#include <cassert>
-#include <cstddef>
-#include <list>
-#include <string>
-
-#include <__stacktrace/context.h>
-#include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct tool {
- char const* progName_;
- explicit tool(char const* progName) : progName_(progName) {}
- constexpr virtual ~tool() = default;
-
- /** Construct complete `argv` for the spawned process.
- Includes the program name at argv[0], followed by flags */
- virtual std::pmr::list<std::pmr::string> buildArgs(context& trace) const = 0;
-
- /** Parse line(s) output by the tool, and modify `entry`. */
- virtual void parseOutput(context& trace, entry& e, std::istream& output) const = 0;
-};
-
-struct llvm_symbolizer : tool {
- constexpr virtual ~llvm_symbolizer() = default;
- llvm_symbolizer() : llvm_symbolizer("llvm-symbolizer") {}
- explicit llvm_symbolizer(char const* progName) : tool{progName} {}
- std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
- void parseOutput(context& trace, entry& entry, std::istream& output) const override;
-};
-
-struct addr2line : tool {
- constexpr virtual ~addr2line() = default;
- addr2line() : addr2line("addr2line") {}
- explicit addr2line(char const* progName) : tool{progName} {}
- std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
- void parseOutput(context& trace, entry& e, std::istream& stream) const override;
-};
-
-struct atos : tool {
- constexpr virtual ~atos() = default;
- atos() : atos("atos") {}
- explicit atos(char const* progName) : tool{progName} {}
- std::pmr::list<std::pmr::string> buildArgs(context& trace) const override;
- void parseOutput(context& trace, entry& entry, std::istream& output) const override;
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_TOOLS_H
diff --git a/libcxx/src/stacktrace/tools/toolspawner.cpp b/libcxx/src/stacktrace/tools/toolspawner.cpp
deleted file mode 100644
index 54ddaf156fc64..0000000000000
--- a/libcxx/src/stacktrace/tools/toolspawner.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_CAN_SPAWN_TOOLS)
-
-# include <__config>
-# include <__config_site>
-# include <cassert>
-# include <cerrno>
-# include <csignal>
-# include <cstddef>
-# include <cstdlib>
-# include <spawn.h>
-# include <string>
-# include <sys/fcntl.h>
-# include <sys/types.h>
-# include <sys/wait.h>
-# include <unistd.h>
-
-# include "../common/failed.h"
-# include "pspawn.h"
-
-# include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-namespace {
-
-/**
-Returns a list of `tool` objects that can be tried during the first stacktrace operation.
-The result is an array of `tool*` pointers with a final `nullptr` terminator.
-
-The returned tool array pointer is saved during `resolve_lines`'s static init,
-with the first working tool being used from that point forward.
-
-This will first add any tools specified by these defines (in order):
-- LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH
-- LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH
-- LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH
-
-Then any tools specified by the environment variables of the same names (and added in the same order).
-
-Finally, this adds the tools `llvm-symbolizer`, `addr2line`, and `atos`, in that order.
-These tools won't have absolute paths, so $PATH will be searched for these.
-
-Called by `findWorkingTool` during its static initialization, therefore this is used in a threadsafe way.
-*/
-tool const** toolList() {
- constexpr static size_t kMax = 20;
- static tool const* array_[kMax];
- size_t count = 0;
-
- auto add = [&](tool* t) {
- assert(count < kMax - 1);
- array_[count++] = t;
- };
-
-# define STRINGIFY0(x) #x
-# define STRINGIFY(x) STRINGIFY0(x)
-
-# if defined(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)
- {
- static llvm_symbolizer t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH)};
- add(&t);
- }
-# endif
-# if defined(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)
- {
- static addr2line t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH)};
- add(&t);
- }
-# endif
-# if defined(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)
- {
- static atos t{STRINGIFY(LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH)};
- add(&t);
- }
-# endif
-
- if (getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")) {
- static llvm_symbolizer t{getenv("LIBCXX_STACKTRACE_FORCE_LLVM_SYMBOLIZER_PATH")};
- add(&t);
- }
- if (getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")) {
- static addr2line t{getenv("LIBCXX_STACKTRACE_FORCE_GNU_ADDR2LINE_PATH")};
- add(&t);
- }
- if (getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")) {
- static atos t{getenv("LIBCXX_STACKTRACE_FORCE_APPLE_ATOS_PATH")};
- add(&t);
- }
-
- {
- static llvm_symbolizer t;
- add(&t);
- }
- {
- static addr2line t;
- add(&t);
- }
- {
- static atos t;
- add(&t);
- }
-
- array_[count] = nullptr; // terminator
- return array_;
-}
-
-// Try a handful of different addr2line-esque tools, returning the first discovered, or else nullptr
-tool const* findWorkingTool(auto& trace) {
- // Try each of these programs and stop at the first one that works.
- // There's no synchronization so this is subject to races on setting `prog`,
- // but as long as one thread ends up setting it to something that works, we're good.
- // Programs to try ($PATH is searched):
- auto* it = toolList();
- tool const* prog;
- while ((prog = *it++)) {
- pspawn test{*prog}; // Just try to run with "--help"
- try {
- std::pmr::list<std::pmr::string> testArgs{&trace.__alloc_};
- testArgs.push_back(prog->progName_);
- testArgs.push_back("--help");
- test.fa_.redirectInNull();
- test.fa_.redirectOutNull();
- test.fa_.redirectErrNull();
- test.spawn(testArgs);
- if (test.wait() == 0) {
- // Success
- return prog;
- }
- } catch (failed const&) {
- /* ignore during probe attempt */
- }
- }
- return nullptr;
-}
-
-} // namespace
-
-void spawner::resolve_lines() {
- // The address-to-line tool that worked (after lazy-setting, below)
- static tool const* prog{nullptr};
- // Whether we should not attempt because tool detection previously failed.
- static bool fail{false};
-
- // If this previously failed, don't try again.
- if (fail) {
- return;
- }
-
- if (!prog) {
- prog = findWorkingTool(cx_);
- if (!prog) {
- fail = true;
- return;
- }
- }
- assert(prog);
-
- char buf[256];
- pspawn_tool proc(*prog, cx_, buf, sizeof(buf));
- try {
- proc.run();
- } catch (failed const& failed) {
- debug() << failed.what();
- if (failed.errno_) {
- debug() << " (" << failed.errno_ << " " << strerror(failed.errno_) << ')';
- }
- debug() << '\n';
- }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif
diff --git a/libcxx/src/stacktrace/unwind/unwind.cpp b/libcxx/src/stacktrace/unwind.cpp
similarity index 91%
rename from libcxx/src/stacktrace/unwind/unwind.cpp
rename to libcxx/src/stacktrace/unwind.cpp
index 51689c5f1c15d..ad5c1f7a443a9 100644
--- a/libcxx/src/stacktrace/unwind/unwind.cpp
+++ b/libcxx/src/stacktrace/unwind.cpp
@@ -6,16 +6,16 @@
//
//===----------------------------------------------------------------------===//
-#include "../common/config.h"
+#include "stacktrace/config.h"
#if defined(_LIBCPP_STACKTRACE_COLLECT_UNWIND)
-# include "./unwind.h"
-
-# include <__stacktrace/context.h>
-# include <__stacktrace/entry.h>
# include <unwind.h>
+# include "stacktrace/unwind.h"
+# include <__stacktrace/basic_stacktrace.h>
+# include <__stacktrace/stacktrace_entry.h>
+
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/stacktrace/unwind/unwind.h b/libcxx/src/stacktrace/unwind.h
similarity index 81%
rename from libcxx/src/stacktrace/unwind/unwind.h
rename to libcxx/src/stacktrace/unwind.h
index a258a32f50c5d..98216e61d6bcf 100644
--- a/libcxx/src/stacktrace/unwind/unwind.h
+++ b/libcxx/src/stacktrace/unwind.h
@@ -14,6 +14,12 @@
#include <cstddef>
#include <cstdlib>
+#include "stacktrace/config.h"
+#include "stacktrace/context.h"
+#include "stacktrace/utils.h"
+#include <__stacktrace/basic_stacktrace.h>
+#include <__stacktrace/stacktrace_entry.h>
+
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
diff --git a/libcxx/src/stacktrace/utils.h b/libcxx/src/stacktrace/utils.h
new file mode 100644
index 0000000000000..ea0e7cd659e63
--- /dev/null
+++ b/libcxx/src/stacktrace/utils.h
@@ -0,0 +1,176 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_STACKTRACE_DEBUG_H
+#define _LIBCPP_STACKTRACE_DEBUG_H
+
+#include <__config>
+#include <cassert>
+#include <cerrno>
+#include <cstdio>
+#include <iostream>
+#include <stdexcept>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <utility>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+struct failed : std::runtime_error {
+ virtual ~failed() = default;
+ int errno_{0};
+ failed() : std::runtime_error({}) {}
+ failed(char const* msg, int err) : std::runtime_error(msg), errno_(err) {}
+};
+
+/** Debug-message output stream. If `LIBCXX_STACKTRACE_DEBUG` is defined in the environment
+or as a macro with exactly the string `1` then this is enabled (prints to `std::cerr`);
+otherwise its does nothing by returning a dummy stream. */
+struct _LIBCPP_HIDE_FROM_ABI debug : std::ostream {
+ _LIBCPP_HIDE_FROM_ABI virtual ~debug();
+
+ _LIBCPP_HIDE_FROM_ABI static bool enabled() {
+#if defined(LIBCXX_STACKTRACE_DEBUG) && LIBCXX_STACKTRACE_DEBUG == 1
+ return true;
+#else
+ static bool ret = [] {
+ auto const* val = getenv("LIBCXX_STACKTRACE_DEBUG");
+ return val && !strncmp(val, "1", 1);
+ }();
+ return ret;
+#endif
+ }
+
+ /** No-op output stream. */
+ struct _LIBCPP_HIDE_FROM_ABI dummy_ostream final : std::ostream {
+ _LIBCPP_HIDE_FROM_ABI virtual ~dummy_ostream();
+ friend std::ostream& operator<<(dummy_ostream& bogus, auto const&) { return bogus; }
+ };
+
+ friend std::ostream& operator<<(debug& dp, auto const& val) {
+ static dummy_ostream kdummy;
+ if (!enabled()) {
+ return kdummy;
+ }
+ std::cerr << val;
+ return std::cerr;
+ }
+};
+
+/** Encapsulates a plain old file descriptor `int`. Avoids copies in order to
+force some component to "own" this, although it's freely convertible back to
+integer form. Default-constructed, closed, and moved-out-of instances will have
+the invalid fd `-1`. */
+class _LIBCPP_HIDE_FROM_ABI fd {
+ int fd_{-1};
+
+public:
+ fd() : fd(-1) {}
+ fd(int fdint) : fd_(fdint) {}
+
+ fd(fd const&) = delete;
+ fd& operator=(fd const&) = delete;
+
+ fd(fd&& rhs) {
+ if (&rhs != this) {
+ std::exchange(fd_, rhs.fd_);
+ }
+ }
+
+ fd& operator=(fd&& rhs) { return *new (this) fd(std::move(rhs)); }
+
+ ~fd() { close(); }
+
+ /** Returns true IFF fd is above zero */
+ bool valid() const { return fd_ > 0; }
+
+ /* implicit */ operator int() const {
+ auto ret = fd_;
+ assert(ret > 0);
+ return ret;
+ }
+
+ void close() {
+ int fd_old = -1;
+ std::exchange(fd_old, fd_);
+ if (fd_old != -1) {
+ ::close(fd_old);
+ }
+ }
+
+ static fd& null_fd() {
+ static fd ret = {::open("/dev/null", O_RDWR)};
+ return ret;
+ }
+
+ static fd open(std::string_view path) {
+ fd ret = {::open(path.data(), O_RDONLY)};
+ return ret;
+ }
+};
+
+/** Wraps a readable fd using the `streambuf` interface. I/O errors arising
+from reading the provided fd will result in a `Failed` being thrown. */
+struct _LIBCPP_HIDE_FROM_ABI fd_streambuf final : std::streambuf {
+ fd& fd_;
+ char* buf_;
+ size_t size_;
+ _LIBCPP_HIDE_FROM_ABI fd_streambuf(fd& fd, char* buf, size_t size) : fd_(fd), buf_(buf), size_(size) {}
+ _LIBCPP_HIDE_FROM_ABI virtual ~fd_streambuf();
+ _LIBCPP_HIDE_FROM_ABI int underflow() override;
+};
+
+/** Wraps an `FDInStreamBuffer` in an `istream` */
+struct fd_istream final : std::istream {
+ fd_streambuf& buf_;
+ _LIBCPP_HIDE_FROM_ABI virtual ~fd_istream();
+ _LIBCPP_HIDE_FROM_ABI explicit fd_istream(fd_streambuf& buf) : std::istream(nullptr), buf_(buf) { rdbuf(&buf_); }
+};
+
+struct fd_mmap final {
+ fd fd_{};
+ size_t size_{0};
+ std::byte const* addr_{nullptr};
+
+ _LIBCPP_HIDE_FROM_ABI explicit fd_mmap(std::string_view path) : fd_mmap(fd::open(path)) {}
+
+ _LIBCPP_HIDE_FROM_ABI explicit fd_mmap(fd&& fd) : fd_(std::move(fd)) {
+ if (fd_) {
+ if ((size_ = ::lseek(fd, 0, SEEK_END))) {
+ addr_ = (std::byte const*)::mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
+ }
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI operator bool() const { return addr_; }
+
+ _LIBCPP_HIDE_FROM_ABI ~fd_mmap() {
+ if (addr_) {
+ ::munmap(const_cast<void*>((void const*)addr_), size_);
+ }
+ }
+};
+
+constexpr unsigned k_max_images = 256;
+
+struct _LIBCPP_HIDE_FROM_ABI image {
+ uintptr_t loaded_at_{};
+ intptr_t slide_{};
+ std::string_view name_{};
+ bool is_main_prog_{};
+
+ _LIBCPP_HIDE_FROM_ABI bool operator<(image const& rhs) const { return loaded_at_ < rhs.loaded_at_; }
+ _LIBCPP_HIDE_FROM_ABI operator bool() const { return !name_.empty(); }
+};
+
+} // namespace __stacktrace
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STACKTRACE_DEBUG_H
diff --git a/libcxx/src/stacktrace/windows/win_impl.cpp b/libcxx/src/stacktrace/windows.cpp
similarity index 74%
rename from libcxx/src/stacktrace/windows/win_impl.cpp
rename to libcxx/src/stacktrace/windows.cpp
index 44d6052706b85..36de17973a231 100644
--- a/libcxx/src/stacktrace/windows/win_impl.cpp
+++ b/libcxx/src/stacktrace/windows.cpp
@@ -6,22 +6,17 @@
//
//===----------------------------------------------------------------------===//
-#include "win_impl.h"
+#include "stacktrace/config.h"
#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-// windows.h must be first
-# include <windows.h>
-// other windows-specific headers
-# include <dbghelp.h>
-# define PSAPI_VERSION 1
-# include <psapi.h>
-// standard headers
-# include <cstdlib>
-# include <mutex>
-
-# include "dll.h"
-# include <__stacktrace/entry.h>
+# include "stacktrace/alloc.h"
+# include "stacktrace/config.h"
+# include "stacktrace/context.h"
+# include "stacktrace/utils.h"
+# include "stacktrace/windows.h"
+# include <__stacktrace/basic_stacktrace.h>
+# include <__stacktrace/stacktrace_entry.h>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
@@ -198,6 +193,54 @@ _LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE void win_impl::collect(size_t skip, size_
}
}
+dll::~dll() { FreeLibrary(module_); }
+
+dll::dll(char const* name) : name_(name), module_(LoadLibrary(name)) {
+ if (!module_) {
+ debug() << "LoadLibrary failed: " << name_ << ": " << GetLastError() << '\n';
+ }
+}
+
+dbghelp_dll::~dbghelp_dll() = default;
+
+dbghelp_dll& dbghelp_dll::get() {
+ dbghelp_dll ret;
+ return ret;
+}
+
+dbghelp_dll::dbghelp_dll() : dll("dbghelp.dll") {
+ // clang-format off
+if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
+ if (!get_func(&StackWalk64, "StackWalk64")) { return; }
+ if (!get_func(&SymCleanup, "SymCleanup")) { return; }
+ if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
+ if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
+ if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
+ if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
+ if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
+ if (!get_func(&SymInitialize, "SymInitialize")) { return; }
+ if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
+ if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
+ valid_ = true;
+ // clang-format on
+}
+
+psapi_dll::~psapi_dll() = default;
+
+psapi_dll& psapi_dll::get() {
+ psapi_dll ret;
+ return ret;
+}
+
+psapi_dll() : dll("psapi.dll") {
+ // clang-format off
+if (!getFunc(&EnumProcessModules, "EnumProcessModules")) { return; }
+ if (!getFunc(&GetModuleInformation, "GetModuleInformation")) { return; }
+ if (!getFunc(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+ valid_ = true;
+ // clang-format on
+}
+
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/stacktrace/windows/dbghelp_dll.h b/libcxx/src/stacktrace/windows.h
similarity index 55%
rename from libcxx/src/stacktrace/windows/dbghelp_dll.h
rename to libcxx/src/stacktrace/windows.h
index b1204a369a3f1..d31274448c5be 100644
--- a/libcxx/src/stacktrace/windows/dbghelp_dll.h
+++ b/libcxx/src/stacktrace/windows.h
@@ -6,10 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
-#define _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
-
-#include "../common/config.h"
+#ifndef _LIBCPP_STACKTRACE_WIN_IMPL_H
+#define _LIBCPP_STACKTRACE_WIN_IMPL_H
#if defined(_LIBCPP_STACKTRACE_WINDOWS)
// windows.h must be first
@@ -18,20 +16,65 @@
# include <dbghelp.h>
# define PSAPI_VERSION 1
# include <psapi.h>
-// standard headers
-# include <cstdlib>
-# include <mutex>
-
-# include <__stacktrace/entry.h>
+#endif
-# include "../common/debug.h"
-# include "dll.h"
+#include <__config>
+#include <__config_site>
+#include <cstddef>
+#include <cstdlib>
+#include <mutex>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace __stacktrace {
+struct context;
+
+struct win_impl {
+ context& cx_;
+ std::lock_guard<std::mutex> guard_;
+ win_impl(context& trace);
+ ~win_impl();
+
+ void collect(size_t skip, size_t max_depth);
+ void ident_modules();
+ void symbolize();
+ void resolve_lines();
+};
+
+#if defined(_LIBCPP_STACKTRACE_WINDOWS)
+
// clang-format off
+struct dll {
+ char const* name_;
+ HMODULE module_;
+ /** Set to true in subclass's ctor if initialized successfully. */
+ bool valid_{false};
+
+ operator bool() const { return valid_; }
+
+ explicit dll(char const* name)
+ : name_(name), module_(LoadLibrary(name)) {
+ if (!module_) {
+ debug() << "LoadLibrary failed: "
+ << name_ << ": " << GetLastError() << '\n';
+ }
+ }
+
+ virtual ~dll() { FreeLibrary(module_); }
+
+ template <typename F>
+ bool get_func(F* func, char const* name) {
+ if (!(*func = (F)GetProcAddress(module_, name))) {
+ debug() << "GetProcAddress failed: "
+ << name << "' (" << name_ << "): "
+ << GetLastError() << "\n";
+ return false;
+ }
+ return true;
+ }
+};
+
struct dbghelp_dll final : dll {
virtual ~dbghelp_dll() = default;
static dbghelp_dll& get() { static dbghelp_dll ret; return ret; }
@@ -64,8 +107,25 @@ struct dbghelp_dll final : dll {
}
};
+struct psapi_dll final : dll {
+ virtual ~psapi_dll() = default;
+ static psapi_dll& get() { static psapi_dll ret; return ret; }
+
+ bool (*EnumProcessModules) (HANDLE, HMODULE*, DWORD, DWORD*);
+ bool (*GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
+ DWORD (*GetModuleBaseName) (HANDLE, HMODULE, char**, DWORD);
+
+ psapi_dll() : dll("psapi.dll") {
+ if (!get_func(&EnumProcessModules, "EnumProcessModules")) { return; }
+ if (!get_func(&GetModuleInformation, "GetModuleInformation")) { return; }
+ if (!get_func(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
+ valid_ = true;
+ }
+};
+
+#endif
+
} // namespace __stacktrace
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_STACKTRACE_WINDOWS
-#endif // _LIBCPP_STACKTRACE_WINDOWS_DBGHELP_DLL_H
+#endif // _LIBCPP_STACKTRACE_WIN_IMPL_H
diff --git a/libcxx/src/stacktrace/windows/dbghelp_dll.cpp b/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
deleted file mode 100644
index 7753ad77d3d26..0000000000000
--- a/libcxx/src/stacktrace/windows/dbghelp_dll.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-# include <windows.h>
-// other windows-specific headers
-# include <dbghelp.h>
-# define PSAPI_VERSION 1
-# include <psapi.h>
-// standard headers
-# include <cstdlib>
-# include <mutex>
-
-# include "dll.h"
-# include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-dbghelp_dll::~dbghelp_dll() = default;
-
-dbghelp_dll& dbghelp_dll::get() {
- dbghelp_dll ret;
- return ret;
-}
-
-dbghelp_dll::dbghelp_dll() : dll("dbghelp.dll") {
- // clang-format off
-if (!get_func(&ImageNtHeader, "ImageNtHeader")) { return; }
- if (!get_func(&StackWalk64, "StackWalk64")) { return; }
- if (!get_func(&SymCleanup, "SymCleanup")) { return; }
- if (!get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")) { return; }
- if (!get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")) { return; }
- if (!get_func(&SymGetModuleBase64, "SymGetModuleBase64")) { return; }
- if (!get_func(&SymGetOptions, "SymGetOptions")) { return; }
- if (!get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")) { return; }
- if (!get_func(&SymInitialize, "SymInitialize")) { return; }
- if (!get_func(&SymLoadModule64, "SymLoadModule64")) { return; }
- if (!get_func(&SymSetOptions, "SymSetOptions")) { return; }
- valid_ = true;
- // clang-format on
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/dll.cpp b/libcxx/src/stacktrace/windows/dll.cpp
deleted file mode 100644
index b0ea2609b55cd..0000000000000
--- a/libcxx/src/stacktrace/windows/dll.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-# include <windows.h>
-// other windows-specific headers
-# include <dbghelp.h>
-# define PSAPI_VERSION 1
-# include <psapi.h>
-// standard headers
-# include <cstdlib>
-# include <mutex>
-
-# include "dll.h"
-# include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-dll::~dll() { FreeLibrary(module_); }
-
-dll::dll(char const* name) : name_(name), module_(LoadLibrary(name)) {
- if (!module_) {
- debug() << "LoadLibrary failed: " << name_ << ": " << GetLastError() << '\n';
- }
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/dll.h b/libcxx/src/stacktrace/windows/dll.h
deleted file mode 100644
index 4ef8e7c5df190..0000000000000
--- a/libcxx/src/stacktrace/windows/dll.h
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_WINDOWS_DLL_H
-#define _LIBCPP_STACKTRACE_WINDOWS_DLL_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-// windows.h must be first
-# include <windows.h>
-// other windows-specific headers
-# include <dbghelp.h>
-# define PSAPI_VERSION 1
-# include <psapi.h>
-// standard headers
-# include <cstdlib>
-# include <mutex>
-
-# include <__stacktrace/entry.h>
-
-# include "../common/debug.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// clang-format off
-
-struct dll {
- char const* name_;
- HMODULE module_;
- /** Set to true in subclass's ctor if initialized successfully. */
- bool valid_{false};
-
- operator bool() const { return valid_; }
-
- explicit dll(char const* name)
- : name_(name), module_(LoadLibrary(name)) {
- if (!module_) {
- debug() << "LoadLibrary failed: "
- << name_ << ": " << GetLastError() << '\n';
- }
- }
-
- virtual ~dll() { FreeLibrary(module_); }
-
- template <typename F>
- bool get_func(F* func, char const* name) {
- if (!(*func = (F)GetProcAddress(module_, name))) {
- debug() << "GetProcAddress failed: "
- << name << "' (" << name_ << "): "
- << GetLastError() << "\n";
- return false;
- }
- return true;
- }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WINDOWS
-#endif // _LIBCPP_STACKTRACE_WINDOWS_DLL_H
diff --git a/libcxx/src/stacktrace/windows/psapi_dll.cpp b/libcxx/src/stacktrace/windows/psapi_dll.cpp
deleted file mode 100644
index d4f7fdc006341..0000000000000
--- a/libcxx/src/stacktrace/windows/psapi_dll.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-
-// windows.h must be first
-# include <windows.h>
-// other windows-specific headers
-# include <dbghelp.h>
-# define PSAPI_VERSION 1
-# include <psapi.h>
-// standard headers
-# include <cstdlib>
-# include <mutex>
-
-# include "dll.h"
-# include <__stacktrace/entry.h>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-psapi_dll::~psapi_dll() = default;
-
-psapi_dll& psapi_dll::get() {
- psapi_dll ret;
- return ret;
-}
-
-psapi_dll() : dll("psapi.dll") {
- // clang-format off
-if (!getFunc(&EnumProcessModules, "EnumProcessModules")) { return; }
- if (!getFunc(&GetModuleInformation, "GetModuleInformation")) { return; }
- if (!getFunc(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
- valid_ = true;
- // clang-format on
-}
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_USE_DBGHELP
diff --git a/libcxx/src/stacktrace/windows/psapi_dll.h b/libcxx/src/stacktrace/windows/psapi_dll.h
deleted file mode 100644
index bcad1bfb72d0a..0000000000000
--- a/libcxx/src/stacktrace/windows/psapi_dll.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
-#define _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
-
-#include "../common/config.h"
-
-#if defined(_LIBCPP_STACKTRACE_WINDOWS)
-// windows.h must be first
-# include <windows.h>
-// other windows-specific headers
-# include <dbghelp.h>
-# define PSAPI_VERSION 1
-# include <psapi.h>
-// standard headers
-# include <cstdlib>
-# include <mutex>
-
-# include <__stacktrace/entry.h>
-
-# include "../common/debug.h"
-# include "dll.h"
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-// clang-format off
-
-struct psapi_dll final : dll {
- virtual ~psapi_dll() = default;
- static psapi_dll& get() { static psapi_dll ret; return ret; }
-
- bool (*EnumProcessModules) (HANDLE, HMODULE*, DWORD, DWORD*);
- bool (*GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
- DWORD (*GetModuleBaseName) (HANDLE, HMODULE, char**, DWORD);
-
- psapi_dll() : dll("psapi.dll") {
- if (!get_func(&EnumProcessModules, "EnumProcessModules")) { return; }
- if (!get_func(&GetModuleInformation, "GetModuleInformation")) { return; }
- if (!get_func(&GetModuleBaseName, "GetModuleBaseNameA")) { return; }
- valid_ = true;
- }
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WINDOWS
-#endif // _LIBCPP_STACKTRACE_WINDOWS_PSAPI_DLL_H
diff --git a/libcxx/src/stacktrace/windows/win_impl.h b/libcxx/src/stacktrace/windows/win_impl.h
deleted file mode 100644
index c065987120425..0000000000000
--- a/libcxx/src/stacktrace/windows/win_impl.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP_STACKTRACE_WIN_IMPL_H
-#define _LIBCPP_STACKTRACE_WIN_IMPL_H
-
-#include <__config>
-#include <__config_site>
-#include <cstddef>
-#include <cstdlib>
-#include <mutex>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-namespace __stacktrace {
-
-struct context;
-
-struct win_impl {
- context& cx_;
- std::lock_guard<std::mutex> guard_;
- win_impl(context& trace);
- ~win_impl();
-
- void collect(size_t skip, size_t max_depth);
- void ident_modules();
- void symbolize();
- void resolve_lines();
-};
-
-} // namespace __stacktrace
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STACKTRACE_WIN_IMPL_H
diff --git a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
index d8b2d9e6553bb..511006fce0149 100644
--- a/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/src/BUILD.gn
@@ -167,38 +167,18 @@ cxx_sources = [
"ryu/d2s.cpp",
"ryu/f2s.cpp",
"shared_mutex.cpp",
- "stacktrace/alloc.cpp",
- "stacktrace/common/config.h",
- "stacktrace/common/debug.cpp",
- "stacktrace/common/debug.h",
- "stacktrace/common/failed.h",
- "stacktrace/common/fd.cpp",
- "stacktrace/common/fd.h",
- "stacktrace/common/images.h",
- "stacktrace/context.cpp",
- "stacktrace/linux/linux-dl.cpp",
- "stacktrace/linux/linux-elf.cpp",
- "stacktrace/linux/linux-sym.cpp",
- "stacktrace/linux/linux.h",
- "stacktrace/osx/osx.cpp",
- "stacktrace/osx/osx.h",
+ "stacktrace/config.h",
+ "stacktrace/linux.cpp",
+ "stacktrace/linux.h",
+ "stacktrace/osx.cpp",
+ "stacktrace/osx.h",
"stacktrace/stacktrace.cpp",
- "stacktrace/tools/addr2line.cpp",
- "stacktrace/tools/atos.cpp",
- "stacktrace/tools/llvm_symbolizer.cpp",
- "stacktrace/tools/pspawn.h",
- "stacktrace/tools/tools.h",
- "stacktrace/tools/toolspawner.cpp",
- "stacktrace/unwind/unwind.cpp",
- "stacktrace/unwind/unwind.h",
- "stacktrace/windows/dbghelp_dll.cpp",
- "stacktrace/windows/dbghelp_dll.h",
- "stacktrace/windows/dll.cpp",
- "stacktrace/windows/dll.h",
- "stacktrace/windows/psapi_dll.cpp",
- "stacktrace/windows/psapi_dll.h",
- "stacktrace/windows/win_impl.cpp",
- "stacktrace/windows/win_impl.h",
+ "stacktrace/tools.cpp",
+ "stacktrace/tools.h",
+ "stacktrace/unwind.cpp",
+ "stacktrace/utils.h",
+ "stacktrace/windows.cpp",
+ "stacktrace/windows.h",
"stdexcept.cpp",
"string.cpp",
"strstream.cpp",
More information about the libcxx-commits
mailing list