mirror of
https://github.com/boostorg/stacktrace.git
synced 2026-02-22 03:32:27 +00:00
Async signal safe backend implmented and notes about async-safety were added to the docs.
This commit is contained in:
@@ -20,17 +20,22 @@
|
||||
|
||||
namespace boost { namespace stacktrace {
|
||||
|
||||
|
||||
#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
|
||||
/// Random access iterator over frames that returns boost::stacktrace::frame on dereference
|
||||
class const_iterator: implementation_details {};
|
||||
|
||||
#else
|
||||
|
||||
// Forward declarations
|
||||
class stacktrace;
|
||||
|
||||
/// Random access iterator over frames that returns `frame` on dereference.
|
||||
class const_iterator: public boost::iterator_facade<
|
||||
const_iterator,
|
||||
frame,
|
||||
boost::random_access_traversal_tag,
|
||||
frame>
|
||||
{
|
||||
/// @cond
|
||||
typedef boost::iterator_facade<
|
||||
const_iterator,
|
||||
frame,
|
||||
@@ -73,9 +78,10 @@ class const_iterator: public boost::iterator_facade<
|
||||
BOOST_ASSERT(impl_ == it.impl_);
|
||||
return it.frame_no_ - frame_no_;
|
||||
}
|
||||
/// @endcond
|
||||
};
|
||||
|
||||
#endif // #ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
|
||||
|
||||
}} // namespace boost::stacktrace
|
||||
|
||||
#endif // BOOST_STACKTRACE_CONST_ITERATOR_HPP
|
||||
|
||||
@@ -25,20 +25,21 @@
|
||||
#endif
|
||||
|
||||
// Backend autodetection
|
||||
#if !defined(BOOST_STACKTRACE_USE_NOOP) && !defined(BOOST_STACKTRACE_USE_WINDBG) && !defined(BOOST_STACKTRACE_USE_LIBUNWIND) \
|
||||
&& !defined(BOOST_STACKTRACE_USE_BACKTRACE) &&!defined(BOOST_STACKTRACE_USE_HEADER)
|
||||
#if !defined(BOOST_STACKTRACE_USE_NOOP) && !defined(BOOST_STACKTRACE_USE_WINDBG) && !defined(BOOST_STACKTRACE_USE_UNWIND) \
|
||||
&& !defined(BOOST_STACKTRACE_USE_BACKTRACE) && !defined(BOOST_STACKTRACE_USE_HEADER)
|
||||
|
||||
#if defined(__has_include) && (!defined(__GNUC__) || __GNUC__ > 4 || BOOST_CLANG)
|
||||
# if __has_include(<execinfo.h>)
|
||||
# define BOOST_STACKTRACE_USE_BACKTRACE
|
||||
# elif __has_include("Dbgeng.h")
|
||||
# if __has_include("Dbgeng.h")
|
||||
# define BOOST_STACKTRACE_USE_WINDBG
|
||||
# else
|
||||
# define BOOST_STACKTRACE_USE_UNWIND
|
||||
# endif
|
||||
#else
|
||||
# if defined(BOOST_WINDOWS)
|
||||
# define BOOST_STACKTRACE_USE_WINDBG
|
||||
# else
|
||||
# define BOOST_STACKTRACE_USE_BACKTRACE
|
||||
# define BOOST_STACKTRACE_USE_UNWIND
|
||||
//# define BOOST_STACKTRACE_USE_BACKTRACE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# include <boost/stacktrace/detail/backend_noop.hpp>
|
||||
#elif defined(BOOST_STACKTRACE_USE_WINDBG)
|
||||
# include <boost/stacktrace/detail/backend_windows.hpp>
|
||||
#elif defined(BOOST_STACKTRACE_USE_BACKTRACE)
|
||||
#elif defined(BOOST_STACKTRACE_USE_BACKTRACE) || defined(BOOST_STACKTRACE_USE_UNWIND)
|
||||
# include <boost/stacktrace/detail/backend_linux.hpp>
|
||||
#else
|
||||
# error No suitable backtrace backend found
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
#include <boost/lexical_cast/try_lexical_convert.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(BOOST_STACKTRACE_USE_UNWIND)
|
||||
#include <unwind.h>
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <execinfo.h>
|
||||
#include <cstdio>
|
||||
@@ -163,6 +167,32 @@ static inline std::string try_demangle(const char* mangled) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(BOOST_STACKTRACE_USE_UNWIND)
|
||||
struct unwind_state {
|
||||
void** current;
|
||||
void** end;
|
||||
};
|
||||
|
||||
inline _Unwind_Reason_Code unwind_callback(struct _Unwind_Context* context, void* arg) {
|
||||
unwind_state* state = static_cast<unwind_state*>(arg);
|
||||
*state->current = reinterpret_cast<void*>(
|
||||
_Unwind_GetIP(context)
|
||||
);
|
||||
|
||||
++state->current;
|
||||
if (!*(state->current - 1) || state->current == state->end) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
backend::backend(void* memory, std::size_t size, std::size_t& hash_code) BOOST_NOEXCEPT
|
||||
: data_(static_cast<backtrace_holder*>(memory))
|
||||
{
|
||||
@@ -170,11 +200,18 @@ backend::backend(void* memory, std::size_t size, std::size_t& hash_code) BOOST_N
|
||||
data_->frames_count = 0;
|
||||
hash_code = 0;
|
||||
|
||||
// TODO: Not async signal safe. Use _Unwind_Backtrace, _Unwind_GetIP
|
||||
#if defined(BOOST_STACKTRACE_USE_UNWIND)
|
||||
unwind_state state = { data_->buffer, data_->buffer + data_->frames_count };
|
||||
_Unwind_Backtrace(&unwind_callback, &state);
|
||||
data_->frames_count = state.current - data_->buffer;
|
||||
#elif defined(BOOST_STACKTRACE_USE_BACKTRACE)
|
||||
data_->frames_count = ::backtrace(data_->buffer, (size - sizeof(backtrace_holder)) / sizeof(void*));
|
||||
if (data_->buffer[data_->frames_count - 1] == 0) {
|
||||
-- data_->frames_count;
|
||||
}
|
||||
#else
|
||||
# error No stacktrace backend defined. Define BOOST_STACKTRACE_USE_UNWIND or BOOST_STACKTRACE_USE_BACKTRACE
|
||||
#endif
|
||||
|
||||
hash_code = boost::hash_range(data_->buffer, data_->buffer + data_->frames_count);
|
||||
}
|
||||
|
||||
@@ -35,33 +35,43 @@ public:
|
||||
frame() = delete;
|
||||
|
||||
/// @brief Copy constructs frame.
|
||||
/// @throws Nothing.
|
||||
///
|
||||
/// @b Complexity: O(1).
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
/// @throws Nothing.
|
||||
frame(const frame&) = default;
|
||||
|
||||
/// @brief Copy assigns frame.
|
||||
/// @throws Nothing.
|
||||
///
|
||||
/// @b Complexity: O(1).
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
/// @throws Nothing.
|
||||
frame& operator=(const frame&) = default;
|
||||
#endif
|
||||
|
||||
/// @brief Constructs frame that can extract information from addr at runtime.
|
||||
/// @throws Nothing.
|
||||
///
|
||||
/// @b Complexity: O(1).
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
/// @throws Nothing.
|
||||
explicit frame(const void* addr) BOOST_NOEXCEPT
|
||||
: addr_(addr)
|
||||
{}
|
||||
|
||||
/// @returns Name of the frame (function name in a human readable form).
|
||||
///
|
||||
/// @b Async-Handler-Safety: Unsafe.
|
||||
/// @throws std::bad_alloc if not enough memory to construct resulting string.
|
||||
std::string name() const {
|
||||
return boost::stacktrace::detail::backend::get_name(address());
|
||||
}
|
||||
|
||||
/// @returns Address of the frame function.
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
/// @throws Nothing.
|
||||
const void* address() const BOOST_NOEXCEPT {
|
||||
return addr_;
|
||||
@@ -70,18 +80,22 @@ public:
|
||||
/// @returns Path to the source file, were the function of the frame is defined. Returns empty string
|
||||
/// if this->source_line() == 0.
|
||||
/// @throws std::bad_alloc if not enough memory to construct resulting string.
|
||||
///
|
||||
/// @b Async-Handler-Safety: Unsafe.
|
||||
std::string source_file() const {
|
||||
return boost::stacktrace::detail::backend::get_source_file(address());
|
||||
}
|
||||
|
||||
/// @returns Code line in the source file, were the function of the frame is defined.
|
||||
/// @throws std::bad_alloc if not enough memory to construct string for internal needs.
|
||||
///
|
||||
/// @b Async-Handler-Safety: Unsafe.
|
||||
std::size_t source_line() const {
|
||||
return boost::stacktrace::detail::backend::get_source_line(address());
|
||||
}
|
||||
};
|
||||
|
||||
/// Comparison operators that provide platform dependant ordering and have O(1) complexity.
|
||||
/// Comparison operators that provide platform dependant ordering and have O(1) complexity; are Async-Handler-Safe.
|
||||
inline bool operator< (const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return lhs.address() < rhs.address(); }
|
||||
inline bool operator> (const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return rhs < lhs; }
|
||||
inline bool operator<=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return !(lhs > rhs); }
|
||||
@@ -89,12 +103,12 @@ inline bool operator>=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { retu
|
||||
inline bool operator==(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return lhs.address() == rhs.address(); }
|
||||
inline bool operator!=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return !(lhs == rhs); }
|
||||
|
||||
/// Hashing support, O(1) complexity.
|
||||
/// Hashing support, O(1) complexity; Async-Handler-Safe.
|
||||
inline std::size_t hash_value(const frame& f) BOOST_NOEXCEPT {
|
||||
return reinterpret_cast<std::size_t>(f.address());
|
||||
}
|
||||
|
||||
/// Outputs stacktrace::frame in a human readable format to output stream.
|
||||
/// Outputs stacktrace::frame in a human readable format to output stream; unsafe to use in async handlers.
|
||||
template <class CharT, class TraitsT>
|
||||
std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const frame& f) {
|
||||
os << f.name();
|
||||
|
||||
@@ -36,29 +36,34 @@ class stacktrace {
|
||||
public:
|
||||
typedef frame reference;
|
||||
|
||||
/// @brief Random access iterator that returns frame.
|
||||
typedef boost::stacktrace::const_iterator iterator;
|
||||
typedef iterator const_iterator;
|
||||
typedef boost::stacktrace::const_iterator const_iterator;
|
||||
typedef std::reverse_iterator<iterator> reverse_iterator;
|
||||
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||
|
||||
/// @brief Stores the current function call sequence inside the class.
|
||||
///
|
||||
/// @b Complexity: O(N) where N is call seaquence length, O(1) for noop backend.
|
||||
/// @b Complexity: O(N) where N is call sequence length, O(1) for noop backend.
|
||||
///
|
||||
/// @b Async-Handler-Safety: Depends on backend, see "Build, Macros and Backends" section.
|
||||
BOOST_FORCEINLINE stacktrace() BOOST_NOEXCEPT
|
||||
: impl_()
|
||||
, hash_code_()
|
||||
, back_(&impl_, sizeof(impl_), hash_code_)
|
||||
{}
|
||||
|
||||
/// @b Complexity: O(1)
|
||||
/// @b Complexity: O(st.size())
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
stacktrace(const stacktrace& st) BOOST_NOEXCEPT
|
||||
: impl_()
|
||||
, hash_code_(st.hash_code_)
|
||||
, back_(st.back_, &impl_)
|
||||
{}
|
||||
|
||||
/// @b Complexity: O(1)
|
||||
/// @b Complexity: O(st.size())
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
stacktrace& operator=(const stacktrace& st) BOOST_NOEXCEPT {
|
||||
hash_code_ = st.hash_code_;
|
||||
back_ = back_;
|
||||
@@ -66,12 +71,16 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @b Complexity: O(N) for libunwind, O(1) for other backends.
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
~stacktrace() BOOST_NOEXCEPT {}
|
||||
|
||||
/// @returns Number of function names stored inside the class.
|
||||
///
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
std::size_t size() const BOOST_NOEXCEPT {
|
||||
return back_.size();
|
||||
}
|
||||
@@ -82,27 +91,45 @@ public:
|
||||
/// @returns frame that references the actual frame info, stored inside *this.
|
||||
///
|
||||
/// @b Complexity: Amortized O(1), O(1) for noop backend.
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
frame operator[](std::size_t frame_no) const BOOST_NOEXCEPT {
|
||||
return *(cbegin() + frame_no);
|
||||
}
|
||||
|
||||
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
const_iterator begin() const BOOST_NOEXCEPT { return const_iterator(&back_, 0); }
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
const_iterator cbegin() const BOOST_NOEXCEPT { return const_iterator(&back_, 0); }
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
const_iterator end() const BOOST_NOEXCEPT { return const_iterator(&back_, size()); }
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
const_iterator cend() const BOOST_NOEXCEPT { return const_iterator(&back_, size()); }
|
||||
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
const_reverse_iterator rbegin() const BOOST_NOEXCEPT { return const_reverse_iterator( const_iterator(&back_, 0) ); }
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
const_reverse_iterator crbegin() const BOOST_NOEXCEPT { return const_reverse_iterator( const_iterator(&back_, 0) ); }
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
const_reverse_iterator rend() const BOOST_NOEXCEPT { return const_reverse_iterator( const_iterator(&back_, size()) ); }
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
const_reverse_iterator crend() const BOOST_NOEXCEPT { return const_reverse_iterator( const_iterator(&back_, size()) ); }
|
||||
|
||||
|
||||
@@ -110,11 +137,15 @@ public:
|
||||
/// @returns `true` if `this->size() != 0`
|
||||
///
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()
|
||||
|
||||
/// @brief Compares stacktraces for less, order is platform dependant.
|
||||
///
|
||||
/// @b Complexity: Amortized O(1); worst case O(size())
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
bool operator< (const stacktrace& rhs) const BOOST_NOEXCEPT {
|
||||
return hash_code_ < rhs.hash_code_ || (hash_code_ == rhs.hash_code_ && back_ < rhs.back_);
|
||||
}
|
||||
@@ -122,6 +153,8 @@ public:
|
||||
/// @brief Compares stacktraces for equality.
|
||||
///
|
||||
/// @b Complexity: Amortized O(1); worst case O(size())
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
bool operator==(const stacktrace& rhs) const BOOST_NOEXCEPT {
|
||||
return hash_code_ == rhs.hash_code_ && back_ == rhs.back_;
|
||||
}
|
||||
@@ -129,6 +162,8 @@ public:
|
||||
/// @brief Returns hashed code of the stacktrace.
|
||||
///
|
||||
/// @b Complexity: O(1)
|
||||
///
|
||||
/// @b Async-Handler-Safety: Safe.
|
||||
std::size_t hash_code() const BOOST_NOEXCEPT { return hash_code_; }
|
||||
|
||||
/// @cond
|
||||
@@ -137,18 +172,18 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/// Comparison operators that provide platform dependant ordering and have amortized O(1) complexity; O(size()) worst case complexity.
|
||||
/// Comparison operators that provide platform dependant ordering and have amortized O(1) complexity; O(size()) worst case complexity; are Async-Handler-Safe.
|
||||
inline bool operator> (const stacktrace& lhs, const stacktrace& rhs) BOOST_NOEXCEPT { return rhs < lhs; }
|
||||
inline bool operator<=(const stacktrace& lhs, const stacktrace& rhs) BOOST_NOEXCEPT { return !(lhs > rhs); }
|
||||
inline bool operator>=(const stacktrace& lhs, const stacktrace& rhs) BOOST_NOEXCEPT { return !(lhs < rhs); }
|
||||
inline bool operator!=(const stacktrace& lhs, const stacktrace& rhs) BOOST_NOEXCEPT { return !(lhs == rhs); }
|
||||
|
||||
/// Hashing support, O(1) complexity.
|
||||
/// Hashing support, O(1) complexity; Async-Handler-Safe.
|
||||
inline std::size_t hash_value(const stacktrace& st) BOOST_NOEXCEPT {
|
||||
return st.hash_code();
|
||||
}
|
||||
|
||||
/// Outputs stacktrace in a human readable format to output stream.
|
||||
/// Outputs stacktrace in a human readable format to output stream; unsafe to use in async handlers.
|
||||
template <class CharT, class TraitsT>
|
||||
std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const stacktrace& bt) {
|
||||
const std::streamsize w = os.width();
|
||||
|
||||
Reference in New Issue
Block a user