1// <stacktrace> -*- C++ -*- 2 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. 9 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. 14 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. 18 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/>. 23 24#ifndef _GLIBCXX_STACKTRACE 25#define _GLIBCXX_STACKTRACE 1 26 27#pragma GCC system_header 28 29#include <bits/c++config.h> 30 31#if __cplusplus > 202002L && _GLIBCXX_HAVE_STACKTRACE 32#include <compare> 33#include <new> 34#include <string> 35#include <sstream> 36#include <bits/stl_algobase.h> 37#include <bits/stl_algo.h> 38#include <bits/stl_iterator.h> 39#include <bits/stl_uninitialized.h> 40#include <ext/numeric_traits.h> 41 42struct __glibcxx_backtrace_state; 43struct __glibcxx_backtrace_simple_data; 44 45extern "C" 46{ 47__glibcxx_backtrace_state* 48__glibcxx_backtrace_create_state(const char*, int, 49 void(*)(void*, const char*, int), 50 void*); 51 52int 53__glibcxx_backtrace_simple(__glibcxx_backtrace_state*, int, 54 int (*) (void*, uintptr_t), 55 void(*)(void*, const char*, int), 56 void*); 57int 58__glibcxx_backtrace_pcinfo(__glibcxx_backtrace_state*, uintptr_t, 59 int (*)(void*, uintptr_t, 60 const char*, int, const char*), 61 void(*)(void*, const char*, int), 62 void*); 63 64int 65__glibcxx_backtrace_syminfo(__glibcxx_backtrace_state*, uintptr_t addr, 66 void (*) (void*, uintptr_t, const char*, 67 uintptr_t, uintptr_t), 68 void(*)(void*, const char*, int), 69 void*); 70} 71 72namespace __cxxabiv1 73{ 74 extern "C" char* 75 __cxa_demangle(const char* __mangled_name, char* __output_buffer, 76 size_t* __length, int* __status); 77} 78 79namespace std _GLIBCXX_VISIBILITY(default) 80{ 81_GLIBCXX_BEGIN_NAMESPACE_VERSION 82 83#define __cpp_lib_stacktrace 202011L 84 85 // [stacktrace.entry], class stacktrace_entry 86 class stacktrace_entry 87 { 88 using uint_least32_t = __UINT_LEAST32_TYPE__; 89 using uintptr_t = __UINTPTR_TYPE__; 90 91 public: 92 using native_handle_type = uintptr_t; 93 94 // [stacktrace.entry.ctor], constructors 95 96 constexpr 97 stacktrace_entry() noexcept = default; 98 99 constexpr 100 stacktrace_entry(const stacktrace_entry& __other) noexcept = default; 101 102 constexpr stacktrace_entry& 103 operator=(const stacktrace_entry& __other) noexcept = default; 104 105 ~stacktrace_entry() = default; 106 107 // [stacktrace.entry.obs], observers 108 109 constexpr native_handle_type 110 native_handle() const noexcept { return _M_pc; } 111 112 constexpr explicit operator bool() const noexcept { return _M_pc != -1; } 113 114 // [stacktrace.entry.query], query 115 string 116 description() const 117 { 118 string __s; 119 _M_get_info(&__s, nullptr, nullptr); 120 return __s; 121 } 122 123 string 124 source_file() const 125 { 126 string __s; 127 _M_get_info(nullptr, &__s, nullptr); 128 return __s; 129 } 130 131 uint_least32_t 132 source_line() const 133 { 134 int __line = 0; 135 _M_get_info(nullptr, nullptr, &__line); 136 return __line; 137 } 138 139 // [stacktrace.entry.cmp], comparison 140 friend constexpr bool 141 operator==(const stacktrace_entry& __x, 142 const stacktrace_entry& __y) noexcept 143 { return __x._M_pc == __y._M_pc; } 144 145 friend constexpr strong_ordering 146 operator<=>(const stacktrace_entry& __x, 147 const stacktrace_entry& __y) noexcept 148 { return __x._M_pc <=> __y._M_pc; } 149 150 private: 151 native_handle_type _M_pc = -1; 152 153 template<typename _Allocator> friend class basic_stacktrace; 154 155 static __glibcxx_backtrace_state* 156 _S_init() 157 { 158 static __glibcxx_backtrace_state* __state 159 = __glibcxx_backtrace_create_state(nullptr, 1, nullptr, nullptr); 160 return __state; 161 } 162 163 friend ostream& 164 operator<<(ostream&, const stacktrace_entry&); 165 166 bool 167 _M_get_info(string* __desc, string* __file, int* __line) const 168 { 169 if (!*this) 170 return false; 171 172 struct _Data 173 { 174 string* _M_desc; 175 string* _M_file; 176 int* _M_line; 177 } __data = { __desc, __file, __line }; 178 179 auto __cb = [](void* __data, uintptr_t, const char* __filename, 180 int __lineno, const char* __function) -> int { 181 auto& __d = *static_cast<_Data*>(__data); 182 if (__function && __d._M_desc) 183 *__d._M_desc = _S_demangle(__function); 184 if (__filename && __d._M_file) 185 *__d._M_file = __filename; 186 if (__d._M_line) 187 *__d._M_line = __lineno; 188 return __function != nullptr; 189 }; 190 const auto __state = _S_init(); 191 if (::__glibcxx_backtrace_pcinfo(__state, _M_pc, +__cb, nullptr, &__data)) 192 return true; 193 if (__desc && __desc->empty()) 194 { 195 auto __cb2 = [](void* __data, uintptr_t, const char* __symname, 196 uintptr_t, uintptr_t) { 197 if (__symname) 198 *static_cast<_Data*>(__data)->_M_desc = _S_demangle(__symname); 199 }; 200 if (::__glibcxx_backtrace_syminfo(__state, _M_pc, +__cb2, nullptr, 201 &__data)) 202 return true; 203 } 204 return false; 205 } 206 207 static string 208 _S_demangle(const char* __name) 209 { 210 string __s; 211 int __status; 212 char* __str = __cxxabiv1::__cxa_demangle(__name, nullptr, nullptr, 213 &__status); 214 if (__status == 0) 215 __s = __str; 216 else 217 __s = __name; 218 __builtin_free(__str); 219 return __s; 220 } 221 }; 222 223 // [stacktrace.basic], class template basic_stacktrace 224 template<typename _Allocator> 225 class basic_stacktrace 226 { 227 using _AllocTraits = allocator_traits<_Allocator>; 228 229 public: 230 using value_type = stacktrace_entry; 231 using const_reference = const value_type&; 232 using reference = value_type&; 233 using const_iterator 234 = __gnu_cxx::__normal_iterator<value_type*, basic_stacktrace>; 235 using iterator = const_iterator; 236 using reverse_iterator = std::reverse_iterator<iterator>; 237 using const_reverse_iterator = std::reverse_iterator<const_iterator>; 238 using difference_type = ptrdiff_t; 239 using size_type = unsigned short; 240 using allocator_type = _Allocator; 241 242 // [stacktrace.basic.ctor], creation and assignment 243 244 [[__gnu__::__noinline__]] 245 static basic_stacktrace 246 current(const allocator_type& __alloc = allocator_type()) noexcept 247 { 248 basic_stacktrace __ret(__alloc); 249 if (auto __cb = __ret._M_prepare()) [[likely]] 250 { 251 auto __state = stacktrace_entry::_S_init(); 252 if (__glibcxx_backtrace_simple(__state, 1, __cb, nullptr, 253 std::__addressof(__ret))) 254 __ret._M_clear(); 255 } 256 return __ret; 257 } 258 259 [[__gnu__::__noinline__]] 260 static basic_stacktrace 261 current(size_type __skip, 262 const allocator_type& __alloc = allocator_type()) noexcept 263 { 264 basic_stacktrace __ret(__alloc); 265 if (__skip >= __INT_MAX__) [[unlikely]] 266 return __ret; 267 if (auto __cb = __ret._M_prepare()) [[likely]] 268 { 269 auto __state = stacktrace_entry::_S_init(); 270 if (__glibcxx_backtrace_simple(__state, __skip + 1, __cb, nullptr, 271 std::__addressof(__ret))) 272 __ret._M_clear(); 273 } 274 275 return __ret; 276 } 277 278 [[__gnu__::__noinline__]] 279 static basic_stacktrace 280 current(size_type __skip, size_type __max_depth, 281 const allocator_type& __alloc = allocator_type()) noexcept 282 { 283 __glibcxx_assert(__skip <= (size_type(-1) - __max_depth)); 284 285 basic_stacktrace __ret(__alloc); 286 if (__max_depth == 0) [[unlikely]] 287 return __ret; 288 if (__skip >= __INT_MAX__) [[unlikely]] 289 return __ret; 290 if (auto __cb = __ret._M_prepare(__max_depth)) [[likely]] 291 { 292 auto __state = stacktrace_entry::_S_init(); 293 int __err = __glibcxx_backtrace_simple(__state, __skip + 1, __cb, 294 nullptr, 295 std::__addressof(__ret)); 296 if (__err < 0) 297 __ret._M_clear(); 298 else if (__ret.size() > __max_depth) 299 { 300 __ret._M_impl._M_resize(__max_depth, __ret._M_alloc); 301 302 if (__ret._M_impl._M_capacity / 2 >= __max_depth) 303 { 304 // shrink to fit 305 _Impl __tmp = __ret._M_impl._M_clone(__ret._M_alloc); 306 if (__tmp._M_capacity) 307 { 308 __ret._M_clear(); 309 __ret._M_impl = __tmp; 310 } 311 } 312 } 313 } 314 return __ret; 315 } 316 317 basic_stacktrace() 318 noexcept(is_nothrow_default_constructible_v<allocator_type>) 319 { } 320 321 explicit 322 basic_stacktrace(const allocator_type& __alloc) noexcept 323 : _M_alloc(__alloc) 324 { } 325 326 basic_stacktrace(const basic_stacktrace& __other) noexcept 327 : basic_stacktrace(__other, 328 _AllocTraits::select_on_container_copy_construction(__other._M_alloc)) 329 { } 330 331 basic_stacktrace(basic_stacktrace&& __other) noexcept 332 : _M_alloc(std::move(__other._M_alloc)), 333 _M_impl(std::__exchange(__other._M_impl, {})) 334 { } 335 336 basic_stacktrace(const basic_stacktrace& __other, 337 const allocator_type& __alloc) noexcept 338 : _M_alloc(__alloc) 339 { 340 if (const auto __s = __other._M_impl._M_size) 341 _M_impl = __other._M_impl._M_clone(_M_alloc); 342 } 343 344 basic_stacktrace(basic_stacktrace&& __other, 345 const allocator_type& __alloc) noexcept 346 : _M_alloc(__alloc) 347 { 348 if constexpr (_Allocator::is_always_equal::value) 349 _M_impl = std::__exchange(__other._M_impl, {}); 350 else if (_M_alloc == __other._M_alloc) 351 _M_impl = std::__exchange(__other._M_impl, {}); 352 else if (const auto __s = __other._M_impl._M_size) 353 _M_impl = __other._M_impl._M_clone(_M_alloc); 354 } 355 356 basic_stacktrace& 357 operator=(const basic_stacktrace& __other) noexcept 358 { 359 if (std::__addressof(__other) == this) 360 return *this; 361 362 constexpr bool __pocca 363 = _AllocTraits::propagate_on_container_copy_assignment::value; 364 constexpr bool __always_eq = _AllocTraits::is_always_equal::value; 365 366 const auto __s = __other.size(); 367 368 if constexpr (!__always_eq && __pocca) 369 { 370 if (_M_alloc != __other._M_alloc) 371 { 372 // Cannot keep the same storage, so deallocate it now. 373 _M_clear(); 374 } 375 } 376 377 if (_M_impl._M_capacity < __s) 378 { 379 // Need to allocate new storage. 380 _M_clear(); 381 382 if constexpr (__pocca) 383 _M_alloc = __other._M_alloc; 384 385 _M_impl = __other._M_impl._M_clone(_M_alloc); 386 } 387 else 388 { 389 // Current storage is large enough. 390 _M_impl._M_resize(0, _M_alloc); 391 _M_impl._M_assign(__other._M_impl, _M_alloc); 392 393 if constexpr (__pocca) 394 _M_alloc = __other._M_alloc; 395 } 396 397 return *this; 398 } 399 400 basic_stacktrace& 401 operator=(basic_stacktrace&& __other) noexcept 402 { 403 if (std::__addressof(__other) == this) 404 return *this; 405 406 constexpr bool __pocma 407 = _AllocTraits::propagate_on_container_move_assignment::value; 408 409 if constexpr (_AllocTraits::is_always_equal::value) 410 std::swap(_M_impl, __other._M_impl); 411 else if (_M_alloc == __other._M_alloc) 412 std::swap(_M_impl, __other._M_impl); 413 else if constexpr (__pocma) 414 { 415 // Free current storage and take ownership of __other's storage. 416 _M_clear(); 417 _M_impl = std::__exchange(__other._M_impl, {}); 418 } 419 else // Allocators are unequal and don't propagate. 420 { 421 const size_type __s = __other.size(); 422 423 if (_M_impl._M_capacity < __s) 424 { 425 // Need to allocate new storage. 426 _M_clear(); 427 _M_impl = __other._M_impl._M_clone(_M_alloc); 428 } 429 else 430 { 431 // Current storage is large enough. 432 _M_impl._M_resize(0, _M_alloc); 433 _M_impl._M_assign(__other._M_impl, _M_alloc); 434 } 435 } 436 437 if constexpr (__pocma) 438 _M_alloc = std::move(__other._M_alloc); 439 440 return *this; 441 } 442 443 constexpr ~basic_stacktrace() 444 { 445 _M_clear(); 446 } 447 448 // [stacktrace.basic.obs], observers 449 allocator_type get_allocator() const noexcept { return _M_alloc; } 450 451 const_iterator 452 begin() const noexcept 453 { return const_iterator{_M_impl._M_frames}; } 454 455 const_iterator 456 end() const noexcept 457 { return begin() + size(); } 458 459 const_reverse_iterator 460 rbegin() const noexcept 461 { return std::make_reverse_iterator(end()); } 462 463 const_reverse_iterator 464 rend() const noexcept 465 { return std::make_reverse_iterator(begin()); } 466 467 const_iterator cbegin() const noexcept { return begin(); } 468 const_iterator cend() const noexcept { return end(); } 469 const_reverse_iterator crbegin() const noexcept { return rbegin(); }; 470 const_reverse_iterator crend() const noexcept { return rend(); }; 471 472 [[nodiscard]] bool empty() const noexcept { return size() == 0; } 473 size_type size() const noexcept { return _M_impl._M_size; } 474 475 size_type 476 max_size() const noexcept 477 { return _Impl::_S_max_size(_M_impl._M_alloc); } 478 479 const_reference 480 operator[](size_type __n) const noexcept 481 { 482 __glibcxx_assert(__n < size()); 483 return begin()[__n]; 484 } 485 486 const_reference 487 at(size_type __n) const 488 { 489 if (__n >= size()) 490 __throw_out_of_range("basic_stacktrace::at: bad frame number"); 491 return begin()[__n]; 492 } 493 494 // [stacktrace.basic.cmp], comparisons 495 template<typename _Allocator2> 496 friend bool 497 operator==(const basic_stacktrace& __x, 498 const basic_stacktrace<_Allocator2>& __y) noexcept 499 { return std::equal(__x.begin(), __x.end(), __y.begin(), __y.end()); } 500 501 template<typename _Allocator2> 502 friend strong_ordering 503 operator<=>(const basic_stacktrace& __x, 504 const basic_stacktrace<_Allocator2>& __y) noexcept 505 { 506 if (auto __s = __x.size() <=> __y.size(); __s != 0) 507 return __s; 508 return std::lexicographical_compare_three_way(__x.begin(), __x.end(), 509 __y.begin(), __y.end()); 510 } 511 512 // [stacktrace.basic.mod], modifiers 513 void 514 swap(basic_stacktrace& __other) noexcept 515 { 516 std::swap(_M_impl, __other._M_impl); 517 if constexpr (_AllocTraits::propagate_on_container_swap::value) 518 std::swap(_M_alloc, __other._M_alloc); 519 else if constexpr (!_AllocTraits::is_always_equal::value) 520 { 521 __glibcxx_assert(_M_alloc == __other._M_alloc); 522 } 523 } 524 525 private: 526 bool 527 _M_push_back(const value_type& __x) noexcept 528 { 529 return _M_impl._M_push_back(_M_alloc, __x); 530 } 531 532 void 533 _M_clear() noexcept 534 { 535 _M_impl._M_resize(0, _M_alloc); 536 _M_impl._M_deallocate(_M_alloc); 537 } 538 539 // Precondition: __max_depth != 0 540 auto 541 _M_prepare(size_type __max_depth = -1) noexcept 542 -> int (*) (void*, uintptr_t) 543 { 544 auto __cb = +[](void* __data, uintptr_t __pc) { 545 auto& __s = *static_cast<basic_stacktrace*>(__data); 546 stacktrace_entry __f; 547 __f._M_pc = __pc; 548 if (__s._M_push_back(__f)) [[likely]] 549 return 0; // continue tracing 550 return -1; // stop tracing due to error 551 }; 552 553 if (__max_depth > 128) 554 __max_depth = 64; // soft limit, _M_push_back will reallocate 555 else 556 __cb = [](void* __data, uintptr_t __pc) { 557 auto& __s = *static_cast<basic_stacktrace*>(__data); 558 stacktrace_entry __f; 559 __f._M_pc = __pc; 560 if (__s.size() == __s._M_impl._M_capacity) [[unlikely]] 561 return 1; // stop tracing due to reaching max depth 562 if (__s._M_push_back(__f)) [[likely]] 563 return 0; // continue tracing 564 return -1; // stop tracing due to error 565 }; 566 567 if (_M_impl._M_allocate(_M_alloc, __max_depth)) [[likely]] 568 return __cb; 569 return nullptr; 570 } 571 572 struct _Impl 573 { 574 using pointer = typename _AllocTraits::pointer; 575 576 pointer _M_frames = nullptr; 577 size_type _M_size = 0; 578 size_type _M_capacity = 0; 579 580 static size_type 581 _S_max_size(const allocator_type& __alloc) noexcept 582 { 583 const size_t __size_max = __gnu_cxx::__int_traits<size_type>::__max; 584 const size_t __alloc_max = _AllocTraits::max_size(__alloc); 585 return std::min(__size_max, __alloc_max); 586 } 587 588#if __has_builtin(__builtin_operator_new) >= 201802L 589# define _GLIBCXX_OPERATOR_NEW __builtin_operator_new 590# define _GLIBCXX_OPERATOR_DELETE __builtin_operator_delete 591#else 592# define _GLIBCXX_OPERATOR_NEW ::operator new 593# define _GLIBCXX_OPERATOR_DELETE ::operator delete 594#endif 595 596 // Precondition: _M_frames == nullptr && __n != 0 597 pointer 598 _M_allocate(allocator_type& __alloc, size_type __n) noexcept 599 { 600 if (__n <= _S_max_size(__alloc)) [[likely]] 601 { 602 if constexpr (is_same_v<allocator_type, allocator<value_type>>) 603 { 604 // For std::allocator we use nothrow-new directly so we 605 // don't need to handle bad_alloc exceptions. 606 size_t __nb = __n * sizeof(value_type); 607 void* const __p = _GLIBCXX_OPERATOR_NEW (__nb, nothrow_t{}); 608 if (__p == nullptr) [[unlikely]] 609 return nullptr; 610 _M_frames = static_cast<pointer>(__p); 611 } 612 else 613 { 614 __try 615 { 616 _M_frames = __alloc.allocate(__n); 617 } 618 __catch (const std::bad_alloc&) 619 { 620 return nullptr; 621 } 622 } 623 _M_capacity = __n; 624 return _M_frames; 625 } 626 return nullptr; 627 } 628 629 void 630 _M_deallocate(allocator_type& __alloc) noexcept 631 { 632 if (_M_capacity) 633 { 634 if constexpr (is_same_v<allocator_type, allocator<value_type>>) 635 _GLIBCXX_OPERATOR_DELETE (static_cast<void*>(_M_frames), 636 _M_capacity * sizeof(value_type)); 637 else 638 __alloc.deallocate(_M_frames, _M_capacity); 639 _M_frames = nullptr; 640 _M_capacity = 0; 641 } 642 } 643 644#undef _GLIBCXX_OPERATOR_DELETE 645#undef _GLIBCXX_OPERATOR_NEW 646 647 // Precondition: __n <= _M_size 648 void 649 _M_resize(size_type __n, allocator_type& __alloc) noexcept 650 { 651 for (size_type __i = __n; __i < _M_size; ++__i) 652 _AllocTraits::destroy(__alloc, &_M_frames[__i]); 653 _M_size = __n; 654 } 655 656 bool 657 _M_push_back(allocator_type& __alloc, 658 const stacktrace_entry& __f) noexcept 659 { 660 if (_M_size == _M_capacity) [[unlikely]] 661 { 662 _Impl __tmp = _M_xclone(_M_capacity ? _M_capacity : 8, __alloc); 663 if (!__tmp._M_capacity) [[unlikely]] 664 return false; 665 _M_resize(0, __alloc); 666 _M_deallocate(__alloc); 667 *this = __tmp; 668 } 669 stacktrace_entry* __addr = std::to_address(_M_frames + _M_size++); 670 _AllocTraits::construct(__alloc, __addr, __f); 671 return true; 672 } 673 674 // Precondition: _M_size != 0 675 _Impl 676 _M_clone(allocator_type& __alloc) const noexcept 677 { 678 return _M_xclone(_M_size, __alloc); 679 } 680 681 // Precondition: _M_size != 0 || __extra != 0 682 _Impl 683 _M_xclone(size_type __extra, allocator_type& __alloc) const noexcept 684 { 685 _Impl __i; 686 if (__i._M_allocate(__alloc, _M_size + __extra)) [[likely]] 687 __i._M_assign(*this, __alloc); 688 return __i; 689 } 690 691 // Precondition: _M_capacity >= __other._M_size 692 void 693 _M_assign(const _Impl& __other, allocator_type& __alloc) noexcept 694 { 695 std::__uninitialized_copy_a(__other._M_frames, 696 __other._M_frames + __other._M_size, 697 _M_frames, __alloc); 698 _M_size = __other._M_size; 699 } 700 }; 701 702 [[no_unique_address]] allocator_type _M_alloc{}; 703 704 _Impl _M_impl{}; 705 }; 706 707 // basic_stacktrace typedef names 708 using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>; 709 710 // [stacktrace.basic.nonmem], non-member functions 711 template<typename _Allocator> 712 inline void 713 swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) 714 noexcept(noexcept(__a.swap(__b))) 715 { __a.swap(__b); } 716 717 inline ostream& 718 operator<<(ostream& __os, const stacktrace_entry& __f) 719 { 720 string __desc, __file; 721 int __line; 722 if (__f._M_get_info(&__desc, &__file, &__line)) 723 { 724 __os.width(4); 725 __os << __desc << " at " << __file << ':' << __line; 726 } 727 return __os; 728 } 729 730 template<typename _Allocator> 731 inline ostream& 732 operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __st) 733 { 734 for (stacktrace::size_type __i = 0; __i < __st.size(); ++__i) 735 { 736 __os.width(4); 737 __os << __i << "# " << __st[__i] << '\n'; 738 } 739 return __os; 740 } 741 742 inline string 743 to_string(const stacktrace_entry& __f) 744 { 745 std::ostringstream __os; 746 __os << __f; 747 return std::move(__os).str(); 748 } 749 750 template<typename _Allocator> 751 string 752 to_string(const basic_stacktrace<_Allocator>& __st) 753 { 754 std::ostringstream __os; 755 __os << __st; 756 return std::move(__os).str(); 757 } 758 759 namespace pmr 760 { 761 template<typename _Tp> class polymorphic_allocator; 762 using stacktrace 763 = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>; 764 } 765 766 // [stacktrace.basic.hash], hash support 767 768 template<> 769 struct hash<stacktrace_entry> 770 { 771 size_t 772 operator()(const stacktrace_entry& __f) const noexcept 773 { 774 using __h = hash<stacktrace_entry::native_handle_type>; 775 return __h()(__f.native_handle()); 776 } 777 }; 778 779 template<typename _Allocator> 780 struct hash<basic_stacktrace<_Allocator>> 781 { 782 size_t 783 operator()(const basic_stacktrace<_Allocator>& __st) const noexcept 784 { 785 hash<stacktrace_entry> __h; 786 size_t __val = _Hash_impl::hash(__st.size()); 787 for (const auto& __f : __st) 788 __val = _Hash_impl::__hash_combine(__h(__f), __val); 789 return __val; 790 } 791 }; 792 793_GLIBCXX_END_NAMESPACE_VERSION 794} // namespace std 795#endif // C++23 796 797#endif /* _GLIBCXX_STACKTRACE */ 798