diff --git a/boost/stacktrace/stacktrace.html b/boost/stacktrace/stacktrace.html index 735658a..7cac170 100644 --- a/boost/stacktrace/stacktrace.html +++ b/boost/stacktrace/stacktrace.html @@ -34,52 +34,60 @@ class stacktrace { public: // construct/copy/destruct - stacktrace() noexcept; - stacktrace(const stacktrace &) noexcept; - stacktrace & operator=(const stacktrace &) noexcept; - ~stacktrace(); + stacktrace() noexcept; + stacktrace(const stacktrace &) noexcept; + stacktrace & operator=(const stacktrace &) noexcept; + ~stacktrace(); - // public member functions - std::size_t size() const noexcept; - std::string operator[](std::size_t) const; + // public member functions + std::size_t size() const noexcept; + std::string operator[](std::size_t) const; + bool operator!() const noexcept; };
stacktrace
public
construct/copy/destructstacktrace() noexcept;Stores the current function call sequence inside the class.
stacktrace(const stacktrace & bt) noexcept;
stacktrace & operator=(const stacktrace & bt) noexcept;
~stacktrace();
stacktrace() noexcept;Stores the current function call sequence inside the class.
Complexity: O(N) where N is call seaquence length, O(1) for noop backend.
+ +stacktrace(const stacktrace & bt) noexcept;+Complexity: O(1)
stacktrace & operator=(const stacktrace & bt) noexcept;+Complexity: O(1)
~stacktrace();+Complexity: O(N) for libunwind, O(1) for other backends.
stacktrace public member functionsstacktrace public member functionsstd::size_t size() const noexcept;+
std::size_t size() const noexcept;
-
+Complexity: O(1)Returns: |
-Number of function names stored inside the class. |
+Number of function names stored inside the class. |
std::string operator[](std::size_t frame) const;+
std::string operator[](std::size_t frame) const;
-
+Complexity: Amortized O(1), O(1) for noop backend.Throws: |
-std::bad_alloc if not enough memory. | +std::bad_alloc if not enough memory to construct resulting string. |
bool operator!() const noexcept;
+ Pretty often assertions provide not enougth information to locate the problem. + For example you can see the following message on out-of-range access: +
+../../../boost/array.hpp:123: T& boost::array<T, N>::operator[](boost::array<T, N>::size_type) [with T = int; long unsigned int N = 5ul]: Assertion '(i < N)&&("out of range")' failed. +Aborted (core dumped) ++
+ That's not enought to locate the problem without debugger. There may be thousends + code lines in real world examples and hundrets places where that assertion + could happen. Let's try to improve the assertions, and make them more informative: +
+// BOOST_ENABLE_ASSERT_DEBUG_HANDLER is defined for the whole project +#include <stdexcept> // std::logic_error +#include <iostream> // std::cerr +#include <boost/stacktrace.hpp> + +namespace boost { + void assertion_failed_msg(char const* expr, char const* msg, char const* function, char const* file, long line) { + std::cerr << "Expression '" << expr << "' is false in function '" << function << "': " << (msg ? msg : "<...>") << ".\n" + << "Backtrace:\n" << boost::stacktrace::stacktrace() << '\n'; + throw std::logic_error("assertion"); + } + + void assertion_failed(char const* expr, char const* function, char const* file, long line) { + ::boost::assertion_failed_msg(expr, 0 /*nullptr*/, function, file, line); + } +} // namespace boost ++
+ We've defined the BOOST_ENABLE_ASSERT_DEBUG_HANDLER
+ macro for the whole project. Now all the BOOST_ASSERT
+ and BOOST_ASSERT_MSG will
+ call our functions assertion_failed
+ and assertion_failed_msg
+ in case of failure. In assertion_failed_msg
+ we output information that was provided by the assertion macro and boost::stacktrace::stacktrace:
+
Expression 'i < N' is false in function 'T& boost::array<T, N>::operator[](boost::array<T, N>::size_type) [with T = int; long unsigned int N = 5ul]': out of range. +Backtrace: + 0# boost::assertion_failed_msg(char const*, char const*, char const*, char const*, long) + 1# boost::array<int, 5ul>::operator[](unsigned long) + 2# oops(unsigned long) + 3# bar(int) + 4# foo(int) + 5# bar(int) + 6# foo(int) + 7# bar(int) + 8# foo(int) + 9# bar(int) +10# foo(int) +11# bar(int) +12# foo(int) +13# bar(int) +14# foo(int) +15# main +16# __libc_start_main +17# _start +18# ?? ++
+ Now we do know the steps that lead to the assertion and could find the error + without debugger. +
+
+ Segmentation Faults and std::terminate
+ calls sometimes happen in programs. Programmers usually wish to get as much
+ information as possible on such incidents, so having a stacktrace will significantly
+ improve debugging and fixing.
+
+ To deal with Segmentation Faults and std::terminate
+ calls we would need to write handlers:
+
#include <exception> // std::set_terminate, std::abort +#include <signal.h> // ::signal +#include <boost/stacktrace.hpp> +#include <iostream> // std::cerr + +void my_terminate_handler() { + std::cerr << "Terminate called:\n" << boost::stacktrace::stacktrace() << '\n'; + std::abort(); +} + +void my_signal_handler(int signum) { + std::cerr << "Signal " << signum << ", backtrace:\n" << boost::stacktrace::stacktrace() << '\n'; + std::abort(); +} ++
+ After that we can set them as a default handlers and get some more information + on incidents: +
+std::set_terminate(&my_terminate_handler); +::signal(SIGSEGV, &my_signal_handler); ++
+ Now we'll get the following output on std::terminate
+ call:
+
Terminate called: + 0# my_terminate_handler() + 1# std::rethrow_exception(std::__exception_ptr::exception_ptr) + 2# std::terminate() + 3# bar(int) + 4# bar(int) + 5# foo(int) + 6# bar(int) + 7# foo(int) + 8# bar(int) + 9# foo(int) +10# bar(int) +11# foo(int) +12# bar(int) +13# foo(int) +14# bar(int) +15# foo(int) +16# main +17# __libc_start_main +18# _start +19# ?? ++
+ And the following output on Segmentation Fault: +
+Signal 11, backtrace: + 0# my_signal_handler(int) + 1# killpg + 2# ?? + 3# bar(int) + 4# foo(int) + 5# bar(int) + 6# foo(int) + 7# bar(int) + 8# foo(int) + 9# bar(int) +10# foo(int) +11# bar(int) +12# foo(int) +13# bar(int) +14# foo(int) +15# main +16# __libc_start_main +17# _start +18# ?? ++
+ The output stacktrace may be corrupted by previous actions. But now at least + some basic information is available to work with. +
++ You can provide more information along with exception by embedding stacktraces + into the exception. For that we will need: +
+#include <boost/stacktrace.hpp> + +struct traced { + const boost::stacktrace::stacktrace trace; + + virtual const char* what() const noexcept = 0; + virtual ~traced(){} +}; ++
traced
+ to any exception:
+ template <class Exception> +struct with_trace : public Exception, public traced { + template <class... Args> + with_trace(Args&&... args) + : Exception(std::forward<Args>(args)...) + {} + + const char* what() const noexcept { + return Exception::what(); + } +}; ++
with_trace<Exception>
+ instead of just Exception:
+ if (i >= 4) + throw with_trace<std::out_of_range>("'i' must be less than 4 in oops()"); +if (i == 0) + throw with_trace<std::logic_error>("'i' must not be zero in oops()"); ++
traced:
+ try { + foo(5); // testing assert handler +} catch (const traced& e) { + std::cerr << e.what() << '\n'; + if (e.trace) { + std::cerr << "Backtrace:\n" << e.trace << '\n'; + } +} catch (const std::exception& e) { + std::cerr << e.what() << '\n'; +} ++
+ Code from above will output: +
+'i' must be less than 4 in oops() +Backtrace: + 0# with_trace<std::out_of_range>::with_trace<char const (&) [34]>(char const (&) [34]) + 1# oops(unsigned long) + 2# bar(int) + 3# foo(int) + 4# bar(int) + 5# foo(int) + 6# bar(int) + 7# foo(int) + 8# bar(int) + 9# foo(int) +10# bar(int) +11# foo(int) +12# bar(int) +13# foo(int) +14# main +15# __libc_start_main +16# _start +17# ?? ++
|