Files
stacktrace/doc/stacktrace.qbk
2016-10-11 23:23:45 +03:00

234 lines
7.8 KiB
Plaintext

[library Boost.Stacktrace
[quickbook 1.6]
[version 1.0]
[copyright 2016 Antony Polukhin]
[category Language Features Emulation]
[license
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])
]
]
[section Motivation]
How to display the call sequence in C++? From what function was called the current function? What call sequence lead to an exception?
Boost.Stacktrace library is a simple library that provides information about call sequence in human readable form.
[warning Library is in early development stage! A lot of thing will change soon!]
[warning This is not an official Boost library! It wasn't reviewed and can't be downloaded from www.boost.org. This library is available to the community to know real interest and get comments for refinement. The intention is to submit library to formal review, if community think that it is interesting!]
[endsect]
[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]
[section Enabling and Disabling stacktraces]
At some point arises a requirement to easily enable/disable stacktraces for a whole project. That could be easily achived.
Just define *BOOST_STACKTRACE_LINK* for a whole project. Now you can enable/disable stacktraces by just linking with different backends:
* link with `boost_stacktrace_noop` to disable backtracing
* link with other backends to output becktraces
See [link boost_stacktrace.build_macros_and_backends section "Build, Macros and Backends"] for more info.
[endsect]
[endsect]
[section Build, Macros and Backends]
By default Boost.Stacktrace is a header-only library and it attempts to detect the tracing backend automatically.
You can define the following macros to explicitly specify backend that you're willing to use in header-only mode (those macros have no effect if defined *BOOST_STACKTRACE_LINK* or *BOOST_STACKTRACE_DYN_LINK*):
[table:configbackend Header only backend specifications
[[Macro name] [Effect] [Platforms] [Uses debug information [footnote This will provide more readable backtraces if the binary is build with debug information.]] [Uses dynamic exports information [footnote This will provide readable function names in backtrace for functions that are exported by the binary.]]]
[[*BOOST_STACKTRACE_USE_LIBUNWIND*] [Use libunwind tracing backend. This is the best known backend for POSIX systems that requires linking with libunwind library.] [POSIX] [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. This is a fallback backend for POSIX platforms that requires linking with libdl library.] [POSIX] [no] [yes]]
[[*BOOST_STACKTRACE_USE_NOOP*] [Use noop tracing backend that does nothing. Use this backend if you wish to disable backtracing.] [POSIX and Windows] [no] [no]]
]
You may use following macro to improve build times or to be able to switch backends without recompiling your project:
[table:linkmacro Link macros
[[Macro name] [Effect]]
[[*BOOST_STACKTRACE_LINK*] [Disable header-only build and require linking with shared or static library that has contains the tracing backend. If *BOOST_ALL_DYN_LINK* is defined - link with shared library.]]
[[*BOOST_STACKTRACE_DYN_LINK*] [Disable header-only build and require linking with shared library that has contains tracing backend.]]
]
If one of the link macros defined you have to manually link your binary with one of the libraries that has the backend implementation:
* boost_stacktrace_libunwind
* boost_stacktrace_windbg
* boost_stacktrace_backtrace
* boost_stacktrace_noop
[endsect]
[xinclude autodoc.xml]