More examples, added content to 'getting started' section in docs, improved some tests

This commit is contained in:
Antony Polukhin
2016-09-14 22:01:49 +03:00
parent bd07777485
commit 0963de1ff7
5 changed files with 283 additions and 15 deletions

View File

@@ -23,6 +23,166 @@ Boost.Stacktrace library is a simple library that provides information about cal
[section Getting Started]
[import ../example/getting_started.cpp]
[import ../example/throwing_st.cpp]
[section Better asserts]
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:
[getting_started_assert_handlers]
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 [classref 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.
[endsect]
[section Handle Terminates and Segmentation Faults]
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:
[getting_started_terminate_handlers]
After that we can set them as a default handlers and get some more information on incidents:
[getting_started_setup_handlers]
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.
[endsect]
[section Exceptions with Stacktrace]
You can provide more information along with exception by embedding stacktraces into the exception. For that we will need:
* Basic class that holds the stacktrace:
[getting_started_class_traced]
* Helper class for appending class `traced` to any exception:
[getting_started_class_with_trace]
* Throw `with_trace<Exception>` instead of just `Exception`:
[getting_started_throwing_with_trace]
* Catch exceptions by `traced`:
[getting_started_catching_trace]
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# ??
```
[endsect]
[endsect]

View File

@@ -10,6 +10,9 @@ BOOST_NOINLINE void foo(int i);
BOOST_NOINLINE void bar(int i);
BOOST_NOINLINE void oops(std::size_t i) {
// std::terminate();
// void (*p)() = 0; p();
boost::array<int, 5> a = {{0, 1, 2, 3, 4}};
foo(a[i]);
}
@@ -58,10 +61,11 @@ Backtrace:
18# ??
*/
//[getting_started_assert_handlers
// BOOST_ENABLE_ASSERT_DEBUG_HANDLER is defined for the whole project
#include <boost/assert.hpp>
#include <stdexcept>
#include <iostream>
#include <stdexcept> // std::logic_error
#include <iostream> // std::cerr
#include <boost/stacktrace.hpp>
namespace boost {
@@ -75,18 +79,20 @@ namespace boost {
::boost::assertion_failed_msg(expr, 0 /*nullptr*/, function, file, line);
}
} // namespace boost
//]
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <exception> // std::set_terminate
//[getting_started_terminate_handlers
#include <exception> // std::set_terminate, std::abort
#include <signal.h> // ::signal
#include <boost/stacktrace.hpp>
#include <iostream>
#include <iostream> // std::cerr
void my_terminate_handler() {
std::cerr << "Terminate called: " << boost::stacktrace::stacktrace() << '\n';
std::cerr << "Terminate called:\n" << boost::stacktrace::stacktrace() << '\n';
std::abort();
}
@@ -94,20 +100,17 @@ void my_signal_handler(int signum) {
std::cerr << "Signal " << signum << ", backtrace:\n" << boost::stacktrace::stacktrace() << '\n';
std::abort();
}
//]
void setup_handlers() {
//[getting_started_setup_handlers
std::set_terminate(&my_terminate_handler);
::signal(SIGSEGV, &my_signal_handler);
//]
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <boost/exception/error_info.hpp>
typedef boost::error_info<struct tag_stacktrace, boost::stacktrace::stacktrace> stacktrace_info;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <boost/core/no_exceptions_support.hpp>
int main() {
setup_handlers();

93
example/throwing_st.cpp Normal file
View File

@@ -0,0 +1,93 @@
// Copyright Antony Polukhin, 2016.
//
// 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)
#include <boost/config.hpp>
#if defined(BOOST_NO_CXX11_NOEXCEPT) || defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
int main(){}
#else
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//[getting_started_class_traced
#include <boost/stacktrace.hpp>
struct traced {
const boost::stacktrace::stacktrace trace;
virtual const char* what() const noexcept = 0;
virtual ~traced(){}
};
//]
//[getting_started_class_with_trace
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();
}
};
//]
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOST_NOINLINE void oops(std::size_t i);
BOOST_NOINLINE void foo(int i);
BOOST_NOINLINE void bar(int i);
#include <stdexcept>
BOOST_NOINLINE void oops(std::size_t i) {
//[getting_started_throwing_with_trace
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()");
//]
foo(i);
}
#include <boost/array.hpp>
BOOST_NOINLINE void bar(int i) {
boost::array<int, 5> a = {{0, 1, 2, 3, 4}};
if (i < 5) {
if (i >= 0) {
foo(a[i]);
} else {
oops(i);
}
}
}
BOOST_NOINLINE void foo(int i) {
bar(--i);
}
#include <iostream>
int main() {
//[getting_started_catching_trace
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';
}
//]
}
#endif

View File

@@ -13,6 +13,7 @@
#endif
#include <boost/aligned_storage.hpp>
#include <boost/core/explicit_operator_bool.hpp>
#include <iosfwd>
#include <string>
@@ -74,6 +75,12 @@ public:
///
/// @b Complexity: Amortized O(1), O(1) for noop backend.
BOOST_STACKTRACE_FUNCTION std::string operator[](std::size_t frame) const;
bool operator!() const BOOST_NOEXCEPT {
return !size();
}
BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()
};

View File

@@ -44,7 +44,7 @@ test-suite stacktrace_tests
[ run test_noop.cpp test_impl.cpp : : : <define>BOOST_STACKTRACE_USE_NOOP $(NOOP_DEPS) : noop_ho ]
[ run test.cpp test_impl.cpp : : : $(AUTO_DEPS) : autodetect_ho ]
# Test wih shared linked backends
# Test with shared linked backends
[ run test.cpp : : : <library>.//test_impl_lib_libunwind $(LINKSHARED_UNW) : libunwind_lib ]
[ run test.cpp : : : <library>.//test_impl_lib_backtrace $(LINKSHARED_BT) : backtrace_lib ]
[ run test.cpp : : : <library>.//test_impl_lib_windbg $(LINKSHARED_WIND) : windbg_lib ]
@@ -55,8 +55,13 @@ test-suite stacktrace_tests
[ run ../example/getting_started.cpp : : : <define>BOOST_ENABLE_ASSERT_DEBUG_HANDLER $(LINKSHARED_BT) : backtrace_getting_started ]
[ run ../example/getting_started.cpp : : : <define>BOOST_ENABLE_ASSERT_DEBUG_HANDLER $(LINKSHARED_WIND) : windbg_getting_started ]
[ run ../example/getting_started.cpp : : : <define>BOOST_ENABLE_ASSERT_DEBUG_HANDLER $(LINKSHARED_NOOP) : noop_getting_started ]
[ run ../example/getting_started.cpp : : : <define>BOOST_ENABLE_ASSERT_DEBUG_HANDLER $(AUTO_DEPS) : autodetect_getting_started ]
[ run ../example/throwing_st.cpp : : : $(LINKSHARED_UNW) : libunwind_throwing_st ]
[ run ../example/throwing_st.cpp : : : $(LINKSHARED_BT) : backtrace_throwing_st ]
[ run ../example/throwing_st.cpp : : : $(LINKSHARED_WIND) : windbg_throwing_st ]
[ run ../example/throwing_st.cpp : : : $(LINKSHARED_NOOP) : noop_throwing_st ]
[ run ../example/throwing_st.cpp : : : $(AUTO_DEPS) : autodetect_throwing_st ]
;