From 4ce841ef65e6a8fbbdeb3979498aeaf2170dfd98 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Thu, 1 Dec 2016 09:55:17 +0300 Subject: [PATCH] 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 --- doc/stacktrace.qbk | 4 +- include/boost/stacktrace/detail/backend.hpp | 53 ++++++++++--- .../stacktrace/detail/backend_common.ipp | 43 +++++----- .../boost/stacktrace/detail/backend_linux.hpp | 78 ++++--------------- .../boost/stacktrace/detail/backend_noop.hpp | 28 ++----- .../stacktrace/detail/backend_windows.hpp | 62 +++------------ include/boost/stacktrace/frame.hpp | 52 ++++++++++--- include/boost/stacktrace/stacktrace.hpp | 23 +++--- test/test.cpp | 8 ++ test/test_noop.cpp | 16 ++++ 10 files changed, 176 insertions(+), 191 deletions(-) diff --git a/doc/stacktrace.qbk b/doc/stacktrace.qbk index e213033..6aa2e83 100644 --- a/doc/stacktrace.qbk +++ b/doc/stacktrace.qbk @@ -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]] ] diff --git a/include/boost/stacktrace/detail/backend.hpp b/include/boost/stacktrace/detail/backend.hpp index 91f56cd..04f6bd0 100644 --- a/include/boost/stacktrace/detail/backend.hpp +++ b/include/boost/stacktrace/detail/backend.hpp @@ -12,7 +12,6 @@ # pragma once #endif -#include #include // 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(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 diff --git a/include/boost/stacktrace/detail/backend_common.ipp b/include/boost/stacktrace/detail/backend_common.ipp index ac17ee5..0420df2 100644 --- a/include/boost/stacktrace/detail/backend_common.ipp +++ b/include/boost/stacktrace/detail/backend_common.ipp @@ -12,35 +12,36 @@ # pragma once #endif +#include + namespace boost { namespace stacktrace { namespace detail { -backend::backend(const backend& b, void* memory) BOOST_NOEXCEPT - : data_(static_cast(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_ + ); } }}} diff --git a/include/boost/stacktrace/detail/backend_linux.hpp b/include/boost/stacktrace/detail/backend_linux.hpp index f846d0b..2ba2e20 100644 --- a/include/boost/stacktrace/detail/backend_linux.hpp +++ b/include/boost/stacktrace/detail/backend_linux.hpp @@ -16,7 +16,6 @@ #include #include #include -#include #if defined(BOOST_STACKTRACE_USE_UNWIND) #include @@ -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(memory)) +backend::backend(void* memory, std::size_t size) BOOST_NOEXCEPT + : hash_code_(0) + , frames_count_(0) + , data_(static_cast(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 diff --git a/include/boost/stacktrace/detail/backend_noop.hpp b/include/boost/stacktrace/detail/backend_noop.hpp index e8e002f..075f31d 100644 --- a/include/boost/stacktrace/detail/backend_noop.hpp +++ b/include/boost/stacktrace/detail/backend_noop.hpp @@ -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 diff --git a/include/boost/stacktrace/detail/backend_windows.hpp b/include/boost/stacktrace/detail/backend_windows.hpp index 3406562..9409513 100644 --- a/include/boost/stacktrace/detail/backend_windows.hpp +++ b/include/boost/stacktrace/detail/backend_windows.hpp @@ -13,8 +13,7 @@ #endif #include -#include -#include +#include #include #include "Dbgeng.h" @@ -95,36 +94,22 @@ inline bool try_init_com(com_holder& 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(memory)) +backend::backend(void* memory, std::size_t size) BOOST_NOEXCEPT + : hash_code_(0) + , frames_count_(0) + , data_(static_cast(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((size - sizeof(backtrace_holder)) / sizeof(void*)), - data_->buffer, + static_cast(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 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 diff --git a/include/boost/stacktrace/frame.hpp b/include/boost/stacktrace/frame.hpp index 88791a1..91da0f8 100644 --- a/include/boost/stacktrace/frame.hpp +++ b/include/boost/stacktrace/frame.hpp @@ -15,25 +15,31 @@ #include #include +#include #include 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 std::basic_ostream& operator<<(std::basic_ostream& 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; diff --git a/include/boost/stacktrace/stacktrace.hpp b/include/boost/stacktrace/stacktrace.hpp index b4f550d..25c6165 100644 --- a/include/boost/stacktrace/stacktrace.hpp +++ b/include/boost/stacktrace/stacktrace.hpp @@ -29,7 +29,6 @@ class stacktrace { /// @cond BOOST_STATIC_CONSTEXPR std::size_t max_implementation_size = sizeof(void*) * 110u; boost::aligned_storage::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(); } diff --git a/test/test.cpp b/test/test.cpp index b1a25a7..32a60ed 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -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() { diff --git a/test/test_noop.cpp b/test/test_noop.cpp index 732e18a..662ae31 100644 --- a/test/test_noop.cpp +++ b/test/test_noop.cpp @@ -21,12 +21,20 @@ void test_deeply_nested_namespaces() { void test_nested() { std::pair 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(); }