libstdc++: std::stacktrace tweaks
[official-gcc.git] / libstdc++-v3 / include / std / stacktrace
blob9d5f6396aed59da85921373fef7ced4887140efc
1 // <stacktrace> -*- C++ -*-
3 // Copyright The GNU Toolchain Authors.
4 //
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
38 #include <compare>
39 #include <format>
40 #include <new>
41 #include <string>
42 #include <sstream>
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
56   {
57     using uint_least32_t = __UINT_LEAST32_TYPE__;
58     using uintptr_t = __UINTPTR_TYPE__;
60   public:
61     using native_handle_type = uintptr_t;
63     // [stacktrace.entry.ctor], constructors
65     constexpr
66     stacktrace_entry() noexcept = default;
68     constexpr
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
84     string
85     description() const
86     {
87       string __s;
88       _M_get_info(&__s, nullptr, nullptr);
89       return __s;
90     }
92     string
93     source_file() const
94     {
95       string __s;
96       _M_get_info(nullptr, &__s, nullptr);
97       return __s;
98     }
100     uint_least32_t
101     source_line() const
102     {
103       int __line = 0;
104       _M_get_info(nullptr, nullptr, &__line);
105       return __line;
106     }
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; }
119   private:
120     native_handle_type _M_pc = -1;
122     template<typename _Allocator> friend class basic_stacktrace;
124     friend ostream&
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.
129     struct _Info
130     {
131       void* _M_desc;
132       void* _M_file;
133       int* _M_line;
134       void (*_M_set)(void*, const char*);
136       _GLIBCXX_DEFAULT_ABI_TAG
137       static void
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)
143       { }
145       bool
146       _M_populate(native_handle_type);
148       void _M_set_file(const char*);
149       void _M_set_desc(const char*);
150     };
152     bool
153     _M_get_info(string* __desc, string* __file, int* __line) const
154     {
155       if (!*this)
156         return false;
157       return _Info(__desc, __file, __line)._M_populate(_M_pc);
158     }
159   };
161   class __stacktrace_impl
162   {
163   protected:
164     static int _S_current(int (*) (void*, __UINTPTR_TYPE__), void*, int = 0);
165   };
167   // [stacktrace.basic], class template basic_stacktrace
168   template<typename _Allocator>
169     class basic_stacktrace
170     : private __stacktrace_impl
171     {
172       using _AllocTraits = allocator_traits<_Allocator>;
173       using uintptr_t = __UINTPTR_TYPE__;
175     public:
176       using value_type = stacktrace_entry;
177       using const_reference = const value_type&;
178       using reference = value_type&;
179       using const_iterator
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
193       {
194         basic_stacktrace __ret(__alloc);
195         if (auto __cb = __ret._M_prepare()) [[likely]]
196           {
197             if (_S_current(__cb, std::__addressof(__ret)))
198               __ret._M_clear();
199           }
200         return __ret;
201       }
203       [[__gnu__::__noinline__]]
204       static basic_stacktrace
205       current(size_type __skip,
206               const allocator_type& __alloc = allocator_type()) noexcept
207       {
208         basic_stacktrace __ret(__alloc);
209         if (__skip >= __INT_MAX__) [[unlikely]]
210           return __ret;
211         if (auto __cb = __ret._M_prepare()) [[likely]]
212           {
213             if (_S_current(__cb, std::__addressof(__ret), __skip))
214               __ret._M_clear();
215           }
217         return __ret;
218       }
220       [[__gnu__::__noinline__]]
221       static basic_stacktrace
222       current(size_type __skip, size_type __max_depth,
223               const allocator_type& __alloc = allocator_type()) noexcept
224       {
225         __glibcxx_assert(__skip <= (size_type(-1) - __max_depth));
227         basic_stacktrace __ret(__alloc);
228         if (__max_depth == 0) [[unlikely]]
229           return __ret;
230         if (__skip >= __INT_MAX__) [[unlikely]]
231           return __ret;
232         if (auto __cb = __ret._M_prepare(__max_depth)) [[likely]]
233           {
234             if (_S_current(__cb, std::__addressof(__ret), __skip) < 0)
235               __ret._M_clear();
236             else if (__ret.size() > __max_depth)
237               {
238                 __ret._M_impl._M_resize(__max_depth, __ret._M_alloc);
240                 if (__ret._M_impl._M_capacity / 2 >= __max_depth)
241                   {
242                     // shrink to fit
243                     _Impl __tmp = __ret._M_impl._M_clone(__ret._M_alloc);
244                     if (__tmp._M_capacity)
245                       {
246                         __ret._M_clear();
247                         __ret._M_impl = __tmp;
248                       }
249                   }
250               }
251           }
252         return __ret;
253       }
255       basic_stacktrace()
256       noexcept(is_nothrow_default_constructible_v<allocator_type>)
257       { }
259       explicit
260       basic_stacktrace(const allocator_type& __alloc) noexcept
261       : _M_alloc(__alloc)
262       { }
264       basic_stacktrace(const basic_stacktrace& __other) noexcept
265       : basic_stacktrace(__other,
266           _AllocTraits::select_on_container_copy_construction(__other._M_alloc))
267       { }
269       basic_stacktrace(basic_stacktrace&& __other) noexcept
270       : _M_alloc(std::move(__other._M_alloc)),
271         _M_impl(std::__exchange(__other._M_impl, {}))
272       { }
274       basic_stacktrace(const basic_stacktrace& __other,
275                        const allocator_type& __alloc) noexcept
276       : _M_alloc(__alloc)
277       {
278         if (const auto __s = __other._M_impl._M_size)
279           _M_impl = __other._M_impl._M_clone(_M_alloc);
280       }
282       basic_stacktrace(basic_stacktrace&& __other,
283                        const allocator_type& __alloc) noexcept
284       : _M_alloc(__alloc)
285       {
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);
292       }
294       basic_stacktrace&
295       operator=(const basic_stacktrace& __other) noexcept
296       {
297         if (std::__addressof(__other) == this)
298           return *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)
307           {
308             if (_M_alloc != __other._M_alloc)
309               {
310                 // Cannot keep the same storage, so deallocate it now.
311                 _M_clear();
312               }
313           }
315         if (_M_impl._M_capacity < __s)
316           {
317             // Need to allocate new storage.
318             _M_clear();
320             if constexpr (__pocca)
321               _M_alloc = __other._M_alloc;
323             _M_impl = __other._M_impl._M_clone(_M_alloc);
324           }
325         else
326           {
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;
333           }
335         return *this;
336       }
338       basic_stacktrace&
339       operator=(basic_stacktrace&& __other) noexcept
340       {
341         if (std::__addressof(__other) == this)
342           return *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)
352           {
353             // Free current storage and take ownership of __other's storage.
354             _M_clear();
355             _M_impl = std::__exchange(__other._M_impl, {});
356           }
357         else // Allocators are unequal and don't propagate.
358           {
359             const size_type __s = __other.size();
361             if (_M_impl._M_capacity < __s)
362               {
363                 // Need to allocate new storage.
364                 _M_clear();
365                 _M_impl = __other._M_impl._M_clone(_M_alloc);
366               }
367             else
368               {
369                 // Current storage is large enough.
370                 _M_impl._M_resize(0, _M_alloc);
371                 _M_impl._M_assign(__other._M_impl, _M_alloc);
372               }
373           }
375         if constexpr (__pocma)
376           _M_alloc = std::move(__other._M_alloc);
378         return *this;
379       }
381       constexpr ~basic_stacktrace()
382       {
383         _M_clear();
384       }
386       // [stacktrace.basic.obs], observers
387       allocator_type get_allocator() const noexcept { return _M_alloc; }
389       const_iterator
390       begin() const noexcept
391       { return const_iterator{_M_impl._M_frames}; }
393       const_iterator
394       end() const noexcept
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; }
413       size_type
414       max_size() const noexcept
415       { return _Impl::_S_max_size(_M_impl._M_alloc); }
417       const_reference
418       operator[](size_type __n) const noexcept
419       {
420         __glibcxx_assert(__n < size());
421         return begin()[__n];
422       }
424       const_reference
425       at(size_type __n) const
426       {
427         if (__n >= size())
428           __throw_out_of_range("basic_stacktrace::at: bad frame number");
429         return begin()[__n];
430       }
432       // [stacktrace.basic.cmp], comparisons
433       template<typename _Allocator2>
434         friend bool
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
443         {
444           if (auto __s = __x.size() <=> __y.size(); __s != 0)
445             return __s;
446           return std::lexicographical_compare_three_way(__x.begin(), __x.end(),
447                                                         __y.begin(), __y.end());
448         }
450       // [stacktrace.basic.mod], modifiers
451       void
452       swap(basic_stacktrace& __other) noexcept
453       {
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)
458           {
459             __glibcxx_assert(_M_alloc == __other._M_alloc);
460           }
461       }
463     private:
464       bool
465       _M_push_back(const value_type& __x) noexcept
466       {
467         return _M_impl._M_push_back(_M_alloc, __x);
468       }
470       void
471       _M_clear() noexcept
472       {
473         _M_impl._M_resize(0, _M_alloc);
474         _M_impl._M_deallocate(_M_alloc);
475       }
477       // Precondition: __max_depth != 0
478       auto
479       _M_prepare(size_type __max_depth = -1) noexcept
480       -> int (*) (void*, uintptr_t)
481       {
482         auto __cb = +[](void* __data, uintptr_t __pc) {
483           auto& __s = *static_cast<basic_stacktrace*>(__data);
484           stacktrace_entry __f;
485           __f._M_pc = __pc;
486           if (__s._M_push_back(__f)) [[likely]]
487             return 0; // continue tracing
488           return -1; // stop tracing due to error
489         };
491         if (__max_depth > 128)
492           __max_depth = 64; // soft limit, _M_push_back will reallocate
493         else
494           __cb = [](void* __data, uintptr_t __pc) {
495             auto& __s = *static_cast<basic_stacktrace*>(__data);
496             stacktrace_entry __f;
497             __f._M_pc = __pc;
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
503           };
505         if (_M_impl._M_allocate(_M_alloc, __max_depth)) [[likely]]
506           return __cb;
507         return nullptr;
508       }
510       struct _Impl
511       {
512         using pointer = typename _AllocTraits::pointer;
514         pointer   _M_frames   = nullptr;
515         size_type _M_size     = 0;
516         size_type _M_capacity = 0;
518         static size_type
519         _S_max_size(const allocator_type& __alloc) noexcept
520         {
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);
524         }
526 #if __has_builtin(__builtin_operator_new) >= 201802L
527 # define _GLIBCXX_OPERATOR_NEW __builtin_operator_new
528 # define _GLIBCXX_OPERATOR_DELETE __builtin_operator_delete
529 #else
530 # define _GLIBCXX_OPERATOR_NEW ::operator new
531 # define _GLIBCXX_OPERATOR_DELETE ::operator delete
532 #endif
534         // Precondition: _M_frames == nullptr && __n != 0
535         pointer
536         _M_allocate(allocator_type& __alloc, size_type __n) noexcept
537         {
538           if (__n <= _S_max_size(__alloc)) [[likely]]
539             {
540               if constexpr (is_same_v<allocator_type, allocator<value_type>>)
541                 {
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]]
547                     return nullptr;
548                   _M_frames = static_cast<pointer>(__p);
549                 }
550               else
551                 {
552                   __try
553                     {
554                       _M_frames = __alloc.allocate(__n);
555                     }
556                   __catch (const std::bad_alloc&)
557                     {
558                       return nullptr;
559                     }
560                 }
561               _M_capacity = __n;
562               return _M_frames;
563             }
564           return nullptr;
565         }
567         void
568         _M_deallocate(allocator_type& __alloc) noexcept
569         {
570           if (_M_capacity)
571             {
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));
575               else
576                 __alloc.deallocate(_M_frames, _M_capacity);
577               _M_frames = nullptr;
578               _M_capacity = 0;
579             }
580         }
582 #undef _GLIBCXX_OPERATOR_DELETE
583 #undef _GLIBCXX_OPERATOR_NEW
585         // Precondition: __n <= _M_size
586         void
587         _M_resize(size_type __n, allocator_type& __alloc) noexcept
588         {
589           for (size_type __i = __n; __i < _M_size; ++__i)
590             _AllocTraits::destroy(__alloc, &_M_frames[__i]);
591           _M_size = __n;
592         }
594         bool
595         _M_push_back(allocator_type& __alloc,
596                      const stacktrace_entry& __f) noexcept
597         {
598           if (_M_size == _M_capacity) [[unlikely]]
599             {
600               _Impl __tmp = _M_xclone(_M_capacity ? _M_capacity : 8, __alloc);
601               if (!__tmp._M_capacity) [[unlikely]]
602                 return false;
603               _M_resize(0, __alloc);
604               _M_deallocate(__alloc);
605               *this = __tmp;
606             }
607           stacktrace_entry* __addr = std::to_address(_M_frames + _M_size++);
608           _AllocTraits::construct(__alloc, __addr, __f);
609           return true;
610         }
612         // Precondition: _M_size != 0
613         _Impl
614         _M_clone(allocator_type& __alloc) const noexcept
615         {
616           return _M_xclone(_M_size, __alloc);
617         }
619         // Precondition: _M_size != 0 || __extra != 0
620         _Impl
621         _M_xclone(size_type __extra, allocator_type& __alloc) const noexcept
622         {
623           _Impl __i;
624           if (__i._M_allocate(__alloc, _M_size + __extra)) [[likely]]
625             __i._M_assign(*this, __alloc);
626           return __i;
627         }
629         // Precondition: _M_capacity >= __other._M_size
630         void
631         _M_assign(const _Impl& __other, allocator_type& __alloc) noexcept
632         {
633           std::__uninitialized_copy_a(__other._M_frames,
634                                       __other._M_frames + __other._M_size,
635                                       _M_frames, __alloc);
636           _M_size = __other._M_size;
637         }
638       };
640       [[no_unique_address]] allocator_type  _M_alloc{};
642       _Impl _M_impl{};
643     };
645   // basic_stacktrace typedef names
646   using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>;
648   // [stacktrace.basic.nonmem], non-member functions
649   template<typename _Allocator>
650     inline void
651     swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b)
652     noexcept(noexcept(__a.swap(__b)))
653     { __a.swap(__b); }
655   inline ostream&
656   operator<<(ostream& __os, const stacktrace_entry& __f)
657   {
658     string __desc, __file;
659     int __line;
660     if (__f._M_get_info(&__desc, &__file, &__line))
661       {
662         __os.width(4);
663         __os << __desc << " at " << __file << ':' << __line;
664       }
665     return __os;
666   }
668   template<typename _Allocator>
669     inline ostream&
670     operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __st)
671     {
672       for (stacktrace::size_type __i = 0; __i < __st.size(); ++__i)
673         {
674           __os.width(4);
675           __os << __i << "# " << __st[__i] << '\n';
676         }
677       return __os;
678     }
680   inline string
681   to_string(const stacktrace_entry& __f)
682   {
683     std::ostringstream __os;
684     __os << __f;
685     return std::move(__os).str();
686   }
688   template<typename _Allocator>
689     string
690     to_string(const basic_stacktrace<_Allocator>& __st)
691     {
692       std::ostringstream __os;
693       __os << __st;
694       return std::move(__os).str();
695     }
697   template<>
698     class formatter<stacktrace_entry>
699     {
700     public:
701       constexpr typename basic_format_parse_context<char>::iterator
702       parse(basic_format_parse_context<char>& __pc)
703       {
704         __format::_Spec<char> __spec{};
705         const auto __last = __pc.end();
706         auto __first = __pc.begin();
708         auto __finalize = [this, &__spec] {
709           _M_spec = __spec;
710         };
712         auto __finished = [&] {
713           if (__first == __last || *__first == '}')
714             {
715               __finalize();
716               return true;
717             }
718           return false;
719         };
721         if (__finished())
722           return __first;
724         __first = __spec._M_parse_fill_and_align(__first, __last);
725         if (__finished())
726           return __first;
728         __first = __spec._M_parse_width(__first, __last, __pc);
729         if (__finished())
730           return __first;
732         __throw_format_error("format error: invalid format-spec for "
733                              "std::stacktrace_entry");
734       }
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
740         {
741           std::ostringstream __os;
742           __os << __x;
743           return __format::__write(__fc.out(), __os.view());
744         }
746     private:
747       __format::_Spec<char> _M_spec;
748     };
750   template<typename _Allocator>
751     class formatter<basic_stacktrace<_Allocator>>
752     {
753     public:
754       constexpr typename basic_format_parse_context<char>::iterator
755       parse(basic_format_parse_context<char>& __pc)
756       {
757         const auto __first = __pc.begin();
758         if (__first == __pc.end() || *__first == '}')
759           return __first;
760         __throw_format_error("format error: invalid format-spec for "
761                              "std::basic_stacktrace");
762       }
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
768         {
769           std::ostringstream __os;
770           __os << __x;
771           return __format::__write(__fc.out(), __os.view());
772         }
773     };
775   namespace pmr
776   {
777     using stacktrace
778       = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
779   }
781   // [stacktrace.basic.hash], hash support
783   template<>
784     struct hash<stacktrace_entry>
785     {
786       size_t
787       operator()(const stacktrace_entry& __f) const noexcept
788       {
789         using __h = hash<stacktrace_entry::native_handle_type>;
790         return __h()(__f.native_handle());
791       }
792     };
794   template<typename _Allocator>
795     struct hash<basic_stacktrace<_Allocator>>
796     {
797       size_t
798       operator()(const basic_stacktrace<_Allocator>& __st) const noexcept
799       {
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);
804         return __val;
805       }
806     };
808 _GLIBCXX_END_NAMESPACE_VERSION
809 } // namespace std
810 #endif // __cpp_lib_stacktrace
811 #endif /* _GLIBCXX_STACKTRACE */