223 lines
9.1 KiB
C++
223 lines
9.1 KiB
C++
// Copyright Antony Polukhin, 2016-2022.
|
|
//
|
|
// 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 <boost/config.hpp>
|
|
#ifdef BOOST_HAS_PRAGMA_ONCE
|
|
# pragma once
|
|
#endif
|
|
|
|
#if defined(BOOST_WINDOWS)
|
|
#include <boost/winapi/config.hpp>
|
|
#endif
|
|
|
|
#include <boost/stacktrace/detail/push_options.h>
|
|
|
|
#ifdef BOOST_INTEL
|
|
# pragma warning(push)
|
|
# pragma warning(disable:2196) // warning #2196: routine is both "inline" and "noinline"
|
|
#endif
|
|
|
|
/// @file safe_dump_to.hpp This header contains low-level async-signal-safe 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) BOOST_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) BOOST_NOEXCEPT;
|
|
#else
|
|
// POSIX
|
|
BOOST_STACKTRACE_FUNCTION std::size_t dump(int fd, const native_frame_ptr_t* frames, std::size_t frames_count) BOOST_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) BOOST_NOEXCEPT;
|
|
|
|
BOOST_NOINLINE static std::size_t safe_dump_to_impl(void* memory, std::size_t size, std::size_t skip) BOOST_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<native_frame_ptr_t*>(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 <class T>
|
|
BOOST_NOINLINE static std::size_t safe_dump_to_impl(T file, std::size_t skip, std::size_t max_depth) BOOST_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: Safe.
|
|
///
|
|
/// @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) BOOST_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: Safe.
|
|
///
|
|
/// @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) BOOST_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: Safe.
|
|
///
|
|
/// @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) BOOST_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: Safe.
|
|
///
|
|
/// @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) BOOST_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: Safe.
|
|
///
|
|
/// @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) BOOST_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: Safe.
|
|
///
|
|
/// @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) BOOST_NOEXCEPT;
|
|
|
|
#elif defined(BOOST_WINDOWS)
|
|
|
|
BOOST_FORCEINLINE std::size_t safe_dump_to(void* fd) BOOST_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) BOOST_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) BOOST_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) BOOST_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 <boost/stacktrace/detail/pop_options.h>
|
|
|
|
#if !defined(BOOST_STACKTRACE_LINK) || defined(BOOST_STACKTRACE_INTERNAL_BUILD_LIBS)
|
|
# if defined(BOOST_STACKTRACE_USE_NOOP)
|
|
# include <boost/stacktrace/detail/safe_dump_noop.ipp>
|
|
# include <boost/stacktrace/detail/collect_noop.ipp>
|
|
# else
|
|
# if defined(BOOST_WINDOWS)
|
|
# include <boost/stacktrace/detail/safe_dump_win.ipp>
|
|
# else
|
|
# include <boost/stacktrace/detail/safe_dump_posix.ipp>
|
|
# endif
|
|
# if defined(BOOST_WINDOWS) && !defined(BOOST_WINAPI_IS_MINGW) // MinGW does not provide RtlCaptureStackBackTrace. MinGW-w64 does.
|
|
# include <boost/stacktrace/detail/collect_msvc.ipp>
|
|
# else
|
|
# include <boost/stacktrace/detail/collect_unwind.ipp>
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
#endif // BOOST_STACKTRACE_SAFE_DUMP_TO_HPP
|