Code deduplicated and simplified, added empty() functions and some explicit bool operators, speed-up and improved the output of the ostream operators, improved tests coverage

This commit is contained in:
Antony Polukhin
2016-12-01 09:55:17 +03:00
parent fd513391ca
commit 4ce841ef65
10 changed files with 176 additions and 191 deletions

View File

@@ -248,10 +248,10 @@ You can define the following macros to explicitly specify backend that you're wi
[table:configbackend Header only backend specifications
[[Macro name] [Effect] [Platforms] [Uses debug information [footnote This will provide more readable backtraces if the binary is built with debug information.]] [Uses dynamic exports information [footnote This will provide readable function names in backtrace for functions that are exported by the binary.]] [Async signal safe[footnote Absolutely safe to construct instances of [classref boost::stacktrace::stacktrace] in async signal handlers using that backend.]]]
[[*BOOST_STACKTRACE_USE_UNWIND*] [Use unwind tracing backend. This is the best known backend for POSIX systems that requires LSB Core Specification 4.1 from OS. Requires linking with libdl library.] [POSIX] [yes] [yes] [Constructors]]
[[*BOOST_STACKTRACE_USE_UNWIND*] [Use unwind tracing backend. This is the best known backend for POSIX systems that requires LSB Core Specification 4.1 from OS. Requires linking with libdl library.] [POSIX] [yes] [yes] [yes]]
[[*BOOST_STACKTRACE_USE_WINDBG*] [Use Windows specific tracing backend that uses DbgHelp. This is the best and only known backend for Windows platform that requires linking with DbgHelp library.] [Windows] [yes] [yes] [???]]
[[*BOOST_STACKTRACE_USE_BACKTRACE*] [Use tracing backend that calls POSIX function `backtrace`. Requires linking with libdl library.] [POSIX] [no] [yes] [no]]
[[*BOOST_STACKTRACE_USE_NOOP*] [Use noop tracing backend that does nothing. Use this backend if you wish to disable backtracing, `stacktrace::size()` with that backend always return 0. ] [POSIX and Windows] [no] [no] [all functions]]
[[*BOOST_STACKTRACE_USE_NOOP*] [Use noop tracing backend that does nothing. Use this backend if you wish to disable backtracing, `stacktrace::size()` with that backend always return 0. ] [POSIX and Windows] [no] [no] [yes]]
]

View File

@@ -12,7 +12,6 @@
# pragma once
#endif
#include <boost/core/noncopyable.hpp>
#include <string>
// Link or header only
@@ -39,7 +38,6 @@
# define BOOST_STACKTRACE_USE_WINDBG
# else
# define BOOST_STACKTRACE_USE_UNWIND
//# define BOOST_STACKTRACE_USE_BACKTRACE
# endif
#endif
@@ -61,25 +59,56 @@
namespace boost { namespace stacktrace { namespace detail {
struct backtrace_holder;
// Class that implements the actual backtracing
class backend: boost::noncopyable {
backtrace_holder* data_;
class backend {
std::size_t hash_code_;
std::size_t frames_count_;
void** data_;
void copy_frames_from(const backend& b) BOOST_NOEXCEPT {
if (data_ == b.data_) {
return;
}
for (std::size_t i = 0; i < frames_count_; ++i) {
data_[i] = b.data_[i];
}
}
public:
BOOST_STACKTRACE_FUNCTION backend(void* memory, std::size_t size, std::size_t& hash_code) BOOST_NOEXCEPT;
BOOST_STACKTRACE_FUNCTION backend(void* memory, std::size_t size) BOOST_NOEXCEPT;
BOOST_STACKTRACE_FUNCTION static std::string get_name(const void* addr);
BOOST_STACKTRACE_FUNCTION const void* get_address(std::size_t frame_no) const BOOST_NOEXCEPT;
const void* get_address(std::size_t frame_no) const BOOST_NOEXCEPT {
return frame_no < frames_count_ ? data_[frame_no] : 0;
}
BOOST_STACKTRACE_FUNCTION static std::string get_source_file(const void* addr);
BOOST_STACKTRACE_FUNCTION static std::size_t get_source_line(const void* addr);
BOOST_STACKTRACE_FUNCTION bool operator< (const backend& rhs) const BOOST_NOEXCEPT;
BOOST_STACKTRACE_FUNCTION bool operator==(const backend& rhs) const BOOST_NOEXCEPT;
BOOST_STACKTRACE_FUNCTION backend(const backend& b, void* memory) BOOST_NOEXCEPT;
BOOST_STACKTRACE_FUNCTION backend& operator=(const backend& b) BOOST_NOEXCEPT;
BOOST_STACKTRACE_FUNCTION ~backend() BOOST_NOEXCEPT;
BOOST_STACKTRACE_FUNCTION std::size_t size() const BOOST_NOEXCEPT;
backend(const backend& b, void* memory) BOOST_NOEXCEPT
: hash_code_(b.hash_code_)
, frames_count_(b.frames_count_)
, data_(static_cast<void**>(memory))
{
copy_frames_from(b);
}
backend& operator=(const backend& b) BOOST_NOEXCEPT {
hash_code_ = b.hash_code_;
frames_count_ = b.frames_count_;
copy_frames_from(b);
return *this;
}
std::size_t size() const BOOST_NOEXCEPT {
return frames_count_;
}
std::size_t hash_code() const BOOST_NOEXCEPT {
return hash_code_;
}
};
}}} // namespace boost::stacktrace::detail

View File

@@ -12,35 +12,36 @@
# pragma once
#endif
#include <algorithm>
namespace boost { namespace stacktrace { namespace detail {
backend::backend(const backend& b, void* memory) BOOST_NOEXCEPT
: data_(static_cast<backtrace_holder*>(memory))
{
new(data_) backtrace_holder(
*b.data_
);
}
backend& backend::operator=(const backend& b) BOOST_NOEXCEPT {
if (data_ == b.data_) {
return *this;
bool backend::operator< (const backend& rhs) const BOOST_NOEXCEPT {
if (frames_count_ != rhs.frames_count_) {
return frames_count_ < rhs.frames_count_;
} else if (hash_code_ != rhs.hash_code_) {
return hash_code_ < rhs.hash_code_;
} else if (data_ == rhs.data_) {
return false;
}
data_->~backtrace_holder();
new(data_) backtrace_holder(
*b.data_
return std::lexicographical_compare(
data_, data_ + frames_count_,
rhs.data_, rhs.data_ + rhs.frames_count_
);
return *this;
}
backend::~backend() BOOST_NOEXCEPT {
data_->~backtrace_holder();
}
bool backend::operator==(const backend& rhs) const BOOST_NOEXCEPT {
if (hash_code_ != rhs.hash_code_ || frames_count_ != rhs.frames_count_) {
return false;
} else if (data_ == rhs.data_) {
return true;
}
std::size_t backend::size() const BOOST_NOEXCEPT {
return data_->frames_count;
return std::equal(
data_, data_ + frames_count_,
rhs.data_
);
}
}}}

View File

@@ -16,7 +16,6 @@
#include <boost/functional/hash.hpp>
#include <boost/stacktrace/detail/to_hex_array.hpp>
#include <boost/lexical_cast/try_lexical_convert.hpp>
#include <algorithm>
#if defined(BOOST_STACKTRACE_USE_UNWIND)
#include <unwind.h>
@@ -32,24 +31,6 @@
namespace boost { namespace stacktrace { namespace detail {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
struct backtrace_holder {
std::size_t frames_count;
void* buffer[];
backtrace_holder() BOOST_NOEXCEPT {}
backtrace_holder(const backtrace_holder& d) BOOST_NOEXCEPT
: frames_count(d.frames_count)
{
std::copy(d.buffer, d.buffer + frames_count, buffer);
}
};
#pragma GCC diagnostic pop
class addr2line_pipe {
FILE* p;
pid_t pid;
@@ -193,27 +174,25 @@ inline _Unwind_Reason_Code unwind_callback(struct _Unwind_Context* context, void
backend::backend(void* memory, std::size_t size, std::size_t& hash_code) BOOST_NOEXCEPT
: data_(static_cast<backtrace_holder*>(memory))
backend::backend(void* memory, std::size_t size) BOOST_NOEXCEPT
: hash_code_(0)
, frames_count_(0)
, data_(static_cast<void**>(memory))
{
new (data_) backtrace_holder();
data_->frames_count = 0;
hash_code = 0;
#if defined(BOOST_STACKTRACE_USE_UNWIND)
unwind_state state = { data_->buffer, data_->buffer + data_->frames_count };
unwind_state state = { data_, data_ + frames_count_ };
_Unwind_Backtrace(&unwind_callback, &state);
data_->frames_count = state.current - data_->buffer;
frames_count_ = state.current - data_;
#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;
frames_count_ = ::backtrace(data_, size / sizeof(void*));
if (data_[frames_count_ - 1] == 0) {
-- 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);
hash_code_ = boost::hash_range(data_, data_ + frames_count_);
}
std::string backend::get_name(const void* addr) {
@@ -228,16 +207,19 @@ std::string backend::get_name(const void* addr) {
res = try_demangle(res.c_str());
}
return res;
}
if (res == "??") {
res.clear();
}
const void* backend::get_address(std::size_t frame) const BOOST_NOEXCEPT {
return frame < data_->frames_count ? data_->buffer[frame] : 0;
return res;
}
std::string backend::get_source_file(const void* addr) {
std::string res = addr2line("-e", addr);
res = res.substr(0, res.find_last_of(':'));
if (res == "??") {
res.clear();
}
return res;
}
@@ -257,32 +239,6 @@ std::size_t backend::get_source_line(const void* addr) {
return line_num;
}
bool backend::operator< (const backend& rhs) const BOOST_NOEXCEPT {
if (data_->frames_count != rhs.data_->frames_count) {
return data_->frames_count < rhs.data_->frames_count;
} else if (this == &rhs) {
return false;
}
return std::lexicographical_compare(
data_->buffer, data_->buffer + data_->frames_count,
rhs.data_->buffer, rhs.data_->buffer + rhs.data_->frames_count
);
}
bool backend::operator==(const backend& rhs) const BOOST_NOEXCEPT {
if (data_->frames_count != rhs.data_->frames_count) {
return false;
} else if (this == &rhs) {
return true;
}
return std::equal(
data_->buffer, data_->buffer + data_->frames_count,
rhs.data_->buffer
);
}
}}} // namespace boost::stacktrace::detail
#include <boost/stacktrace/detail/backend_common.ipp>

View File

@@ -15,20 +15,16 @@
namespace boost { namespace stacktrace { namespace detail {
backend::backend(void* /*memory*/, std::size_t /*size*/, std::size_t& hash_code) BOOST_NOEXCEPT
: data_(0)
{
hash_code = 0;
}
backend::backend(void* /*memory*/, std::size_t /*size*/) BOOST_NOEXCEPT
: hash_code_(0)
, frames_count_(0)
, data_(0)
{}
std::string backend::get_name(const void* /*addr*/) {
return std::string();
}
const void* backend::get_address(std::size_t /*frame*/) const BOOST_NOEXCEPT {
return data_; // returns 0. Suppressing `private field 'data_' is not used` warning
}
std::string backend::get_source_file(const void* /*addr*/) {
return std::string();
}
@@ -45,20 +41,6 @@ bool backend::operator==(const backend& /*rhs*/) const BOOST_NOEXCEPT {
return true;
}
backend::backend(const backend& /*b*/, void* /*memory*/) BOOST_NOEXCEPT
: data_(0)
{}
backend& backend::operator=(const backend& /*b*/) BOOST_NOEXCEPT {
return *this;
}
backend::~backend() BOOST_NOEXCEPT {}
std::size_t backend::size() const BOOST_NOEXCEPT {
return 0;
}
}}} // namespace boost::stacktrace::detail
#endif // BOOST_STACKTRACE_DETAIL_BACKEND_LIBUNWIND_HPP

View File

@@ -13,8 +13,7 @@
#endif
#include <boost/core/noncopyable.hpp>
#include <boost/functional/hash.hpp>
#include <algorithm>
#include <boost/functional/hash.hpp>
#include <windows.h>
#include "Dbgeng.h"
@@ -95,36 +94,22 @@ inline bool try_init_com(com_holder<IDebugSymbols>& idebug_) BOOST_NOEXCEPT {
}
struct backtrace_holder {
std::size_t frames_count;
void* buffer[];
backtrace_holder() BOOST_NOEXCEPT {}
backtrace_holder(const backtrace_holder& d) BOOST_NOEXCEPT
: frames_count(d.frames_count)
{
std::copy(d.buffer, d.buffer + frames_count, buffer);
}
};
backend::backend(void* memory, std::size_t size, std::size_t& hash_code) BOOST_NOEXCEPT
: data_(static_cast<backtrace_holder*>(memory))
backend::backend(void* memory, std::size_t size) BOOST_NOEXCEPT
: hash_code_(0)
, frames_count_(0)
, data_(static_cast<void**>(memory))
{
new (data_) backtrace_holder();
data_->frames_count = 0;
hash_code = 0;
boost::detail::winapi::ULONG_ hc = 0;
data_->frames_count = CaptureStackBackTrace(
frames_count_ = CaptureStackBackTrace(
0,
static_cast<boost::detail::winapi::ULONG_>((size - sizeof(backtrace_holder)) / sizeof(void*)),
data_->buffer,
static_cast<boost::detail::winapi::ULONG_>(size / sizeof(void*)),
data_,
&hc
);
boost::hash_combine(hash_code, hc);
boost::hash_combine(hash_code_, hc);
}
std::string backend::get_name(const void* addr) {
@@ -166,10 +151,6 @@ std::string backend::get_name(const void* addr) {
return result;
}
const void* backend::get_address(std::size_t frame) const BOOST_NOEXCEPT {
return frame < data_->frames_count ? data_->buffer[frame] : 0;
}
std::string backend::get_source_file(const void* addr) {
std::string result;
com_holder<IDebugSymbols> idebug_;
@@ -232,31 +213,6 @@ std::size_t backend::get_source_line(const void* addr) {
return (is_ok ? line_num : 0);
}
bool backend::operator< (const backend& rhs) const BOOST_NOEXCEPT {
if (data_->frames_count != rhs.data_->frames_count) {
return data_->frames_count < rhs.data_->frames_count;
} else if (this == &rhs) {
return false;
}
return std::lexicographical_compare(
data_->buffer, data_->buffer + data_->frames_count,
rhs.data_->buffer, rhs.data_->buffer + rhs.data_->frames_count
);
}
bool backend::operator==(const backend& rhs) const BOOST_NOEXCEPT {
if (data_->frames_count != rhs.data_->frames_count) {
return false;
} else if (this == &rhs) {
return true;
}
return std::equal(
data_->buffer, data_->buffer + data_->frames_count,
rhs.data_->buffer
);
}
}}} // namespace boost::stacktrace::detail

View File

@@ -15,25 +15,31 @@
#include <iosfwd>
#include <string>
#include <boost/core/explicit_operator_bool.hpp>
#include <boost/stacktrace/detail/backend.hpp>
namespace boost { namespace stacktrace {
// Forward declarations
class const_iterator;
/// Non-owning class that references the frame information stored inside the boost::stacktrace::stacktrace class.
class frame {
/// @cond
const void* addr_;
frame(); // = delete
/// @endcond
public:
#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
frame() = delete;
/// @brief Constructs frame that references NULL address.
/// Calls to source_file() and source_line() wil lreturn empty string.
/// Calls to source_line() will return 0.
///
/// @b Complexity: O(1).
///
/// @b Async-Handler-Safety: Safe.
/// @throws Nothing.
frame() BOOST_NOEXCEPT
: addr_(0)
{}
#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
/// @brief Copy constructs frame.
///
/// @b Complexity: O(1).
@@ -93,6 +99,26 @@ public:
std::size_t source_line() const {
return boost::stacktrace::detail::backend::get_source_line(address());
}
/// @brief Checks that frame is not references NULL address.
/// @returns `true` if `this->address() != 0`
///
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()
/// @brief Checks that frame references NULL address.
/// @returns `true` if `this->address() == 0`
///
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
bool empty() const BOOST_NOEXCEPT { return !address(); }
/// @cond
bool operator!() const BOOST_NOEXCEPT { return !address(); }
/// @endcond
};
/// Comparison operators that provide platform dependant ordering and have O(1) complexity; are Async-Handler-Safe.
@@ -111,10 +137,16 @@ inline std::size_t hash_value(const frame& f) BOOST_NOEXCEPT {
/// 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();
std::string name = f.name();
if (!name.empty()) {
os << name;
} else {
os << f.address();
}
if (f.source_line()) {
return os << '\t' << f.source_file() << ':' << f.source_line();
const std::size_t source_line = f.source_line();
if (source_line) {
os << " at " << f.source_file() << ':' << source_line;
}
return os;

View File

@@ -29,7 +29,6 @@ class stacktrace {
/// @cond
BOOST_STATIC_CONSTEXPR std::size_t max_implementation_size = sizeof(void*) * 110u;
boost::aligned_storage<max_implementation_size>::type impl_;
std::size_t hash_code_;
boost::stacktrace::detail::backend back_;
/// @endcond
@@ -48,8 +47,7 @@ public:
/// @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_)
, back_(&impl_, sizeof(impl_))
{}
/// @b Complexity: O(st.size())
@@ -57,7 +55,6 @@ public:
/// @b Async-Handler-Safety: Safe.
stacktrace(const stacktrace& st) BOOST_NOEXCEPT
: impl_()
, hash_code_(st.hash_code_)
, back_(st.back_, &impl_)
{}
@@ -65,8 +62,7 @@ public:
///
/// @b Async-Handler-Safety: Safe.
stacktrace& operator=(const stacktrace& st) BOOST_NOEXCEPT {
hash_code_ = st.hash_code_;
back_ = back_;
back_ = st.back_;
return *this;
}
@@ -141,13 +137,22 @@ public:
/// @b Async-Handler-Safety: Safe.
BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()
/// @brief Allows to check that stack trace failed.
/// @returns `true` if `this->size() == 0`
///
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
bool empty() const BOOST_NOEXCEPT { return !size(); }
/// @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_);
return back_ < rhs.back_;
}
/// @brief Compares stacktraces for equality.
@@ -156,7 +161,7 @@ public:
///
/// @b Async-Handler-Safety: Safe.
bool operator==(const stacktrace& rhs) const BOOST_NOEXCEPT {
return hash_code_ == rhs.hash_code_ && back_ == rhs.back_;
return back_ == rhs.back_;
}
/// @brief Returns hashed code of the stacktrace.
@@ -164,7 +169,7 @@ public:
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
std::size_t hash_code() const BOOST_NOEXCEPT { return hash_code_; }
std::size_t hash_code() const BOOST_NOEXCEPT { return back_.hash_code(); }
/// @cond
bool operator!() const BOOST_NOEXCEPT { return !size(); }

View File

@@ -182,17 +182,25 @@ void test_frame() {
BOOST_TEST(st[i] >= st[i]);
frame fv = nst[2];
BOOST_TEST(fv);
if (i >= 2 && i < min_size - 3) { // Begin and end of the trace may match, skipping them
BOOST_TEST(st[i].name() != fv.name());
BOOST_TEST(st[i] != fv);
BOOST_TEST(st[i] < fv || st[i] > fv);
BOOST_TEST(hash_value(st[i]) != hash_value(fv));
BOOST_TEST(st[i].source_line() == 0 || st[i].source_file() != fv.source_file());
BOOST_TEST(st[i]);
}
fv = st[i];
BOOST_TEST(hash_value(st[i]) == hash_value(fv));
}
boost::stacktrace::frame empty_frame;
BOOST_TEST(!empty_frame);
BOOST_TEST(empty_frame.source_file() == "");
BOOST_TEST(empty_frame.name() == "");
BOOST_TEST(empty_frame.source_line() == 0);
}
int main() {

View File

@@ -21,12 +21,20 @@ void test_deeply_nested_namespaces() {
void test_nested() {
std::pair<stacktrace, stacktrace> res = foo2(15);
BOOST_TEST(!res.first);
BOOST_TEST(res.first.size() == 0);
BOOST_TEST(res.first.begin()->name() == "");
BOOST_TEST(res.first.begin()->source_file() == "");
BOOST_TEST(res.first.begin()->source_line() == 0);
BOOST_TEST(!res.second);
BOOST_TEST(res.second.size() == 0);
BOOST_TEST(res.second.begin()->name() == "");
BOOST_TEST(res.second.begin()->source_file() == "");
BOOST_TEST(res.second.begin()->source_line() == 0);
BOOST_TEST(res.second[0].name() == "");
BOOST_TEST(res.second[0].source_file() == "");
BOOST_TEST(res.second[0].source_line() == 0);
BOOST_TEST(res.second[0].address() == 0);
BOOST_TEST(res.second <= res.first);
@@ -36,10 +44,18 @@ void test_nested() {
BOOST_TEST(!(res.second > res.first));
}
void test_empty_frame() {
boost::stacktrace::frame empty_frame;
BOOST_TEST(!empty_frame);
BOOST_TEST(empty_frame.source_file() == "");
BOOST_TEST(empty_frame.name() == "");
BOOST_TEST(empty_frame.source_line() == 0);
}
int main() {
test_deeply_nested_namespaces();
test_nested();
test_empty_frame();
return boost::report_errors();
}