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