diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt --- a/libcxx/CMakeLists.txt +++ b/libcxx/CMakeLists.txt @@ -907,6 +907,8 @@ # easier to grep for target specific flags once the feature is complete. config_define_if_not(LIBCXX_ENABLE_INCOMPLETE_FEATURES _LIBCPP_HAS_NO_INCOMPLETE_FORMAT) config_define_if_not(LIBCXX_ENABLE_INCOMPLETE_FEATURES _LIBCPP_HAS_NO_INCOMPLETE_RANGES) +config_define_if(LIBCXXABI_USE_LLVM_UNWINDER _LIBCPP_UNWINDLIB_LIBUNWIND) +config_define_if(LIBCXX_HAS_GCC_S_LIB _LIBCPP_UNWINDLIB_GCC_S) if (LIBCXX_ABI_DEFINES) set(abi_defines) diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -342,7 +342,7 @@ ------------------------------------------------- ----------------- ``__cpp_lib_spanstream`` *unimplemented* ------------------------------------------------- ----------------- - ``__cpp_lib_stacktrace`` *unimplemented* + ``__cpp_lib_stacktrace`` ``202011L`` ------------------------------------------------- ----------------- ``__cpp_lib_stdatomic_h`` *unimplemented* ------------------------------------------------- ----------------- diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -400,6 +400,8 @@ __split_buffer __std_stream __string + __stacktrace/stacktrace_entry.h + __stacktrace/basic_stacktrace.h __support/android/locale_bionic.h __support/fuchsia/xlocale.h __support/ibm/gettod_zos.h @@ -560,6 +562,7 @@ span sstream stack + stacktrace stdbool.h stddef.h stdexcept diff --git a/libcxx/include/__availability b/libcxx/include/__availability --- a/libcxx/include/__availability +++ b/libcxx/include/__availability @@ -176,6 +176,8 @@ // should be avoided. # define _LIBCPP_AVAILABILITY_DEFAULT_ASSERTION_HANDLER +# define _LIBCPP_AVAILABILITY_STACKTRACE + #elif defined(__APPLE__) # define _LIBCPP_AVAILABILITY_SHARED_MUTEX \ @@ -280,6 +282,9 @@ # define _LIBCPP_AVAILABILITY_DEFAULT_ASSERTION_HANDLER \ __attribute__((unavailable)) + +#define _LIBCPP_AVAILABILITY_STACKTRACE __attribute__((unavailable)) + #else // ...New vendors can add availability markup here... diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in --- a/libcxx/include/__config_site.in +++ b/libcxx/include/__config_site.in @@ -33,6 +33,8 @@ #cmakedefine _LIBCPP_HAS_NO_INCOMPLETE_FORMAT #cmakedefine _LIBCPP_HAS_NO_INCOMPLETE_RANGES #cmakedefine01 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT +#cmakedefine _LIBCPP_UNWINDLIB_LIBUNWIND +#cmakedefine _LIBCPP_UNWINDLIB_GCC_S // __USE_MINGW_ANSI_STDIO gets redefined on MinGW #ifdef __clang__ diff --git a/libcxx/include/__stacktrace/basic_stacktrace.h b/libcxx/include/__stacktrace/basic_stacktrace.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__stacktrace/basic_stacktrace.h @@ -0,0 +1,142 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___STACKTRACE_BASIC_STACKTRACE_H +#define _LIBCPP___STACKTRACE_BASIC_STACKTRACE_H + +#include <__availability> +#include <__config> +#include <__stacktrace/stacktrace_entry.h> +#include +#include +#include +#include +#include + +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +using __stacktrace_emplace_callback = void(stacktrace_entry&&, void*); +_LIBCPP_AVAILABILITY_STACKTRACE _LIBCPP_FUNC_VIS +void __get_stacktrace(size_t __skip, size_t __max_depth, __stacktrace_emplace_callback* __emplace_back, void* __vector); + +template +class basic_stacktrace { +public: + using _Vec = vector; + using value_type = stacktrace_entry; + using const_reference = const value_type&; + using reference = value_type&; + using const_iterator = typename _Vec::const_iterator; + using iterator = const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using difference_type = typename _Vec::difference_type; + using size_type = typename _Vec::size_type; + using allocator_type = _Alloc; + + static _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE + basic_stacktrace current(const allocator_type& __alloc = allocator_type()) noexcept { + return current(0, __alloc); + } + + static _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE + basic_stacktrace current(size_type __skip, const allocator_type& __alloc = allocator_type()) noexcept { + return current(__skip, numeric_limits::max(), __alloc); + } + + static _LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE + basic_stacktrace current(size_type __skip, size_type __max_depth, + const allocator_type& __alloc = allocator_type()) noexcept { + if (__max_depth == 0) + return {}; + basic_stacktrace __ret(__alloc); + std::__get_stacktrace(__skip, __max_depth, + [](stacktrace_entry&& __entry, void* __vec) { static_cast<_Vec*>(__vec)->emplace_back(std::move(__entry)); }, + &__ret.__stack_); + return __ret; + } + + _LIBCPP_HIDE_FROM_ABI basic_stacktrace() noexcept(is_nothrow_default_constructible_v) = default; + _LIBCPP_HIDE_FROM_ABI explicit basic_stacktrace(const allocator_type& __alloc) noexcept : __stack_{__alloc} {} + + _LIBCPP_HIDE_FROM_ABI basic_stacktrace(const basic_stacktrace& other) = default; + _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& other) noexcept = default; + _LIBCPP_HIDE_FROM_ABI basic_stacktrace(const basic_stacktrace& __other, const allocator_type& __alloc) + : __stack_{__other.__stack_, __alloc} {} + _LIBCPP_HIDE_FROM_ABI basic_stacktrace(basic_stacktrace&& __other, const allocator_type& __alloc) + : __stack_{std::move(__other.__stack_), __alloc} {} + _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(const basic_stacktrace& other) = default; + _LIBCPP_HIDE_FROM_ABI basic_stacktrace& operator=(basic_stacktrace&& other) noexcept( + allocator_traits::propagate_on_container_move_assignment::value || + allocator_traits::is_always_equal::value) = default; + _LIBCPP_HIDE_FROM_ABI ~basic_stacktrace() = default; + + _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __stack_.get_allocator(); } + + _LIBCPP_HIDE_FROM_ABI const_iterator begin() const noexcept { return __stack_.cbegin(); } + _LIBCPP_HIDE_FROM_ABI const_iterator end() const noexcept { return __stack_.cend(); } + _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rbegin() const noexcept { return __stack_.crbegin(); } + _LIBCPP_HIDE_FROM_ABI const_reverse_iterator rend() const noexcept { return __stack_.crend(); } + + _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const noexcept { return __stack_.cbegin(); } + _LIBCPP_HIDE_FROM_ABI const_iterator cend() const noexcept { return __stack_.cend(); } + _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crbegin() const noexcept { return __stack_.crbegin(); } + _LIBCPP_HIDE_FROM_ABI const_reverse_iterator crend() const noexcept { return __stack_.crend(); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool empty() { return __stack_.empty(); } + _LIBCPP_HIDE_FROM_ABI size_type size() const noexcept { return __stack_.size(); } + _LIBCPP_HIDE_FROM_ABI size_type max_size() const noexcept { return __stack_.max_size(); } + + _LIBCPP_HIDE_FROM_ABI const_reference operator[](size_type __pos) const { return __stack_[__pos]; } + _LIBCPP_HIDE_FROM_ABI const_reference at(size_type __pos) const { return __stack_.at(__pos); } + + template + _LIBCPP_HIDE_FROM_ABI friend + bool operator==(const basic_stacktrace& __lhs, const basic_stacktrace<_Alloc2> __rhs) noexcept { + return std::equal(__lhs.begin(), __lhs.end(), __rhs.begin(), __rhs.end()); + } + + template + _LIBCPP_HIDE_FROM_ABI friend + strong_ordering operator<=>(const basic_stacktrace& __lhs, const basic_stacktrace<_Alloc2>& __rhs) noexcept { + if (__lhs.size() != __rhs.size()) + return __lhs.size() <=> __rhs.size(); + // TODO: use std::lexicographical_compare_three_way once available + // return std::lexicographical_compare_three_way(__lhs.begin(), __lhs.end(), __rhs.begin(), __rhs.end()); + return std::strong_ordering::less; + } + +private: + _Vec __stack_{}; +}; + +using stacktrace = basic_stacktrace>; + +template +_LIBCPP_HIDE_FROM_ABI inline string to_string(const basic_stacktrace<_Alloc>& __trace) { + constexpr auto __function_name_length = 75; + constexpr auto __formatting_character_count = 5; + string __ret; + __ret.reserve(__trace.size() * (__function_name_length + __formatting_character_count)); + size_t __counter = 0; + for (const auto& __e : __trace) { + __ret += ' '; + __ret += std::to_string(__counter); + __ret += "# "; + __ret += std::to_string(__e); + __ret += '\n'; + ++__counter; + } + return __ret; +} + +_LIBCPP_END_NAMESPACE_STD + +#endif diff --git a/libcxx/include/__stacktrace/stacktrace_entry.h b/libcxx/include/__stacktrace/stacktrace_entry.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__stacktrace/stacktrace_entry.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_STACKTRACE_ENTRY_H +#define _LIBCPP___STACKTRACE_STACKTRACE_ENTRY_H + +#include <__availability> +#include <__config> +#include +#include +#include +#include + +#pragma push_macro("_U") +#undef _U +#define UNW_LOCAL_ONLY +#ifdef _LIBCPP_UNWINDLIB_LIBUNWIND +# include +namespace __stacktrace_types { + using native_handle_type = unw_cursor_t; +} // namespace __stacktrace_types +#elif defined(_LIBCPP_UNWINDLIB_GCC_S) +# include +namespace __stacktrace_types { + using native_handle_type = uintptr_t; +} // namespace __stacktrace_types +#else +# error "Could not find an unwind library" +#endif +#pragma pop_macro("_U") + +_LIBCPP_BEGIN_NAMESPACE_STD + +struct __stacktrace_entry_cursor_ctor_tag {}; + +class stacktrace_entry { + +public: + using native_handle_type = __stacktrace_types::native_handle_type; + + // [stacktrace.entry.ctor] + _LIBCPP_HIDE_FROM_ABI constexpr stacktrace_entry() noexcept = default; + _LIBCPP_HIDE_FROM_ABI stacktrace_entry(__stacktrace_entry_cursor_ctor_tag, native_handle_type __cursor) + : __cursor_{__cursor}, __engaged_{true} {} + + _LIBCPP_HIDE_FROM_ABI constexpr stacktrace_entry(const stacktrace_entry&) noexcept = default; + _LIBCPP_HIDE_FROM_ABI constexpr stacktrace_entry& operator=(const stacktrace_entry&) noexcept = default; + _LIBCPP_HIDE_FROM_ABI ~stacktrace_entry() = default; + + // [stacktrace.entry.obs] + _LIBCPP_HIDE_FROM_ABI constexpr native_handle_type native_handle() const noexcept { return __cursor_; } + _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { + return __engaged_; + } + + // [stacktrace.entry.query] + _LIBCPP_HIDE_FROM_ABI string description() const { + if (!__engaged_) { + std::puts("Not engaged!"); + return {}; + } + return __unwind_description(); + } + + _LIBCPP_HIDE_FROM_ABI string source_file() const { return {}; } + _LIBCPP_HIDE_FROM_ABI uint_least32_t source_line() const { return {}; } + + // [stacktrace.entry.cmp] + _LIBCPP_HIDE_FROM_ABI friend + constexpr bool operator==(const stacktrace_entry& __lhs, const stacktrace_entry& __rhs) noexcept { + if (!__lhs.__engaged_ && !__rhs.__engaged_) + return true; + return (__lhs.__engaged_ == __rhs.__engaged_) + && ::__builtin_memcmp(&__lhs.__cursor_, &__rhs.__cursor_, sizeof(native_handle_type)) == 0; + } + + _LIBCPP_HIDE_FROM_ABI friend + constexpr strong_ordering operator<=>(const stacktrace_entry& __lhs, const stacktrace_entry& __rhs) noexcept { + return ::__builtin_memcmp(&__lhs.__cursor_, &__rhs.__cursor_, sizeof(native_handle_type)) <=> 0; + } + +private: + native_handle_type __cursor_{}; + bool __engaged_{}; + + _LIBCPP_AVAILABILITY_STACKTRACE _LIBCPP_FUNC_VIS string __unwind_description() const; +}; + +_LIBCPP_FUNC_VIS string to_string(const stacktrace_entry&); + +_LIBCPP_END_NAMESPACE_STD + +#endif diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -923,6 +923,15 @@ export initializer_list export * } + module stacktrace { + header "stacktrace" + export * + + module __stacktrace { + module basic_stacktrace { private header "__stacktrace/basic_stacktrace.h" } + module stacktrace_entry { private header "__stacktrace/stacktrace_entry.h" } + } + } module stdexcept { header "stdexcept" export * diff --git a/libcxx/include/stacktrace b/libcxx/include/stacktrace new file mode 100644 --- /dev/null +++ b/libcxx/include/stacktrace @@ -0,0 +1,151 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_STACKTRACE +#define _LIBCPP_STACKTRACE + +/* +namespace std { + // [stacktrace.entry], class stacktrace_entry + class stacktrace_entry { + public: + using native_handle_type = implementation-defined; + + // [stacktrace.entry.ctor], 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], class template basic_stacktrace + template + 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; + using const_reverse_iterator = std::reverse_iterator; + using difference_type = implementation-defined; + using size_type = implementation-defined; + using allocator_type = Allocator; + + // [stacktrace.basic.ctor], 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); + 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::propagate_on_container_move_assignment::value || + allocator_traits::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; + + [[nodiscard]] 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 + friend bool operator==(const basic_stacktrace& x, + const basic_stacktrace& y) noexcept; + template + friend strong_ordering operator<=>(const basic_stacktrace& x, + const basic_stacktrace& y) noexcept; + + // [stacktrace.basic.mod], modifiers + void swap(basic_stacktrace& other) + noexcept(allocator_traits::propagate_on_container_swap::value || + allocator_traits::is_always_equal::value); + + private: + vector frames_; // exposition only + }; + + // basic_stacktrace typedef-names + using stacktrace = basic_stacktrace>; + + // [stacktrace.basic.nonmem], non-member functions + template + void swap(basic_stacktrace& a, basic_stacktrace& b) + noexcept(noexcept(a.swap(b))); + + string to_string(const stacktrace_entry& f); + + template + string to_string(const basic_stacktrace& st); + + template + basic_ostream& + operator<<(basic_ostream& os, const stacktrace_entry& f); + + template + basic_ostream& + operator<<(basic_ostream& os, const basic_stacktrace& st); + + namespace pmr { + using stacktrace = basic_stacktrace>; + } + + // [stacktrace.basic.hash], hash support + template struct hash; + template<> struct hash; + template struct hash>; +} +*/ + +#include <__config> +#include <__stacktrace/basic_stacktrace.h> +#include <__stacktrace/stacktrace_entry.h> + +#endif diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -395,7 +395,7 @@ // # define __cpp_lib_ranges_zip 202110L // # define __cpp_lib_reference_from_temporary 202202L // # define __cpp_lib_spanstream 202106L -// # define __cpp_lib_stacktrace 202011L +# define __cpp_lib_stacktrace 202011L // # define __cpp_lib_stdatomic_h 202011L # define __cpp_lib_string_contains 202011L # define __cpp_lib_string_resize_and_overwrite 202110L diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -41,6 +41,8 @@ ryu/d2s.cpp ryu/f2s.cpp shared_mutex.cpp + stacktrace/basic_stacktrace.cpp + stacktrace/stacktrace_entry.cpp stdexcept.cpp string.cpp support/runtime/exception_fallback.ipp @@ -227,6 +229,8 @@ cxx_add_common_build_flags(cxx_shared) cxx_set_common_defines(cxx_shared) + set(LIBCXX_USE_LIBUNWIND True) + # Link against LLVM libunwind if (LIBCXXABI_USE_LLVM_UNWINDER) if (NOT LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY AND (TARGET unwind_shared OR HAVE_LIBUNWIND)) @@ -234,8 +238,30 @@ elseif (LIBCXXABI_STATICALLY_LINK_UNWINDER_IN_SHARED_LIBRARY AND (TARGET unwind_static OR HAVE_LIBUNWIND)) # libunwind is already included in libc++abi else() - target_link_libraries(cxx_shared PUBLIC unwind) + target_link_libraries(cxx_shared PUBLIC gcc_s) + set(LIBCXX_USE_LIBUNWIND False) + endif() + if (${LIBCXX_USE_LIBUNWIND}) + find_path(LIBCXX_LIBUNWIND_INCLUDES libunwind.h + PATHS ${LLVM_MAIN_SRC_DIR}/projects/libunwind/include + ${LLVM_MAIN_SRC_DIR}/runtimes/libunwind/include + ${LLVM_MAIN_SRC_DIR}/../libunwind/include + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) endif() + else() + target_link_libraries(cxx_shared PUBLIC gcc_s) + endif() + + target_link_libraries(cxx_shared PUBLIC dl) + + if (LIBCXX_LIBUNWIND_INCLUDES STREQUAL "LIBCXX_LIBUNWIND_INCLUDES-NOTFOUND") + set(LIBCXX_LIBUNWIND_INCLUDES "") + endif() + + if (NOT LIBCXX_LIBUNWIND_INCLUDES STREQUAL "") + target_include_directories(cxx_shared PRIVATE "${LIBCXX_LIBUNWIND_INCLUDES}") endif() # Link against libc++abi diff --git a/libcxx/src/stacktrace/basic_stacktrace.cpp b/libcxx/src/stacktrace/basic_stacktrace.cpp new file mode 100644 --- /dev/null +++ b/libcxx/src/stacktrace/basic_stacktrace.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 +// +//===----------------------------------------------------------------------===// + +#include <__config> +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +#ifdef _LIBCPP_UNWINDLIB_LIBUNWIND +_LIBCPP_FUNC_VIS void __get_stacktrace(const size_t skip, + const size_t max_depth, + __stacktrace_emplace_callback* const emplace, + void* const vec) { + unw_context_t context; + unw_getcontext(&context); + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + + for (size_t i = 0; i != skip; ++i) { + if (unw_step(&cursor) < 0) { + return; + } + } + + for (size_t i = 0; i != max_depth; ++i) { + if (const auto ret = unw_step(&cursor); ret <= 0) { + return; + } + try { + emplace({__stacktrace_entry_cursor_ctor_tag{}, cursor}, vec); + } catch (...) { + return; + } + } +} +#elif defined(_LIBCPP_UNWINDLIB_GCC_S) +namespace { + struct TraceData { + size_t skip; + size_t max_depth; + __stacktrace_emplace_callback* emplace_back; + void* vector; + }; + + _Unwind_Reason_Code unwind_cb(_Unwind_Context* context, void* trace_data_) { + auto& trace_data = *static_cast(trace_data_); + + if (trace_data.max_depth-- == 0) + return _URC_END_OF_STACK; + + if (trace_data.skip != 0) { + --trace_data.skip; + return _URC_NO_REASON; + } + + auto pc = _Unwind_GetIP(context); + try { + trace_data.emplace_back({__stacktrace_entry_cursor_ctor_tag{}, pc}, trace_data.vector); + } catch(...) { + return _URC_END_OF_STACK; + } + return _URC_NO_REASON; + } +} // namespace + +_LIBCPP_FUNC_VIS void __get_stacktrace(const size_t skip, + const size_t max_depth, + __stacktrace_emplace_callback* const emplace_back, + void* const vector) { + TraceData data { + .skip = skip, + .max_depth = max_depth, + .emplace_back = emplace_back, + .vector = vector, + }; + _Unwind_Backtrace(unwind_cb, static_cast(&data)); +} +#endif + +_LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/src/stacktrace/stacktrace_entry.cpp b/libcxx/src/stacktrace/stacktrace_entry.cpp new file mode 100644 --- /dev/null +++ b/libcxx/src/stacktrace/stacktrace_entry.cpp @@ -0,0 +1,725 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _LIBCPP_UNWINDLIB_GCC_S +# include +#endif + +#ifdef __linux__ +# include +# include +# include +# include +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace { +string shorten_name(string str, string_view search, string_view replace) { + std::string::iterator p; + while (true) { + p = std::search(str.begin(), str.end(), search.begin(), search.end()); + if (p == str.end()) + return str; + str.replace(p, p + search.size(), replace); + } + +} + +static constexpr pair rename[] = { + {"std::" _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE), "std"}, + {"> >", ">>"}, + {"std::basic_string, std::allocator>", "std::string"}, + {"std::basic_string_view>", "std::string_view"}, + {"std::integral_constant", "std::false_type"}, + {"std::integral_constant", "std::true_type"}, + {"std::basic_stacktrace>", "std::stacktrace"}, +}; + +string shorten_name(string str) { + for (const auto& e : rename) + str = shorten_name(str, e.first, e.second); + + return str; +} + +string try_demangle(const char* const mangled_name) { + int status; + unique_ptr demangeled_name = + {abi::__cxa_demangle(mangled_name, nullptr, 0, &status), std::free}; + + if (demangeled_name == nullptr) + return {mangled_name, strlen(mangled_name)}; + + string ret{demangeled_name.get(), strlen(demangeled_name.get())}; + return shorten_name(ret); + +} + +string get_function_name_from_dladdr(uintptr_t address) { + Dl_info info; + if (dladdr(reinterpret_cast(address), &info) == 0) + return {}; + if (info.dli_sname != nullptr) + return try_demangle(info.dli_sname); + return {}; +} + +string get_exe_path() { + return std::filesystem::read_symlink("/proc/self/exe"); +} + +string read_executable(string path) { + std::ifstream file(path, std::ios::in | std::ios::binary); + auto size = std::filesystem::file_size(path); + std::string str; + str.resize(size); + file.read(str.data(), str.size()); + return str; +} + +#if __SIZEOF_SIZE_T__ == 8 +using ElfHeader = Elf64_Ehdr; +using ElfSectionHeader = Elf64_Shdr; +#else +using ElfHeader = Elf32_Ehdr; +using ElfSectionHeader = Elf32_Shdr; +#endif + +ElfSectionHeader getSectionHeader(string_view file, size_t offset, size_t index) { + ElfSectionHeader sectionHeader; + std::memcpy(§ionHeader, file.data() + offset + index * sizeof(ElfSectionHeader), sizeof(ElfSectionHeader)); + return sectionHeader; +} + +struct DWARF_Data { + std::unique_ptr data; + const char* debug_info; + const char* debug_abbrev; + const char* debug_str; + const char* debug_str_offsets; + const char* debug_addr; +}; + +DWARF_Data find_dwarf_data(string elf) { + ElfHeader header; + + if (elf.size() < sizeof(header)) + return {}; + + std::memcpy(&header, elf.data(), sizeof(header)); + + if (header.e_ident[EI_MAG0] != 0x7f + || header.e_ident[EI_MAG1] != 'E' + || header.e_ident[EI_MAG2] != 'L' + || header.e_ident[EI_MAG3] != 'F') + return {}; + +#if __SIZEOF_SIZE_T__ == 8 + if (header.e_ident[EI_CLASS] != ELFCLASS64) + return {}; +#else + if (header.e_ident[EI_CLASS] != ELFCLASS32) + return {}; +#endif + + // std::cout << std::boolalpha; + // std::cout << "Is executable: " << (header.e_type == ET_EXEC) << '\n' + // << "Is AMD64: " << (header.e_machine == EM_X86_64) << '\n' + // << "Is current version: " << (header.e_version == EV_CURRENT) << '\n' + // << "Header size: " << header.e_ehsize << '\n' + // << "Section header size: " << header.e_shentsize << '\n' + // << "Section header count: " << header.e_shnum << '\n' + // << "Section header offset: " << header.e_shoff << '\n' + // << "Section name header offset: " << header.e_shstrndx << '\n' + // << '\n'; + ElfSectionHeader stringSectionHeader = getSectionHeader(elf, header.e_shoff, header.e_shstrndx); + // std::cout << "Is String table: " << (stringSectionHeader.sh_type == SHT_STRTAB) << '\n' + // << "table offset: " << stringSectionHeader.sh_offset << '\n' + // << "table size: " << stringSectionHeader.sh_size << '\n' + // << '\n'; + + // std::cout << "String section content: "; + // for (size_t i = 0; i != stringSectionHeader.sh_size; ++i) { + // + // std::cout << (char)((elf.data() + stringSectionHeader.sh_offset)[i] ?: '-'); + // } + // std::cout << '\n'; + + ElfSectionHeader debug_info {}; + ElfSectionHeader debug_str {}; + ElfSectionHeader debug_abbrev {}; + ElfSectionHeader debug_str_offsets {}; + ElfSectionHeader debug_addr {}; + + + for (size_t i = 0; i != header.e_shnum; ++i) { + ElfSectionHeader sectionHeader = getSectionHeader(elf, header.e_shoff, i); + string_view headerName = (elf.data() + stringSectionHeader.sh_offset + sectionHeader.sh_name); + + if (headerName == ".debug_info") + debug_info = sectionHeader; + else if (headerName == ".debug_str") + debug_str = sectionHeader; + else if (headerName == ".debug_abbrev") + debug_abbrev = sectionHeader; + else if (headerName == ".debug_str_offsets") + debug_str_offsets = sectionHeader; + else if (headerName == ".debug_addr") + debug_addr = sectionHeader; + } + + DWARF_Data dwarf_data; + + const auto copy_section = [&](ElfSectionHeader section, char* out) { + return std::copy_n(elf.data() + section.sh_offset, section.sh_size, out); + }; + + dwarf_data.data = std::make_unique(debug_info.sh_size + + debug_abbrev.sh_size + + debug_str.sh_size + + debug_str_offsets.sh_size + + debug_addr.sh_size); + + dwarf_data.debug_info = dwarf_data.data.get(); + auto* debug_abbrev_ptr = copy_section(debug_info, dwarf_data.data.get()); + + dwarf_data.debug_abbrev = debug_abbrev_ptr; + auto* debug_str_ptr = copy_section(debug_abbrev, debug_abbrev_ptr); + + dwarf_data.debug_str = debug_str_ptr; + auto* debug_str_offsets_ptr = copy_section(debug_str, debug_str_ptr); + + dwarf_data.debug_str_offsets = debug_str_offsets_ptr; + auto* debug_addr_ptr = copy_section(debug_str_offsets, debug_str_offsets_ptr); + + dwarf_data.debug_addr = debug_addr_ptr; + copy_section(debug_addr, debug_addr_ptr); + + return dwarf_data; +} + +struct DWARF4_CUHeader { + uint32_t size; + uint16_t version; + uint32_t abbreviation_offset; + uint8_t address_size; +} __attribute__((packed)); + +struct DWARF5_32bit_CUHeader { + uint32_t size; + uint16_t version; + uint8_t type; + uint8_t address_size; + uint32_t abbreviation_offset; +} __attribute__((packed)); + +struct DWARF5_64bit_CUHeader { + uint32_t useless_size; + uint64_t size; + uint16_t version; + uint8_t type; + uint64_t abbreviation_offset; +} __attribute__((packed)); + +union DWARF_CUHeader { + DWARF4_CUHeader dwarf4; + DWARF5_32bit_CUHeader dwarf5_32bit; + DWARF5_64bit_CUHeader dwarf5_64bit; +}; + +size_t consumeNextULEB128(const char*& d) { + size_t number {}; + size_t shift {}; + while (true) { + auto num = *d++; + number |= num & ~0x80 << shift; + if ((num & 0x80) == 0) + break; + shift += 7; + } + return number; +} + +ssize_t consumeNextSLEB128(const char*& d) { + ssize_t number {}; + size_t shift {}; + char num; + do { + num = *d++; + number |= (num & ~0x80) << shift; + shift += 7; + } while ((num & 0x80) != 0); + + if (shift < sizeof(ssize_t) * CHAR_BIT && (num & 0x40)) + number |= (~static_cast(0) << shift); + + return number; +} + +struct DWARF_Attribute { + uint64_t name; + uint64_t form; +}; + +struct DWARF_Abbreviation { + uint32_t tag {}; + uint8_t has_child_entries {}; + std::vector attributes; +}; + +void print_data(const char* data, size_t count) { + std::cerr << std::hex << std::left; + for (size_t i = 0; i != count; ++i) { + if (i % 8 == 0) + std::cerr << '\n'; + std::cerr << (static_cast(data[i]) & 0xFF) << ' '; + } + std::cerr << '\n'; +} + +using DWARF_AbbreviationTable = unordered_map; + +auto parse_abbreviation_table(const char* table) { + DWARF_AbbreviationTable abbreviations; + + while (true) { + uint64_t code = consumeNextULEB128(table); + if (code == 0) + break; + DWARF_Abbreviation abbrev; + + abbrev.tag = consumeNextULEB128(table); + abbrev.has_child_entries = *table++ == DW_CHILDREN_yes; + + while (true) { + DWARF_Attribute attr { + .name = consumeNextULEB128(table), + .form = consumeNextULEB128(table), + }; + if (attr.name == 0 && attr.form == 0) + break; + abbrev.attributes.emplace_back(attr); + + if (attr.form == DW_FORM_implicit_const) + consumeNextULEB128(table); + } + abbreviations[code] = abbrev; + } + + abbreviations[0] = {}; + + return abbreviations; +} + +void consume_attribute(DWARF_Attribute attribute, const char*& data) { + switch (attribute.form) { + case DW_FORM_flag_present: + case DW_FORM_implicit_const: + break; + + case DW_FORM_flag: + case DW_FORM_strx1: + case DW_FORM_data1: + data += 1; + break; + + case DW_FORM_strx2: + case DW_FORM_data2: + data += 2; + break; + + case DW_FORM_ref4: + case DW_FORM_data4: + case DW_FORM_sec_offset: // TODO: Make that depend on the dwarf type size + case DW_FORM_strp: // TODO: Make that depend on the dwarf type size + case DW_FORM_line_strp: // TODO: Make that depend on the dwarf type size + data += 4; + break; + + case DW_FORM_addr: // TODO: Make that depend on the pointer size + data += 8; + break; + + case DW_FORM_udata: + case DW_FORM_rnglistx: + case DW_FORM_addrx: + consumeNextULEB128(data); + break; + + case DW_FORM_sdata: + consumeNextSLEB128(data); + break; + + case DW_FORM_exprloc: + data += consumeNextULEB128(data); + break; + + default: + throw std::runtime_error(string("Unknown DWARF attribute form: ") + to_string(attribute.form)); + } +} + +void consume_tag(const DWARF_Abbreviation& entry, const char*& data) { + for (const auto& e : entry.attributes) + consume_attribute(e, data); +} + +struct CompileUnitInfo { + uint32_t address_offset; + uint32_t string_offsets_offset; +}; + +string_view read_string(CompileUnitInfo cu_info, DWARF_Attribute attr, const DWARF_Data& data) { + switch (attr.form) { + case DW_FORM_strx1: { + uint32_t str_offset; + std::memcpy(&str_offset, + data.debug_str_offsets + cu_info.string_offsets_offset + static_cast(*data.debug_info) * 4, + sizeof(str_offset)); + return data.debug_str + str_offset; + } + + case DW_FORM_strx2: { + uint16_t offset; + std::memcpy(&offset, data.debug_info, sizeof(offset)); + uint32_t str_offset; + std::memcpy(&str_offset, data.debug_str_offsets + cu_info.string_offsets_offset + 4 * offset, sizeof(str_offset)); + return data.debug_str + str_offset; + } + + default: + throw std::runtime_error("Unknown string FORM: " + to_string(attr.form)); + } +} + +size_t read_low_pc(uint32_t addr_offset, DWARF_Attribute attr, const DWARF_Data& data) { + switch (attr.form) { + + case DW_FORM_addrx: { + const char* debug_info = data.debug_info; + return consumeNextULEB128(debug_info); + } + + default: + throw std::runtime_error("Unkown low PC FORM: " + to_string(attr.form)); + } +} + +pair read_high_pc(DWARF_Attribute attr, const DWARF_Data& data) { + switch (attr.form) { + + case DW_FORM_data4: { + uint32_t pc {}; + std::memcpy(&pc, data.debug_info, 4); + return {true, pc}; + } + + default: + throw std::runtime_error("Unknown high PC FORM: " + to_string(attr.form)); + } +} + +struct Function { + string name {}; + uintptr_t low_pc {}; + uintptr_t high_pc {}; +}; + +void parse_subprogram(CompileUnitInfo cu_info, + const DWARF_Abbreviation& abbrev_entry, + DWARF_Data& data, + vector& functions) { + Function func {}; + bool is_relative_high_address {}; + bool ignore_func = false; + + for (const auto& attr : abbrev_entry.attributes) { + switch (attr.name) { + case DW_AT_linkage_name: + func.name = read_string(cu_info, attr, data); + break; + + case DW_AT_name: + if (func.name.empty()) + func.name = read_string(cu_info, attr, data); + break; + + case DW_AT_low_pc: + func.low_pc = read_low_pc(cu_info.address_offset, attr, data); + break; + + case DW_AT_high_pc: { + auto [is_relative, high_pc] = read_high_pc(attr, data); + func.high_pc = high_pc; + is_relative_high_address = is_relative; + break; + } + + case DW_AT_ranges: + throw std::runtime_error("Found DW_AT_ranges"); + + case DW_AT_specification: + case DW_AT_object_pointer: + case DW_AT_frame_base: + case DW_AT_accessibility: + case DW_AT_external: + case DW_AT_type: + case DW_AT_containing_type: + case DW_AT_vtable_elem_location: + case DW_AT_virtuality: + case DW_AT_explicit: + break; + + case DW_AT_decl_line: + case DW_AT_decl_file: + case DW_AT_inline: + break; + + case DW_AT_declaration: + case DW_AT_artificial: + ignore_func = true; + break; + } + consume_attribute(attr, data.debug_info); + } + + uintptr_t low_pc; + std::memcpy(&low_pc, data.debug_addr + cu_info.address_offset + func.low_pc * sizeof(uintptr_t), sizeof(uintptr_t)); + func.low_pc = low_pc; + func.high_pc += low_pc; + + if (!ignore_func && !func.name.empty()) + functions.emplace_back(func); +} + +CompileUnitInfo parse_compile_unit(const DWARF_Abbreviation& entry, DWARF_Data& data) { + CompileUnitInfo info {}; + for (const auto& attr : entry.attributes) { + switch (attr.name) { + case DW_AT_addr_base: + std::memcpy(&info.address_offset, data.debug_info, sizeof(uint32_t)); + break; + + case DW_AT_str_offsets_base: // TODO: Don't assume 32 bits + std::memcpy(&info.string_offsets_offset, data.debug_info, sizeof(uint32_t)); + break; + } + + consume_attribute(attr, data.debug_info); + } + return info; +} + +struct TextOffset { + const char* name; + uintptr_t text_begin; + uintptr_t text_end; +}; + +int exe_load_ptr_cb(dl_phdr_info* info, size_t size, void* data) { + if (size != sizeof(dl_phdr_info)) { + std::cout << "Error: size != sizeof(dl_phdr_info)\n"; + return 1; + } + + // std::cout << "Address: " << info->dlpi_addr << '\n' + // << "Name: " << info->dlpi_name << '\n' + // << "Elf header count: " << info->dlpi_phnum << '\n'; + + if (std::strcmp(info->dlpi_name, "") != 0) + return 0; + + auto& offset = *static_cast(data); + offset.name = info->dlpi_name; + offset.text_begin = info->dlpi_addr; + return 1; +} + +auto get_exe_load_ptr() { + TextOffset offset {}; + dl_iterate_phdr(exe_load_ptr_cb, static_cast(&offset)); + return offset; +} + +struct DWARF_32bit_AddressTableHeader { + uint32_t size; + uint16_t version; + uint8_t address_size; + uint8_t segment_selector_size; +} __attribute__((packed)); + +struct DWARF_64bit_AddressTableHeader { + uint32_t useless_size; + uint64_t size; + uint16_t version; + uint8_t address_size; + uint8_t segment_selector_size; +} __attribute__((packed)); + + +union DWARF_AddressTableHeader { + DWARF_32bit_AddressTableHeader dwarf_32bit; + DWARF_64bit_AddressTableHeader dwarf_64bit; +} __attribute__((packed)); + +vector parse_address_table(const char* address_table) { + vector offsets; + + DWARF_AddressTableHeader header; + std::memcpy(&header, address_table, sizeof(DWARF_32bit_AddressTableHeader)); + + // std::cerr << "Size: " << header.dwarf_32bit.size << '\n' + // << "Address size: " << int(header.dwarf_32bit.address_size) << '\n' + // << "Segment selector size: " << int(header.dwarf_32bit.segment_selector_size) << '\n' + // << "version: " << header.dwarf_32bit.version << '\n'; + + for (size_t i = 0; i != header.dwarf_32bit.size / 8; ++i) { + auto addr = address_table + sizeof(DWARF_32bit_AddressTableHeader) + i * 8; + uintptr_t ptr; + std::memcpy(&ptr, addr, sizeof(uintptr_t)); + // std::cout << "Address: " << ptr << '\n'; + } + + return offsets; +} + +vector parse_dwarf(DWARF_Data data) { + DWARF_CUHeader header; + std::memcpy(&header, data.debug_info, sizeof(header)); + // std::cout << "Unit Size: " << header.dwarf5_32bit.size << '\n' + // << "Version: " << header.dwarf5_32bit.version << '\n' + // << "Abbreviation offset: " << header.dwarf5_32bit.abbreviation_offset << '\n' + // << "Address size: " << static_cast(header.dwarf5_32bit.address_size) << '\n' + // << '\n'; + if (header.dwarf5_32bit.size >= 0xfffffff0) + throw std::runtime_error("It's a 64-bit Unit!"); + if (header.dwarf5_32bit.type != DW_UT_compile) + throw std::runtime_error("Unit type isn't DW_UT_compile; it's " + std::to_string(header.dwarf5_32bit.type)); + data.debug_info += sizeof(DWARF5_32bit_CUHeader); + const auto abbrev_table = parse_abbreviation_table(data.debug_abbrev); + vector address_table; + + vector functions; + + size_t child_count {}; + bool new_subchild = false; + CompileUnitInfo cu_info; + auto memory_area = get_exe_load_ptr(); + if (memory_area.name == nullptr) + std::cerr << "Memory area not found"; + + do { + auto val = consumeNextULEB128(data.debug_info); + const auto& abbrev_entry = abbrev_table.at(val); + + switch (abbrev_entry.tag) { + + case DW_TAG_compile_unit: { + cu_info = parse_compile_unit(abbrev_entry, data); + address_table = parse_address_table(data.debug_addr + + cu_info.address_offset + - sizeof(DWARF_32bit_AddressTableHeader)); + break; + } + + case DW_TAG_subprogram: + parse_subprogram(cu_info, abbrev_entry, data, functions); + break; + + default: + consume_tag(abbrev_entry, data.debug_info); + } + + if (abbrev_entry.has_child_entries) { + ++child_count; + new_subchild = true; + } else { + if (!new_subchild && abbrev_entry.tag == 0) + --child_count; + new_subchild = false; + } + } while(child_count != 0); + + // for (const auto& func : functions) + // std::cout << "Low PC: " << func.low_pc << '\n' + // << "High PC: " << func.high_pc << '\n' + // << "Name: " << try_demangle(func.name.c_str()) << '\n' + // << '\n'; + + return functions; +} + +} // namespace + +#ifdef _LIBCPP_UNWINDLIB_LIBUNWIND +_LIBCPP_FUNC_VIS string stacktrace_entry::__unwind_description() const { + constexpr auto initial_buffer_size = 128; + auto mangled_name = std::make_unique(initial_buffer_size); + size_t alloc_size = initial_buffer_size * 2; + unw_word_t off; + auto cursor = __cursor_; + + if (int ret_val = unw_get_proc_name(&cursor, mangled_name.get(), initial_buffer_size, &off); ret_val != 0) { + while (ret_val == UNW_ENOMEM) { + mangled_name = std::make_unique(alloc_size); + alloc_size *= 2; + ret_val = unw_get_proc_name(&cursor, mangled_name.get(), alloc_size, &off); + } + if (ret_val != 0) { + unw_proc_info_t proc_info; + unw_get_proc_info(&cursor, &proc_info); + return get_function_name_from_dladdr(proc_info.start_ip); + } + } + + return try_demangle(mangled_name.get()); +} +#elif defined(_LIBCPP_UNWINDLIB_GCC_S) + +_LIBCPP_FUNC_VIS string stacktrace_entry::__unwind_description() const { + std::cout << std::hex; + std::cerr << std::hex; + try { + auto exe = read_executable(get_exe_path()); + auto data = find_dwarf_data(std::move(exe)); + auto functions = parse_dwarf(std::move(data)); + + for (const auto& func : functions) { + if (func.low_pc <= __cursor_ && __cursor_ < func.high_pc) + return try_demangle(func.name.c_str()); + } + } catch (const std::runtime_error& err) { std::cout << "Error: " << err.what() << '\n'; } catch (...) {} + + auto name = get_function_name_from_dladdr(__cursor_); + if (name.empty()) + return std::format("0x{:x}", __cursor_); + return name; +} +#endif + +_LIBCPP_FUNC_VIS string to_string(const stacktrace_entry& entry) { + return entry.description(); +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/libcxx/assertions/headers_declare_assertion_handler.sh.cpp b/libcxx/test/libcxx/assertions/headers_declare_assertion_handler.sh.cpp --- a/libcxx/test/libcxx/assertions/headers_declare_assertion_handler.sh.cpp +++ b/libcxx/test/libcxx/assertions/headers_declare_assertion_handler.sh.cpp @@ -532,236 +532,242 @@ using HandlerType = decltype(std::__libcpp_assertion_handler); #endif -// RUN: %{build} -DTEST_92 -#if defined(TEST_92) -# include +// RUN: %{build} -DTEST_90 +#if defined(TEST_90) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif -// RUN: %{build} -DTEST_96 -#if defined(TEST_96) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) -# include +// RUN: %{build} -DTEST_93 +#if defined(TEST_93) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_97 -#if defined(TEST_97) -# include +#if defined(TEST_97) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif -// RUN: %{build} -DTEST_99 -#if defined(TEST_99) -# include +// RUN: %{build} -DTEST_98 +#if defined(TEST_98) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_100 -#if defined(TEST_100) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) -# include +#if defined(TEST_100) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_101 -#if defined(TEST_101) -# include +#if defined(TEST_101) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif -// RUN: %{build} -DTEST_103 -#if defined(TEST_103) && !defined(_LIBCPP_HAS_NO_THREADS) -# include +// RUN: %{build} -DTEST_102 +#if defined(TEST_102) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_104 -#if defined(TEST_104) -# include +#if defined(TEST_104) && !defined(_LIBCPP_HAS_NO_THREADS) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_105 #if defined(TEST_105) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_106 #if defined(TEST_106) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_107 #if defined(TEST_107) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif -// RUN: %{build} -DTEST_109 -#if defined(TEST_109) -# include +// RUN: %{build} -DTEST_108 +#if defined(TEST_108) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_110 #if defined(TEST_110) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_111 #if defined(TEST_111) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_112 #if defined(TEST_112) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_113 #if defined(TEST_113) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_114 #if defined(TEST_114) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_115 #if defined(TEST_115) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif -// RUN: %{build} -DTEST_118 -#if defined(TEST_118) -# include +// RUN: %{build} -DTEST_116 +#if defined(TEST_116) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_119 -#if defined(TEST_119) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES) -# include +#if defined(TEST_119) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_120 -#if defined(TEST_120) && __cplusplus >= 201103L -# include +#if defined(TEST_120) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_121 #if defined(TEST_121) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_122 -#if defined(TEST_122) -# include +#if defined(TEST_122) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_123 #if defined(TEST_123) -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_124 -#if defined(TEST_124) && __cplusplus >= 201103L -# include +#if defined(TEST_124) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_125 #if defined(TEST_125) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_126 #if defined(TEST_126) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_127 -#if defined(TEST_127) -# include +#if defined(TEST_127) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_128 -#if defined(TEST_128) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L -# include +#if defined(TEST_128) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_129 -#if defined(TEST_129) && __cplusplus >= 201103L -# include +#if defined(TEST_129) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_130 -#if defined(TEST_130) -# include +#if defined(TEST_130) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_131 -#if defined(TEST_131) && __cplusplus >= 201103L -# include +#if defined(TEST_131) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_132 -#if defined(TEST_132) -# include +#if defined(TEST_132) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_133 -#if defined(TEST_133) && __cplusplus >= 201103L -# include +#if defined(TEST_133) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_134 #if defined(TEST_134) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_135 -#if defined(TEST_135) -# include +#if defined(TEST_135) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_136 -#if defined(TEST_136) && __cplusplus >= 201103L -# include +#if defined(TEST_136) +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_137 -#if defined(TEST_137) -# include +#if defined(TEST_137) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif // RUN: %{build} -DTEST_138 #if defined(TEST_138) +# include + using HandlerType = decltype(std::__libcpp_assertion_handler); +#endif + +// RUN: %{build} -DTEST_139 +#if defined(TEST_139) # include using HandlerType = decltype(std::__libcpp_assertion_handler); #endif diff --git a/libcxx/test/libcxx/clang_tidy.sh.cpp b/libcxx/test/libcxx/clang_tidy.sh.cpp --- a/libcxx/test/libcxx/clang_tidy.sh.cpp +++ b/libcxx/test/libcxx/clang_tidy.sh.cpp @@ -166,6 +166,7 @@ # include #endif #include +#include #include #include #include diff --git a/libcxx/test/libcxx/double_include.sh.cpp b/libcxx/test/libcxx/double_include.sh.cpp --- a/libcxx/test/libcxx/double_include.sh.cpp +++ b/libcxx/test/libcxx/double_include.sh.cpp @@ -169,6 +169,7 @@ # include #endif #include +#include #include #include #include diff --git a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp --- a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp +++ b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp @@ -259,6 +259,8 @@ #endif #include TEST_MACROS(); +#include +TEST_MACROS(); #include TEST_MACROS(); #include diff --git a/libcxx/test/libcxx/nasty_macros.compile.pass.cpp b/libcxx/test/libcxx/nasty_macros.compile.pass.cpp --- a/libcxx/test/libcxx/nasty_macros.compile.pass.cpp +++ b/libcxx/test/libcxx/nasty_macros.compile.pass.cpp @@ -279,6 +279,7 @@ # include #endif #include +#include #include #include #include diff --git a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp --- a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp +++ b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp @@ -166,6 +166,7 @@ # include #endif #include +#include #include #include #include diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -429,6 +429,8 @@ #include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}} #include <__ranges/zip_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/zip_view.h'}} #include <__split_buffer> // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}} +#include <__stacktrace/basic_stacktrace.h> // expected-error@*:* {{use of private header from outside its module: '__stacktrace/basic_stacktrace.h'}} +#include <__stacktrace/stacktrace_entry.h> // expected-error@*:* {{use of private header from outside its module: '__stacktrace/stacktrace_entry.h'}} #include <__std_stream> // expected-error@*:* {{use of private header from outside its module: '__std_stream'}} #include <__string> // expected-error@*:* {{use of private header from outside its module: '__string'}} #include <__thread/poll_with_backoff.h> // expected-error@*:* {{use of private header from outside its module: '__thread/poll_with_backoff.h'}} diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.compile.pass.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// WARNING: This test was generated by generate_feature_test_macro_components.py +// and should not be edited manually. +// +// clang-format off + +// + +// Test the feature test macros defined by + +/* Constant Value + __cpp_lib_stacktrace 202011L [C++2b] +*/ + +#include +#include "test_macros.h" + +#if TEST_STD_VER < 14 + +# ifdef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should not be defined before c++2b" +# endif + +#elif TEST_STD_VER == 14 + +# ifdef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should not be defined before c++2b" +# endif + +#elif TEST_STD_VER == 17 + +# ifdef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should not be defined before c++2b" +# endif + +#elif TEST_STD_VER == 20 + +# ifdef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should not be defined before c++2b" +# endif + +#elif TEST_STD_VER > 20 + +# ifndef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should be defined in c++2b" +# endif +# if __cpp_lib_stacktrace != 202011L +# error "__cpp_lib_stacktrace should have the value 202011L in c++2b" +# endif + +#endif // TEST_STD_VER > 20 + diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stacktrace.version.pass.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 +// +//===----------------------------------------------------------------------===// +// +// WARNING: This test was generated by generate_feature_test_macro_components.py +// and should not be edited manually. +// +// clang-format off + +// + +// Test the feature test macros defined by + +/* Constant Value + __cpp_lib_stacktrace 202011L [C++2b] +*/ + +#include +#include "test_macros.h" + +#if TEST_STD_VER < 14 + +# ifdef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should not be defined before c++2b" +# endif + +#elif TEST_STD_VER == 14 + +# ifdef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should not be defined before c++2b" +# endif + +#elif TEST_STD_VER == 17 + +# ifdef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should not be defined before c++2b" +# endif + +#elif TEST_STD_VER == 20 + +# ifdef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should not be defined before c++2b" +# endif + +#elif TEST_STD_VER > 20 + +# ifndef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should be defined in c++2b" +# endif +# if __cpp_lib_stacktrace != 202011L +# error "__cpp_lib_stacktrace should have the value 202011L in c++2b" +# endif + +#endif // TEST_STD_VER > 20 + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -4787,17 +4787,11 @@ # error "__cpp_lib_ssize should have the value 201902L in c++2b" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_stacktrace -# error "__cpp_lib_stacktrace should be defined in c++2b" -# endif -# if __cpp_lib_stacktrace != 202011L -# error "__cpp_lib_stacktrace should have the value 202011L in c++2b" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_stacktrace -# error "__cpp_lib_stacktrace should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_stacktrace +# error "__cpp_lib_stacktrace should be defined in c++2b" +# endif +# if __cpp_lib_stacktrace != 202011L +# error "__cpp_lib_stacktrace should have the value 202011L in c++2b" # endif # ifndef __cpp_lib_starts_ends_with diff --git a/libcxx/test/std/utilities/stacktrace/basic_stacktrace/assignment.pass.cpp b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/assignment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/assignment.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include "test_allocator.h" + +static_assert(std::is_nothrow_assignable_v); +static_assert(!std::is_nothrow_assignable_v>, + std::basic_stacktrace>>); + +int main(int, char**) { + { // (const basic_stacktrace&) + std::stacktrace tr = std::stacktrace::current(); + std::stacktrace tr2; + assert(tr != tr2); + tr2 = tr; + assert(tr == tr2); + } + { // (basic_stacktrace&&) + std::stacktrace tr = std::stacktrace::current(); + std::stacktrace tr2; + assert(tr != tr2); + tr2 = std::move(tr); + LIBCPP_ASSERT(tr.empty()); + } + + return 0; +} diff --git a/libcxx/test/std/utilities/stacktrace/basic_stacktrace/ctors.pass.cpp b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/ctors.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/ctors.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 +#include + +#include "test_allocator.h" + +static_assert(std::is_nothrow_default_constructible_v); +static_assert(!std::is_nothrow_default_constructible_v>>); + +template +void test() { + using ST = std::basic_stacktrace; + { // () + ST st; + assert(st.empty()); + } + + { // (const basic_stacktrace&) + ST st = ST::current(); + ST st2 = st; + assert(st.size() == st2.size());; + for (size_t i = 0; i != st.size(); ++i) { + assert(st[i] == st2[i]); + } + } + + { // (basic_stacktrace&&) + ST st = ST::current(); + ST st2 = std::move(st); + LIBCPP_ASSERT(st.empty()); + } +} + +int main(int, char**) { + test>(); + test>(); + using Alloc = test_allocator; + using ST = std::basic_stacktrace; + { // (const allocator_type&) + test_allocator_statistics stats; + ST st{Alloc(&stats)}; + assert(st.get_allocator().get_stats() == &stats); + } + { // (const basic_stacktrace&, const allocator_type&) + test_allocator_statistics stats; + ST st = ST::current(); + ST st2 = {st, Alloc(&stats)}; + assert(st2.get_allocator().get_stats() == &stats); + assert(st.get_allocator().get_stats() == nullptr); + } + { // (basic_stacktrace&&, const allocator_type&) + test_allocator_statistics stats; + ST st = ST::current(); + ST st2 = {std::move(st), Alloc(&stats)}; + assert(st2.get_allocator().get_stats() == &stats); + } +} diff --git a/libcxx/test/std/utilities/stacktrace/basic_stacktrace/current.pass.cpp b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/current.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/current.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include + +template +void test() { + using ST = std::basic_stacktrace; + { + auto st = ST::current(); + assert(std::all_of(st.begin(), st.end(), [](auto& e) { return bool(e); })); + } + { + auto st = ST::current(0); + assert(std::all_of(st.begin(), st.end(), [](auto& e) { return bool(e); })); + } + { + auto st = ST::current(0, 0); + assert(st.empty()); + } + { + auto st = ST::current(0, 1); + assert(st.size() <= 1); + if (!st.empty()) + assert(st[0]); + } +} + +int main(int, char**) { +} diff --git a/libcxx/test/std/utilities/stacktrace/basic_stacktrace/member_types.compile.pass.cpp b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/member_types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/member_types.compile.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include + +#include "test_allocator.h" + +namespace N1 { + using ST = std::stacktrace; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); +} // namespace N1 + +namespace N2 { + using ST = std::basic_stacktrace>; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); +} // namespace N2 diff --git a/libcxx/test/std/utilities/stacktrace/basic_stacktrace/to_string.pass.cpp b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/to_string.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/basic_stacktrace/to_string.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 +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +#include + +void func() { + std::cerr << std::to_string(std::stacktrace::current()); +} + +int main(int, char**) { + // auto stacktrace = std::stacktrace::current(); + // if (!stacktrace.empty()) { + // assert(!std::to_string(stacktrace).empty()); + // } else { + // assert(std::to_string(stacktrace).empty()); + // } + + // assert(stacktrace[0]); + func(); + assert(false); + + return 0; +} diff --git a/libcxx/test/std/utilities/stacktrace/stacktrace_entry/assignment.pass.cpp b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/assignment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/assignment.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept; + +#include +#include + +constexpr bool test_constexpr() { + std::stacktrace_entry entry; + assert(!entry); + std::stacktrace_entry entry2; + entry2 = entry; + assert(!entry2); + + return true; +} + +void test() { + std::stacktrace trace = std::stacktrace::current(); + if (!trace.empty()) { + auto entry = trace[0]; + std::stacktrace_entry entry2; + assert(!entry2); + assert(entry != entry2); + entry2 = entry; + assert(entry2); + assert(entry == entry2); + } +} + +int main(int, char**) { + test(); + test_constexpr(); + static_assert(test_constexpr()); +} diff --git a/libcxx/test/std/utilities/stacktrace/stacktrace_entry/compare.pass.cpp b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/compare.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/compare.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// string source_file() const; + +#include +#include +#include + +constexpr bool test_constexpr() { + std::stacktrace_entry entry; + std::stacktrace_entry entry2; + + assert(entry == entry2); + assert(entry <=> entry2 == std::strong_ordering::equal); + + return true; +} + +void test() { + std::stacktrace trace = std::stacktrace::current(); + if (trace.size() >= 2) { + assert(trace[0] != trace[1]); + assert(trace[0] <=> trace[1] != std::strong_ordering::equal); + + assert(trace[0] == trace[0]); + assert(trace[0] <=> trace[0] == std::strong_ordering::equal); + } +} + +int main(int, char**) { + test(); +} diff --git a/libcxx/test/std/utilities/stacktrace/stacktrace_entry/ctors.pass.cpp b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/ctors.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/ctors.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr stacktrace_entry() noexcept; +// constexpr stacktrace_entry(const stacktrace_entry& other) noexcept; + +#include +#include + +constexpr bool test_constexpr() { + std::stacktrace_entry entry; + assert(!entry); + auto entry2 = entry; + assert(!entry2); + + return true; +} + +int main(int, char**) { + test_constexpr(); + static_assert(test_constexpr()); +} diff --git a/libcxx/test/std/utilities/stacktrace/stacktrace_entry/description.pass.cpp b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/description.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/description.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// string description() const; + +#include +#include +#include +#include + +void test() { + std::stacktrace trace = std::stacktrace::current(); + if (!trace.empty()) { + auto entry = trace[0]; + const std::stacktrace_entry entry2 = entry; + static_assert(std::is_same_v); + assert(entry.description() == entry2.description()); + } +} + +int main(int, char**) { + test(); +} diff --git a/libcxx/test/std/utilities/stacktrace/stacktrace_entry/native_handle.compile.pass.cpp b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/native_handle.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/native_handle.compile.pass.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr native_handle_type native_handle() const noexcept; + +#include +#include + +static_assert(std::is_same_v().native_handle()), + std::stacktrace_entry::native_handle_type>); diff --git a/libcxx/test/std/utilities/stacktrace/stacktrace_entry/source_file.pass.cpp b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/source_file.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/source_file.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// string source_file() const; + +#include +#include +#include +#include + +void test() { + std::stacktrace trace = std::stacktrace::current(); + if (!trace.empty()) { + auto entry = trace[0]; + const std::stacktrace_entry entry2 = entry; + static_assert(std::is_same_v); + assert(entry.source_file() == entry2.source_file()); + } +} + +int main(int, char**) { + test(); +} diff --git a/libcxx/test/std/utilities/stacktrace/stacktrace_entry/source_line.pass.cpp b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/source_line.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/stacktrace/stacktrace_entry/source_line.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 + +// + +// uint_least32_t source_line() const; + +#include +#include +#include +#include + +void test() { + std::stacktrace trace = std::stacktrace::current(); + if (!trace.empty()) { + auto entry = trace[0]; + const std::stacktrace_entry entry2 = entry; + static_assert(std::is_same_v); + assert(entry.source_line() == entry2.source_line()); + } +} + +int main(int, char**) { + test(); +} diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h --- a/libcxx/test/support/test_allocator.h +++ b/libcxx/test/support/test_allocator.h @@ -180,6 +180,7 @@ TEST_CONSTEXPR int get_data() const { return data_; } TEST_CONSTEXPR int get_id() const { return id_; } + TEST_CONSTEXPR test_allocator_statistics* get_stats() { return stats_; }; }; template diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -649,7 +649,6 @@ "name": "__cpp_lib_stacktrace", "values": { "c++2b": 202011 }, "headers": ["stacktrace"], - "unimplemented": True, }, { "name": "__cpp_lib_starts_ends_with", "values": { "c++20": 201711 },