Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:51:32

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