[library Boost.Stacktrace
    [quickbook 1.6]
    [version 1.0]
    [id stacktrace]
    [copyright 2016-2024 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 can one display the call sequence in C++? What function called the current function? What call sequence led to an exception?

Boost.Stacktrace library is a simple C++03 library that provides information about call sequence in a human-readable form.

[endsect]

[section Getting Started]

[import ../example/assert_handler.cpp]
[import ../example/terminate_handler.cpp]
[import ../example/throwing_st.cpp]
[import ../example/trace_addresses.cpp]
[import ../example/debug_function.cpp]
[import ../example/user_config.hpp]

[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>

// ... somewhere 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) at /path/to/source/file.cpp:70
 1# bar(int) at /path/to/source/file.cpp:70
 2# bar(int) at /path/to/source/file.cpp:70
 3# bar(int) at /path/to/source/file.cpp:70
 4# main at /path/to/main.cpp:93
 5# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 6# _start
```

[note By default the Stacktrace library is very conservative in methods to decode stacktrace. If your output does not look as fancy as in example from above, see [link stacktrace.configuration_and_build section "Configuration and Build"] for allowing advanced features of the library. ]


[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; boost::array<T, N>::reference = int&; boost::array<T, N>::size_type = long unsigned int]': out of range.
Backtrace:
 0# boost::assertion_failed_msg(char const*, char const*, char const*, char const*, long) at ../example/assert_handler.cpp:39
 1# boost::array<int, 5ul>::operator[](unsigned long) at ../../../boost/array.hpp:124
 2# bar(int) at ../example/assert_handler.cpp:17
 3# foo(int) at ../example/assert_handler.cpp:25
 4# bar(int) at ../example/assert_handler.cpp:17
 5# foo(int) at ../example/assert_handler.cpp:25
 6# main at ../example/assert_handler.cpp:54
 7# 0x00007F991FD69F45 in /lib/x86_64-linux-gnu/libc.so.6
 8# 0x0000000000401139
```

Now we do know the steps that led to the assertion and can find the error without debugger.

[endsect]

[section Handle terminates]

`std::terminate` calls sometimes happen in programs. Programmers usually wish to get as much information as possible on such incidents, so having a stacktrace significantly improves debugging and fixing.

Here's how to write a terminate handler that dumps stacktrace:

[getting_started_terminate_handlers]

Here's how to register it:

[getting_started_setup_terminate_handlers]

Now we'll get the following output on `std::terminate` call:

```
Previous run crashed:
 0# my_terminate_handler(int) at ../example/terminate_handler.cpp:37
 1# __cxxabiv1::__terminate(void (*)()) at ../../../../src/libstdc++-v3/libsupc++/eh_terminate.cc:48
 2# 0x00007F3CE65E5901 in /usr/lib/x86_64-linux-gnu/libstdc++.so.6
 3# bar(int) at ../example/terminate_handler.cpp:18
 4# foo(int) at ../example/terminate_handler.cpp:22
 5# bar(int) at ../example/terminate_handler.cpp:14
 6# foo(int) at ../example/terminate_handler.cpp:22
 7# main at ../example/terminate_handler.cpp:84
 8# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 9# 0x0000000000402209
```

[warning There's a temptation to write a signal handler that prints the stacktrace on `SIGSEGV` or abort. Unfortunately,
there's no cross platform way to do that without a risk of deadlocking.
Not all the platforms provide means for even getting stacktrace in async signal safe way.

Signal handler is often invoked on a separate stack and trash is returned on attempt to get a trace!

Generic recommendation is to *avoid signal handlers! Use* platform specific ways to store and decode *core files*.

See [link stacktrace.theoretical_async_signal_safety "Theoretical async signal safety"] for more info.
]


[endsect]

[section Stacktrace from arbitrary exception]

[warning At the moment the functionality is only available for some of the
         popular C++ runtimes for POSIX systems with *libbacktrace* and for Windows.
         Make sure that your platform is supported by running some tests.
]

The library provides a way to get stacktrace from an exception as if the
stacktrace was captured at the point the exception was thrown. Works even if
the exception was thrown from a third party binary library.

Link with
`boost_stacktrace_from_exception` library (or just `LD_PRELOAD` it!) and call
boost::stacktrace::stacktrace::from_current_exception() to get the trace:

```
#include <iostream>
#include <stdexcept>
#include <string_view>
#include <boost/stacktrace.hpp>

void foo(std::string_view key);
void bar(std::string_view key);

int main() {
  try {
    foo("test1");
    bar("test2");
  } catch (const std::exception& exc) {
    boost::stacktrace::stacktrace trace = boost::stacktrace::stacktrace::from_current_exception();  // <---
    std::cerr << "Caught exception: " << exc.what() << ", trace:\n" << trace;
  }
}
```

The output of the above sample may be the following:

```
Caught exception: std::map::at, trace:
 0# get_data_from_config(std::string_view) at /home/axolm/basic.cpp:600
 1# bar(std::string_view) at /home/axolm/basic.cpp:6
 2# main at /home/axolm/basic.cpp:17
```

With the above technique a developer can locate the source file and the function
that has thrown the exception without a debugger help. it is especially useful
for testing in containers (github CI, other CIs), where the developer has no
direct access to the testing environment and reproducing the issue is
complicated.

Note that linking with `boost_stacktrace_from_exception` may increase memory
consumption of the application, as the exceptions now additionally store traces.

At runtime switch boost::stacktrace::this_thread::set_capture_stacktraces_at_throw()
allows to disable/enable capturing and storing traces in exceptions.

To disable the `boost_stacktrace_from_exception` library builds the
`boost.stacktrace.from_exception=off` option, for example
`./b2 boost.stacktrace.from_exception=off`.

[endsect]


[section Exceptions with stacktrace]

You can provide more information along with exception by embedding stacktraces into the exception. There are many ways to do that, here's how to do that using Boost.Exception:

* Declare a `boost::error_info` typedef that holds the stacktrace:

[getting_started_class_traced]

* Write a helper class for throwing any exception with stacktrace:

[getting_started_class_with_trace]

* Use `throw_with_trace(E);` instead of just `throw E;`:

[getting_started_throwing_with_trace]

* Process exceptions:

[getting_started_catching_trace]

Code from above will output:

```
'i' must not be greater than zero in oops()
 0# void throw_with_trace<std::logic_error>(std::logic_error const&) at ../example/throwing_st.cpp:22
 1# oops(int) at ../example/throwing_st.cpp:38
 2# bar(int) at ../example/throwing_st.cpp:54
 3# foo(int) at ../example/throwing_st.cpp:59
 4# bar(int) at ../example/throwing_st.cpp:49
 5# foo(int) at ../example/throwing_st.cpp:59
 6# main at ../example/throwing_st.cpp:76
 7# 0x00007FAC113BEF45 in /lib/x86_64-linux-gnu/libc.so.6
 8# 0x0000000000402ED9
```

[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 achieved.

Just define *BOOST_STACKTRACE_LINK* for a whole project. Now you can enable/disable stacktraces by just linking with different libraries:

* link with `boost_stacktrace_noop` to disable backtracing
* link with other `boost_stacktrace_*` libraries


See [link stacktrace.configuration_and_build section "Configuration and Build"] 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:

```
0x7fbcfd17f6b5,0x400d4a,0x400d61,0x400d61,0x400d61,0x400d61,0x400d77,0x400cbf,0x400dc0,0x7fbcfc82d830,0x400a79,
```

[endsect]

[section Getting function information from pointer]

[classref boost::stacktrace::frame] provides information about functions. You may construct that class from function pointer and get the function name at runtime:

[getting_started_debug_function]

Code from above will output:

```
my_signal_handler(int) at boost/libs/stacktrace/example/debug_function.cpp:21
```

[endsect]

[section Global control over stacktrace output format]

You may override the behavior of default stacktrace output operator by defining the macro from Boost.Config [macroref BOOST_USER_CONFIG] to point to a file like following:

[getting_started_user_config]

Implementation of `do_stream_st` may be the following:

[getting_started_user_config_impl]

Code from above will output:

```
Terminate called:
 0# bar(int)
 1# foo(int)
 2# bar(int)
 3# foo(int)
```

[endsect]


[endsect]

[section Configuration and Build]

By default Boost.Stacktrace is a header-only library, but you may change that and use the following macros to improve build times or to be able to tune library 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 implementation. 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 implementation.]]
]


In header only mode library could be tuned by macro. If one of the link macro from above is defined, you have to manually link with one of the libraries:
[table:libconfig Config
    [[Macro name or default] [Library] [Effect] [Platforms] [Uses debug information [footnote This will provide more readable backtraces with *source code locations* 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. Compiling with `-rdynamic` flag, without `-fvisibility=hidden` or marking functions as exported produce a better stacktraces.]] ]
    [[['default for MSVC, Intel on Windows, MinGW-w64] / *BOOST_STACKTRACE_USE_WINDBG*] [*boost_stacktrace_windbg*] [ Uses `dbgeng.h` to show debug info, stores the implementation internals in a static varaible protected with mutex. May require linking with *ole32* and *dbgeng*. ] [MSVC, MinGW-w64, Intel on Windows] [yes] [no]]
    [[['default for other platforms]] [*boost_stacktrace_basic*] [Uses compiler intrinsics to collect stacktrace and if possible `::dladdr` to show information about the symbol. Requires linking with *libdl* library on POSIX platforms.] [Any compiler on POSIX or MinGW] [no] [yes]]
    [[*BOOST_STACKTRACE_USE_WINDBG_CACHED*] [*boost_stacktrace_windbg_cached*] [ Uses `dbgeng.h` to show debug info and caches implementation internals in TLS for better performance. Useful only for cases when traces are gathered very often. May require linking with *ole32* and *dbgeng*. ] [MSVC, Intel on Windows] [yes] [no]]
    [[*BOOST_STACKTRACE_USE_BACKTRACE*] [*boost_stacktrace_backtrace*] [Requires linking with *libdl* on POSIX and *libbacktrace* libraries[footnote Some *libbacktrace* packages SEGFAULT if there's a concurrent work with the same `backtrace_state` instance. To avoid that issue the Boost.Stacktrace library uses `thread_local` states, unfortunately this may consume a lot of memory if you often create and destroy execution threads in your application. Define *BOOST_STACKTRACE_BACKTRACE_FORCE_STATIC* to force single instance, but make sure that [@https://github.com/boostorg/stacktrace/blob/develop/test/thread_safety_checking.cpp thread_safety_checking.cpp] works well in your setup. ]. *libbacktrace* is probably already installed in your system[footnote If you are using Clang with libstdc++ you could get into troubles of including `<backtrace.h>`, because on some platforms Clang does not search for headers in the GCC's include paths and any attempt to add GCC's include path leads to linker errors. To explicitly specify a path to the `<backtrace.h>` header you could define the *BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE* to a full path to the header. For example on Ubuntu Xenial use the command line option *-DBOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE=</usr/lib/gcc/x86_64-linux-gnu/5/include/backtrace.h>* while building with Clang. ], or built into your compiler.

     Otherwise (if you are a *MinGW*/*MinGW-w64* user for example) it can be downloaded [@https://github.com/ianlancetaylor/libbacktrace from here] or [@https://github.com/gcc-mirror/gcc/tree/master/libbacktrace from here]. ] [Any compiler on POSIX, or MinGW, or MinGW-w64] [yes] [yes]]
    [[*BOOST_STACKTRACE_USE_ADDR2LINE*] [*boost_stacktrace_addr2line*] [Use *addr2line* program to retrieve stacktrace. Requires linking with *libdl* library and `::fork` system call. Macro *BOOST_STACKTRACE_ADDR2LINE_LOCATION* must be defined to the absolute path to the addr2line executable if it is not located in /usr/bin/addr2line. ] [Any compiler on POSIX] [yes] [yes]]
    [[*BOOST_STACKTRACE_USE_NOOP*] [*boost_stacktrace_noop*] [Use this if you wish to disable backtracing. `stacktrace::size()` with that macro always returns 0. ] [All] [no] [no]]
]

[*Examples:]

* if you wish to switch to more powerful implementation on Clang/MinGW and *BOOST_STACKTRACE_LINK* is defined, you just need link with "*-lboost_stacktrace_backtrace -ldl -lbacktrace*" or "*-lboost_stacktrace_addr2line -ldl*"
* if you wish to disable backtracing and *BOOST_STACKTRACE_LINK* is defined, you just need link with *-lboost_stacktrace_noop*
* if you wish to disable backtracing and you use the library in header only mode, you just need to define *BOOST_STACKTRACE_USE_NOOP* for the whole project and recompile it

[section MinGW and MinGW-w64 specific notes]

MinGW-w64 and MinGW (without -w64) users have to install libbacktrace for getting better stacktraces. Follow the instruction:

Let's assume that you've installed MinGW into C:\MinGW and downloaded [@https://github.com/ianlancetaylor/libbacktrace libbacktrace sources] into C:\libbacktrace-master

* Configure & build libbacktrace from console:
    * C:\MinGW\msys\1.0\bin\sh.exe
    * cd /c/libbacktrace-master
    * ./configure CC=/c/MinGW/bin/gcc.exe  CXX=/c/MinGW/bin/g++.exe
    * make
    * ./libtool --mode=install /usr/bin/install -c libbacktrace.la '/c/libbacktrace-master'
* Add info to the project-config.jam in the Boost folder:
    * using gcc : 6 : "C:\\MinGW\\bin\\g++.exe" : <compileflags>-I"C:\\libbacktrace-master\\" <linkflags>-L"C:\\libbacktrace-master\\" ; 
* Now you can use a header only version by defining *BOOST_STACKTRACE_USE_BACKTRACE* for your project or build the stacktrace library from Boost folder:
    * b2.exe toolset=gcc-6 --with-stacktrace

[endsect]

[section Windows deployment and symbol files]

Function names may not be resolved after deployment of your application to a different system.

There are multiple ways to deal with that issue if you distribute PDB files along with your application:

* Link your application and shared libraries with a properly set `/PDBALTPATH` flag, for example `/PDBALTPATH:%_PDB%`. See [@https://docs.microsoft.com/en-us/cpp/build/reference/pdbaltpath-use-alternate-pdb-path official documentation for more info].
* Set the `_NT_ALT_SYMBOL_PATH` or `_NT_SYMBOL_PATH` environment variables of the target system to the path of the PDBs. See [@https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/symbol-path#controlling-the-symbol-path official documentation for more info].

[endsect]

[endsect]

[section Theoretical async signal safety]

In theory, walking the stack without decoding and demangling should be async signal safe.

In practice, it is not:

* Looks like a page fault while dumping the trace on a containerized/virtualized
  Windows system has a chance to deadlock. Page fault could happen easily
  as we have to write the dump either to memory or to a file.
* On POSIX systems a deadlock could happen if a signal is received when throwing
  an exception [@https://github.com/boostorg/stacktrace/issues/131 #131].
  Theoretically this could be worked around by bypassing the mutex locking
  in C++-runtime at exception throw
  ([@https://github.com/userver-framework/userver/blob/4246909c99506d3ab34bd130a5154b4acc8e87de/core/src/engine/task/exception_hacks.cpp#L241-L244 sample implementation]
  in the 🐙 userver framework), or by using a very modern runtime
  (glibc-2.35+ with [@https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71744#c32 modern]
  libgcc or [@https://reviews.llvm.org/D130668 modern] LLVM's libunwind).
* `-fomit-frame-pointer` like flags add additional complexity to the stack
  walking implementation, which may also negatively affect the signal safety.

As a rule of thumb: do *not* capture stack traces in signal handlers unless you
are absolutely sure in your environment and inspected all of its source codes.


[endsect]

[section Acknowledgments]

In order of helping and advising:

* Great thanks to Bjorn Reese for highlighting the async-signal-safety issues.
* Great thanks to Nat Goodspeed for requesting [classref boost::stacktrace::frame] like class.
* Great thanks to Niall Douglas for making an initial review, helping with some platforms and giving great hints on library design.
* Great thanks to all the library reviewers.
* Great thanks to Andrei Nekrashevich for prototyping the idea of stacktraces
  from arbitrary exception in `libsfe`.

[endsect]

[xinclude autodoc.xml]

