mirror of
https://github.com/boostorg/stacktrace.git
synced 2026-01-21 17:32:15 +00:00
276 lines
9.3 KiB
Plaintext
276 lines
9.3 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 led to an exception?
|
|
|
|
Boost.Stacktrace library is a simple library that provides information about call sequence in a human-readable form.
|
|
|
|
[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 for formal review, if community thinks that it is interesting!]
|
|
|
|
[endsect]
|
|
|
|
[section Getting Started]
|
|
|
|
[import ../example/getting_started.cpp]
|
|
[import ../example/throwing_st.cpp]
|
|
|
|
[section How to print current call stack]
|
|
|
|
[classref boost::stacktrace::stacktrace] contains methods for working with call-stack/backtraces/stacktraces. Here's a small example:
|
|
```
|
|
#include <boost/stacktrace.hpp>
|
|
|
|
// ... somewere inside the `bar(int)` function that is called recursively:
|
|
std::cout << boost::stacktrace::stacktrace();
|
|
```
|
|
|
|
In that example:
|
|
|
|
* `boost::stacktrace::` is the namespace that has all the classes and functions to work with stacktraces
|
|
* [classref boost::stacktrace::stacktrace stacktrace()] is the default constructor call; constructor stores the current function call sequence inside the stacktrace class.
|
|
|
|
Code from above will output something like this:
|
|
|
|
```
|
|
0# bar(int)
|
|
1# bar(int)
|
|
2# bar(int)
|
|
3# bar(int)
|
|
4# main
|
|
5# __libc_start_main
|
|
6# _start
|
|
```
|
|
|
|
[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 Better asserts]
|
|
|
|
Pretty often assertions provide not enough 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 enough to locate the problem without debugger. There may be thousand code lines in real world examples and hundred 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 led to the assertion and can find the error without debugger.
|
|
|
|
[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 backtraces
|
|
|
|
See [link boost_stacktrace.build_macros_and_backends section "Build, Macros and Backends"] for more info.
|
|
|
|
[endsect]
|
|
|
|
[section Saving stacktraces by specified format]
|
|
|
|
[classref boost::stacktrace::stacktrace] provides access to individual [classref boost::stacktrace::frame frames] of the stacktrace,
|
|
so that you could save stacktrace information in your own format. Consider the example, that saves only function addresses of each frame:
|
|
|
|
[getting_started_trace_addresses]
|
|
|
|
Code from above will output:
|
|
|
|
```
|
|
0x401a25,0x401a25,0x401a25,0x401a25,0x401a25,0x401a25,0x4019cb,0x401a7f,0x7f9da8a46e50,0x4013e0,0,
|
|
```
|
|
|
|
[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 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] [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] [yes]]
|
|
]
|
|
|
|
|
|
You may use the following macros 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 contains the tracing backend. If *BOOST_ALL_DYN_LINK* is defined, then link with shared library.]]
|
|
[[*BOOST_STACKTRACE_DYN_LINK*] [Disable header-only build and require linking with shared library that contains tracing backend.]]
|
|
]
|
|
|
|
If one of the link macros is defined, you have to manually link your binary with one of the libraries that has the backend implementation:
|
|
|
|
* boost_stacktrace_unwind
|
|
* boost_stacktrace_windbg
|
|
* boost_stacktrace_backtrace
|
|
* boost_stacktrace_noop
|
|
|
|
[endsect]
|
|
|
|
[xinclude autodoc.xml]
|
|
|