Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-09 08:27:17

0001 // Copyright Antony Polukhin, 2016-2024.
0002 //
0003 // Distributed under the Boost Software License, Version 1.0. (See
0004 // accompanying file LICENSE_1_0.txt or copy at
0005 // http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef BOOST_STACKTRACE_STACKTRACE_HPP
0008 #define BOOST_STACKTRACE_STACKTRACE_HPP
0009 
0010 #include <boost/config.hpp>
0011 #ifdef BOOST_HAS_PRAGMA_ONCE
0012 #   pragma once
0013 #endif
0014 
0015 #include <boost/core/no_exceptions_support.hpp>
0016 #include <boost/container_hash/hash_fwd.hpp>
0017 
0018 #include <iosfwd>
0019 #include <string>
0020 #include <vector>
0021 
0022 #ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
0023 #   include <type_traits>
0024 #endif
0025 
0026 #include <boost/stacktrace/stacktrace_fwd.hpp>
0027 #include <boost/stacktrace/safe_dump_to.hpp>
0028 #include <boost/stacktrace/detail/frame_decl.hpp>
0029 #include <boost/stacktrace/frame.hpp>
0030 
0031 #ifdef BOOST_INTEL
0032 #   pragma warning(push)
0033 #   pragma warning(disable:2196) // warning #2196: routine is both "inline" and "noinline"
0034 #endif
0035 
0036 #if defined(BOOST_MSVC)
0037 
0038 extern "C" {
0039 
0040 BOOST_SYMBOL_EXPORT inline void* boost_stacktrace_impl_return_nullptr() { return nullptr; }
0041 const char* boost_stacktrace_impl_current_exception_stacktrace();
0042 bool* boost_stacktrace_impl_ref_capture_stacktraces_at_throw();
0043 
0044 }
0045 
0046 #ifdef _M_IX86
0047 #   pragma comment(linker, "/ALTERNATENAME:_boost_stacktrace_impl_current_exception_stacktrace=_boost_stacktrace_impl_return_nullptr")
0048 #   pragma comment(linker, "/ALTERNATENAME:_boost_stacktrace_impl_ref_capture_stacktraces_at_throw=_boost_stacktrace_impl_return_nullptr")
0049 #else
0050 #   pragma comment(linker, "/ALTERNATENAME:boost_stacktrace_impl_current_exception_stacktrace=boost_stacktrace_impl_return_nullptr")
0051 #   pragma comment(linker, "/ALTERNATENAME:boost_stacktrace_impl_ref_capture_stacktraces_at_throw=boost_stacktrace_impl_return_nullptr")
0052 #endif
0053 
0054 #endif
0055 
0056 namespace boost { namespace stacktrace {
0057 
0058 namespace impl {
0059 
0060 #if defined(__GNUC__) && defined(__ELF__)
0061 
0062 BOOST_NOINLINE BOOST_SYMBOL_VISIBLE __attribute__((weak))
0063 const char* current_exception_stacktrace() noexcept;
0064 
0065 BOOST_NOINLINE BOOST_SYMBOL_VISIBLE __attribute__((weak))
0066 bool& ref_capture_stacktraces_at_throw() noexcept;
0067 
0068 #endif
0069 
0070 } // namespace impl
0071 
0072 /// Class that on construction copies minimal information about call stack into its internals and provides access to that information.
0073 /// @tparam Allocator Allocator to use during stack capture.
0074 template <class Allocator>
0075 class basic_stacktrace {
0076     std::vector<boost::stacktrace::frame, Allocator> impl_;
0077     typedef boost::stacktrace::detail::native_frame_ptr_t native_frame_ptr_t;
0078 
0079     /// @cond
0080     void fill(native_frame_ptr_t* begin, std::size_t size) {
0081         if (!size) {
0082             return;
0083         }
0084 
0085         impl_.reserve(static_cast<std::size_t>(size));
0086         for (std::size_t i = 0; i < size; ++i) {
0087             if (!begin[i]) {
0088                 return;
0089             }
0090             impl_.push_back(
0091                 frame(begin[i])
0092             );
0093         }
0094     }
0095 
0096     static std::size_t frames_count_from_buffer_size(std::size_t buffer_size) noexcept {
0097         const std::size_t ret = (buffer_size > sizeof(native_frame_ptr_t) ? buffer_size / sizeof(native_frame_ptr_t) : 0);
0098         return (ret > 1024 ? 1024 : ret); // Dealing with suspiciously big sizes
0099     }
0100 
0101     BOOST_NOINLINE void init(std::size_t frames_to_skip, std::size_t max_depth) {
0102         constexpr std::size_t buffer_size = 128;
0103         if (!max_depth) {
0104             return;
0105         }
0106 
0107         BOOST_TRY {
0108             {   // Fast path without additional allocations
0109                 native_frame_ptr_t buffer[buffer_size];
0110                 const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(buffer, buffer_size < max_depth ? buffer_size : max_depth, frames_to_skip + 1);
0111                 if (buffer_size > frames_count || frames_count == max_depth) {
0112                     fill(buffer, frames_count);
0113                     return;
0114                 }
0115             }
0116 
0117             // Failed to fit in `buffer_size`. Allocating memory:
0118 #ifdef BOOST_NO_CXX11_ALLOCATOR
0119             typedef typename Allocator::template rebind<native_frame_ptr_t>::other allocator_void_t;
0120 #else
0121             typedef typename std::allocator_traits<Allocator>::template rebind_alloc<native_frame_ptr_t> allocator_void_t;
0122 #endif
0123             std::vector<native_frame_ptr_t, allocator_void_t> buf(buffer_size * 2, 0, impl_.get_allocator());
0124             do {
0125                 const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(&buf[0], buf.size() < max_depth ? buf.size() : max_depth, frames_to_skip + 1);
0126                 if (buf.size() > frames_count || frames_count == max_depth) {
0127                     fill(&buf[0], frames_count);
0128                     return;
0129                 }
0130 
0131                 buf.resize(buf.size() * 2);
0132             } while (buf.size() < buf.max_size()); // close to `true`, but suppresses `C4127: conditional expression is constant`.
0133         } BOOST_CATCH (...) {
0134             // ignore exception
0135         }
0136         BOOST_CATCH_END
0137     }
0138     /// @endcond
0139 
0140 public:
0141     typedef typename std::vector<boost::stacktrace::frame, Allocator>::value_type             value_type;
0142     typedef typename std::vector<boost::stacktrace::frame, Allocator>::allocator_type         allocator_type;
0143     typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_pointer          pointer;
0144     typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_pointer          const_pointer;
0145     typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reference        reference;
0146     typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reference        const_reference;
0147     typedef typename std::vector<boost::stacktrace::frame, Allocator>::size_type              size_type;
0148     typedef typename std::vector<boost::stacktrace::frame, Allocator>::difference_type        difference_type;
0149     typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_iterator         iterator;
0150     typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_iterator         const_iterator;
0151     typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reverse_iterator reverse_iterator;
0152     typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reverse_iterator const_reverse_iterator;
0153 
0154     /// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
0155     ///
0156     /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
0157     ///
0158     /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
0159     BOOST_FORCEINLINE basic_stacktrace() noexcept
0160         : impl_()
0161     {
0162         init(0 , static_cast<std::size_t>(-1));
0163     }
0164 
0165     /// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
0166     ///
0167     /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
0168     ///
0169     /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
0170     ///
0171     /// @param a Allocator that would be passed to underlying storage.
0172     BOOST_FORCEINLINE explicit basic_stacktrace(const allocator_type& a) noexcept
0173         : impl_(a)
0174     {
0175         init(0 , static_cast<std::size_t>(-1));
0176     }
0177 
0178     /// @brief Stores [skip, skip + max_depth) of the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
0179     ///
0180     /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
0181     ///
0182     /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
0183     ///
0184     /// @param skip How many top calls to skip and do not store in *this.
0185     ///
0186     /// @param max_depth Max call sequence depth to collect.
0187     ///
0188     /// @param a Allocator that would be passed to underlying storage.
0189     ///
0190     /// @throws Nothing. Note that default construction of allocator may throw, however it is
0191     /// performed outside the constructor and exception in `allocator_type()` would not result in calling `std::terminate`.
0192     BOOST_FORCEINLINE basic_stacktrace(std::size_t skip, std::size_t max_depth, const allocator_type& a = allocator_type()) noexcept
0193         : impl_(a)
0194     {
0195         init(skip , max_depth);
0196     }
0197 
0198     /// @b Complexity: O(st.size())
0199     ///
0200     /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
0201     basic_stacktrace(const basic_stacktrace& st)
0202         : impl_(st.impl_)
0203     {}
0204 
0205     /// @b Complexity: O(st.size())
0206     ///
0207     /// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
0208     basic_stacktrace& operator=(const basic_stacktrace& st) {
0209         impl_ = st.impl_;
0210         return *this;
0211     }
0212 
0213 #ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
0214     /// @b Complexity: O(1)
0215     ///
0216     /// @b Async-Handler-Safety: \asyncsafe if Allocator::deallocate is async signal safe.
0217     ~basic_stacktrace() noexcept = default;
0218 #endif
0219 
0220 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
0221     /// @b Complexity: O(1)
0222     ///
0223     /// @b Async-Handler-Safety: \asyncsafe if Allocator construction and copying are async signal safe.
0224     basic_stacktrace(basic_stacktrace&& st) noexcept
0225         : impl_(std::move(st.impl_))
0226     {}
0227 
0228     /// @b Complexity: O(st.size())
0229     ///
0230     /// @b Async-Handler-Safety: \asyncsafe if Allocator construction and copying are async signal safe.
0231     basic_stacktrace& operator=(basic_stacktrace&& st)
0232 #ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
0233         noexcept(( std::is_nothrow_move_assignable< std::vector<boost::stacktrace::frame, Allocator> >::value ))
0234 #else
0235         noexcept
0236 #endif
0237     {
0238         impl_ = std::move(st.impl_);
0239         return *this;
0240     }
0241 #endif
0242 
0243     /// @returns Number of function names stored inside the class.
0244     ///
0245     /// @b Complexity: O(1)
0246     ///
0247     /// @b Async-Handler-Safety: \asyncsafe.
0248     size_type size() const noexcept {
0249         return impl_.size();
0250     }
0251 
0252     /// @param frame_no Zero based index of frame to return. 0
0253     /// is the function index where stacktrace was constructed and
0254     /// index close to this->size() contains function `main()`.
0255     /// @returns frame that references the actual frame info, stored inside *this.
0256     ///
0257     /// @b Complexity: O(1).
0258     ///
0259     /// @b Async-Handler-Safety: \asyncsafe.
0260     const_reference operator[](std::size_t frame_no) const noexcept {
0261         return impl_[frame_no];
0262     }
0263 
0264     /// @b Complexity: O(1)
0265     ///
0266     /// @b Async-Handler-Safety: \asyncsafe.
0267     const_iterator begin() const noexcept { return impl_.begin(); }
0268     /// @b Complexity: O(1)
0269     ///
0270     /// @b Async-Handler-Safety: \asyncsafe.
0271     const_iterator cbegin() const noexcept { return impl_.begin(); }
0272     /// @b Complexity: O(1)
0273     ///
0274     /// @b Async-Handler-Safety: \asyncsafe.
0275     const_iterator end() const noexcept { return impl_.end(); }
0276     /// @b Complexity: O(1)
0277     ///
0278     /// @b Async-Handler-Safety: \asyncsafe.
0279     const_iterator cend() const noexcept { return impl_.end(); }
0280 
0281     /// @b Complexity: O(1)
0282     ///
0283     /// @b Async-Handler-Safety: \asyncsafe.
0284     const_reverse_iterator rbegin() const noexcept { return impl_.rbegin(); }
0285     /// @b Complexity: O(1)
0286     ///
0287     /// @b Async-Handler-Safety: \asyncsafe.
0288     const_reverse_iterator crbegin() const noexcept { return impl_.rbegin(); }
0289     /// @b Complexity: O(1)
0290     ///
0291     /// @b Async-Handler-Safety: \asyncsafe.
0292     const_reverse_iterator rend() const noexcept { return impl_.rend(); }
0293     /// @b Complexity: O(1)
0294     ///
0295     /// @b Async-Handler-Safety: \asyncsafe.
0296     const_reverse_iterator crend() const noexcept { return impl_.rend(); }
0297 
0298 
0299     /// @brief Allows to check that stack trace capturing was successful.
0300     /// @returns `true` if `this->size() != 0`
0301     ///
0302     /// @b Complexity: O(1)
0303     ///
0304     /// @b Async-Handler-Safety: \asyncsafe.
0305     constexpr explicit operator bool () const noexcept { return !empty(); }
0306 
0307     /// @brief Allows to check that stack trace failed.
0308     /// @returns `true` if `this->size() == 0`
0309     ///
0310     /// @b Complexity: O(1)
0311     ///
0312     /// @b Async-Handler-Safety: \asyncsafe.
0313     bool empty() const noexcept { return !size(); }
0314 
0315     const std::vector<boost::stacktrace::frame, Allocator>& as_vector() const noexcept {
0316         return impl_;
0317     }
0318 
0319     /// Constructs stacktrace from basic_istreamable that references the dumped stacktrace. Terminating zero frame is discarded.
0320     ///
0321     /// @b Complexity: O(N)
0322     template <class Char, class Trait>
0323     static basic_stacktrace from_dump(std::basic_istream<Char, Trait>& in, const allocator_type& a = allocator_type()) {
0324         typedef typename std::basic_istream<Char, Trait>::pos_type pos_type;
0325         basic_stacktrace ret(0, 0, a);
0326 
0327         // reserving space
0328         const pos_type pos = in.tellg();
0329         in.seekg(0, in.end);
0330         const std::size_t frames_count = frames_count_from_buffer_size(static_cast<std::size_t>(in.tellg()));
0331         in.seekg(pos);
0332         
0333         if (!frames_count) {
0334             return ret;
0335         }
0336 
0337         native_frame_ptr_t ptr = 0;
0338         ret.impl_.reserve(frames_count);
0339         while (in.read(reinterpret_cast<Char*>(&ptr), sizeof(ptr))) {
0340             if (!ptr) {
0341                 break;
0342             }
0343 
0344             ret.impl_.push_back(frame(ptr));
0345         }
0346 
0347         return ret;
0348     }
0349 
0350     /// Constructs stacktrace from raw memory dump. Terminating zero frame is discarded.
0351     ///
0352     /// @param begin Beginning of the memory where the stacktrace was saved using the boost::stacktrace::safe_dump_to
0353     ///
0354     /// @param buffer_size_in_bytes Size of the memory. Usually the same value that was passed to the boost::stacktrace::safe_dump_to
0355     ///
0356     /// @b Complexity: O(size) in worst case
0357     static basic_stacktrace from_dump(const void* begin, std::size_t buffer_size_in_bytes, const allocator_type& a = allocator_type()) {
0358         basic_stacktrace ret(0, 0, a);
0359         const native_frame_ptr_t* first = static_cast<const native_frame_ptr_t*>(begin);
0360         const std::size_t frames_count = frames_count_from_buffer_size(buffer_size_in_bytes);
0361         if (!frames_count) {
0362             return ret;
0363         }
0364 
0365         const native_frame_ptr_t* const last = first + frames_count;
0366         ret.impl_.reserve(frames_count);
0367         for (; first != last; ++first) {
0368             if (!*first) {
0369                 break;
0370             }
0371 
0372             ret.impl_.push_back(frame(*first));
0373         }
0374 
0375         return ret;
0376     }
0377 
0378     /// Returns a basic_stacktrace object containing a stacktrace captured at
0379     /// the point where the currently handled exception was thrown by its
0380     /// initial throw-expression (i.e. not a rethrow), or an empty
0381     /// basic_stacktrace object if:
0382     ///
0383     /// - the `boost_stacktrace_from_exception` library is not linked to the
0384     ///   current binary, or
0385     /// - the initialization of stacktrace failed, or
0386     /// - stacktrace captures are not enabled for the throwing thread, or
0387     /// - no exception is being handled, or
0388     /// - due to implementation-defined reasons.
0389     ///
0390     /// `alloc` is passed to the constructor of the stacktrace object.
0391     /// Rethrowing an exception using a throw-expression with no operand does
0392     /// not alter the captured stacktrace.
0393     ///
0394     /// Implements https://wg21.link/p2370r1
0395     static basic_stacktrace<Allocator> from_current_exception(const allocator_type& alloc = allocator_type()) noexcept {
0396         // Matches the constant from implementation
0397         constexpr std::size_t kStacktraceDumpSize = 4096;
0398 
0399         const char* trace = nullptr;
0400 #if defined(__GNUC__) && defined(__ELF__)
0401         if (impl::current_exception_stacktrace) {
0402             trace = impl::current_exception_stacktrace();
0403         }
0404 #elif defined(BOOST_MSVC)
0405         trace = boost_stacktrace_impl_current_exception_stacktrace();
0406 #endif
0407 
0408         if (trace) {
0409             try {
0410                 return basic_stacktrace<Allocator>::from_dump(trace, kStacktraceDumpSize, alloc);
0411             } catch (const std::exception&) {
0412                 // ignore
0413             }
0414         }
0415         return basic_stacktrace<Allocator>{0, 0, alloc};
0416     }
0417 };
0418 
0419 /// @brief Compares stacktraces for less, order is platform dependent.
0420 ///
0421 /// @b Complexity: Amortized O(1); worst case O(size())
0422 ///
0423 /// @b Async-Handler-Safety: \asyncsafe.
0424 template <class Allocator1, class Allocator2>
0425 bool operator< (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
0426     return lhs.size() < rhs.size() || (lhs.size() == rhs.size() && lhs.as_vector() < rhs.as_vector());
0427 }
0428 
0429 /// @brief Compares stacktraces for equality.
0430 ///
0431 /// @b Complexity: Amortized O(1); worst case O(size())
0432 ///
0433 /// @b Async-Handler-Safety: \asyncsafe.
0434 template <class Allocator1, class Allocator2>
0435 bool operator==(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
0436     return lhs.as_vector() == rhs.as_vector();
0437 }
0438 
0439 
0440 /// Comparison operators that provide platform dependant ordering and have amortized O(1) complexity; O(size()) worst case complexity; are Async-Handler-Safe.
0441 template <class Allocator1, class Allocator2>
0442 bool operator> (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
0443     return rhs < lhs;
0444 }
0445 
0446 template <class Allocator1, class Allocator2>
0447 bool operator<=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
0448     return !(lhs > rhs);
0449 }
0450 
0451 template <class Allocator1, class Allocator2>
0452 bool operator>=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
0453     return !(lhs < rhs);
0454 }
0455 
0456 template <class Allocator1, class Allocator2>
0457 bool operator!=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
0458     return !(lhs == rhs);
0459 }
0460 
0461 /// Fast hashing support, O(st.size()) complexity; Async-Handler-Safe.
0462 template <class Allocator>
0463 std::size_t hash_value(const basic_stacktrace<Allocator>& st) noexcept {
0464     return boost::hash_range(st.as_vector().begin(), st.as_vector().end());
0465 }
0466 
0467 /// Returns std::string with the stacktrace in a human readable format; unsafe to use in async handlers.
0468 template <class Allocator>
0469 std::string to_string(const basic_stacktrace<Allocator>& bt) {
0470     if (!bt) {
0471         return std::string();
0472     }
0473 
0474     return boost::stacktrace::detail::to_string(&bt.as_vector()[0], bt.size());
0475 }
0476 
0477 /// Outputs stacktrace in a human readable format to the output stream `os`; unsafe to use in async handlers.
0478 template <class CharT, class TraitsT, class Allocator>
0479 std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt) {
0480     return os << boost::stacktrace::to_string(bt);
0481 }
0482 
0483 /// This is the typedef to use unless you'd like to provide a specific allocator to boost::stacktrace::basic_stacktrace.
0484 typedef basic_stacktrace<> stacktrace;
0485 
0486 }} // namespace boost::stacktrace
0487 
0488 #ifdef BOOST_INTEL
0489 #   pragma warning(pop)
0490 #endif
0491 
0492 #endif // BOOST_STACKTRACE_STACKTRACE_HPP