1 // <stacktrace> -*- C++ -*-
3 // Copyright The GNU Toolchain Authors.
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3.
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // Under Section 7 of GPL version 3, you are granted additional
16 // permissions described in the GCC Runtime Library Exception, version
17 // 3.1, as published by the Free Software Foundation.
19 // You should have received a copy of the GNU General Public License and
20 // a copy of the GCC Runtime Library Exception along with this program;
21 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
22 // <http://www.gnu.org/licenses/>.
24 #ifndef _GLIBCXX_STACKTRACE
25 #define _GLIBCXX_STACKTRACE 1
27 #pragma GCC system_header
29 #include <bits/requires_hosted.h> // std::string bound
31 #include <bits/c++config.h>
33 #define __glibcxx_want_stacktrace
34 #define __glibcxx_want_formatters
35 #include <bits/version.h>
37 #ifdef __cpp_lib_stacktrace // C++ >= 23 && hosted && HAVE_STACKTRACE
43 #include <bits/memory_resource.h>
44 #include <bits/stl_algobase.h>
45 #include <bits/stl_algo.h>
46 #include <bits/stl_iterator.h>
47 #include <bits/stl_uninitialized.h>
48 #include <ext/numeric_traits.h>
50 namespace std _GLIBCXX_VISIBILITY(default)
52 _GLIBCXX_BEGIN_NAMESPACE_VERSION
54 // [stacktrace.entry], class stacktrace_entry
55 class stacktrace_entry
57 using uint_least32_t = __UINT_LEAST32_TYPE__;
58 using uintptr_t = __UINTPTR_TYPE__;
61 using native_handle_type = uintptr_t;
63 // [stacktrace.entry.ctor], constructors
66 stacktrace_entry() noexcept = default;
69 stacktrace_entry(const stacktrace_entry& __other) noexcept = default;
71 constexpr stacktrace_entry&
72 operator=(const stacktrace_entry& __other) noexcept = default;
74 ~stacktrace_entry() = default;
76 // [stacktrace.entry.obs], observers
78 constexpr native_handle_type
79 native_handle() const noexcept { return _M_pc; }
81 constexpr explicit operator bool() const noexcept { return _M_pc != -1; }
83 // [stacktrace.entry.query], query
88 _M_get_info(&__s, nullptr, nullptr);
96 _M_get_info(nullptr, &__s, nullptr);
104 _M_get_info(nullptr, nullptr, &__line);
108 // [stacktrace.entry.cmp], comparison
109 friend constexpr bool
110 operator==(const stacktrace_entry& __x,
111 const stacktrace_entry& __y) noexcept
112 { return __x._M_pc == __y._M_pc; }
114 friend constexpr strong_ordering
115 operator<=>(const stacktrace_entry& __x,
116 const stacktrace_entry& __y) noexcept
117 { return __x._M_pc <=> __y._M_pc; }
120 native_handle_type _M_pc = -1;
122 template<typename _Allocator> friend class basic_stacktrace;
125 operator<<(ostream&, const stacktrace_entry&);
127 // Type-erased wrapper for the fields of a stacktrace entry.
128 // This type is independent of which std::string ABI is in use.
134 void (*_M_set)(void*, const char*);
136 _GLIBCXX_DEFAULT_ABI_TAG
138 _S_set(void* __dest, const char* __str)
139 { static_cast<string*>(__dest)->assign(__str); }
141 _Info(string* __desc, string* __file, int* __line)
142 : _M_desc(__desc), _M_file(__file), _M_line(__line), _M_set(_S_set)
146 _M_populate(native_handle_type);
148 void _M_set_file(const char*);
149 void _M_set_desc(const char*);
153 _M_get_info(string* __desc, string* __file, int* __line) const
157 return _Info(__desc, __file, __line)._M_populate(_M_pc);
161 class __stacktrace_impl
164 static int _S_current(int (*) (void*, __UINTPTR_TYPE__), void*, int = 0);
167 // [stacktrace.basic], class template basic_stacktrace
168 template<typename _Allocator>
169 class basic_stacktrace
170 : private __stacktrace_impl
172 using _AllocTraits = allocator_traits<_Allocator>;
173 using uintptr_t = __UINTPTR_TYPE__;
176 using value_type = stacktrace_entry;
177 using const_reference = const value_type&;
178 using reference = value_type&;
180 = __gnu_cxx::__normal_iterator<value_type*, basic_stacktrace>;
181 using iterator = const_iterator;
182 using reverse_iterator = std::reverse_iterator<iterator>;
183 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
184 using difference_type = ptrdiff_t;
185 using size_type = unsigned short;
186 using allocator_type = _Allocator;
188 // [stacktrace.basic.ctor], creation and assignment
190 [[__gnu__::__noinline__]]
191 static basic_stacktrace
192 current(const allocator_type& __alloc = allocator_type()) noexcept
194 basic_stacktrace __ret(__alloc);
195 if (auto __cb = __ret._M_prepare()) [[likely]]
197 if (_S_current(__cb, std::__addressof(__ret)))
203 [[__gnu__::__noinline__]]
204 static basic_stacktrace
205 current(size_type __skip,
206 const allocator_type& __alloc = allocator_type()) noexcept
208 basic_stacktrace __ret(__alloc);
209 if (__skip >= __INT_MAX__) [[unlikely]]
211 if (auto __cb = __ret._M_prepare()) [[likely]]
213 if (_S_current(__cb, std::__addressof(__ret), __skip))
220 [[__gnu__::__noinline__]]
221 static basic_stacktrace
222 current(size_type __skip, size_type __max_depth,
223 const allocator_type& __alloc = allocator_type()) noexcept
225 __glibcxx_assert(__skip <= (size_type(-1) - __max_depth));
227 basic_stacktrace __ret(__alloc);
228 if (__max_depth == 0) [[unlikely]]
230 if (__skip >= __INT_MAX__) [[unlikely]]
232 if (auto __cb = __ret._M_prepare(__max_depth)) [[likely]]
234 if (_S_current(__cb, std::__addressof(__ret), __skip) < 0)
236 else if (__ret.size() > __max_depth)
238 __ret._M_impl._M_resize(__max_depth, __ret._M_alloc);
240 if (__ret._M_impl._M_capacity / 2 >= __max_depth)
243 _Impl __tmp = __ret._M_impl._M_clone(__ret._M_alloc);
244 if (__tmp._M_capacity)
247 __ret._M_impl = __tmp;
256 noexcept(is_nothrow_default_constructible_v<allocator_type>)
260 basic_stacktrace(const allocator_type& __alloc) noexcept
264 basic_stacktrace(const basic_stacktrace& __other) noexcept
265 : basic_stacktrace(__other,
266 _AllocTraits::select_on_container_copy_construction(__other._M_alloc))
269 basic_stacktrace(basic_stacktrace&& __other) noexcept
270 : _M_alloc(std::move(__other._M_alloc)),
271 _M_impl(std::__exchange(__other._M_impl, {}))
274 basic_stacktrace(const basic_stacktrace& __other,
275 const allocator_type& __alloc) noexcept
278 if (const auto __s = __other._M_impl._M_size)
279 _M_impl = __other._M_impl._M_clone(_M_alloc);
282 basic_stacktrace(basic_stacktrace&& __other,
283 const allocator_type& __alloc) noexcept
286 if constexpr (_Allocator::is_always_equal::value)
287 _M_impl = std::__exchange(__other._M_impl, {});
288 else if (_M_alloc == __other._M_alloc)
289 _M_impl = std::__exchange(__other._M_impl, {});
290 else if (const auto __s = __other._M_impl._M_size)
291 _M_impl = __other._M_impl._M_clone(_M_alloc);
295 operator=(const basic_stacktrace& __other) noexcept
297 if (std::__addressof(__other) == this)
300 constexpr bool __pocca
301 = _AllocTraits::propagate_on_container_copy_assignment::value;
302 constexpr bool __always_eq = _AllocTraits::is_always_equal::value;
304 const auto __s = __other.size();
306 if constexpr (!__always_eq && __pocca)
308 if (_M_alloc != __other._M_alloc)
310 // Cannot keep the same storage, so deallocate it now.
315 if (_M_impl._M_capacity < __s)
317 // Need to allocate new storage.
320 if constexpr (__pocca)
321 _M_alloc = __other._M_alloc;
323 _M_impl = __other._M_impl._M_clone(_M_alloc);
327 // Current storage is large enough.
328 _M_impl._M_resize(0, _M_alloc);
329 _M_impl._M_assign(__other._M_impl, _M_alloc);
331 if constexpr (__pocca)
332 _M_alloc = __other._M_alloc;
339 operator=(basic_stacktrace&& __other) noexcept
341 if (std::__addressof(__other) == this)
344 constexpr bool __pocma
345 = _AllocTraits::propagate_on_container_move_assignment::value;
347 if constexpr (_AllocTraits::is_always_equal::value)
348 std::swap(_M_impl, __other._M_impl);
349 else if (_M_alloc == __other._M_alloc)
350 std::swap(_M_impl, __other._M_impl);
351 else if constexpr (__pocma)
353 // Free current storage and take ownership of __other's storage.
355 _M_impl = std::__exchange(__other._M_impl, {});
357 else // Allocators are unequal and don't propagate.
359 const size_type __s = __other.size();
361 if (_M_impl._M_capacity < __s)
363 // Need to allocate new storage.
365 _M_impl = __other._M_impl._M_clone(_M_alloc);
369 // Current storage is large enough.
370 _M_impl._M_resize(0, _M_alloc);
371 _M_impl._M_assign(__other._M_impl, _M_alloc);
375 if constexpr (__pocma)
376 _M_alloc = std::move(__other._M_alloc);
381 constexpr ~basic_stacktrace()
386 // [stacktrace.basic.obs], observers
387 allocator_type get_allocator() const noexcept { return _M_alloc; }
390 begin() const noexcept
391 { return const_iterator{_M_impl._M_frames}; }
395 { return begin() + size(); }
397 const_reverse_iterator
398 rbegin() const noexcept
399 { return std::make_reverse_iterator(end()); }
401 const_reverse_iterator
402 rend() const noexcept
403 { return std::make_reverse_iterator(begin()); }
405 const_iterator cbegin() const noexcept { return begin(); }
406 const_iterator cend() const noexcept { return end(); }
407 const_reverse_iterator crbegin() const noexcept { return rbegin(); };
408 const_reverse_iterator crend() const noexcept { return rend(); };
410 [[nodiscard]] bool empty() const noexcept { return size() == 0; }
411 size_type size() const noexcept { return _M_impl._M_size; }
414 max_size() const noexcept
415 { return _Impl::_S_max_size(_M_impl._M_alloc); }
418 operator[](size_type __n) const noexcept
420 __glibcxx_assert(__n < size());
425 at(size_type __n) const
428 __throw_out_of_range("basic_stacktrace::at: bad frame number");
432 // [stacktrace.basic.cmp], comparisons
433 template<typename _Allocator2>
435 operator==(const basic_stacktrace& __x,
436 const basic_stacktrace<_Allocator2>& __y) noexcept
437 { return std::equal(__x.begin(), __x.end(), __y.begin(), __y.end()); }
439 template<typename _Allocator2>
440 friend strong_ordering
441 operator<=>(const basic_stacktrace& __x,
442 const basic_stacktrace<_Allocator2>& __y) noexcept
444 if (auto __s = __x.size() <=> __y.size(); __s != 0)
446 return std::lexicographical_compare_three_way(__x.begin(), __x.end(),
447 __y.begin(), __y.end());
450 // [stacktrace.basic.mod], modifiers
452 swap(basic_stacktrace& __other) noexcept
454 std::swap(_M_impl, __other._M_impl);
455 if constexpr (_AllocTraits::propagate_on_container_swap::value)
456 std::swap(_M_alloc, __other._M_alloc);
457 else if constexpr (!_AllocTraits::is_always_equal::value)
459 __glibcxx_assert(_M_alloc == __other._M_alloc);
465 _M_push_back(const value_type& __x) noexcept
467 return _M_impl._M_push_back(_M_alloc, __x);
473 _M_impl._M_resize(0, _M_alloc);
474 _M_impl._M_deallocate(_M_alloc);
477 // Precondition: __max_depth != 0
479 _M_prepare(size_type __max_depth = -1) noexcept
480 -> int (*) (void*, uintptr_t)
482 auto __cb = +[](void* __data, uintptr_t __pc) {
483 auto& __s = *static_cast<basic_stacktrace*>(__data);
484 stacktrace_entry __f;
486 if (__s._M_push_back(__f)) [[likely]]
487 return 0; // continue tracing
488 return -1; // stop tracing due to error
491 if (__max_depth > 128)
492 __max_depth = 64; // soft limit, _M_push_back will reallocate
494 __cb = [](void* __data, uintptr_t __pc) {
495 auto& __s = *static_cast<basic_stacktrace*>(__data);
496 stacktrace_entry __f;
498 if (__s.size() == __s._M_impl._M_capacity) [[unlikely]]
499 return 1; // stop tracing due to reaching max depth
500 if (__s._M_push_back(__f)) [[likely]]
501 return 0; // continue tracing
502 return -1; // stop tracing due to error
505 if (_M_impl._M_allocate(_M_alloc, __max_depth)) [[likely]]
512 using pointer = typename _AllocTraits::pointer;
514 pointer _M_frames = nullptr;
515 size_type _M_size = 0;
516 size_type _M_capacity = 0;
519 _S_max_size(const allocator_type& __alloc) noexcept
521 const size_t __size_max = __gnu_cxx::__int_traits<size_type>::__max;
522 const size_t __alloc_max = _AllocTraits::max_size(__alloc);
523 return std::min(__size_max, __alloc_max);
526 #if __has_builtin(__builtin_operator_new) >= 201802L
527 # define _GLIBCXX_OPERATOR_NEW __builtin_operator_new
528 # define _GLIBCXX_OPERATOR_DELETE __builtin_operator_delete
530 # define _GLIBCXX_OPERATOR_NEW ::operator new
531 # define _GLIBCXX_OPERATOR_DELETE ::operator delete
534 // Precondition: _M_frames == nullptr && __n != 0
536 _M_allocate(allocator_type& __alloc, size_type __n) noexcept
538 if (__n <= _S_max_size(__alloc)) [[likely]]
540 if constexpr (is_same_v<allocator_type, allocator<value_type>>)
542 // For std::allocator we use nothrow-new directly so we
543 // don't need to handle bad_alloc exceptions.
544 size_t __nb = __n * sizeof(value_type);
545 void* const __p = _GLIBCXX_OPERATOR_NEW (__nb, nothrow_t{});
546 if (__p == nullptr) [[unlikely]]
548 _M_frames = static_cast<pointer>(__p);
554 _M_frames = __alloc.allocate(__n);
556 __catch (const std::bad_alloc&)
568 _M_deallocate(allocator_type& __alloc) noexcept
572 if constexpr (is_same_v<allocator_type, allocator<value_type>>)
573 _GLIBCXX_OPERATOR_DELETE (static_cast<void*>(_M_frames),
574 _M_capacity * sizeof(value_type));
576 __alloc.deallocate(_M_frames, _M_capacity);
582 #undef _GLIBCXX_OPERATOR_DELETE
583 #undef _GLIBCXX_OPERATOR_NEW
585 // Precondition: __n <= _M_size
587 _M_resize(size_type __n, allocator_type& __alloc) noexcept
589 for (size_type __i = __n; __i < _M_size; ++__i)
590 _AllocTraits::destroy(__alloc, &_M_frames[__i]);
595 _M_push_back(allocator_type& __alloc,
596 const stacktrace_entry& __f) noexcept
598 if (_M_size == _M_capacity) [[unlikely]]
600 _Impl __tmp = _M_xclone(_M_capacity ? _M_capacity : 8, __alloc);
601 if (!__tmp._M_capacity) [[unlikely]]
603 _M_resize(0, __alloc);
604 _M_deallocate(__alloc);
607 stacktrace_entry* __addr = std::to_address(_M_frames + _M_size++);
608 _AllocTraits::construct(__alloc, __addr, __f);
612 // Precondition: _M_size != 0
614 _M_clone(allocator_type& __alloc) const noexcept
616 return _M_xclone(_M_size, __alloc);
619 // Precondition: _M_size != 0 || __extra != 0
621 _M_xclone(size_type __extra, allocator_type& __alloc) const noexcept
624 if (__i._M_allocate(__alloc, _M_size + __extra)) [[likely]]
625 __i._M_assign(*this, __alloc);
629 // Precondition: _M_capacity >= __other._M_size
631 _M_assign(const _Impl& __other, allocator_type& __alloc) noexcept
633 std::__uninitialized_copy_a(__other._M_frames,
634 __other._M_frames + __other._M_size,
636 _M_size = __other._M_size;
640 [[no_unique_address]] allocator_type _M_alloc{};
645 // basic_stacktrace typedef names
646 using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
648 // [stacktrace.basic.nonmem], non-member functions
649 template<typename _Allocator>
651 swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b)
652 noexcept(noexcept(__a.swap(__b)))
656 operator<<(ostream& __os, const stacktrace_entry& __f)
658 string __desc, __file;
660 if (__f._M_get_info(&__desc, &__file, &__line))
663 __os << __desc << " at " << __file << ':' << __line;
668 template<typename _Allocator>
670 operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __st)
672 for (stacktrace::size_type __i = 0; __i < __st.size(); ++__i)
675 __os << __i << "# " << __st[__i] << '\n';
681 to_string(const stacktrace_entry& __f)
683 std::ostringstream __os;
685 return std::move(__os).str();
688 template<typename _Allocator>
690 to_string(const basic_stacktrace<_Allocator>& __st)
692 std::ostringstream __os;
694 return std::move(__os).str();
698 class formatter<stacktrace_entry>
701 constexpr typename basic_format_parse_context<char>::iterator
702 parse(basic_format_parse_context<char>& __pc)
704 __format::_Spec<char> __spec{};
705 const auto __last = __pc.end();
706 auto __first = __pc.begin();
708 auto __finalize = [this, &__spec] {
712 auto __finished = [&] {
713 if (__first == __last || *__first == '}')
724 __first = __spec._M_parse_fill_and_align(__first, __last);
728 __first = __spec._M_parse_width(__first, __last, __pc);
732 __throw_format_error("format error: invalid format-spec for "
733 "std::stacktrace_entry");
736 template<typename _Out>
737 typename basic_format_context<_Out, char>::iterator
738 format(const stacktrace_entry& __x,
739 basic_format_context<_Out, char>& __fc) const
741 std::ostringstream __os;
743 return __format::__write(__fc.out(), __os.view());
747 __format::_Spec<char> _M_spec;
750 template<typename _Allocator>
751 class formatter<basic_stacktrace<_Allocator>>
754 constexpr typename basic_format_parse_context<char>::iterator
755 parse(basic_format_parse_context<char>& __pc)
757 const auto __first = __pc.begin();
758 if (__first == __pc.end() || *__first == '}')
760 __throw_format_error("format error: invalid format-spec for "
761 "std::basic_stacktrace");
764 template<typename _Out>
765 typename basic_format_context<_Out, char>::iterator
766 format(const basic_stacktrace<_Allocator>& __x,
767 basic_format_context<_Out, char>& __fc) const
769 std::ostringstream __os;
771 return __format::__write(__fc.out(), __os.view());
778 = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
781 // [stacktrace.basic.hash], hash support
784 struct hash<stacktrace_entry>
787 operator()(const stacktrace_entry& __f) const noexcept
789 using __h = hash<stacktrace_entry::native_handle_type>;
790 return __h()(__f.native_handle());
794 template<typename _Allocator>
795 struct hash<basic_stacktrace<_Allocator>>
798 operator()(const basic_stacktrace<_Allocator>& __st) const noexcept
800 hash<stacktrace_entry> __h;
801 size_t __val = _Hash_impl::hash(__st.size());
802 for (const auto& __f : __st)
803 __val = _Hash_impl::__hash_combine(__h(__f), __val);
808 _GLIBCXX_END_NAMESPACE_VERSION
810 #endif // __cpp_lib_stacktrace
811 #endif /* _GLIBCXX_STACKTRACE */