// Copyright Antony Polukhin, 2016-2024. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_STACKTRACE_SAFE_DUMP_TO_HPP #define BOOST_STACKTRACE_SAFE_DUMP_TO_HPP #include #ifdef BOOST_HAS_PRAGMA_ONCE # pragma once #endif #include #if defined(BOOST_WINDOWS) #include #endif #include #ifdef BOOST_INTEL # pragma warning(push) # pragma warning(disable:2196) // warning #2196: routine is both "inline" and "noinline" #endif /// @file safe_dump_to.hpp \asyncsafe low-level /// functions for dumping call stacks. Dumps are binary serialized arrays of `void*`, /// so you could read them by using 'od -tx8 -An stacktrace_dump_failename' /// Linux command or using boost::stacktrace::stacktrace::from_dump functions. namespace boost { namespace stacktrace { /// @cond namespace detail { typedef const void* native_frame_ptr_t; // TODO: change to `typedef void(*native_frame_ptr_t)();` enum helper{ max_frames_dump = 128 }; BOOST_STACKTRACE_FUNCTION std::size_t from_dump(const char* filename, native_frame_ptr_t* out_frames); BOOST_STACKTRACE_FUNCTION std::size_t dump(const char* file, const native_frame_ptr_t* frames, std::size_t frames_count) noexcept; #if defined(BOOST_WINDOWS) BOOST_STACKTRACE_FUNCTION std::size_t dump(void* fd, const native_frame_ptr_t* frames, std::size_t frames_count) noexcept; #else // POSIX BOOST_STACKTRACE_FUNCTION std::size_t dump(int fd, const native_frame_ptr_t* frames, std::size_t frames_count) noexcept; #endif struct this_thread_frames { // struct is required to avoid warning about usage of inline+BOOST_NOINLINE BOOST_NOINLINE BOOST_STACKTRACE_FUNCTION static std::size_t collect(native_frame_ptr_t* out_frames, std::size_t max_frames_count, std::size_t skip) noexcept; BOOST_NOINLINE static std::size_t safe_dump_to_impl(void* memory, std::size_t size, std::size_t skip) noexcept { typedef boost::stacktrace::detail::native_frame_ptr_t native_frame_ptr_t; if (size < sizeof(native_frame_ptr_t)) { return 0; } native_frame_ptr_t* mem = static_cast(memory); const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(mem, size / sizeof(native_frame_ptr_t) - 1, skip + 1); mem[frames_count] = 0; return frames_count + 1; } template BOOST_NOINLINE static std::size_t safe_dump_to_impl(T file, std::size_t skip, std::size_t max_depth) noexcept { typedef boost::stacktrace::detail::native_frame_ptr_t native_frame_ptr_t; native_frame_ptr_t buffer[boost::stacktrace::detail::max_frames_dump + 1]; if (max_depth > boost::stacktrace::detail::max_frames_dump) { max_depth = boost::stacktrace::detail::max_frames_dump; } const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(buffer, max_depth, skip + 1); buffer[frames_count] = 0; return boost::stacktrace::detail::dump(file, buffer, frames_count + 1); } }; } // namespace detail /// @endcond /// @brief Stores current function call sequence into the memory. /// /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. /// /// @b Async-Handler-Safety: \asyncsafe. /// /// @returns Stored call sequence depth including terminating zero frame. To get the actually consumed bytes multiply this value by the sizeof(boost::stacktrace::frame::native_frame_ptr_t) /// /// @param memory Preallocated buffer to store current function call sequence into. /// /// @param size Size of the preallocated buffer. BOOST_FORCEINLINE std::size_t safe_dump_to(void* memory, std::size_t size) noexcept { return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(memory, size, 0); } /// @brief Stores current function call sequence into the memory. /// /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. /// /// @b Async-Handler-Safety: \asyncsafe. /// /// @returns Stored call sequence depth including terminating zero frame. To get the actually consumed bytes multiply this value by the sizeof(boost::stacktrace::frame::native_frame_ptr_t) /// /// @param skip How many top calls to skip and do not store. /// /// @param memory Preallocated buffer to store current function call sequence into. /// /// @param size Size of the preallocated buffer. BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, void* memory, std::size_t size) noexcept { return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(memory, size, skip); } /// @brief Opens a file and rewrites its content with current function call sequence if such operations are async signal safe. /// /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. /// /// @b Async-Handler-Safety: \asyncsafe. /// /// @returns Stored call sequence depth including terminating zero frame. /// /// @param file File to store current function call sequence. BOOST_FORCEINLINE std::size_t safe_dump_to(const char* file) noexcept { return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(file, 0, boost::stacktrace::detail::max_frames_dump); } /// @brief Opens a file and rewrites its content with current function call sequence if such operations are async signal safe. /// /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. /// /// @b Async-Handler-Safety: \asyncsafe. /// /// @returns Stored call sequence depth including terminating zero frame. /// /// @param skip How many top calls to skip and do not store. /// /// @param max_depth Max call sequence depth to collect. /// /// @param file File to store current function call sequence. BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, const char* file) noexcept { return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(file, skip, max_depth); } #ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED /// @brief Writes into the provided file descriptor the current function call sequence if such operation is async signal safe. /// /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. /// /// @b Async-Handler-Safety: \asyncsafe. /// /// @returns Stored call sequence depth including terminating zero frame. /// /// @param file File to store current function call sequence. BOOST_FORCEINLINE std::size_t safe_dump_to(platform_specific_descriptor fd) noexcept; /// @brief Writes into the provided file descriptor the current function call sequence if such operation is async signal safe. /// /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. /// /// @b Async-Handler-Safety: \asyncsafe. /// /// @returns Stored call sequence depth including terminating zero frame. /// /// @param skip How many top calls to skip and do not store. /// /// @param max_depth Max call sequence depth to collect. /// /// @param file File to store current function call sequence. BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, platform_specific_descriptor fd) noexcept; #elif defined(BOOST_WINDOWS) BOOST_FORCEINLINE std::size_t safe_dump_to(void* fd) noexcept { return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(fd, 0, boost::stacktrace::detail::max_frames_dump); } BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, void* fd) noexcept { return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(fd, skip, max_depth); } #else // POSIX BOOST_FORCEINLINE std::size_t safe_dump_to(int fd) noexcept { return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(fd, 0, boost::stacktrace::detail::max_frames_dump); } BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, int fd) noexcept { return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(fd, skip, max_depth); } #endif }} // namespace boost::stacktrace #ifdef BOOST_INTEL # pragma warning(pop) #endif #include #if !defined(BOOST_STACKTRACE_LINK) || defined(BOOST_STACKTRACE_INTERNAL_BUILD_LIBS) # if defined(BOOST_STACKTRACE_USE_NOOP) # include # include # else # if defined(BOOST_WINDOWS) # include # else # include # endif # if defined(BOOST_WINDOWS) && !defined(BOOST_WINAPI_IS_MINGW) // MinGW does not provide RtlCaptureStackBackTrace. MinGW-w64 does. # include # else # include # endif # endif #endif #endif // BOOST_STACKTRACE_SAFE_DUMP_TO_HPP